/* 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 "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 (); }