rttable-default: (from otti) configures a new policy routing table for the default...
[olsrd.git] / src / linux / kernel_routes.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto@olsr.org)
4  * Copyright (c) 2007, Sven-Ola for the policy routing stuff
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without 
8  * modification, are permitted provided that the following conditions 
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright 
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright 
14  *   notice, this list of conditions and the following disclaimer in 
15  *   the documentation and/or other materials provided with the 
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its 
18  *   contributors may be used to endorse or promote products derived 
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 #include "kernel_routes.h"
43 #include "ipc_frontend.h"
44
45 #if !LINUX_POLICY_ROUTING
46 #include "log.h"
47
48 static int delete_all_inet_gws(void);
49
50 #else /* !LINUX_POLICY_ROUTING */
51
52 #include <assert.h>
53 #include <linux/types.h>
54 #include <linux/rtnetlink.h>
55
56 struct olsr_rtreq
57 {
58   struct nlmsghdr n;
59   struct rtmsg    r;
60   char            buf[512];
61 };
62
63 static void olsr_netlink_addreq(struct olsr_rtreq *req, int type, const void *data, int len)
64 {
65   struct rtattr *rta = (struct rtattr*)(((char*)req) + NLMSG_ALIGN(req->n.nlmsg_len));
66   req->n.nlmsg_len = NLMSG_ALIGN(req->n.nlmsg_len) + RTA_LENGTH(len);
67   assert(req->n.nlmsg_len < sizeof(struct olsr_rtreq));
68   rta->rta_type = type;
69   rta->rta_len = RTA_LENGTH(len);
70   memcpy(RTA_DATA(rta), data, len);
71 }
72
73 static int olsr_netlink_route(const struct rt_entry *rt, olsr_u8_t family, olsr_u8_t rttable, __u16 cmd)
74 {
75   int ret = 0;
76   struct olsr_rtreq req;
77   struct iovec iov;
78   struct sockaddr_nl nladdr;
79   struct msghdr msg =
80   {
81     &nladdr,
82     sizeof(nladdr),
83     &iov,
84     1,
85     NULL,
86     0,
87     0
88   };
89   olsr_u32_t metric = FIBM_FLAT != olsr_cnf->fib_metric ? (RTM_NEWROUTE == cmd ?
90     rt->rt_best->rtp_metric.hops : rt->rt_metric.hops): RT_METRIC_DEFAULT;
91   const struct rt_nexthop* nexthop = (RTM_NEWROUTE == cmd) ?
92     &rt->rt_best->rtp_nexthop : &rt->rt_nexthop;
93
94   memset(&req, 0, sizeof(req));
95   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
96   req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
97   req.n.nlmsg_type = cmd;
98   req.r.rtm_family = family;
99   req.r.rtm_table = rttable;
100   req.r.rtm_protocol = RTPROT_BOOT;
101   req.r.rtm_scope = RT_SCOPE_LINK;
102   req.r.rtm_type = RTN_UNICAST;
103   req.r.rtm_dst_len = rt->rt_dst.prefix_len;
104
105   if (AF_INET == family)
106   {
107     if (rt->rt_dst.prefix.v4.s_addr != nexthop->gateway.v4.s_addr)
108     {
109       olsr_netlink_addreq(&req, RTA_GATEWAY, &nexthop->gateway.v4, sizeof(nexthop->gateway.v4));
110       req.r.rtm_scope = RT_SCOPE_UNIVERSE;
111     }
112     olsr_netlink_addreq(&req, RTA_DST, &rt->rt_dst.prefix.v4, sizeof(rt->rt_dst.prefix.v4));
113   }
114   else
115   {
116     if (0 != memcmp(&rt->rt_dst.prefix.v6, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6)))
117     {
118       olsr_netlink_addreq(&req, RTA_GATEWAY, &nexthop->gateway.v6, sizeof(nexthop->gateway.v6));
119       req.r.rtm_scope = RT_SCOPE_UNIVERSE;
120     }
121     olsr_netlink_addreq(&req, RTA_DST, &rt->rt_dst.prefix.v6, sizeof(rt->rt_dst.prefix.v6));
122   }
123   if (FIBM_APPROX != olsr_cnf->fib_metric || RTM_NEWROUTE == cmd)
124   {
125     olsr_netlink_addreq(&req, RTA_PRIORITY, &metric, sizeof(metric));
126   }
127   olsr_netlink_addreq(&req, RTA_OIF, &nexthop->iif_index, sizeof(nexthop->iif_index));
128   iov.iov_base = &req.n;
129   iov.iov_len = req.n.nlmsg_len;
130   memset(&nladdr, 0, sizeof(nladdr));
131   nladdr.nl_family = AF_NETLINK;
132   if (0 <= (ret = sendmsg(olsr_cnf->rtnl_s, &msg, 0)))
133   {
134     iov.iov_base = req.buf;
135     iov.iov_len = sizeof(req.buf);
136     if (0 < (ret = recvmsg(olsr_cnf->rtnl_s, &msg, 0)))
137     {
138       struct nlmsghdr* h = (struct nlmsghdr*)req.buf;
139       while (NLMSG_OK(h, (unsigned int)ret)) {
140         if (NLMSG_DONE == h->nlmsg_type) break;
141         if (NLMSG_ERROR == h->nlmsg_type)
142         {
143           if (NLMSG_LENGTH(sizeof(struct nlmsgerr) <= h->nlmsg_len))
144           {
145             struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
146             errno = -l_err->error;
147             if (0 != errno) ret = -1;
148           }
149           break;
150         }
151         h = NLMSG_NEXT(h, ret);
152       }
153     }
154     if (0 <= ret && olsr_cnf->ipc_connections > 0)
155     {
156       ipc_route_send_rtentry(
157         &rt->rt_dst.prefix,
158         &nexthop->gateway,
159         metric,
160         RTM_NEWROUTE == cmd,
161         if_ifwithindex_name(nexthop->iif_index));
162     }
163   }
164   return ret;
165 }
166 #endif /* LINUX_POLICY_ROUTING */
167
168 /**
169  * Insert a route in the kernel routing table
170  *
171  * @param destination the route to add
172  *
173  * @return negative on error
174  */
175 int
176 olsr_ioctl_add_route(const struct rt_entry *rt)
177 {
178 #if !LINUX_POLICY_ROUTING
179   struct rtentry kernel_route;
180   union olsr_ip_addr mask;
181   int rslt;
182 #endif /* LINUX_POLICY_ROUTING */
183
184   OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
185
186 #if !LINUX_POLICY_ROUTING
187   memset(&kernel_route, 0, sizeof(struct rtentry));
188
189   ((struct sockaddr_in*)&kernel_route.rt_dst)->sin_family = AF_INET;
190   ((struct sockaddr_in*)&kernel_route.rt_gateway)->sin_family = AF_INET;
191   ((struct sockaddr_in*)&kernel_route.rt_genmask)->sin_family = AF_INET;
192
193   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr = rt->rt_dst.prefix.v4;
194
195   if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
196     return -1;
197   }
198   ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr = mask.v4;
199
200   if (rt->rt_dst.prefix.v4.s_addr != rt->rt_best->rtp_nexthop.gateway.v4.s_addr) {
201     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr =
202       rt->rt_best->rtp_nexthop.gateway.v4;
203   }
204
205   kernel_route.rt_flags = olsr_rt_flags(rt);
206   kernel_route.rt_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
207
208   /*
209    * Set interface
210    */
211   kernel_route.rt_dev = if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index);
212
213   /* delete existing default route before ? */
214   if((olsr_cnf->del_gws) &&
215      (rt->rt_dst.prefix.v4.s_addr == INADDR_ANY) &&
216      (rt->rt_dst.prefix_len == INADDR_ANY)) {
217     delete_all_inet_gws();
218     olsr_cnf->del_gws = OLSR_FALSE;
219   }
220
221   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
222
223     /*
224      * Send IPC route update message
225      */
226     ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway,
227                            rt->rt_best->rtp_metric.hops, 1,
228                            if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
229   }
230
231   return rslt;
232 #else /* !LINUX_POLICY_ROUTING */
233   if (0 == olsr_cnf->rttable_default && 0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable)
234   {
235     /*
236      * Users start whining about not having internet with policy
237      * routing activated and no static default route in table 254.
238      * We maintain a fallback defroute in the default=253 table.
239      */
240     olsr_netlink_route(rt, AF_INET, 253, RTM_NEWROUTE);
241   }
242   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0)
243     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable_default, RTM_NEWROUTE);
244   else
245     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_NEWROUTE);
246 #endif /* LINUX_POLICY_ROUTING */
247 }
248
249
250 /**
251  *Insert a route in the kernel routing table
252  *
253  *@param destination the route to add
254  *
255  *@return negative on error
256  */
257 int
258 olsr_ioctl_add_route6(const struct rt_entry *rt)
259 {
260 #if !LINUX_POLICY_ROUTING
261   struct in6_rtmsg kernel_route;
262   int rslt;
263
264   OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
265
266   memset(&kernel_route, 0, sizeof(struct in6_rtmsg));
267
268   kernel_route.rtmsg_dst     = rt->rt_dst.prefix.v6;
269   kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
270
271   kernel_route.rtmsg_gateway = rt->rt_best->rtp_nexthop.gateway.v6;
272
273   kernel_route.rtmsg_flags = olsr_rt_flags(rt);
274   kernel_route.rtmsg_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
275   
276   /*
277    * set interface
278    */
279   kernel_route.rtmsg_ifindex = rt->rt_best->rtp_nexthop.iif_index;
280   
281   /* XXX delete 0/0 route before ? */
282
283   if((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
284
285     /*
286      * Send IPC route update message
287      */
288     ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway, 
289                            rt->rt_best->rtp_metric.hops, 1,
290                            if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
291   }
292
293   return rslt;
294 #else /* !LINUX_POLICY_ROUTING */
295   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0)
296     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable_default, RTM_NEWROUTE);
297   else
298     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_NEWROUTE);
299 #endif /* LINUX_POLICY_ROUTING */
300 }
301
302
303 /**
304  *Remove a route from the kernel
305  *
306  *@param destination the route to remove
307  *
308  *@return negative on error
309  */
310 int
311 olsr_ioctl_del_route(const struct rt_entry *rt)
312 {
313 #if !LINUX_POLICY_ROUTING
314   struct rtentry kernel_route;
315   union olsr_ip_addr mask;
316   int rslt;
317 #endif /* LINUX_POLICY_ROUTING */
318
319   OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
320
321 #if !LINUX_POLICY_ROUTING
322   memset(&kernel_route,0,sizeof(struct rtentry));
323
324   ((struct sockaddr_in*)&kernel_route.rt_dst)->sin_family = AF_INET;
325   ((struct sockaddr_in*)&kernel_route.rt_gateway)->sin_family = AF_INET;
326   ((struct sockaddr_in*)&kernel_route.rt_genmask)->sin_family = AF_INET;
327
328   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr = rt->rt_dst.prefix.v4;
329
330   if (rt->rt_dst.prefix.v4.s_addr != rt->rt_nexthop.gateway.v4.s_addr) {
331     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr = rt->rt_nexthop.gateway.v4;
332   }
333
334   if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
335     return -1;
336   } else {
337     ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr = mask.v4;
338   }
339
340   kernel_route.rt_flags = olsr_rt_flags(rt);
341   kernel_route.rt_metric = olsr_fib_metric(&rt->rt_metric.hops);
342
343   /*
344    * Set interface
345    */
346   kernel_route.rt_dev = NULL;
347
348   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route)) >= 0) {
349
350     /*
351      * Send IPC route update message
352      */
353     ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
354   }
355
356   return rslt;
357 #else /* !LINUX_POLICY_ROUTING */
358   if (0 == olsr_cnf->rttable_default && 0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable)
359   {
360     /*
361      * Also remove the fallback default route
362      */
363     olsr_netlink_route(rt, AF_INET, 253, RTM_DELROUTE);
364   }
365   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0 )
366     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable_default, RTM_DELROUTE);
367   else
368     return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_DELROUTE);
369 #endif /* LINUX_POLICY_ROUTING */
370 }
371
372
373 /**
374  *Remove a route from the kernel
375  *
376  *@param destination the route to remove
377  *
378  *@return negative on error
379  */
380 int
381 olsr_ioctl_del_route6(const struct rt_entry *rt)
382 {
383 #if !LINUX_POLICY_ROUTING
384   struct in6_rtmsg kernel_route;
385   int rslt;
386 #endif /* LINUX_POLICY_ROUTING */
387
388   OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
389
390 #if !LINUX_POLICY_ROUTING
391   memset(&kernel_route,0,sizeof(struct in6_rtmsg));
392
393
394   kernel_route.rtmsg_dst     = rt->rt_dst.prefix.v6;
395   kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
396
397   kernel_route.rtmsg_gateway = rt->rt_best->rtp_nexthop.gateway.v6;
398
399   kernel_route.rtmsg_flags = olsr_rt_flags(rt);
400   kernel_route.rtmsg_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
401
402   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route) >= 0)) {
403
404     /*
405      * Send IPC route update message
406      */
407     ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
408   }
409
410   return rslt;
411 #else /* !LINUX_POLICY_ROUTING */
412   if (0 == rt->rt_dst.prefix_len && olsr_cnf->rttable_default != 0)
413     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable_default, RTM_DELROUTE);
414   else
415     return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_DELROUTE);
416 #endif /* LINUX_POLICY_ROUTING */
417 }
418
419 #if !LINUX_POLICY_ROUTING
420 static int delete_all_inet_gws(void)
421 {  
422   int s;
423   char buf[BUFSIZ], *cp, *cplim;
424   struct ifconf ifc;
425   struct ifreq *ifr;
426   
427   OLSR_PRINTF(1, "Internet gateway detected...\nTrying to delete default gateways\n");
428   
429   /* Get a socket */
430   if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
431     {
432       olsr_syslog(OLSR_LOG_ERR, "socket: %m");
433       return -1;
434     }
435   
436   ifc.ifc_len = sizeof (buf);
437   ifc.ifc_buf = buf;
438   if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) 
439     {
440       olsr_syslog(OLSR_LOG_ERR, "ioctl (get interface configuration)");
441       close(s);
442       return -1;
443     }
444
445   ifr = ifc.ifc_req;
446   cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
447   for (cp = buf; cp < cplim; cp += sizeof (ifr->ifr_name) + sizeof(ifr->ifr_addr)) 
448     {
449       struct rtentry kernel_route;
450       ifr = (struct ifreq *)cp;
451       
452       
453       if(strcmp(ifr->ifr_ifrn.ifrn_name, "lo") == 0)
454         {
455           OLSR_PRINTF(1, "Skipping loopback...\n");
456           continue;
457         }
458
459       OLSR_PRINTF(1, "Trying 0.0.0.0/0 %s...", ifr->ifr_ifrn.ifrn_name);
460       
461       
462       memset(&kernel_route,0,sizeof(struct rtentry));
463       
464       ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr.s_addr = 0;
465       ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_family=AF_INET;
466       ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr.s_addr = 0;
467       ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_family=AF_INET;
468
469       ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr.s_addr = INADDR_ANY;
470       ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_family=AF_INET;
471       
472
473       kernel_route.rt_flags = RTF_UP | RTF_GATEWAY;
474            
475       kernel_route.rt_dev = ifr->ifr_ifrn.ifrn_name;
476
477       if((ioctl(s, SIOCDELRT, &kernel_route)) < 0)
478          OLSR_PRINTF(1, "NO\n");
479       else
480          OLSR_PRINTF(1, "YES\n");
481     }  
482   close(s);
483   return 0;       
484 }
485 #endif /* LINUX_POLICY_ROUTING */
486
487 /*
488  * Local Variables:
489  * c-basic-offset: 2
490  * End:
491  */