summaryrefslogtreecommitdiff
path: root/zebra/rt_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/rt_socket.c')
-rw-r--r--zebra/rt_socket.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
new file mode 100644
index 00000000..fe88be81
--- /dev/null
+++ b/zebra/rt_socket.c
@@ -0,0 +1,441 @@
+/*
+ * 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 "zebra/debug.h"
+#include "zebra/rib.h"
+
+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. */
+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. */
+int
+kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family)
+
+{
+ struct sockaddr_in *mask;
+ 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_info ("kernel_rtm_ipv4(): No useful nexthop.");
+ return 0;
+ }
+
+ return 0; /*XXX*/
+}
+
+int
+kernel_add_ipv4 (struct prefix *p, struct rib *rib)
+{
+ return kernel_rtm_ipv4 (RTM_ADD, p, rib, AF_INET);
+}
+
+int
+kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
+{
+ return kernel_rtm_ipv4 (RTM_DELETE, p, rib, AF_INET);
+}
+
+#ifdef HAVE_IPV6
+
+/* Calculate sin6_len value for netmask socket value. */
+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. */
+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_UNSPEC;
+#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. */
+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_UNSPEC;
+#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_info ("kernel_rtm_ipv6_multipath(): No useful nexthop.");
+ return 0;
+ }
+
+ return 0; /*XXX*/
+}
+
+int
+kernel_add_ipv6 (struct prefix *p, struct rib *rib)
+{
+ return kernel_rtm_ipv6_multipath (RTM_ADD, p, rib, AF_INET6);
+}
+
+int
+kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
+{
+ return kernel_rtm_ipv6_multipath (RTM_DELETE, 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_rtm_ipv6 (RTM_DELETE, dest, gate, index, flags);
+}
+#endif /* HAVE_IPV6 */