Fix IPv6 route adds on FreeBSD and other BSDs.
[olsrd.git] / src / bsd / kernel_routes.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, the olsr.org team - see HISTORY file
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 "olsr.h"
44 #include "defs.h"
45 #include "process_routes.h"
46 #include "net_olsr.h"
47 #include "ipcalc.h"
48 #include "olsr_logging.h"
49
50 #include <errno.h>
51 #include <unistd.h>
52 #include <net/if_dl.h>
53
54 #ifdef _WRS_KERNEL
55 #include <net/ifaddrs.h>
56 #include <wrn/coreip/net/route.h>
57 #include <m2Lib.h>
58 #define OLSR_PID taskIdSelf ()
59 #else
60 #include <ifaddrs.h>
61 #define OLSR_PID getpid ()
62 #endif
63
64 /**
65  *
66  * Calculate the kernel route flags.
67  * Called before enqueuing a change/delete operation
68  *
69  */
70 static uint8_t
71 olsr_rt_flags(const struct rt_entry *rt)
72 {
73   const struct rt_nexthop *nh;
74   uint8_t flags = RTF_UP;
75
76   /* destination is host */
77   if (rt->rt_dst.prefix_len == 8 * olsr_cnf->ipsize) {
78     flags |= RTF_HOST;
79   }
80
81   nh = olsr_get_nh(rt);
82
83   if (olsr_ipcmp(&rt->rt_dst.prefix, &nh->gateway) != 0) {
84     flags |= RTF_GATEWAY;
85   }
86
87   return flags;
88 }
89
90 static unsigned int seq = 0;
91
92 /*
93  * Sends an add or delete message via the routing socket.
94  * The message consists of:
95  *  - a header i.e. struct rt_msghdr
96  *  - 0-8 socket address structures
97  */
98 static int
99 add_del_route(const struct rt_entry *rt, int add)
100 {
101   struct rt_msghdr *rtm;               /* message to send to the routing socket */
102   unsigned char buff[512];
103   unsigned char *walker;               /* points within the buffer */
104   struct sockaddr_in sin4;             /* internet style sockaddr */
105   struct sockaddr_dl *sdl;             /* link level sockaddr */
106   struct ifaddrs *addrs;
107   struct ifaddrs *awalker;
108   const struct rt_nexthop *nexthop;
109   union olsr_ip_addr mask;             /* netmask as ip address */
110   int sin_size, sdl_size;              /* payload of the message */
111   int len;                             /* message size written to routing socket */
112
113   if (add) {
114     OLSR_DEBUG(LOG_ROUTING, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
115   } else {
116     OLSR_DEBUG(LOG_ROUTING, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
117   }
118
119   memset(buff, 0, sizeof(buff));
120   memset(&sin4, 0, sizeof(sin4));
121
122   sin4.sin_len = sizeof(sin4);
123   sin4.sin_family = AF_INET;
124
125   sin_size = 1 + ((sizeof(struct sockaddr_in) - 1) | 3);
126   sdl_size = 1 + ((sizeof(struct sockaddr_dl) - 1) | 3);
127
128   /**********************************************************************
129    *                  FILL THE ROUTING MESSAGE HEADER
130    **********************************************************************/
131
132   /* position header to the beginning of the buffer */
133   rtm = (struct rt_msghdr *)buff;
134
135   rtm->rtm_version = RTM_VERSION;
136   rtm->rtm_type = add ? RTM_ADD : RTM_DELETE;
137   rtm->rtm_index = 0;           /* is ignored in outgoing messages */
138   rtm->rtm_flags = olsr_rt_flags(rt);
139   rtm->rtm_pid = OLSR_PID;
140   rtm->rtm_seq = ++seq;
141
142   /* walk to the end of the header */
143   walker = buff + sizeof(struct rt_msghdr);
144
145   /**********************************************************************
146    *                  SET  DESTINATION OF THE ROUTE
147    **********************************************************************/
148
149 #ifdef _WRS_KERNEL
150   /*
151    * vxWorks: change proto or tos
152    */
153   OLSR_DEBUG(LOG_ROUTING, "\t- Setting Protocol: 0\n");
154   ((struct sockaddr_rt *)(&sin4))->srt_proto = 0;
155   OLSR_DEBUG(LOG_ROUTING, "\t- Setting TOS: 0\n");
156   ((struct sockaddr_rt *)(&sin4))->srt_tos = 0;
157 #endif
158
159   sin4.sin_addr = rt->rt_dst.prefix.v4;
160   memcpy(walker, &sin4, sizeof(sin4));
161   walker += sin_size;
162   rtm->rtm_addrs = RTA_DST;
163
164   /**********************************************************************
165    *                  SET GATEWAY OF THE ROUTE
166    **********************************************************************/
167
168 #ifdef _WRS_KERNEL
169   /*
170    * vxWorks: Route with no gateway is deleted
171    */
172   if (add) {
173 #endif
174     nexthop = olsr_get_nh(rt);
175     if (0 != (rtm->rtm_flags & RTF_GATEWAY)) {
176       sin4.sin_addr = nexthop->gateway.v4;
177       memcpy(walker, &sin4, sizeof(sin4));
178       walker += sin_size;
179       rtm->rtm_addrs |= RTA_GATEWAY;
180     }
181     else {
182       /*
183        * Host is directly reachable, so add
184        * the output interface MAC address.
185        */
186       if (getifaddrs(&addrs)) {
187         OLSR_WARN(LOG_ROUTING, "\ngetifaddrs() failed\n");
188         return -1;
189       }
190
191       for (awalker = addrs; awalker != NULL; awalker = awalker->ifa_next)
192         if (awalker->ifa_addr->sa_family == AF_LINK && strcmp(awalker->ifa_name, nexthop->interface->int_name) == 0)
193           break;
194
195       if (awalker == NULL) {
196         OLSR_WARN(LOG_ROUTING, "\nInterface %s not found\n", nexthop->interface->int_name);
197         freeifaddrs(addrs);
198         return -1;
199       }
200
201       /* sdl is "struct sockaddr_dl" */
202       sdl = (struct sockaddr_dl *)awalker->ifa_addr;
203       memcpy(walker, sdl, sdl->sdl_len);
204       walker += sdl_size;
205       rtm->rtm_addrs |= RTA_GATEWAY;
206 #ifdef RTF_CLONING
207       rtm->rtm_flags |= RTF_CLONING;
208 #endif
209 #ifndef _WRS_KERNEL
210       rtm->rtm_flags &= ~RTF_HOST;
211 #endif
212       freeifaddrs(addrs);
213     }
214 #ifdef _WRS_KERNEL
215   }
216 #endif
217
218   /**********************************************************************
219    *                         SET  NETMASK
220    **********************************************************************/
221
222   if (0 == (rtm->rtm_flags & RTF_HOST)) {
223     olsr_prefix_to_netmask(&mask, rt->rt_dst.prefix_len);
224     sin4.sin_addr = mask.v4;
225     memcpy(walker, &sin4, sizeof(sin4));
226     walker += sin_size;
227     rtm->rtm_addrs |= RTA_NETMASK;
228   }
229
230   /**********************************************************************
231    *           WRITE CONFIGURATION MESSAGE TO THE ROUTING SOCKET
232    **********************************************************************/
233
234   rtm->rtm_msglen = (unsigned short)(walker - buff);
235   len = write(olsr_cnf->rts_bsd, buff, rtm->rtm_msglen);
236   if (0 != rtm->rtm_errno || len < rtm->rtm_msglen) {
237     OLSR_WARN(LOG_ROUTING, "\nCannot write to routing socket: (rtm_errno= 0x%x) (last error message: %s)\n", rtm->rtm_errno,
238             strerror(errno));
239   }
240   return 0;
241 }
242
243 static int
244 add_del_route6(const struct rt_entry *rt, int add)
245 {
246   struct rt_msghdr *rtm;
247   unsigned char buff[512];
248   unsigned char *walker;
249   struct sockaddr_in6 sin6;
250   struct sockaddr_dl sdl;
251   const struct rt_nexthop *nexthop;
252   int sin_size, sdl_size;
253   int len;
254
255   if (add) {
256     OLSR_DEBUG(LOG_ROUTING, "KERN: Adding %s\n", olsr_rtp_to_string(rt->rt_best));
257   } else {
258     OLSR_DEBUG(LOG_ROUTING, "KERN: Deleting %s\n", olsr_rt_to_string(rt));
259   }
260
261   memset(buff, 0, sizeof(buff));
262   memset(&sin6, 0, sizeof(sin6));
263   memset(&sdl, 0, sizeof(sdl));
264
265   sin6.sin6_len = sizeof(sin6);
266   sin6.sin6_family = AF_INET6;
267   sdl.sdl_len = sizeof(sdl);
268   sdl.sdl_family = AF_LINK;
269
270   sin_size = 1 + ((sizeof(struct sockaddr_in6) - 1) | 3);
271   sdl_size = 1 + ((sizeof(struct sockaddr_dl) - 1) | 3);
272
273   /**********************************************************************
274    *                  FILL THE ROUTING MESSAGE HEADER
275    **********************************************************************/
276
277   /* position header to the beginning of the buffer */
278   rtm = (struct rt_msghdr *)buff;
279   rtm->rtm_version = RTM_VERSION;
280   rtm->rtm_type = (add != 0) ? RTM_ADD : RTM_DELETE;
281   rtm->rtm_index = 0;
282   rtm->rtm_flags = olsr_rt_flags(rt);
283   rtm->rtm_pid = OLSR_PID;
284   rtm->rtm_seq = ++seq;
285
286   /* walk to the end of the header */
287   walker = buff + sizeof(struct rt_msghdr);
288
289   /**********************************************************************
290    *                  SET  DESTINATION OF THE ROUTE
291    **********************************************************************/
292
293   memcpy(&sin6.sin6_addr.s6_addr, &rt->rt_dst.prefix.v6, sizeof(struct in6_addr));
294   memcpy(walker, &sin6, sizeof(sin6));
295   walker += sin_size;
296   rtm->rtm_addrs = RTA_DST;
297
298   /**********************************************************************
299    *                  SET GATEWAY OF THE ROUTE
300    **********************************************************************/
301
302   nexthop = olsr_get_nh(rt);
303   if (0 != (rtm->rtm_flags & RTF_GATEWAY)) {
304     memcpy(&sin6.sin6_addr.s6_addr, &nexthop->gateway.v6, sizeof(struct in6_addr));
305     memset(&sin6.sin6_addr.s6_addr, 0, 8);
306     sin6.sin6_addr.s6_addr[0] = 0xfe;
307     sin6.sin6_addr.s6_addr[1] = 0x80;
308     sin6.sin6_scope_id = nexthop->interface->if_index;
309 #ifdef __KAME__
310     *(u_int16_t *) & sin6.sin6_addr.s6_addr[2] = htons(sin6.sin6_scope_id);
311     sin6.sin6_scope_id = 0;
312 #endif
313     memcpy(walker, &sin6, sizeof(sin6));
314     walker += sin_size;
315     rtm->rtm_addrs |= RTA_GATEWAY;
316   }
317   else {
318     /*
319      * Host is directly reachable, so add
320      * the output interface MAC address.
321      */
322     memcpy(&sin6.sin6_addr.s6_addr, &rt->rt_dst.prefix.v6, sizeof(struct in6_addr));
323     memset(&sin6.sin6_addr.s6_addr, 0, 8);
324     sin6.sin6_addr.s6_addr[0] = 0xfe;
325     sin6.sin6_addr.s6_addr[1] = 0x80;
326     sin6.sin6_scope_id = nexthop->interface->if_index;
327 #ifdef __KAME__
328     *(u_int16_t *) & sin6.sin6_addr.s6_addr[2] = htons(sin6.sin6_scope_id);
329     sin6.sin6_scope_id = 0;
330 #endif
331     memcpy(walker, &sin6, sizeof(sin6));
332     walker += sin_size;
333     rtm->rtm_addrs |= RTA_GATEWAY;
334     rtm->rtm_flags |= RTF_GATEWAY;
335   }
336
337   /**********************************************************************
338    *                         SET  NETMASK
339    **********************************************************************/
340
341   if (0 == (rtm->rtm_flags & RTF_HOST)) {
342     olsr_prefix_to_netmask((union olsr_ip_addr *)&sin6.sin6_addr, rt->rt_dst.prefix_len);
343     memcpy(walker, &sin6, sizeof(sin6));
344     walker += sin_size;
345     rtm->rtm_addrs |= RTA_NETMASK;
346   }
347
348   /**********************************************************************
349    *           WRITE CONFIGURATION MESSAGE TO THE ROUTING SOCKET
350    **********************************************************************/
351
352   rtm->rtm_msglen = (unsigned short)(walker - buff);
353   len = write(olsr_cnf->rts_bsd, buff, rtm->rtm_msglen);
354   if (len < 0 && !(errno == EEXIST || errno == ESRCH)) {
355     OLSR_WARN(LOG_ROUTING, "cannot write to routing socket: %s\n", strerror(errno));
356   }
357
358   /*
359    * If we get an EEXIST error while adding, delete and retry.
360    */
361   if (len < 0 && errno == EEXIST && rtm->rtm_type == RTM_ADD) {
362     struct rt_msghdr *drtm;
363     unsigned char dbuff[512];
364
365     memset(dbuff, 0, sizeof(dbuff));
366     drtm = (struct rt_msghdr *)dbuff;
367     drtm->rtm_version = RTM_VERSION;
368     drtm->rtm_type = RTM_DELETE;
369     drtm->rtm_index = 0;
370     drtm->rtm_flags = olsr_rt_flags(rt);
371     drtm->rtm_seq = ++seq;
372
373     walker = dbuff + sizeof(struct rt_msghdr);
374     memcpy(&sin6.sin6_addr.s6_addr, &rt->rt_dst.prefix.v6, sizeof(struct in6_addr));
375     memcpy(walker, &sin6, sizeof(sin6));
376     walker += sin_size;
377     drtm->rtm_addrs = RTA_DST;
378     drtm->rtm_msglen = (unsigned short)(walker - dbuff);
379     len = write(olsr_cnf->rts_bsd, dbuff, drtm->rtm_msglen);
380     if (len < 0) {
381       OLSR_WARN(LOG_ROUTING, "cannot delete route: %s\n", strerror(errno));
382     }
383     rtm->rtm_seq = ++seq;
384     len = write(olsr_cnf->rts_bsd, buff, rtm->rtm_msglen);
385     if (len < 0) {
386       OLSR_WARN(LOG_ROUTING, "still cannot add route: %s\n", strerror(errno));
387     }
388   }
389   return 0;
390 }
391
392 int
393 olsr_kernel_add_route(const struct rt_entry *rt, int ip_version)
394 {
395   return AF_INET == ip_version ? add_del_route(rt, 1) : add_del_route6(rt, 1);
396 }
397
398 int
399 olsr_kernel_del_route(const struct rt_entry *rt, int ip_version)
400 {
401   return AF_INET == ip_version ? add_del_route(rt, 0) : add_del_route6(rt, 0);
402 }
403
404 int olsr_create_lo_interface(union olsr_ip_addr *ip  __attribute__((unused))) {
405   return 0;
406 }
407
408 int olsr_delete_lo_interface(union olsr_ip_addr *ip   __attribute__((unused))) {
409   return 0;
410 }
411
412 /*
413  * Local Variables:
414  * c-basic-offset: 2
415  * indent-tabs-mode: nil
416  * End:
417  */