210c176b3acac3dc62257e3777305eda858d95d5
[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 == 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         return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_NEWROUTE);
243 #endif /* LINUX_POLICY_ROUTING */
244 }
245
246
247 /**
248  *Insert a route in the kernel routing table
249  *
250  *@param destination the route to add
251  *
252  *@return negative on error
253  */
254 int
255 olsr_ioctl_add_route6(const struct rt_entry *rt)
256 {
257 #if !LINUX_POLICY_ROUTING
258   struct in6_rtmsg kernel_route;
259   int rslt;
260
261   OLSR_PRINTF(2, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
262
263   memset(&kernel_route, 0, sizeof(struct in6_rtmsg));
264
265   kernel_route.rtmsg_dst     = rt->rt_dst.prefix.v6;
266   kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
267
268   kernel_route.rtmsg_gateway = rt->rt_best->rtp_nexthop.gateway.v6;
269
270   kernel_route.rtmsg_flags = olsr_rt_flags(rt);
271   kernel_route.rtmsg_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
272   
273   /*
274    * set interface
275    */
276   kernel_route.rtmsg_ifindex = rt->rt_best->rtp_nexthop.iif_index;
277   
278   /* XXX delete 0/0 route before ? */
279
280   if((rslt = ioctl(olsr_cnf->ioctl_s, SIOCADDRT, &kernel_route)) >= 0) {
281
282     /*
283      * Send IPC route update message
284      */
285     ipc_route_send_rtentry(&rt->rt_dst.prefix, &rt->rt_best->rtp_nexthop.gateway, 
286                            rt->rt_best->rtp_metric.hops, 1,
287                            if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
288   }
289
290   return rslt;
291 #else /* !LINUX_POLICY_ROUTING */
292         return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_NEWROUTE);
293 #endif /* LINUX_POLICY_ROUTING */
294 }
295
296
297 /**
298  *Remove a route from the kernel
299  *
300  *@param destination the route to remove
301  *
302  *@return negative on error
303  */
304 int
305 olsr_ioctl_del_route(const struct rt_entry *rt)
306 {
307 #if !LINUX_POLICY_ROUTING
308   struct rtentry kernel_route;
309   union olsr_ip_addr mask;
310   int rslt;
311 #endif /* LINUX_POLICY_ROUTING */
312
313   OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
314
315 #if !LINUX_POLICY_ROUTING
316   memset(&kernel_route,0,sizeof(struct rtentry));
317
318   ((struct sockaddr_in*)&kernel_route.rt_dst)->sin_family = AF_INET;
319   ((struct sockaddr_in*)&kernel_route.rt_gateway)->sin_family = AF_INET;
320   ((struct sockaddr_in*)&kernel_route.rt_genmask)->sin_family = AF_INET;
321
322   ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr = rt->rt_dst.prefix.v4;
323
324   if (rt->rt_dst.prefix.v4.s_addr != rt->rt_nexthop.gateway.v4.s_addr) {
325     ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr = rt->rt_nexthop.gateway.v4;
326   }
327
328   if (!olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len)) {
329     return -1;
330   } else {
331     ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr = mask.v4;
332   }
333
334   kernel_route.rt_flags = olsr_rt_flags(rt);
335   kernel_route.rt_metric = olsr_fib_metric(&rt->rt_metric.hops);
336
337   /*
338    * Set interface
339    */
340   kernel_route.rt_dev = NULL;
341
342   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route)) >= 0) {
343
344     /*
345      * Send IPC route update message
346      */
347     ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
348   }
349
350   return rslt;
351 #else /* !LINUX_POLICY_ROUTING */
352         if (0 == rt->rt_dst.prefix_len && 253 > olsr_cnf->rttable)
353         {
354                 /*
355                  * Also remove the fallback default route
356                  */
357                 olsr_netlink_route(rt, AF_INET, 253, RTM_DELROUTE);
358         }
359         return olsr_netlink_route(rt, AF_INET, olsr_cnf->rttable, RTM_DELROUTE);
360 #endif /* LINUX_POLICY_ROUTING */
361 }
362
363
364 /**
365  *Remove a route from the kernel
366  *
367  *@param destination the route to remove
368  *
369  *@return negative on error
370  */
371 int
372 olsr_ioctl_del_route6(const struct rt_entry *rt)
373 {
374 #if !LINUX_POLICY_ROUTING
375   struct in6_rtmsg kernel_route;
376   int rslt;
377 #endif /* LINUX_POLICY_ROUTING */
378
379   OLSR_PRINTF(2, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
380
381 #if !LINUX_POLICY_ROUTING
382   memset(&kernel_route,0,sizeof(struct in6_rtmsg));
383
384
385   kernel_route.rtmsg_dst     = rt->rt_dst.prefix.v6;
386   kernel_route.rtmsg_dst_len = rt->rt_dst.prefix_len;
387
388   kernel_route.rtmsg_gateway = rt->rt_best->rtp_nexthop.gateway.v6;
389
390   kernel_route.rtmsg_flags = olsr_rt_flags(rt);
391   kernel_route.rtmsg_metric = olsr_fib_metric(&rt->rt_best->rtp_metric.hops);
392
393   if ((rslt = ioctl(olsr_cnf->ioctl_s, SIOCDELRT, &kernel_route) >= 0)) {
394
395     /*
396      * Send IPC route update message
397      */
398     ipc_route_send_rtentry(&rt->rt_dst.prefix, NULL, 0, 0, NULL);
399   }
400
401   return rslt;
402 #else /* !LINUX_POLICY_ROUTING */
403         return olsr_netlink_route(rt, AF_INET6, olsr_cnf->rttable, RTM_DELROUTE);
404 #endif /* LINUX_POLICY_ROUTING */
405 }
406
407 #if !LINUX_POLICY_ROUTING
408 static int delete_all_inet_gws(void)
409 {  
410   int s;
411   char buf[BUFSIZ], *cp, *cplim;
412   struct ifconf ifc;
413   struct ifreq *ifr;
414   
415   OLSR_PRINTF(1, "Internet gateway detected...\nTrying to delete default gateways\n");
416   
417   /* Get a socket */
418   if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
419     {
420       olsr_syslog(OLSR_LOG_ERR, "socket: %m");
421       return -1;
422     }
423   
424   ifc.ifc_len = sizeof (buf);
425   ifc.ifc_buf = buf;
426   if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) 
427     {
428       olsr_syslog(OLSR_LOG_ERR, "ioctl (get interface configuration)");
429       close(s);
430       return -1;
431     }
432
433   ifr = ifc.ifc_req;
434   cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
435   for (cp = buf; cp < cplim; cp += sizeof (ifr->ifr_name) + sizeof(ifr->ifr_addr)) 
436     {
437       struct rtentry kernel_route;
438       ifr = (struct ifreq *)cp;
439       
440       
441       if(strcmp(ifr->ifr_ifrn.ifrn_name, "lo") == 0)
442         {
443           OLSR_PRINTF(1, "Skipping loopback...\n");
444           continue;
445         }
446
447       OLSR_PRINTF(1, "Trying 0.0.0.0/0 %s...", ifr->ifr_ifrn.ifrn_name);
448       
449       
450       memset(&kernel_route,0,sizeof(struct rtentry));
451       
452       ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_addr.s_addr = 0;
453       ((struct sockaddr_in *)&kernel_route.rt_dst)->sin_family=AF_INET;
454       ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_addr.s_addr = 0;
455       ((struct sockaddr_in *)&kernel_route.rt_genmask)->sin_family=AF_INET;
456
457       ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_addr.s_addr = INADDR_ANY;
458       ((struct sockaddr_in *)&kernel_route.rt_gateway)->sin_family=AF_INET;
459       
460
461       kernel_route.rt_flags = RTF_UP | RTF_GATEWAY;
462            
463       kernel_route.rt_dev = ifr->ifr_ifrn.ifrn_name;
464
465       if((ioctl(s, SIOCDELRT, &kernel_route)) < 0)
466          OLSR_PRINTF(1, "NO\n");
467       else
468          OLSR_PRINTF(1, "YES\n");
469     }  
470   close(s);
471   return 0;       
472 }
473 #endif /* LINUX_POLICY_ROUTING */
474
475 /*
476  * Local Variables:
477  * c-basic-offset: 2
478  * End:
479  */