| /* |
| * Kernel routing table updates by routing socket. |
| * 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 "if.h" |
| #include "prefix.h" |
| #include "sockunion.h" |
| #include "log.h" |
| #include "str.h" |
| #include "privs.h" |
| |
| #include "zebra/debug.h" |
| #include "zebra/rib.h" |
| #include "zebra/rt.h" |
| |
| extern struct zebra_privs_t zserv_privs; |
| |
| /* kernel socket export */ |
| extern int rtm_write (int message, union sockunion *dest, |
| union sockunion *mask, union sockunion *gate, |
| unsigned int index, int zebra_flags, int metric); |
| |
| /* Adjust netmask socket length. Return value is a adjusted sin_len |
| value. */ |
| static int |
| sin_masklen (struct in_addr mask) |
| { |
| char *p, *lim; |
| int len; |
| struct sockaddr_in sin; |
| |
| if (mask.s_addr == 0) |
| return sizeof (long); |
| |
| sin.sin_addr = mask; |
| len = sizeof (struct sockaddr_in); |
| |
| lim = (char *) &sin.sin_addr; |
| p = lim + sizeof (sin.sin_addr); |
| |
| while (*--p == 0 && p >= lim) |
| len--; |
| return len; |
| } |
| |
| /* Interface between zebra message and rtm message. */ |
| static int |
| kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) |
| |
| { |
| struct sockaddr_in *mask = NULL; |
| struct sockaddr_in sin_dest, sin_mask, sin_gate; |
| struct nexthop *nexthop; |
| int nexthop_num = 0; |
| unsigned int ifindex = 0; |
| int gate = 0; |
| int error; |
| |
| 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; |
| |
| memset (&sin_mask, 0, sizeof (struct sockaddr_in)); |
| |
| 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 */ |
| |
| /* Make gateway. */ |
| for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) |
| { |
| gate = 0; |
| |
| if ((cmd == RTM_ADD |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) |
| || (cmd == RTM_DELETE |
| #if 0 |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) |
| #endif |
| )) |
| { |
| if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) |
| { |
| if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || |
| nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) |
| { |
| sin_gate.sin_addr = nexthop->rgate.ipv4; |
| gate = 1; |
| } |
| if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX |
| || nexthop->rtype == NEXTHOP_TYPE_IFNAME |
| || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) |
| ifindex = nexthop->rifindex; |
| } |
| else |
| { |
| if (nexthop->type == NEXTHOP_TYPE_IPV4 || |
| nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) |
| { |
| sin_gate.sin_addr = nexthop->gate.ipv4; |
| gate = 1; |
| } |
| if (nexthop->type == NEXTHOP_TYPE_IFINDEX |
| || nexthop->type == NEXTHOP_TYPE_IFNAME |
| || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) |
| ifindex = nexthop->ifindex; |
| if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) |
| { |
| struct in_addr loopback; |
| loopback.s_addr = htonl (INADDR_LOOPBACK); |
| sin_gate.sin_addr = loopback; |
| gate = 1; |
| } |
| } |
| |
| if (cmd == RTM_ADD) |
| SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); |
| |
| if (gate && p->prefixlen == 32) |
| mask = NULL; |
| else |
| { |
| masklen2ip (p->prefixlen, &sin_mask.sin_addr); |
| sin_mask.sin_family = AF_UNSPEC; |
| #ifdef HAVE_SIN_LEN |
| sin_mask.sin_len = sin_masklen (sin_mask.sin_addr); |
| #endif /* HAVE_SIN_LEN */ |
| mask = &sin_mask; |
| } |
| } |
| |
| error = rtm_write (cmd, |
| (union sockunion *)&sin_dest, |
| (union sockunion *)mask, |
| gate ? (union sockunion *)&sin_gate : NULL, |
| ifindex, |
| rib->flags, |
| rib->metric); |
| |
| #if 0 |
| if (error) |
| { |
| zlog_info ("kernel_rtm_ipv4(): nexthop %d add error=%d.", |
| nexthop_num, error); |
| } |
| #endif |
| |
| nexthop_num++; |
| } |
| |
| /* If there is no useful nexthop then return. */ |
| if (nexthop_num == 0) |
| { |
| if (IS_ZEBRA_DEBUG_KERNEL) |
| zlog_debug ("kernel_rtm_ipv4(): No useful nexthop."); |
| return 0; |
| } |
| |
| return 0; /*XXX*/ |
| } |
| |
| int |
| kernel_add_ipv4 (struct prefix *p, struct rib *rib) |
| { |
| int route; |
| |
| if (zserv_privs.change(ZPRIVS_RAISE)) |
| zlog (NULL, LOG_ERR, "Can't raise privileges"); |
| route = kernel_rtm_ipv4 (RTM_ADD, p, rib, AF_INET); |
| if (zserv_privs.change(ZPRIVS_LOWER)) |
| zlog (NULL, LOG_ERR, "Can't lower privileges"); |
| |
| return route; |
| } |
| |
| int |
| kernel_delete_ipv4 (struct prefix *p, struct rib *rib) |
| { |
| int route; |
| |
| if (zserv_privs.change(ZPRIVS_RAISE)) |
| zlog (NULL, LOG_ERR, "Can't raise privileges"); |
| route = kernel_rtm_ipv4 (RTM_DELETE, p, rib, AF_INET); |
| if (zserv_privs.change(ZPRIVS_LOWER)) |
| zlog (NULL, LOG_ERR, "Can't lower privileges"); |
| |
| return route; |
| } |
| |
| #ifdef HAVE_IPV6 |
| |
| /* Calculate sin6_len value for netmask socket value. */ |
| static int |
| sin6_masklen (struct in6_addr mask) |
| { |
| struct sockaddr_in6 sin6; |
| char *p, *lim; |
| int len; |
| |
| #if defined (INRIA) |
| if (IN_ANYADDR6 (mask)) |
| return sizeof (long); |
| #else /* ! INRIA */ |
| if (IN6_IS_ADDR_UNSPECIFIED (&mask)) |
| return sizeof (long); |
| #endif /* ! INRIA */ |
| |
| sin6.sin6_addr = mask; |
| len = sizeof (struct sockaddr_in6); |
| |
| lim = (char *) & sin6.sin6_addr; |
| p = lim + sizeof (sin6.sin6_addr); |
| |
| while (*--p == 0 && p >= lim) |
| len--; |
| |
| return len; |
| } |
| |
| /* Interface between zebra message and rtm message. */ |
| static int |
| kernel_rtm_ipv6 (int message, struct prefix_ipv6 *dest, |
| struct in6_addr *gate, int index, int flags) |
| { |
| struct sockaddr_in6 *mask; |
| struct sockaddr_in6 sin_dest, sin_mask, sin_gate; |
| |
| memset (&sin_dest, 0, sizeof (struct sockaddr_in6)); |
| sin_dest.sin6_family = AF_INET6; |
| #ifdef SIN6_LEN |
| sin_dest.sin6_len = sizeof (struct sockaddr_in6); |
| #endif /* SIN6_LEN */ |
| |
| memset (&sin_mask, 0, sizeof (struct sockaddr_in6)); |
| |
| memset (&sin_gate, 0, sizeof (struct sockaddr_in6)); |
| sin_gate.sin6_family = AF_INET6; |
| #ifdef SIN6_LEN |
| sin_gate.sin6_len = sizeof (struct sockaddr_in6); |
| #endif /* SIN6_LEN */ |
| |
| sin_dest.sin6_addr = dest->prefix; |
| |
| if (gate) |
| memcpy (&sin_gate.sin6_addr, gate, sizeof (struct in6_addr)); |
| |
| /* Under kame set interface index to link local address. */ |
| #ifdef KAME |
| |
| #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ |
| do { \ |
| (a).s6_addr[2] = ((i) >> 8) & 0xff; \ |
| (a).s6_addr[3] = (i) & 0xff; \ |
| } while (0) |
| |
| if (gate && IN6_IS_ADDR_LINKLOCAL(gate)) |
| SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, index); |
| #endif /* KAME */ |
| |
| if (gate && dest->prefixlen == 128) |
| mask = NULL; |
| else |
| { |
| masklen2ip6 (dest->prefixlen, &sin_mask.sin6_addr); |
| sin_mask.sin6_family = AF_INET6; |
| #ifdef SIN6_LEN |
| sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr); |
| #endif /* SIN6_LEN */ |
| mask = &sin_mask; |
| } |
| |
| return rtm_write (message, |
| (union sockunion *) &sin_dest, |
| (union sockunion *) mask, |
| gate ? (union sockunion *)&sin_gate : NULL, |
| index, |
| flags, |
| 0); |
| } |
| |
| /* Interface between zebra message and rtm message. */ |
| static int |
| kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, |
| int family) |
| { |
| struct sockaddr_in6 *mask; |
| struct sockaddr_in6 sin_dest, sin_mask, sin_gate; |
| struct nexthop *nexthop; |
| int nexthop_num = 0; |
| unsigned int ifindex = 0; |
| int gate = 0; |
| int error; |
| |
| memset (&sin_dest, 0, sizeof (struct sockaddr_in6)); |
| sin_dest.sin6_family = AF_INET6; |
| #ifdef SIN6_LEN |
| sin_dest.sin6_len = sizeof (struct sockaddr_in6); |
| #endif /* SIN6_LEN */ |
| sin_dest.sin6_addr = p->u.prefix6; |
| |
| memset (&sin_mask, 0, sizeof (struct sockaddr_in6)); |
| |
| memset (&sin_gate, 0, sizeof (struct sockaddr_in6)); |
| sin_gate.sin6_family = AF_INET6; |
| #ifdef HAVE_SIN_LEN |
| sin_gate.sin6_len = sizeof (struct sockaddr_in6); |
| #endif /* HAVE_SIN_LEN */ |
| |
| /* Make gateway. */ |
| for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) |
| { |
| gate = 0; |
| |
| if ((cmd == RTM_ADD |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) |
| || (cmd == RTM_DELETE |
| #if 0 |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) |
| #endif |
| )) |
| { |
| 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) |
| { |
| sin_gate.sin6_addr = nexthop->rgate.ipv6; |
| gate = 1; |
| } |
| if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX |
| || nexthop->rtype == NEXTHOP_TYPE_IFNAME |
| || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) |
| ifindex = nexthop->rifindex; |
| } |
| else |
| { |
| if (nexthop->type == NEXTHOP_TYPE_IPV6 |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) |
| { |
| sin_gate.sin6_addr = nexthop->gate.ipv6; |
| gate = 1; |
| } |
| if (nexthop->type == NEXTHOP_TYPE_IFINDEX |
| || nexthop->type == NEXTHOP_TYPE_IFNAME |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) |
| ifindex = nexthop->ifindex; |
| } |
| |
| if (cmd == RTM_ADD) |
| SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); |
| } |
| |
| /* Under kame set interface index to link local address. */ |
| #ifdef KAME |
| |
| #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ |
| do { \ |
| (a).s6_addr[2] = ((i) >> 8) & 0xff; \ |
| (a).s6_addr[3] = (i) & 0xff; \ |
| } while (0) |
| |
| if (gate && IN6_IS_ADDR_LINKLOCAL(&sin_gate.sin6_addr)) |
| SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, ifindex); |
| #endif /* KAME */ |
| |
| if (gate && p->prefixlen == 128) |
| mask = NULL; |
| else |
| { |
| masklen2ip6 (p->prefixlen, &sin_mask.sin6_addr); |
| sin_mask.sin6_family = AF_INET6; |
| #ifdef SIN6_LEN |
| sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr); |
| #endif /* SIN6_LEN */ |
| mask = &sin_mask; |
| } |
| |
| error = rtm_write (cmd, |
| (union sockunion *) &sin_dest, |
| (union sockunion *) mask, |
| gate ? (union sockunion *)&sin_gate : NULL, |
| ifindex, |
| rib->flags, |
| rib->metric); |
| |
| #if 0 |
| if (error) |
| { |
| zlog_info ("kernel_rtm_ipv6_multipath(): nexthop %d add error=%d.", |
| nexthop_num, error); |
| } |
| #endif |
| |
| nexthop_num++; |
| } |
| |
| /* If there is no useful nexthop then return. */ |
| if (nexthop_num == 0) |
| { |
| if (IS_ZEBRA_DEBUG_KERNEL) |
| zlog_debug ("kernel_rtm_ipv6_multipath(): No useful nexthop."); |
| return 0; |
| } |
| |
| return 0; /*XXX*/ |
| } |
| |
| int |
| kernel_add_ipv6 (struct prefix *p, struct rib *rib) |
| { |
| int route; |
| |
| if (zserv_privs.change(ZPRIVS_RAISE)) |
| zlog (NULL, LOG_ERR, "Can't raise privileges"); |
| route = kernel_rtm_ipv6_multipath (RTM_ADD, p, rib, AF_INET6); |
| if (zserv_privs.change(ZPRIVS_LOWER)) |
| zlog (NULL, LOG_ERR, "Can't lower privileges"); |
| |
| return route; |
| } |
| |
| int |
| kernel_delete_ipv6 (struct prefix *p, struct rib *rib) |
| { |
| int route; |
| |
| if (zserv_privs.change(ZPRIVS_RAISE)) |
| zlog (NULL, LOG_ERR, "Can't raise privileges"); |
| route = kernel_rtm_ipv6_multipath (RTM_DELETE, p, rib, AF_INET6); |
| if (zserv_privs.change(ZPRIVS_LOWER)) |
| zlog (NULL, LOG_ERR, "Can't lower privileges"); |
| |
| return route; |
| } |
| |
| /* 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) |
| { |
| int route; |
| |
| if (zserv_privs.change(ZPRIVS_RAISE)) |
| zlog (NULL, LOG_ERR, "Can't raise privileges"); |
| route = kernel_rtm_ipv6 (RTM_DELETE, dest, gate, index, flags); |
| if (zserv_privs.change(ZPRIVS_LOWER)) |
| zlog (NULL, LOG_ERR, "Can't lower privileges"); |
| |
| return route; |
| } |
| #endif /* HAVE_IPV6 */ |