/* 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 #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 "privs.h" #include "zebra/interface.h" #include "zebra/zserv.h" #include "zebra/debug.h" extern struct zebra_privs_t zserv_privs; extern struct zebra_t zebrad; /* 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. */ #if defined(HAVE_SA_LEN) #define WRAPUP(X) ROUNDUP(((struct sockaddr *)(X))->sa_len) #elif defined(HAVE_IPV6) #define WRAPUP(X) \ do { \ (((struct sockaddr *)(X))->sa_family == AF_INET ? \ ROUNDUP(sizeof(struct sockaddr_in)):\ (((struct sockaddr *)(X))->sa_family == AF_INET6 ? \ ROUNDUP(sizeof(struct sockaddr_in6)) : \ (((struct sockaddr *)(X))->sa_family == AF_LINK ? \ ROUNDUP(sizeof(struct sockaddr_dl)) : sizeof(struct sockaddr)))) \ } while (0) #else /* HAVE_IPV6 */ #define WRAPUP(X) \ (((struct sockaddr *)(X))->sa_family == AF_INET ? \ ROUNDUP(sizeof(struct sockaddr_in)):\ (((struct sockaddr *)(X))->sa_family == AF_LINK ? \ ROUNDUP(sizeof(struct sockaddr_dl)) : 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]; buf[0] = '0'; 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 */ /* * Handle struct if_msghdr obtained from reading routing socket or * sysctl (from interface_list). There may or may not be sockaddrs * present after the header. */ int ifm_read (struct if_msghdr *ifm) { struct interface *ifp = NULL; struct sockaddr_dl *sdl = NULL; char ifname[IFNAMSIZ]; /* paranoia: sanity check structure */ if (ifm->ifm_msglen < sizeof(struct if_msghdr)) { zlog_err ("ifm_read: ifm->ifm_msglen %d too short\n", ifm->ifm_msglen); return -1; } /* * Check for a sockaddr_dl following the message. */ #ifdef SUNOS_5 int i; struct sockaddr *sa; u_char *cp = (u_char *)(ifm + 1); /* * if_msghdr_t on 64 bit kernels in Solaris 9 and earlier versions * is 12 bytes larger than the 32 bit version, so make adjustment * here. */ sa = (struct sockaddr *)cp; if (sa->sa_family == AF_UNSPEC) cp = cp + 12; for (i = 1; i != 0; i <<= 1) { if (i & ifm->ifm_addrs) { sa = (struct sockaddr *)cp; cp += WRAPUP(sa); if (i & RTA_IFP) { sdl = (struct sockaddr_dl *)sa; break; } } } /* * After here, If RTA_IFP was set in ifm_addrs, sdl should point to * the sockaddr_dl. */ #else /* sockaddrs_present? */ if (ifm->ifm_addrs) { if (ifm->ifm_addrs == RTA_IFP) { /* just the one we want */ sdl = (struct sockaddr_dl *)(ifm + 1); } else { /* * Not strictly an error, but more complicated parsing than * is implemented is required to handle this case. */ zlog_err ("ifm_read: addrs %x != RTA_IFP (unhandled, ignoring)\n", ifm->ifm_addrs); return -1; } } /* * Past here, either ifm_addrs == 0 or ifm_addrs == RTA_IFP and sdl * points to a RTA_IFP sockaddr. */ #endif /* Check that sdl, if present, is actually a sockaddr_dl before use. */ if (sdl != NULL) switch (sdl->sdl_family) { case AF_LINK: /* Standard AF for link-layer address sockaddrs. */ case AF_DLI: /* * XXX Comment in NetBSD net/if_dl.h says AF_DLI, but this * seems wrong. Accept it for now. */ break; default: zlog_err ("ifm_read: sockaddr_dl bad AF %d\n", sdl->sdl_family); return -1; } /* * Look up on ifindex. This is useful if this is an up/down * notification for an interface of which we are already aware. * (This happens on NetBSD 1.6.2, for example.) */ if (ifp == NULL) ifp = if_lookup_by_index (ifm->ifm_index); /* * If lookup by index was unsuccessful and we have a name, try * looking up by name. Interfaces specified in the configuration * file for which the ifindex has not been determined will have * ifindex == -1, and such interfaces are found by this search, and * then their ifindex values can be filled in. */ if (ifp == NULL && sdl != NULL) { /* * paranoia: sanity check name length. nlen does not include * trailing zero, but IFNAMSIZ max length does. */ if (sdl->sdl_nlen >= IFNAMSIZ) { zlog_err ("ifm_read: illegal sdl_nlen %d\n", sdl->sdl_nlen); return -1; } memcpy (ifname, sdl->sdl_data, sdl->sdl_nlen); ifname[sdl->sdl_nlen] = '\0'; ifp = if_lookup_by_name (ifname); } /* * If ifp does not exist or has an invalid index (-1), create or * fill in an interface. */ if ((ifp == NULL) || (ifp->ifindex == -1)) { /* Check interface's address.*/ if (! (ifm->ifm_addrs & RTA_IFP)) { zlog_warn ("Interface index %d (new) missing RTA_IFP sockaddr\n", ifm->ifm_index); return -1; } /* * paranoia: sdl-finding code above guarantees that sdl is * non-NULL and valid if RTA_IFP is set in ifm_addrs, so this * check is in theory not necessary. */ if (sdl == NULL) { zlog_warn ("ifm_read: no sockaddr_dl present"); return -1; } if (ifp == NULL) ifp = if_create (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; } /* XXX sockaddr_dl can be larger than base structure */ memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl)); if_add_update (ifp); } else /* * Interface structure exists. Adjust stored flags from * notification. If interface has up->down or down->up * transition, call state change routines (to adjust routes, * notify routing daemons, etc.). (Other flag changes are stored * but apparently do not trigger action.) */ { 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); /* This is a reject or blackhole route */ if (flags & RTF_REJECT) SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT); if (flags & RTF_BLACKHOLE) SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE); 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; if (zebra_flags & ZEBRA_FLAG_REJECT) msg.rtm.rtm_flags |= RTF_REJECT; #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" /* 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; /* * This must be big enough for any message the kernel might send. * The code previously used RTAX_MAX struct sockaddrs in all cases, * but now that sockaddrs are variable size, this doesn't work * (Solaris has 244 bytes of sdl_data!). For now, add a struct * sockaddr_dl to the case where it is used. */ union { /* Routing information. */ struct { struct rt_msghdr rtm; struct sockaddr addr[RTAX_MAX]; } r; /* Interface information. */ struct { struct if_msghdr ifm; struct sockaddr_dl; struct sockaddr addr[RTAX_MAX-1]; } 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 (zebrad.master, kernel_read, NULL, sock); if (IS_ZEBRA_DEBUG_KERNEL) rtmsg_debug (&buf.r.rtm); rtm = &buf.r.rtm; if (rtm->rtm_msglen != nbytes) { zlog_warn ("kernel_read: rtm->rtm_msglen %d, nbytes %d, type %d\n", rtm->rtm_msglen, nbytes, rtm->rtm_type); return -1; } 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: if (IS_ZEBRA_DEBUG_KERNEL) zlog_info("Unprocessed RTM_type: %d", rtm->rtm_type); break; } return 0; } /* Make routing socket. */ void routing_socket () { if ( zserv_privs.change (ZPRIVS_RAISE) ) zlog_err ("routing_socket: Can't raise privileges"); routing_sock = socket (AF_ROUTE, SOCK_RAW, 0); if (routing_sock < 0) { if ( zserv_privs.change (ZPRIVS_LOWER) ) zlog_err ("routing_socket: Can't lower privileges"); 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"); if ( zserv_privs.change (ZPRIVS_LOWER) ) zlog_err ("routing_socket: Can't lower privileges"); /* kernel_read needs rewrite. */ thread_add_read (zebrad.master, kernel_read, NULL, routing_sock); } /* Exported interface function. This function simply calls routing_socket (). */ void kernel_init () { routing_socket (); }