blob: d470572b357911b40cd89f0e1a8430531c8f2006 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/*
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. */
34void
35kernel_init ()
36{
37 return;
38}
39
40/* Dummy function of routing socket. */
41void
42kernel_read (int sock)
43{
44 return;
45}
46
47#if 0
48/* Initialization prototype of struct sockaddr_in. */
49static 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. */
64int
65kernel_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)", strerror (errno), errno);
155 return 1;
156 }
157 close (sock);
158
159 return ret;
160}
161
162/* Interface to ioctl route message. */
163int
164kernel_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)", strerror (errno), errno);
330 return ret;
331 }
332 close (sock);
333
334 return ret;
335}
336
337int
338kernel_add_ipv4 (struct prefix *p, struct rib *rib)
339{
340 return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET);
341}
342
343int
344kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
345{
346 return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET);
347}
348
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
363int
364kernel_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 strerror(errno));
415 ret = errno;
416 close (sock);
417 return ret;
418 }
419 close (sock);
420
421 return ret;
422}
423
424int
425kernel_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 strerror(errno));
530 ret = errno;
531 close (sock);
532 return ret;
533 }
534 close (sock);
535
536 return ret;
537}
538
539int
540kernel_add_ipv6 (struct prefix *p, struct rib *rib)
541{
542 return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6);
543}
544
545int
546kernel_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. */
552int
553kernel_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 */