/* * kernel routing table update by ioctl(). * Copyright (C) 1997, 98 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "prefix.h" #include "log.h" #include "if.h" #include "zebra/zserv.h" #include "zebra/rib.h" #include "zebra/debug.h" #include "zebra/rt.h" /* Initialize of kernel interface. There is no kernel communication support under ioctl(). So this is dummy stub function. */ void kernel_init (void) { return; } /* Dummy function of routing socket. */ static void kernel_read (int sock) { return; } #if 0 /* Initialization prototype of struct sockaddr_in. */ static struct sockaddr_in sin_proto = { #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sizeof (struct sockaddr_in), #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ AF_INET, 0, {0}, {0} }; #endif /* 0 */ /* Solaris has ortentry. */ #ifdef HAVE_OLD_RTENTRY #define rtentry ortentry #endif /* HAVE_OLD_RTENTRY */ /* Interface to ioctl route message. */ int kernel_add_route (struct prefix_ipv4 *dest, struct in_addr *gate, int index, int flags) { int ret; int sock; struct rtentry rtentry; struct sockaddr_in sin_dest, sin_mask, sin_gate; memset (&rtentry, 0, sizeof (struct rtentry)); /* Make destination. */ memset (&sin_dest, 0, sizeof (struct sockaddr_in)); sin_dest.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_dest.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ sin_dest.sin_addr = dest->prefix; /* Make gateway. */ if (gate) { memset (&sin_gate, 0, sizeof (struct sockaddr_in)); sin_gate.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_gate.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ sin_gate.sin_addr = *gate; } memset (&sin_mask, 0, sizeof (struct sockaddr_in)); sin_mask.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_gate.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ masklen2ip (dest->prefixlen, &sin_mask.sin_addr); /* Set destination address, mask and gateway.*/ memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); if (gate) memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); #ifndef SUNOS_5 memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); #endif /* SUNOS_5 */ /* Routing entry flag set. */ if (dest->prefixlen == 32) rtentry.rt_flags |= RTF_HOST; if (gate && gate->s_addr != INADDR_ANY) rtentry.rt_flags |= RTF_GATEWAY; rtentry.rt_flags |= RTF_UP; /* Additional flags */ rtentry.rt_flags |= flags; /* For tagging route. */ /* rtentry.rt_flags |= RTF_DYNAMIC; */ /* Open socket for ioctl. */ sock = socket (AF_INET, SOCK_DGRAM, 0); if (sock < 0) { zlog_warn ("can't make socket\n"); return -1; } /* Send message by ioctl(). */ ret = ioctl (sock, SIOCADDRT, &rtentry); if (ret < 0) { switch (errno) { case EEXIST: close (sock); return ZEBRA_ERR_RTEXIST; break; case ENETUNREACH: close (sock); return ZEBRA_ERR_RTUNREACH; break; case EPERM: close (sock); return ZEBRA_ERR_EPERM; break; } close (sock); zlog_warn ("write : %s (%d)", safe_strerror (errno), errno); return 1; } close (sock); return ret; } /* Interface to ioctl route message. */ static int kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family) { int ret; int sock; struct rtentry rtentry; struct sockaddr_in sin_dest, sin_mask, sin_gate; struct nexthop *nexthop, *tnexthop; int recursing; int nexthop_num = 0; struct interface *ifp; memset (&rtentry, 0, sizeof (struct rtentry)); /* Make destination. */ memset (&sin_dest, 0, sizeof (struct sockaddr_in)); sin_dest.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_dest.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ sin_dest.sin_addr = p->u.prefix4; if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) { SET_FLAG (rtentry.rt_flags, RTF_REJECT); if (cmd == SIOCADDRT) for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { /* We shouldn't encounter recursive nexthops on discard routes, * but it is probably better to handle that case correctly anyway. */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } goto skip; } memset (&sin_gate, 0, sizeof (struct sockaddr_in)); /* Make gateway. */ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; if ((cmd == SIOCADDRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == SIOCDELRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { sin_gate.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_gate.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ sin_gate.sin_addr = nexthop->gate.ipv4; rtentry.rt_flags |= RTF_GATEWAY; } if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IFNAME) { ifp = if_lookup_by_index (nexthop->ifindex); if (ifp) rtentry.rt_dev = ifp->name; else return -1; } if (cmd == SIOCADDRT) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); nexthop_num++; break; } } /* If there is no useful nexthop then return. */ if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("netlink_route_multipath(): No useful nexthop."); return 0; } skip: memset (&sin_mask, 0, sizeof (struct sockaddr_in)); sin_mask.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin_mask.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ masklen2ip (p->prefixlen, &sin_mask.sin_addr); /* Set destination address, mask and gateway.*/ memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); if (rtentry.rt_flags & RTF_GATEWAY) memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); #ifndef SUNOS_5 memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); #endif /* SUNOS_5 */ /* Metric. It seems metric minus one value is installed... */ rtentry.rt_metric = rib->metric; /* Routing entry flag set. */ if (p->prefixlen == 32) rtentry.rt_flags |= RTF_HOST; rtentry.rt_flags |= RTF_UP; /* Additional flags */ /* rtentry.rt_flags |= flags; */ /* For tagging route. */ /* rtentry.rt_flags |= RTF_DYNAMIC; */ /* Open socket for ioctl. */ sock = socket (AF_INET, SOCK_DGRAM, 0); if (sock < 0) { zlog_warn ("can't make socket\n"); return -1; } /* Send message by ioctl(). */ ret = ioctl (sock, cmd, &rtentry); if (ret < 0) { switch (errno) { case EEXIST: close (sock); return ZEBRA_ERR_RTEXIST; break; case ENETUNREACH: close (sock); return ZEBRA_ERR_RTUNREACH; break; case EPERM: close (sock); return ZEBRA_ERR_EPERM; break; } close (sock); zlog_warn ("write : %s (%d)", safe_strerror (errno), errno); return ret; } close (sock); return ret; } int kernel_add_ipv4 (struct prefix *p, struct rib *rib) { return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET); } int kernel_delete_ipv4 (struct prefix *p, struct rib *rib) { return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET); } #ifdef HAVE_IPV6 /* Below is hack for GNU libc definition and Linux 2.1.X header. */ #undef RTF_DEFAULT #undef RTF_ADDRCONF #include #if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 /* struct in6_rtmsg will be declared in net/route.h. */ #else #include #endif static int kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate, int index, int flags) { int ret; int sock; struct in6_rtmsg rtm; memset (&rtm, 0, sizeof (struct in6_rtmsg)); rtm.rtmsg_flags |= RTF_UP; rtm.rtmsg_metric = 1; memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr)); rtm.rtmsg_dst_len = dest->prefixlen; /* We need link local index. But this should be done caller... if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) { index = if_index_address (&rtm.rtmsg_gateway); rtm.rtmsg_ifindex = index; } else rtm.rtmsg_ifindex = 0; */ rtm.rtmsg_flags |= RTF_GATEWAY; /* For tagging route. */ /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr)); if (index) rtm.rtmsg_ifindex = index; else rtm.rtmsg_ifindex = 0; rtm.rtmsg_metric = 1; sock = socket (AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { zlog_warn ("can't make socket\n"); return -1; } /* Send message via ioctl. */ ret = ioctl (sock, type, &rtm); if (ret < 0) { zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete", safe_strerror(errno)); ret = errno; close (sock); return ret; } close (sock); return ret; } static int kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib, int family) { int ret; int sock; struct in6_rtmsg rtm; struct nexthop *nexthop, *tnexthop; int recursing; int nexthop_num = 0; memset (&rtm, 0, sizeof (struct in6_rtmsg)); rtm.rtmsg_flags |= RTF_UP; rtm.rtmsg_metric = rib->metric; memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr)); rtm.rtmsg_dst_len = p->prefixlen; /* We need link local index. But this should be done caller... if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) { index = if_index_address (&rtm.rtmsg_gateway); rtm.rtmsg_ifindex = index; } else rtm.rtmsg_ifindex = 0; */ rtm.rtmsg_flags |= RTF_GATEWAY; /* For tagging route. */ /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ /* Make gateway. */ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; if ((cmd == SIOCADDRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == SIOCDELRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6, sizeof (struct in6_addr)); } if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) rtm.rtmsg_ifindex = nexthop->ifindex; else rtm.rtmsg_ifindex = 0; if (cmd == SIOCADDRT) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); nexthop_num++; break; } } /* If there is no useful nexthop then return. */ if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("netlink_route_multipath(): No useful nexthop."); return 0; } sock = socket (AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { zlog_warn ("can't make socket\n"); return -1; } /* Send message via ioctl. */ ret = ioctl (sock, cmd, &rtm); if (ret < 0) { zlog_warn ("can't %s ipv6 route: %s\n", cmd == SIOCADDRT ? "add" : "delete", safe_strerror(errno)); ret = errno; close (sock); return ret; } close (sock); return ret; } int kernel_add_ipv6 (struct prefix *p, struct rib *rib) { return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6); } int kernel_delete_ipv6 (struct prefix *p, struct rib *rib) { return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6); } /* Delete IPv6 route from the kernel. */ int kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, unsigned int index, int flags, int table) { return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags); } #endif /* HAVE_IPV6 */