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. */
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  memset (&sin_gate, 0, sizeof (struct sockaddr_in));
185
186  /* Make gateway. */
187  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
188    {
189      if ((cmd == SIOCADDRT
190	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
191	  || (cmd == SIOCDELRT
192	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
193	{
194	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
195	    {
196	      if (nexthop->rtype == NEXTHOP_TYPE_IPV4 ||
197		  nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
198		{
199		  sin_gate.sin_family = AF_INET;
200#ifdef HAVE_SIN_LEN
201		  sin_gate.sin_len = sizeof (struct sockaddr_in);
202#endif /* HAVE_SIN_LEN */
203		  sin_gate.sin_addr = nexthop->rgate.ipv4;
204		  rtentry.rt_flags |= RTF_GATEWAY;
205		}
206	      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
207		  || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
208		{
209		  ifp = if_lookup_by_index (nexthop->rifindex);
210		  if (ifp)
211		    rtentry.rt_dev = ifp->name;
212		  else
213		    return -1;
214		}
215	    }
216	  else
217	    {
218	      if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
219		  nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
220		{
221		  sin_gate.sin_family = AF_INET;
222#ifdef HAVE_SIN_LEN
223		  sin_gate.sin_len = sizeof (struct sockaddr_in);
224#endif /* HAVE_SIN_LEN */
225		  sin_gate.sin_addr = nexthop->gate.ipv4;
226		  rtentry.rt_flags |= RTF_GATEWAY;
227		}
228	      if (nexthop->type == NEXTHOP_TYPE_IFINDEX
229		  || nexthop->type == NEXTHOP_TYPE_IFNAME)
230		{
231		  ifp = if_lookup_by_index (nexthop->ifindex);
232		  if (ifp)
233		    rtentry.rt_dev = ifp->name;
234		  else
235		    return -1;
236		}
237	    }
238
239	  if (cmd == SIOCADDRT)
240	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
241
242	  nexthop_num++;
243	  break;
244	}
245    }
246
247  /* If there is no useful nexthop then return. */
248  if (nexthop_num == 0)
249    {
250      if (IS_ZEBRA_DEBUG_KERNEL)
251	zlog_info ("netlink_route_multipath(): No useful nexthop.");
252      return 0;
253    }
254
255  memset (&sin_mask, 0, sizeof (struct sockaddr_in));
256  sin_mask.sin_family = AF_INET;
257#ifdef HAVE_SIN_LEN
258  sin_mask.sin_len = sizeof (struct sockaddr_in);
259#endif /* HAVE_SIN_LEN */
260  masklen2ip (p->prefixlen, &sin_mask.sin_addr);
261
262  /* Set destination address, mask and gateway.*/
263  memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in));
264
265  if (rtentry.rt_flags & RTF_GATEWAY)
266    memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in));
267
268#ifndef SUNOS_5
269  memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in));
270#endif /* SUNOS_5 */
271
272  /* Metric.  It seems metric minus one value is installed... */
273  rtentry.rt_metric = rib->metric;
274
275  /* Routing entry flag set. */
276  if (p->prefixlen == 32)
277    rtentry.rt_flags |= RTF_HOST;
278
279  rtentry.rt_flags |= RTF_UP;
280
281  /* Additional flags */
282  /* rtentry.rt_flags |= flags; */
283
284  /* For tagging route. */
285  /* rtentry.rt_flags |= RTF_DYNAMIC; */
286
287  /* Open socket for ioctl. */
288  sock = socket (AF_INET, SOCK_DGRAM, 0);
289  if (sock < 0)
290    {
291      zlog_warn ("can't make socket\n");
292      return -1;
293    }
294
295  /* Send message by ioctl(). */
296  ret = ioctl (sock, cmd, &rtentry);
297  if (ret < 0)
298    {
299      switch (errno)
300	{
301	case EEXIST:
302	  close (sock);
303	  return ZEBRA_ERR_RTEXIST;
304	  break;
305	case ENETUNREACH:
306	  close (sock);
307	  return ZEBRA_ERR_RTUNREACH;
308	  break;
309	case EPERM:
310	  close (sock);
311	  return ZEBRA_ERR_EPERM;
312	  break;
313	}
314
315      close (sock);
316      zlog_warn ("write : %s (%d)", strerror (errno), errno);
317      return ret;
318    }
319  close (sock);
320
321  return ret;
322}
323
324int
325kernel_add_ipv4 (struct prefix *p, struct rib *rib)
326{
327  return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET);
328}
329
330int
331kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
332{
333  return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET);
334}
335
336#ifdef HAVE_IPV6
337
338/* Below is hack for GNU libc definition and Linux 2.1.X header. */
339#undef RTF_DEFAULT
340#undef RTF_ADDRCONF
341
342#include <asm/types.h>
343
344#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
345/* struct in6_rtmsg will be declared in net/route.h. */
346#else
347#include <linux/ipv6_route.h>
348#endif
349
350int
351kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate,
352		   int index, int flags)
353{
354  int ret;
355  int sock;
356  struct in6_rtmsg rtm;
357
358  memset (&rtm, 0, sizeof (struct in6_rtmsg));
359
360  rtm.rtmsg_flags |= RTF_UP;
361  rtm.rtmsg_metric = 1;
362  memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr));
363  rtm.rtmsg_dst_len = dest->prefixlen;
364
365  /* We need link local index. But this should be done caller...
366  if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway))
367    {
368      index = if_index_address (&rtm.rtmsg_gateway);
369      rtm.rtmsg_ifindex = index;
370    }
371  else
372    rtm.rtmsg_ifindex = 0;
373  */
374
375  rtm.rtmsg_flags |= RTF_GATEWAY;
376
377  /* For tagging route. */
378  /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
379
380  memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr));
381
382  if (index)
383    rtm.rtmsg_ifindex = index;
384  else
385    rtm.rtmsg_ifindex = 0;
386
387  rtm.rtmsg_metric = 1;
388
389  sock = socket (AF_INET6, SOCK_DGRAM, 0);
390  if (sock < 0)
391    {
392      zlog_warn ("can't make socket\n");
393      return -1;
394    }
395
396  /* Send message via ioctl. */
397  ret = ioctl (sock, type, &rtm);
398  if (ret < 0)
399    {
400      zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete",
401	   strerror(errno));
402      ret = errno;
403      close (sock);
404      return ret;
405    }
406  close (sock);
407
408  return ret;
409}
410
411int
412kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib,
413			     int family)
414{
415  int ret;
416  int sock;
417  struct in6_rtmsg rtm;
418  struct nexthop *nexthop;
419  int nexthop_num = 0;
420
421  memset (&rtm, 0, sizeof (struct in6_rtmsg));
422
423  rtm.rtmsg_flags |= RTF_UP;
424  rtm.rtmsg_metric = rib->metric;
425  memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr));
426  rtm.rtmsg_dst_len = p->prefixlen;
427
428  /* We need link local index. But this should be done caller...
429  if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway))
430    {
431      index = if_index_address (&rtm.rtmsg_gateway);
432      rtm.rtmsg_ifindex = index;
433    }
434  else
435    rtm.rtmsg_ifindex = 0;
436  */
437
438  rtm.rtmsg_flags |= RTF_GATEWAY;
439
440  /* For tagging route. */
441  /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
442
443  /* Make gateway. */
444  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
445    {
446      if ((cmd == SIOCADDRT
447	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
448	  || (cmd == SIOCDELRT
449	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
450	{
451	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
452	    {
453	      if (nexthop->rtype == NEXTHOP_TYPE_IPV6
454		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
455		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
456		{
457		  memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6,
458			  sizeof (struct in6_addr));
459		}
460	      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
461		  || nexthop->rtype == NEXTHOP_TYPE_IFNAME
462		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
463		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
464		rtm.rtmsg_ifindex = nexthop->rifindex;
465	      else
466		rtm.rtmsg_ifindex = 0;
467
468	    }
469	  else
470	    {
471	      if (nexthop->type == NEXTHOP_TYPE_IPV6
472		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
473		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
474		{
475		  memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6,
476			  sizeof (struct in6_addr));
477		}
478	      if (nexthop->type == NEXTHOP_TYPE_IFINDEX
479		  || nexthop->type == NEXTHOP_TYPE_IFNAME
480		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
481		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
482		rtm.rtmsg_ifindex = nexthop->ifindex;
483	      else
484		rtm.rtmsg_ifindex = 0;
485	    }
486
487	  if (cmd == SIOCADDRT)
488	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
489
490	  nexthop_num++;
491	  break;
492	}
493    }
494
495  /* If there is no useful nexthop then return. */
496  if (nexthop_num == 0)
497    {
498      if (IS_ZEBRA_DEBUG_KERNEL)
499	zlog_info ("netlink_route_multipath(): No useful nexthop.");
500      return 0;
501    }
502
503  sock = socket (AF_INET6, SOCK_DGRAM, 0);
504  if (sock < 0)
505    {
506      zlog_warn ("can't make socket\n");
507      return -1;
508    }
509
510  /* Send message via ioctl. */
511  ret = ioctl (sock, cmd, &rtm);
512  if (ret < 0)
513    {
514      zlog_warn ("can't %s ipv6 route: %s\n",
515		 cmd == SIOCADDRT ? "add" : "delete",
516	   strerror(errno));
517      ret = errno;
518      close (sock);
519      return ret;
520    }
521  close (sock);
522
523  return ret;
524}
525
526int
527kernel_add_ipv6 (struct prefix *p, struct rib *rib)
528{
529  return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6);
530}
531
532int
533kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
534{
535  return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6);
536}
537
538/* Delete IPv6 route from the kernel. */
539int
540kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate,
541		    int index, int flags, int table)
542{
543  return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags);
544}
545#endif /* HAVE_IPV6 */
546