/*
* The olsr.org Optimized Link-State Routing daemon(olsrd)
* Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
* Copyright (c) 2007, Sven-Ola for the policy routing stuff
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of olsr.org, olsrd nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Visit http://www.olsr.org for more information.
*
* If you find this software useful feel free to make a donation
* to the project. For more information see the website or contact
* the copyright holders.
*
* $Id: kernel_routes.c,v 1.29 2007/10/13 12:31:04 bernd67 Exp $
*/
#include "kernel_routes.h"
#include "ipc_frontend.h"
#if !LINUX_POLICY_ROUTING
#include "log.h"
static int delete_all_inet_gws(void);
#else /* !LINUX_POLICY_ROUTING */
#include <assert.h>
#include <linux/types.h>
#include <linux/rtnetlink.h>
struct olsr_rtreq
{
struct nlmsghdr n;
struct rtmsg r;
char buf[512];
};
static void olsr_netlink_addreq(struct olsr_rtreq *req, int type, void *data, int len)
{
struct rtattr *rta = (struct rtattr*)(((char*)req) + NLMSG_ALIGN(req->n.nlmsg_len));
req->n.nlmsg_len = NLMSG_ALIGN(req->n.nlmsg_len) + RTA_LENGTH(len);
assert(req->n.nlmsg_len < sizeof(struct olsr_rtreq));
rta->rta_type = type;
rta->rta_len = RTA_LENGTH(len);
memcpy(RTA_DATA(rta), data, len);
}
static int olsr_netlink_route(struct rt_entry *rt, olsr_u8_t family, olsr_u8_t rttable, __u16 cmd)
{
int ret = 0;
struct olsr_rtreq req;
struct iovec iov;
struct sockaddr_nl nladdr;
struct msghdr msg =
{
&nladdr,
sizeof(nladdr),
&iov,
1,
NULL,
0,
0
};
olsr_u32_t metric = 1;
struct rt_nexthop* nexthop = (RTM_NEWROUTE == cmd) ?
&rt->rt_best->rtp_nexthop : &rt->rt_nexthop;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
req.n.nlmsg_type = cmd;
req.r.rtm_family = family;
req.r.rtm_table = rttable;
req.r.rtm_protocol = RTPROT_BOOT;
req.r.rtm_scope = RT_SCOPE_LINK;
req.r.rtm_type = RTN_UNICAST;
req.r.rtm_dst_len = rt->rt_dst.prefix_len;
if (AF_INET == family)
{
if (rt->rt_dst.prefix.v4 != nexthop->gateway.v4)
{
olsr_netlink_addreq(&req, RTA_GATEWAY, &nexthop->gateway.v4, sizeof(nexthop->gateway.v4));
req.r.rtm_scope = RT_SCOPE_UNIVERSE;
metric = RT_METRIC_DEFAULT;
}
olsr_netlink_addreq(&req, RTA_DST, &rt->rt_dst.prefix.v4, sizeof(rt->rt_dst.prefix.v4));
}
else
{
if (0 != memcmp(&rt->rt_dst.prefix.v6, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6)))
{
olsr_netlink_addreq(&req, RTA_GATEWAY, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6));
req.r.rtm_scope = RT_SCOPE_UNIVERSE;
metric = RT_METRIC_DEFAULT;
}
olsr_netlink_addreq(&req, RTA_DST, &rt->rt_dst.prefix.v6, sizeof(rt->rt_dst.prefix.v6));
}
//!!!olsr_netlink_addreq(&req, RTA_PRIORITY, &rt->rt_best->rtp_metric.hops, sizeof(rt->rt_best->rtp_metric.hops));
olsr_netlink_addreq(&req, RTA_PRIORITY, &metric, sizeof(metric));
olsr_netlink_addreq(&req, RTA_OIF, &nexthop->iif_index, sizeof(nexthop->iif_index));
iov.iov_base = &req.n;
iov.iov_len = req.n.nlmsg_len;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
if (0 <= (ret = sendmsg(olsr_cnf->rtnl_s, &msg, 0)))
{
iov.iov_base = req.buf;
iov.iov_len = sizeof(req.buf);
if (0 < (ret = recvmsg(olsr_cnf->rtnl_s, &msg, 0)))
{
struct nlmsghdr* h = (struct nlmsghdr*)req.buf;
while (NLMSG_OK(h, (unsigned int)ret)) {
if (NLMSG_DONE == h->nlmsg_type) break;
if (NLMSG_ERROR == h->nlmsg_type)
{
if (NLMSG_LENGTH(sizeof(struct nlmsgerr) <= h->nlmsg_len))
{
struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
errno = -l_err->error;
if (0 != errno) ret = -1;
}
break;
}
h = NLMSG_NEXT(h, ret);
}
}
if (0 <= ret && olsr_cnf->open_ipc)
{
ipc_route_send_rtentry(
&rt->rt_dst.prefix,
&nexthop->gateway,
metric,
RTM_NEWROUTE == cmd,
if_ifwithindex_name(nexthop->iif_index));
}
}
return ret;
}
#endif /* LINUX_POLICY_ROUTING */
/**
* Insert a route in the kernel routing table
*
* @param destination the route to add
*
* @return negative on error
*/
int
olsr_ioctl_add_route(struct rt_entry *rt)
{
#if !LINUX_POLICY_ROUTING
struct rtentry kernel_route;
union olsr_ip_addr mask;
int rslt;
#endif /* LINUX_POLICY_ROUTING */
OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
#if !LINUX_POLICY_ROUTING
memset(&kernel_route, 0, sizeof(struct rtentry));
((struct sockaddr_in*)&kernel_route.rt_dst)->sin_family = AF_INET;
((struct sockaddr_in*)&kernel_route.rt_gateway)->sin_family = AF_INET;
((struct sockaddr_in*)&kernel_route.rt_genmask)->sin_family = AF_INET;
((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr.s_addr =
rt->rt_dst.prefix.v4;
if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
return -1;
}
((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr.s_addr = mask.v4;
if (rt->rt_dst.prefix.v4 != rt->rt_best->rtp_nexthop.gateway.v4) {
((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr.s_addr =
rt->rt_best->rtp_nexthop.gateway.v4;
}
kernel_route.rt_flags = olsr_rt_flags(rt);
kernel_route.rt_metric = RT_METRIC_DEFAULT;
/*
* Set interface
*/
kernel_route.rt_dev = if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index);
/* delete existing default route before ? */
if((olsr_cnf->del_gws) &&
(rt->rt_dst.prefix.v4 == INADDR_ANY) &&
(rt->rt_dst.prefix_len == INADDR_ANY)) {
delete_all_inet_gws();
olsr_cnf->del_gws = OLSR_FALSE;
}
if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
/*
* Send IPC route update message
*/
ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway,
rt->rt_best->rtp_metric.hops, 1,
if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
}
return rslt;
#else /* !LINUX_POLICY_ROUTING */
if (0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable)
{
/*
* Users start whining about not having internet with policy
* routing activated and no static default route in table 254.
* We maintain a fallback defroute in the default=253 table.
*/
olsr_netlink_route(rt, AF_INET, 253, RTM_NEWROUTE);
}
return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_NEWROUTE);
#endif /* LINUX_POLICY_ROUTING */
}
/**
*Insert a route in the kernel routing table
*
*@param destination the route to add
*
*@return negative on error
*/
int
olsr_ioctl_add_route6(struct rt_entry *rt)
{
#if !LINUX_POLICY_ROUTING
struct in6_rtmsg kernel_route;
int rslt;
OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
memset(&kernel_route, 0, sizeof(struct in6_rtmsg));
COPY_IP(&kernel_route.rtmsg_dst, &rt->rt_dst.prefix);
kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
COPY_IP(&kernel_route.rtmsg_gateway, &rt->rt_best->rtp_nexthop.gateway);
kernel_route.rtmsg_flags = olsr_rt_flags(rt);
kernel_route.rtmsg_metric = RT_METRIC_DEFAULT;
/*
* set interface
*/
kernel_route.rtmsg_ifindex = rt->rt_best->rtp_nexthop.iif_index;
/* XXX delete 0/0 route before ? */
if((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
/*
* Send IPC route update message
*/
ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway,
rt->rt_best->rtp_metric.hops, 1,
if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
}
return rslt;
#else /* !LINUX_POLICY_ROUTING */
return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_NEWROUTE);
#endif /* LINUX_POLICY_ROUTING */
}
/**
*Remove a route from the kernel
*
*@param destination the route to remove
*
*@return negative on error
*/
int
olsr_ioctl_del_route(struct rt_entry *rt)
{
#if !LINUX_POLICY_ROUTING
struct rtentry kernel_route;
union olsr_ip_addr mask;
int rslt;
#endif /* LINUX_POLICY_ROUTING */
OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
#if !LINUX_POLICY_ROUTING
memset(&kernel_route,0,sizeof(struct rtentry));
((struct sockaddr_in*)&kernel_route.rt_dst)->sin_family = AF_INET;
((struct sockaddr_in*)&kernel_route.rt_gateway)->sin_family = AF_INET;
((struct sockaddr_in*)&kernel_route.rt_genmask)->sin_family = AF_INET;
((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr.s_addr =
rt->rt_dst.prefix.v4;
if (rt->rt_dst.prefix.v4 != rt->rt_nexthop.gateway.v4) {
((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr.s_addr =
rt->rt_nexthop.gateway.v4;
}
if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
return -1;
} else {
((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr.s_addr = mask.v4;
}
kernel_route.rt_flags = olsr_rt_flags(rt);
kernel_route.rt_metric = RT_METRIC_DEFAULT;
/*
* Set interface
*/
kernel_route.rt_dev = NULL;
if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route)) >= 0) {
/*
* Send IPC route update message
*/
ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
}
return rslt;
#else /* !LINUX_POLICY_ROUTING */
if (0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable)
{
/*
* Also remove the fallback default route
*/
olsr_netlink_route(rt, AF_INET, 253, RTM_DELROUTE);
}
return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_DELROUTE);
#endif /* LINUX_POLICY_ROUTING */
}
/**
*Remove a route from the kernel
*
*@param destination the route to remove
*
*@return negative on error
*/
int
olsr_ioctl_del_route6(struct rt_entry *rt)
{
#if !LINUX_POLICY_ROUTING
struct in6_rtmsg kernel_route;
int rslt;
#endif /* LINUX_POLICY_ROUTING */
OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
#if !LINUX_POLICY_ROUTING
memset(&kernel_route,0,sizeof(struct in6_rtmsg));
COPY_IP(&kernel_route.rtmsg_dst, &rt->rt_dst.prefix);
kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
COPY_IP(&kernel_route.rtmsg_gateway, &rt->rt_best->rtp_nexthop.gateway);
kernel_route.rtmsg_flags = olsr_rt_flags(rt);
kernel_route.rtmsg_metric = RT_METRIC_DEFAULT;
if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route) >= 0)) {
/*
* Send IPC route update message
*/
ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
}
return rslt;
#else /* !LINUX_POLICY_ROUTING */
return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_DELROUTE);
#endif /* LINUX_POLICY_ROUTING */
}
#if !LINUX_POLICY_ROUTING
static int delete_all_inet_gws(void)
{
int s;
char buf[BUFSIZ], *cp, *cplim;
struct ifconf ifc;
struct ifreq *ifr;
OLSR_PRINTF(1, "Internet gateway detected...\nTrying to delete default gateways\n");
/* Get a socket */
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
olsr_syslog(OLSR_LOG_ERR, "socket: %m");
return -1;
}
ifc.ifc_len = sizeof (buf);
ifc.ifc_buf = buf;
if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
{
olsr_syslog(OLSR_LOG_ERR, "ioctl (get interface configuration)");
close(s);
return -1;
}
ifr = ifc.ifc_req;
cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
for (cp = buf; cp < cplim; cp += sizeof (ifr->ifr_name) + sizeof(ifr->ifr_addr))
{
struct rtentry kernel_route;
ifr = (struct ifreq *)cp;
if(strcmp(ifr->ifr_ifrn.ifrn_name, "lo") == 0)
{
OLSR_PRINTF(1, "Skipping loopback...\n");
continue;
}
OLSR_PRINTF(1, "Trying 0.0.0.0/0 %s...", ifr->ifr_ifrn.ifrn_name);
memset(&kernel_route,0,sizeof(struct rtentry));
((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr.s_addr = 0;
((struct sockaddr_in *)&kernel_route.rt_dst)->sin_family=AF_INET;
((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr.s_addr = 0;
((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_family=AF_INET;
((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr.s_addr = INADDR_ANY;
((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_family=AF_INET;
kernel_route.rt_flags = RTF_UP | RTF_GATEWAY;
kernel_route.rt_dev = ifr->ifr_ifrn.ifrn_name;
if((ioctl(s, SIOCDELRT, &kernel_route)) < 0)
OLSR_PRINTF(1, "NO\n");
else
OLSR_PRINTF(1, "YES\n");
}
close(s);
return 0;
}
#endif /* LINUX_POLICY_ROUTING */
/*
* Local Variables:
* c-basic-offset: 2
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1