summaryrefslogtreecommitdiff
path: root/zebra/kernel_socket.c
diff options
context:
space:
mode:
authorpaul <paul>2002-12-13 20:15:29 +0000
committerpaul <paul>2002-12-13 20:15:29 +0000
commit718e3744195351130f4ce7dbe0613f4b3e23df93 (patch)
treebac2ad39971cd43f31241ef123bd4e470f695ac9 /zebra/kernel_socket.c
Initial revision
Diffstat (limited to 'zebra/kernel_socket.c')
-rw-r--r--zebra/kernel_socket.c811
1 files changed, 811 insertions, 0 deletions
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
new file mode 100644
index 00000000..a47f4f63
--- /dev/null
+++ b/zebra/kernel_socket.c
@@ -0,0 +1,811 @@
+/* Kernel communication using routing socket.
+ * Copyright (C) 1999 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 "connected.h"
+#include "memory.h"
+#include "ioctl.h"
+#include "log.h"
+#include "str.h"
+#include "table.h"
+#include "rib.h"
+
+#include "zebra/interface.h"
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+
+/* Socket length roundup function. */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+/* And this macro is wrapper for handling sa_len. */
+#ifdef HAVE_SA_LEN
+#define WRAPUP(X) ROUNDUP(((struct sockaddr *)(X))->sa_len)
+#else
+#define WRAPUP(X) ROUNDUP(sizeof (struct sockaddr))
+#endif /* HAVE_SA_LEN */
+
+/* Routing socket message types. */
+struct message rtm_type_str[] =
+{
+ {RTM_ADD, "RTM_ADD"},
+ {RTM_DELETE, "RTM_DELETE"},
+ {RTM_CHANGE, "RTM_CHANGE"},
+ {RTM_GET, "RTM_GET"},
+ {RTM_LOSING, "RTM_LOSING"},
+ {RTM_REDIRECT, "RTM_REDIRECT"},
+ {RTM_MISS, "RTM_MISS"},
+ {RTM_LOCK, "RTM_LOCK"},
+ {RTM_OLDADD, "RTM_OLDADD"},
+ {RTM_OLDDEL, "RTM_OLDDEL"},
+ {RTM_RESOLVE, "RTM_RESOLVE"},
+ {RTM_NEWADDR, "RTM_NEWADDR"},
+ {RTM_DELADDR, "RTM_DELADDR"},
+ {RTM_IFINFO, "RTM_IFINFO"},
+#ifdef RTM_OIFINFO
+ {RTM_OIFINFO, "RTM_OIFINFO"},
+#endif /* RTM_OIFINFO */
+#ifdef RTM_NEWMADDR
+ {RTM_NEWMADDR, "RTM_NEWMADDR"},
+#endif /* RTM_NEWMADDR */
+#ifdef RTM_DELMADDR
+ {RTM_DELMADDR, "RTM_DELMADDR"},
+#endif /* RTM_DELMADDR */
+#ifdef RTM_IFANNOUNCE
+ {RTM_IFANNOUNCE, "RTM_IFANNOUNCE"},
+#endif /* RTM_IFANNOUNCE */
+ {0, NULL}
+};
+
+struct message rtm_flag_str[] =
+{
+ {RTF_UP, "UP"},
+ {RTF_GATEWAY, "GATEWAY"},
+ {RTF_HOST, "HOST"},
+ {RTF_REJECT, "REJECT"},
+ {RTF_DYNAMIC, "DYNAMIC"},
+ {RTF_MODIFIED, "MODIFIED"},
+ {RTF_DONE, "DONE"},
+#ifdef RTF_MASK
+ {RTF_MASK, "MASK"},
+#endif /* RTF_MASK */
+ {RTF_CLONING, "CLONING"},
+ {RTF_XRESOLVE, "XRESOLVE"},
+ {RTF_LLINFO, "LLINFO"},
+ {RTF_STATIC, "STATIC"},
+ {RTF_BLACKHOLE, "BLACKHOLE"},
+ {RTF_PROTO1, "PROTO1"},
+ {RTF_PROTO2, "PROTO2"},
+#ifdef RTF_PRCLONING
+ {RTF_PRCLONING, "PRCLONING"},
+#endif /* RTF_PRCLONING */
+#ifdef RTF_WASCLONED
+ {RTF_WASCLONED, "WASCLONED"},
+#endif /* RTF_WASCLONED */
+#ifdef RTF_PROTO3
+ {RTF_PROTO3, "PROTO3"},
+#endif /* RTF_PROTO3 */
+#ifdef RTF_PINNED
+ {RTF_PINNED, "PINNED"},
+#endif /* RTF_PINNED */
+#ifdef RTF_LOCAL
+ {RTF_LOCAL, "LOCAL"},
+#endif /* RTF_LOCAL */
+#ifdef RTF_BROADCAST
+ {RTF_BROADCAST, "BROADCAST"},
+#endif /* RTF_BROADCAST */
+#ifdef RTF_MULTICAST
+ {RTF_MULTICAST, "MULTICAST"},
+#endif /* RTF_MULTICAST */
+ {0, NULL}
+};
+
+/* Kernel routing update socket. */
+int routing_sock = -1;
+
+/* Yes I'm checking ugly routing socket behavior. */
+/* #define DEBUG */
+
+/* Supported address family check. */
+static int
+af_check (int family)
+{
+ if (family == AF_INET)
+ return 1;
+#ifdef HAVE_IPV6
+ if (family == AF_INET6)
+ return 1;
+#endif /* HAVE_IPV6 */
+ return 0;
+}
+
+/* Dump routing table flag for debug purpose. */
+void
+rtm_flag_dump (int flag)
+{
+ struct message *mes;
+ static char buf[BUFSIZ];
+
+ for (mes = rtm_flag_str; mes->key != 0; mes++)
+ {
+ if (mes->key & flag)
+ {
+ strlcat (buf, mes->str, BUFSIZ);
+ strlcat (buf, " ", BUFSIZ);
+ }
+ }
+ zlog_info ("Kernel: %s", buf);
+}
+
+#ifdef RTM_IFANNOUNCE
+/* Interface adding function */
+int
+ifan_read (struct if_announcemsghdr *ifan)
+{
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index (ifan->ifan_index);
+ if (ifp == NULL && ifan->ifan_what == IFAN_ARRIVAL)
+ {
+ /* Create Interface */
+ ifp = if_get_by_name (ifan->ifan_name);
+ ifp->ifindex = ifan->ifan_index;
+
+ if_add_update (ifp);
+ }
+ else if (ifp != NULL && ifan->ifan_what == IFAN_DEPARTURE)
+ {
+ if_delete_update (ifp);
+ if_delete (ifp);
+ }
+
+ if_get_flags (ifp);
+ if_get_mtu (ifp);
+ if_get_metric (ifp);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_info ("interface %s index %d", ifp->name, ifp->ifindex);
+
+ return 0;
+}
+#endif /* RTM_IFANNOUNCE */
+
+/* Interface adding function called from interface_list. */
+int
+ifm_read (struct if_msghdr *ifm)
+{
+ struct interface *ifp;
+ struct sockaddr_dl *sdl = NULL;
+
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+
+ /* Use sdl index. */
+ ifp = if_lookup_by_index (ifm->ifm_index);
+
+ if (ifp == NULL)
+ {
+ /* Check interface's address.*/
+ if (! (ifm->ifm_addrs & RTA_IFP))
+ {
+ zlog_warn ("There must be RTA_IFP address for ifindex %d\n",
+ ifm->ifm_index);
+ return -1;
+ }
+
+ ifp = if_create ();
+
+ strncpy (ifp->name, sdl->sdl_data, sdl->sdl_nlen);
+ ifp->ifindex = ifm->ifm_index;
+ ifp->flags = ifm->ifm_flags;
+#if defined(__bsdi__)
+ if_kvm_get_mtu (ifp);
+#else
+ if_get_mtu (ifp);
+#endif /* __bsdi__ */
+ if_get_metric (ifp);
+
+ /* Fetch hardware address. */
+ if (sdl->sdl_family != AF_LINK)
+ {
+ zlog_warn ("sockaddr_dl->sdl_family is not AF_LINK");
+ return -1;
+ }
+ memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl));
+
+ if_add_update (ifp);
+ }
+ else
+ {
+ /* There is a case of promisc, allmulti flag modification. */
+ if (if_is_up (ifp))
+ {
+ ifp->flags = ifm->ifm_flags;
+ if (! if_is_up (ifp))
+ if_down (ifp);
+ }
+ else
+ {
+ ifp->flags = ifm->ifm_flags;
+ if (if_is_up (ifp))
+ if_up (ifp);
+ }
+ }
+
+#ifdef HAVE_NET_RT_IFLIST
+ ifp->stats = ifm->ifm_data;
+#endif /* HAVE_NET_RT_IFLIST */
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_info ("interface %s index %d", ifp->name, ifp->ifindex);
+
+ return 0;
+}
+
+/* Address read from struct ifa_msghdr. */
+void
+ifam_read_mesg (struct ifa_msghdr *ifm,
+ union sockunion *addr,
+ union sockunion *mask,
+ union sockunion *dest)
+{
+ caddr_t pnt, end;
+
+ pnt = (caddr_t)(ifm + 1);
+ end = ((caddr_t)ifm) + ifm->ifam_msglen;
+
+#define IFAMADDRGET(X,R) \
+ if (ifm->ifam_addrs & (R)) \
+ { \
+ int len = WRAPUP(pnt); \
+ if (((X) != NULL) && af_check (((struct sockaddr *)pnt)->sa_family)) \
+ memcpy ((caddr_t)(X), pnt, len); \
+ pnt += len; \
+ }
+#define IFAMMASKGET(X,R) \
+ if (ifm->ifam_addrs & (R)) \
+ { \
+ int len = WRAPUP(pnt); \
+ if ((X) != NULL) \
+ memcpy ((caddr_t)(X), pnt, len); \
+ pnt += len; \
+ }
+
+ /* Be sure structure is cleared */
+ memset (mask, 0, sizeof (union sockunion));
+ memset (addr, 0, sizeof (union sockunion));
+ memset (dest, 0, sizeof (union sockunion));
+
+ /* We fetch each socket variable into sockunion. */
+ IFAMADDRGET (NULL, RTA_DST);
+ IFAMADDRGET (NULL, RTA_GATEWAY);
+ IFAMMASKGET (mask, RTA_NETMASK);
+ IFAMADDRGET (NULL, RTA_GENMASK);
+ IFAMADDRGET (NULL, RTA_IFP);
+ IFAMADDRGET (addr, RTA_IFA);
+ IFAMADDRGET (NULL, RTA_AUTHOR);
+ IFAMADDRGET (dest, RTA_BRD);
+
+ /* Assert read up end point matches to end point */
+ if (pnt != end)
+ zlog_warn ("ifam_read() does't read all socket data");
+}
+
+/* Interface's address information get. */
+int
+ifam_read (struct ifa_msghdr *ifam)
+{
+ struct interface *ifp;
+ union sockunion addr, mask, gate;
+
+ /* Check does this interface exist or not. */
+ ifp = if_lookup_by_index (ifam->ifam_index);
+ if (ifp == NULL)
+ {
+ zlog_warn ("no interface for index %d", ifam->ifam_index);
+ return -1;
+ }
+
+ /* Allocate and read address information. */
+ ifam_read_mesg (ifam, &addr, &mask, &gate);
+
+ /* Check interface flag for implicit up of the interface. */
+ if_refresh (ifp);
+
+ /* Add connected address. */
+ switch (sockunion_family (&addr))
+ {
+ case AF_INET:
+ if (ifam->ifam_type == RTM_NEWADDR)
+ connected_add_ipv4 (ifp, 0, &addr.sin.sin_addr,
+ ip_masklen (mask.sin.sin_addr),
+ &gate.sin.sin_addr, NULL);
+ else
+ connected_delete_ipv4 (ifp, 0, &addr.sin.sin_addr,
+ ip_masklen (mask.sin.sin_addr),
+ &gate.sin.sin_addr, NULL);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ /* Unset interface index from link-local address when IPv6 stack
+ is KAME. */
+ if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr))
+ SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0);
+
+ if (ifam->ifam_type == RTM_NEWADDR)
+ connected_add_ipv6 (ifp,
+ &addr.sin6.sin6_addr,
+ ip6_masklen (mask.sin6.sin6_addr),
+ &gate.sin6.sin6_addr);
+ else
+ connected_delete_ipv6 (ifp,
+ &addr.sin6.sin6_addr,
+ ip6_masklen (mask.sin6.sin6_addr),
+ &gate.sin6.sin6_addr);
+ break;
+#endif /* HAVE_IPV6 */
+ default:
+ /* Unsupported family silently ignore... */
+ break;
+ }
+ return 0;
+}
+
+/* Interface function for reading kernel routing table information. */
+int
+rtm_read_mesg (struct rt_msghdr *rtm,
+ union sockunion *dest,
+ union sockunion *mask,
+ union sockunion *gate)
+{
+ caddr_t pnt, end;
+
+ /* Pnt points out socket data start point. */
+ pnt = (caddr_t)(rtm + 1);
+ end = ((caddr_t)rtm) + rtm->rtm_msglen;
+
+ /* rt_msghdr version check. */
+ if (rtm->rtm_version != RTM_VERSION)
+ zlog (NULL, LOG_WARNING,
+ "Routing message version different %d should be %d."
+ "This may cause problem\n", rtm->rtm_version, RTM_VERSION);
+
+#define RTMADDRGET(X,R) \
+ if (rtm->rtm_addrs & (R)) \
+ { \
+ int len = WRAPUP (pnt); \
+ if (((X) != NULL) && af_check (((struct sockaddr *)pnt)->sa_family)) \
+ memcpy ((caddr_t)(X), pnt, len); \
+ pnt += len; \
+ }
+#define RTMMASKGET(X,R) \
+ if (rtm->rtm_addrs & (R)) \
+ { \
+ int len = WRAPUP (pnt); \
+ if ((X) != NULL) \
+ memcpy ((caddr_t)(X), pnt, len); \
+ pnt += len; \
+ }
+
+ /* Be sure structure is cleared */
+ memset (dest, 0, sizeof (union sockunion));
+ memset (gate, 0, sizeof (union sockunion));
+ memset (mask, 0, sizeof (union sockunion));
+
+ /* We fetch each socket variable into sockunion. */
+ RTMADDRGET (dest, RTA_DST);
+ RTMADDRGET (gate, RTA_GATEWAY);
+ RTMMASKGET (mask, RTA_NETMASK);
+ RTMADDRGET (NULL, RTA_GENMASK);
+ RTMADDRGET (NULL, RTA_IFP);
+ RTMADDRGET (NULL, RTA_IFA);
+ RTMADDRGET (NULL, RTA_AUTHOR);
+ RTMADDRGET (NULL, RTA_BRD);
+
+ /* If there is netmask information set it's family same as
+ destination family*/
+ if (rtm->rtm_addrs & RTA_NETMASK)
+ mask->sa.sa_family = dest->sa.sa_family;
+
+ /* Assert read up to the end of pointer. */
+ if (pnt != end)
+ zlog (NULL, LOG_WARNING, "rtm_read() does't read all socket data.");
+
+ return rtm->rtm_flags;
+}
+
+void
+rtm_read (struct rt_msghdr *rtm)
+{
+ int flags;
+ u_char zebra_flags;
+ union sockunion dest, mask, gate;
+
+ zebra_flags = 0;
+
+ /* Discard self send message. */
+ if (rtm->rtm_type != RTM_GET
+ && (rtm->rtm_pid == pid || rtm->rtm_pid == old_pid))
+ return;
+
+ /* Read destination and netmask and gateway from rtm message
+ structure. */
+ flags = rtm_read_mesg (rtm, &dest, &mask, &gate);
+
+#ifdef RTF_CLONED /*bsdi, netbsd 1.6*/
+ if (flags & RTF_CLONED)
+ return;
+#endif
+#ifdef RTF_WASCLONED /*freebsd*/
+ if (flags & RTF_WASCLONED)
+ return;
+#endif
+
+ if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP))
+ return;
+
+ /* This is connected route. */
+ if (! (flags & RTF_GATEWAY))
+ return;
+
+ if (flags & RTF_PROTO1)
+ SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE);
+
+ /* This is persistent route. */
+ if (flags & RTF_STATIC)
+ SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC);
+
+ if (dest.sa.sa_family == AF_INET)
+ {
+ struct prefix_ipv4 p;
+
+ p.family = AF_INET;
+ p.prefix = dest.sin.sin_addr;
+ if (flags & RTF_HOST)
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ else
+ p.prefixlen = ip_masklen (mask.sin.sin_addr);
+
+ if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD)
+ rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags,
+ &p, &gate.sin.sin_addr, 0, 0, 0, 0);
+ else
+ rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags,
+ &p, &gate.sin.sin_addr, 0, 0);
+ }
+#ifdef HAVE_IPV6
+ if (dest.sa.sa_family == AF_INET6)
+ {
+ struct prefix_ipv6 p;
+ unsigned int ifindex = 0;
+
+ p.family = AF_INET6;
+ p.prefix = dest.sin6.sin6_addr;
+ if (flags & RTF_HOST)
+ p.prefixlen = IPV6_MAX_PREFIXLEN;
+ else
+ p.prefixlen = ip6_masklen (mask.sin6.sin6_addr);
+
+#ifdef KAME
+ if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr))
+ {
+ ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr);
+ SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0);
+ }
+#endif /* KAME */
+
+ if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD)
+ rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
+ &p, &gate.sin6.sin6_addr, ifindex, 0);
+ else
+ rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
+ &p, &gate.sin6.sin6_addr, ifindex, 0);
+ }
+#endif /* HAVE_IPV6 */
+}
+
+/* Interface function for the kernel routing table updates. Support
+ for RTM_CHANGE will be needed. */
+int
+rtm_write (int message,
+ union sockunion *dest,
+ union sockunion *mask,
+ union sockunion *gate,
+ unsigned int index,
+ int zebra_flags,
+ int metric)
+{
+ int ret;
+ caddr_t pnt;
+ struct interface *ifp;
+ struct sockaddr_in tmp_gate;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 tmp_gate6;
+#endif /* HAVE_IPV6 */
+
+ /* Sequencial number of routing message. */
+ static int msg_seq = 0;
+
+ /* Struct of rt_msghdr and buffer for storing socket's data. */
+ struct
+ {
+ struct rt_msghdr rtm;
+ char buf[512];
+ } msg;
+
+ memset (&tmp_gate, 0, sizeof (struct sockaddr_in));
+ tmp_gate.sin_family = AF_INET;
+#ifdef HAVE_SIN_LEN
+ tmp_gate.sin_len = sizeof (struct sockaddr_in);
+#endif /* HAVE_SIN_LEN */
+
+#ifdef HAVE_IPV6
+ memset (&tmp_gate6, 0, sizeof (struct sockaddr_in6));
+ tmp_gate6.sin6_family = AF_INET6;
+#ifdef SIN6_LEN
+ tmp_gate6.sin6_len = sizeof (struct sockaddr_in6);
+#endif /* SIN6_LEN */
+#endif /* HAVE_IPV6 */
+
+ if (routing_sock < 0)
+ return ZEBRA_ERR_EPERM;
+
+ /* Clear and set rt_msghdr values */
+ memset (&msg, 0, sizeof (struct rt_msghdr));
+ msg.rtm.rtm_version = RTM_VERSION;
+ msg.rtm.rtm_type = message;
+ msg.rtm.rtm_seq = msg_seq++;
+ msg.rtm.rtm_addrs = RTA_DST;
+ msg.rtm.rtm_addrs |= RTA_GATEWAY;
+ msg.rtm.rtm_flags = RTF_UP;
+ msg.rtm.rtm_index = index;
+
+ if (metric != 0)
+ {
+ msg.rtm.rtm_rmx.rmx_hopcount = metric;
+ msg.rtm.rtm_inits |= RTV_HOPCOUNT;
+ }
+
+ ifp = if_lookup_by_index (index);
+
+ if (gate && message == RTM_ADD)
+ msg.rtm.rtm_flags |= RTF_GATEWAY;
+
+ if (! gate && message == RTM_ADD && ifp &&
+ (ifp->flags & IFF_POINTOPOINT) == 0)
+ msg.rtm.rtm_flags |= RTF_CLONING;
+
+ /* If no protocol specific gateway is specified, use link
+ address for gateway. */
+ if (! gate)
+ {
+ if (!ifp)
+ {
+ zlog_warn ("no gateway found for interface index %d", index);
+ return -1;
+ }
+ gate = (union sockunion *) & ifp->sdl;
+ }
+
+ if (mask)
+ msg.rtm.rtm_addrs |= RTA_NETMASK;
+ else if (message == RTM_ADD)
+ msg.rtm.rtm_flags |= RTF_HOST;
+
+ /* Tagging route with flags */
+ msg.rtm.rtm_flags |= (RTF_PROTO1);
+
+ /* Additional flags. */
+ if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
+ msg.rtm.rtm_flags |= RTF_BLACKHOLE;
+
+#ifdef HAVE_SIN_LEN
+#define SOCKADDRSET(X,R) \
+ if (msg.rtm.rtm_addrs & (R)) \
+ { \
+ int len = ROUNDUP ((X)->sa.sa_len); \
+ memcpy (pnt, (caddr_t)(X), len); \
+ pnt += len; \
+ }
+#else
+#define SOCKADDRSET(X,R) \
+ if (msg.rtm.rtm_addrs & (R)) \
+ { \
+ int len = ROUNDUP (sizeof((X)->sa)); \
+ memcpy (pnt, (caddr_t)(X), len); \
+ pnt += len; \
+ }
+#endif /* HAVE_SIN_LEN */
+
+ pnt = (caddr_t) msg.buf;
+
+ /* Write each socket data into rtm message buffer */
+ SOCKADDRSET (dest, RTA_DST);
+ SOCKADDRSET (gate, RTA_GATEWAY);
+ SOCKADDRSET (mask, RTA_NETMASK);
+
+ msg.rtm.rtm_msglen = pnt - (caddr_t) &msg;
+
+ ret = write (routing_sock, &msg, msg.rtm.rtm_msglen);
+
+ if (ret != msg.rtm.rtm_msglen)
+ {
+ if (errno == EEXIST)
+ return ZEBRA_ERR_RTEXIST;
+ if (errno == ENETUNREACH)
+ return ZEBRA_ERR_RTUNREACH;
+
+ zlog_warn ("write : %s (%d)", strerror (errno), errno);
+ return -1;
+ }
+ return 0;
+}
+
+
+#include "thread.h"
+#include "zebra/zserv.h"
+
+extern struct thread_master *master;
+
+/* For debug purpose. */
+void
+rtmsg_debug (struct rt_msghdr *rtm)
+{
+ char *type = "Unknown";
+ struct message *mes;
+
+ for (mes = rtm_type_str; mes->str; mes++)
+ if (mes->key == rtm->rtm_type)
+ {
+ type = mes->str;
+ break;
+ }
+
+ zlog_info ("Kernel: Len: %d Type: %s", rtm->rtm_msglen, type);
+ rtm_flag_dump (rtm->rtm_flags);
+ zlog_info ("Kernel: message seq %d", rtm->rtm_seq);
+ zlog_info ("Kernel: pid %d", rtm->rtm_pid);
+}
+
+/* This is pretty gross, better suggestions welcome -- mhandler */
+#ifndef RTAX_MAX
+#ifdef RTA_NUMBITS
+#define RTAX_MAX RTA_NUMBITS
+#else
+#define RTAX_MAX 8
+#endif /* RTA_NUMBITS */
+#endif /* RTAX_MAX */
+
+/* Kernel routing table and interface updates via routing socket. */
+int
+kernel_read (struct thread *thread)
+{
+ int sock;
+ int nbytes;
+ struct rt_msghdr *rtm;
+
+ union
+ {
+ /* Routing information. */
+ struct
+ {
+ struct rt_msghdr rtm;
+ struct sockaddr addr[RTAX_MAX];
+ } r;
+
+ /* Interface information. */
+ struct
+ {
+ struct if_msghdr ifm;
+ struct sockaddr addr[RTAX_MAX];
+ } im;
+
+ /* Interface address information. */
+ struct
+ {
+ struct ifa_msghdr ifa;
+ struct sockaddr addr[RTAX_MAX];
+ } ia;
+
+#ifdef RTM_IFANNOUNCE
+ /* Interface arrival/departure */
+ struct
+ {
+ struct if_announcemsghdr ifan;
+ struct sockaddr addr[RTAX_MAX];
+ } ian;
+#endif /* RTM_IFANNOUNCE */
+
+ } buf;
+
+ /* Fetch routing socket. */
+ sock = THREAD_FD (thread);
+
+ nbytes= read (sock, &buf, sizeof buf);
+
+ if (nbytes <= 0)
+ {
+ if (nbytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
+ zlog_warn ("routing socket error: %s", strerror (errno));
+ return 0;
+ }
+
+ thread_add_read (master, kernel_read, NULL, sock);
+
+#ifdef DEBUG
+ rtmsg_debug (&buf.r.rtm);
+#endif /* DEBUG */
+
+ rtm = &buf.r.rtm;
+
+ switch (rtm->rtm_type)
+ {
+ case RTM_ADD:
+ case RTM_DELETE:
+ rtm_read (rtm);
+ break;
+ case RTM_IFINFO:
+ ifm_read (&buf.im.ifm);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam_read (&buf.ia.ifa);
+ break;
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+ ifan_read (&buf.ian.ifan);
+ break;
+#endif /* RTM_IFANNOUNCE */
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Make routing socket. */
+void
+routing_socket ()
+{
+ routing_sock = socket (AF_ROUTE, SOCK_RAW, 0);
+
+ if (routing_sock < 0)
+ {
+ zlog_warn ("Can't init kernel routing socket");
+ return;
+ }
+
+ if (fcntl (routing_sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog_warn ("Can't set O_NONBLOCK to routing socket");
+
+ /* kernel_read needs rewrite. */
+ thread_add_read (master, kernel_read, NULL, routing_sock);
+}
+
+/* Exported interface function. This function simply calls
+ routing_socket (). */
+void
+kernel_init ()
+{
+ routing_socket ();
+}