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