]> git.proxmox.com Git - mirror_frr.git/blob - zebra/rt_ioctl.c
2004-11-19 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
[mirror_frr.git] / zebra / rt_ioctl.c
1 /*
2 * kernel routing table update by ioctl().
3 * Copyright (C) 1997, 98 Kunihiro Ishiguro
4 *
5 * This file is part of GNU Zebra.
6 *
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Zebra; see the file COPYING. If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23 #include <zebra.h>
24
25 #include "prefix.h"
26 #include "log.h"
27 #include "if.h"
28
29 #include "zebra/rib.h"
30 #include "zebra/debug.h"
31
32 /* Initialize of kernel interface. There is no kernel communication
33 support under ioctl(). So this is dummy stub function. */
34 void
35 kernel_init ()
36 {
37 return;
38 }
39
40 /* Dummy function of routing socket. */
41 void
42 kernel_read (int sock)
43 {
44 return;
45 }
46
47 #if 0
48 /* Initialization prototype of struct sockaddr_in. */
49 static struct sockaddr_in sin_proto =
50 {
51 #ifdef HAVE_SIN_LEN
52 sizeof (struct sockaddr_in),
53 #endif /* HAVE_SIN_LEN */
54 AF_INET, 0, {0}, {0}
55 };
56 #endif /* 0 */
57
58 /* Solaris has ortentry. */
59 #ifdef HAVE_OLD_RTENTRY
60 #define rtentry ortentry
61 #endif /* HAVE_OLD_RTENTRY */
62
63 /* Interface to ioctl route message. */
64 int
65 kernel_add_route (struct prefix_ipv4 *dest, struct in_addr *gate,
66 int index, int flags)
67 {
68 int ret;
69 int sock;
70 struct rtentry rtentry;
71 struct sockaddr_in sin_dest, sin_mask, sin_gate;
72
73 memset (&rtentry, 0, sizeof (struct rtentry));
74
75 /* Make destination. */
76 memset (&sin_dest, 0, sizeof (struct sockaddr_in));
77 sin_dest.sin_family = AF_INET;
78 #ifdef HAVE_SIN_LEN
79 sin_dest.sin_len = sizeof (struct sockaddr_in);
80 #endif /* HAVE_SIN_LEN */
81 sin_dest.sin_addr = dest->prefix;
82
83 /* Make gateway. */
84 if (gate)
85 {
86 memset (&sin_gate, 0, sizeof (struct sockaddr_in));
87 sin_gate.sin_family = AF_INET;
88 #ifdef HAVE_SIN_LEN
89 sin_gate.sin_len = sizeof (struct sockaddr_in);
90 #endif /* HAVE_SIN_LEN */
91 sin_gate.sin_addr = *gate;
92 }
93
94 memset (&sin_mask, 0, sizeof (struct sockaddr_in));
95 sin_mask.sin_family = AF_INET;
96 #ifdef HAVE_SIN_LEN
97 sin_gate.sin_len = sizeof (struct sockaddr_in);
98 #endif /* HAVE_SIN_LEN */
99 masklen2ip (dest->prefixlen, &sin_mask.sin_addr);
100
101 /* Set destination address, mask and gateway.*/
102 memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in));
103 if (gate)
104 memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in));
105 #ifndef SUNOS_5
106 memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in));
107 #endif /* SUNOS_5 */
108
109 /* Routing entry flag set. */
110 if (dest->prefixlen == 32)
111 rtentry.rt_flags |= RTF_HOST;
112
113 if (gate && gate->s_addr != INADDR_ANY)
114 rtentry.rt_flags |= RTF_GATEWAY;
115
116 rtentry.rt_flags |= RTF_UP;
117
118 /* Additional flags */
119 rtentry.rt_flags |= flags;
120
121
122 /* For tagging route. */
123 /* rtentry.rt_flags |= RTF_DYNAMIC; */
124
125 /* Open socket for ioctl. */
126 sock = socket (AF_INET, SOCK_DGRAM, 0);
127 if (sock < 0)
128 {
129 zlog_warn ("can't make socket\n");
130 return -1;
131 }
132
133 /* Send message by ioctl(). */
134 ret = ioctl (sock, SIOCADDRT, &rtentry);
135 if (ret < 0)
136 {
137 switch (errno)
138 {
139 case EEXIST:
140 close (sock);
141 return ZEBRA_ERR_RTEXIST;
142 break;
143 case ENETUNREACH:
144 close (sock);
145 return ZEBRA_ERR_RTUNREACH;
146 break;
147 case EPERM:
148 close (sock);
149 return ZEBRA_ERR_EPERM;
150 break;
151 }
152
153 close (sock);
154 zlog_warn ("write : %s (%d)", safe_strerror (errno), errno);
155 return 1;
156 }
157 close (sock);
158
159 return ret;
160 }
161
162 /* Interface to ioctl route message. */
163 int
164 kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family)
165 {
166 int ret;
167 int sock;
168 struct rtentry rtentry;
169 struct sockaddr_in sin_dest, sin_mask, sin_gate;
170 struct nexthop *nexthop;
171 int nexthop_num = 0;
172 struct interface *ifp;
173
174 memset (&rtentry, 0, sizeof (struct rtentry));
175
176 /* Make destination. */
177 memset (&sin_dest, 0, sizeof (struct sockaddr_in));
178 sin_dest.sin_family = AF_INET;
179 #ifdef HAVE_SIN_LEN
180 sin_dest.sin_len = sizeof (struct sockaddr_in);
181 #endif /* HAVE_SIN_LEN */
182 sin_dest.sin_addr = p->u.prefix4;
183
184 if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE))
185 {
186 SET_FLAG (rtentry.rt_flags, RTF_REJECT);
187
188 if (cmd == SIOCADDRT)
189 for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
190 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
191
192 goto skip;
193 }
194
195 memset (&sin_gate, 0, sizeof (struct sockaddr_in));
196
197 /* Make gateway. */
198 for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
199 {
200 if ((cmd == SIOCADDRT
201 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
202 || (cmd == SIOCDELRT
203 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
204 {
205 if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
206 {
207 if (nexthop->rtype == NEXTHOP_TYPE_IPV4 ||
208 nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
209 {
210 sin_gate.sin_family = AF_INET;
211 #ifdef HAVE_SIN_LEN
212 sin_gate.sin_len = sizeof (struct sockaddr_in);
213 #endif /* HAVE_SIN_LEN */
214 sin_gate.sin_addr = nexthop->rgate.ipv4;
215 rtentry.rt_flags |= RTF_GATEWAY;
216 }
217 if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
218 || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
219 {
220 ifp = if_lookup_by_index (nexthop->rifindex);
221 if (ifp)
222 rtentry.rt_dev = ifp->name;
223 else
224 return -1;
225 }
226 }
227 else
228 {
229 if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
230 nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
231 {
232 sin_gate.sin_family = AF_INET;
233 #ifdef HAVE_SIN_LEN
234 sin_gate.sin_len = sizeof (struct sockaddr_in);
235 #endif /* HAVE_SIN_LEN */
236 sin_gate.sin_addr = nexthop->gate.ipv4;
237 rtentry.rt_flags |= RTF_GATEWAY;
238 }
239 if (nexthop->type == NEXTHOP_TYPE_IFINDEX
240 || nexthop->type == NEXTHOP_TYPE_IFNAME)
241 {
242 ifp = if_lookup_by_index (nexthop->ifindex);
243 if (ifp)
244 rtentry.rt_dev = ifp->name;
245 else
246 return -1;
247 }
248 }
249
250 if (cmd == SIOCADDRT)
251 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
252
253 nexthop_num++;
254 break;
255 }
256 }
257
258 /* If there is no useful nexthop then return. */
259 if (nexthop_num == 0)
260 {
261 if (IS_ZEBRA_DEBUG_KERNEL)
262 zlog_info ("netlink_route_multipath(): No useful nexthop.");
263 return 0;
264 }
265
266 skip:
267
268 memset (&sin_mask, 0, sizeof (struct sockaddr_in));
269 sin_mask.sin_family = AF_INET;
270 #ifdef HAVE_SIN_LEN
271 sin_mask.sin_len = sizeof (struct sockaddr_in);
272 #endif /* HAVE_SIN_LEN */
273 masklen2ip (p->prefixlen, &sin_mask.sin_addr);
274
275 /* Set destination address, mask and gateway.*/
276 memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in));
277
278 if (rtentry.rt_flags & RTF_GATEWAY)
279 memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in));
280
281 #ifndef SUNOS_5
282 memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in));
283 #endif /* SUNOS_5 */
284
285 /* Metric. It seems metric minus one value is installed... */
286 rtentry.rt_metric = rib->metric;
287
288 /* Routing entry flag set. */
289 if (p->prefixlen == 32)
290 rtentry.rt_flags |= RTF_HOST;
291
292 rtentry.rt_flags |= RTF_UP;
293
294 /* Additional flags */
295 /* rtentry.rt_flags |= flags; */
296
297 /* For tagging route. */
298 /* rtentry.rt_flags |= RTF_DYNAMIC; */
299
300 /* Open socket for ioctl. */
301 sock = socket (AF_INET, SOCK_DGRAM, 0);
302 if (sock < 0)
303 {
304 zlog_warn ("can't make socket\n");
305 return -1;
306 }
307
308 /* Send message by ioctl(). */
309 ret = ioctl (sock, cmd, &rtentry);
310 if (ret < 0)
311 {
312 switch (errno)
313 {
314 case EEXIST:
315 close (sock);
316 return ZEBRA_ERR_RTEXIST;
317 break;
318 case ENETUNREACH:
319 close (sock);
320 return ZEBRA_ERR_RTUNREACH;
321 break;
322 case EPERM:
323 close (sock);
324 return ZEBRA_ERR_EPERM;
325 break;
326 }
327
328 close (sock);
329 zlog_warn ("write : %s (%d)", safe_strerror (errno), errno);
330 return ret;
331 }
332 close (sock);
333
334 return ret;
335 }
336
337 int
338 kernel_add_ipv4 (struct prefix *p, struct rib *rib)
339 {
340 return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET);
341 }
342
343 int
344 kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
345 {
346 return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET);
347 }
348 \f
349 #ifdef HAVE_IPV6
350
351 /* Below is hack for GNU libc definition and Linux 2.1.X header. */
352 #undef RTF_DEFAULT
353 #undef RTF_ADDRCONF
354
355 #include <asm/types.h>
356
357 #if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
358 /* struct in6_rtmsg will be declared in net/route.h. */
359 #else
360 #include <linux/ipv6_route.h>
361 #endif
362
363 int
364 kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate,
365 int index, int flags)
366 {
367 int ret;
368 int sock;
369 struct in6_rtmsg rtm;
370
371 memset (&rtm, 0, sizeof (struct in6_rtmsg));
372
373 rtm.rtmsg_flags |= RTF_UP;
374 rtm.rtmsg_metric = 1;
375 memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr));
376 rtm.rtmsg_dst_len = dest->prefixlen;
377
378 /* We need link local index. But this should be done caller...
379 if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway))
380 {
381 index = if_index_address (&rtm.rtmsg_gateway);
382 rtm.rtmsg_ifindex = index;
383 }
384 else
385 rtm.rtmsg_ifindex = 0;
386 */
387
388 rtm.rtmsg_flags |= RTF_GATEWAY;
389
390 /* For tagging route. */
391 /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
392
393 memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr));
394
395 if (index)
396 rtm.rtmsg_ifindex = index;
397 else
398 rtm.rtmsg_ifindex = 0;
399
400 rtm.rtmsg_metric = 1;
401
402 sock = socket (AF_INET6, SOCK_DGRAM, 0);
403 if (sock < 0)
404 {
405 zlog_warn ("can't make socket\n");
406 return -1;
407 }
408
409 /* Send message via ioctl. */
410 ret = ioctl (sock, type, &rtm);
411 if (ret < 0)
412 {
413 zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete",
414 safe_strerror(errno));
415 ret = errno;
416 close (sock);
417 return ret;
418 }
419 close (sock);
420
421 return ret;
422 }
423
424 int
425 kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib,
426 int family)
427 {
428 int ret;
429 int sock;
430 struct in6_rtmsg rtm;
431 struct nexthop *nexthop;
432 int nexthop_num = 0;
433
434 memset (&rtm, 0, sizeof (struct in6_rtmsg));
435
436 rtm.rtmsg_flags |= RTF_UP;
437 rtm.rtmsg_metric = rib->metric;
438 memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr));
439 rtm.rtmsg_dst_len = p->prefixlen;
440
441 /* We need link local index. But this should be done caller...
442 if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway))
443 {
444 index = if_index_address (&rtm.rtmsg_gateway);
445 rtm.rtmsg_ifindex = index;
446 }
447 else
448 rtm.rtmsg_ifindex = 0;
449 */
450
451 rtm.rtmsg_flags |= RTF_GATEWAY;
452
453 /* For tagging route. */
454 /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
455
456 /* Make gateway. */
457 for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
458 {
459 if ((cmd == SIOCADDRT
460 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
461 || (cmd == SIOCDELRT
462 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
463 {
464 if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
465 {
466 if (nexthop->rtype == NEXTHOP_TYPE_IPV6
467 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
468 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
469 {
470 memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6,
471 sizeof (struct in6_addr));
472 }
473 if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
474 || nexthop->rtype == NEXTHOP_TYPE_IFNAME
475 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
476 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
477 rtm.rtmsg_ifindex = nexthop->rifindex;
478 else
479 rtm.rtmsg_ifindex = 0;
480
481 }
482 else
483 {
484 if (nexthop->type == NEXTHOP_TYPE_IPV6
485 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
486 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
487 {
488 memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6,
489 sizeof (struct in6_addr));
490 }
491 if (nexthop->type == NEXTHOP_TYPE_IFINDEX
492 || nexthop->type == NEXTHOP_TYPE_IFNAME
493 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
494 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
495 rtm.rtmsg_ifindex = nexthop->ifindex;
496 else
497 rtm.rtmsg_ifindex = 0;
498 }
499
500 if (cmd == SIOCADDRT)
501 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
502
503 nexthop_num++;
504 break;
505 }
506 }
507
508 /* If there is no useful nexthop then return. */
509 if (nexthop_num == 0)
510 {
511 if (IS_ZEBRA_DEBUG_KERNEL)
512 zlog_info ("netlink_route_multipath(): No useful nexthop.");
513 return 0;
514 }
515
516 sock = socket (AF_INET6, SOCK_DGRAM, 0);
517 if (sock < 0)
518 {
519 zlog_warn ("can't make socket\n");
520 return -1;
521 }
522
523 /* Send message via ioctl. */
524 ret = ioctl (sock, cmd, &rtm);
525 if (ret < 0)
526 {
527 zlog_warn ("can't %s ipv6 route: %s\n",
528 cmd == SIOCADDRT ? "add" : "delete",
529 safe_strerror(errno));
530 ret = errno;
531 close (sock);
532 return ret;
533 }
534 close (sock);
535
536 return ret;
537 }
538
539 int
540 kernel_add_ipv6 (struct prefix *p, struct rib *rib)
541 {
542 return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6);
543 }
544
545 int
546 kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
547 {
548 return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6);
549 }
550
551 /* Delete IPv6 route from the kernel. */
552 int
553 kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate,
554 int index, int flags, int table)
555 {
556 return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags);
557 }
558 #endif /* HAVE_IPV6 */