| /* |
| * 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 <zebra.h> |
| |
| #include "prefix.h" |
| #include "log.h" |
| #include "if.h" |
| |
| #include "zebra/rib.h" |
| #include "zebra/debug.h" |
| |
| /* Initialize of kernel interface. There is no kernel communication |
| support under ioctl(). So this is dummy stub function. */ |
| void |
| kernel_init () |
| { |
| return; |
| } |
| |
| /* Dummy function of routing socket. */ |
| void |
| kernel_read (int sock) |
| { |
| return; |
| } |
| |
| #if 0 |
| /* Initialization prototype of struct sockaddr_in. */ |
| static struct sockaddr_in sin_proto = |
| { |
| #ifdef HAVE_SIN_LEN |
| sizeof (struct sockaddr_in), |
| #endif /* HAVE_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_SIN_LEN |
| sin_dest.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_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_SIN_LEN |
| sin_gate.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_SIN_LEN */ |
| sin_gate.sin_addr = *gate; |
| } |
| |
| memset (&sin_mask, 0, sizeof (struct sockaddr_in)); |
| sin_mask.sin_family = AF_INET; |
| #ifdef HAVE_SIN_LEN |
| sin_gate.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_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. */ |
| 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; |
| 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_SIN_LEN |
| sin_dest.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_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 (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) |
| SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); |
| |
| goto skip; |
| } |
| |
| memset (&sin_gate, 0, sizeof (struct sockaddr_in)); |
| |
| /* Make gateway. */ |
| for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) |
| { |
| if ((cmd == SIOCADDRT |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) |
| || (cmd == SIOCDELRT |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) |
| { |
| if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) |
| { |
| if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || |
| nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) |
| { |
| sin_gate.sin_family = AF_INET; |
| #ifdef HAVE_SIN_LEN |
| sin_gate.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_SIN_LEN */ |
| sin_gate.sin_addr = nexthop->rgate.ipv4; |
| rtentry.rt_flags |= RTF_GATEWAY; |
| } |
| if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX |
| || nexthop->rtype == NEXTHOP_TYPE_IFNAME) |
| { |
| ifp = if_lookup_by_index (nexthop->rifindex); |
| if (ifp) |
| rtentry.rt_dev = ifp->name; |
| else |
| return -1; |
| } |
| } |
| else |
| { |
| if (nexthop->type == NEXTHOP_TYPE_IPV4 || |
| nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) |
| { |
| sin_gate.sin_family = AF_INET; |
| #ifdef HAVE_SIN_LEN |
| sin_gate.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_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_info ("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_SIN_LEN |
| sin_mask.sin_len = sizeof (struct sockaddr_in); |
| #endif /* HAVE_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 <asm/types.h> |
| |
| #if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 |
| /* struct in6_rtmsg will be declared in net/route.h. */ |
| #else |
| #include <linux/ipv6_route.h> |
| #endif |
| |
| 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; |
| } |
| |
| 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; |
| 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 (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) |
| { |
| if ((cmd == SIOCADDRT |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) |
| || (cmd == SIOCDELRT |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) |
| { |
| if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) |
| { |
| if (nexthop->rtype == NEXTHOP_TYPE_IPV6 |
| || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) |
| { |
| memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6, |
| sizeof (struct in6_addr)); |
| } |
| if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX |
| || nexthop->rtype == NEXTHOP_TYPE_IFNAME |
| || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) |
| rtm.rtmsg_ifindex = nexthop->rifindex; |
| else |
| rtm.rtmsg_ifindex = 0; |
| |
| } |
| else |
| { |
| 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_info ("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, |
| int index, int flags, int table) |
| { |
| return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags); |
| } |
| #endif /* HAVE_IPV6 */ |