diff options
Diffstat (limited to 'zebra/if_ioctl.c')
-rw-r--r-- | zebra/if_ioctl.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c new file mode 100644 index 0000000..e02f3d5 --- /dev/null +++ b/zebra/if_ioctl.c @@ -0,0 +1,315 @@ +/* + * Interface looking up by ioctl (). + * 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#ifdef OPEN_BSD + +#include "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "ioctl.h" +#include "connected.h" +#include "memory.h" +#include "log.h" +#include "vrf.h" +#include "vty.h" +#include "lib_errors.h" + +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/zebra_errors.h" + +#include <ifaddrs.h> + +/* Interface looking up using infamous SIOCGIFCONF. */ +static int interface_list_ioctl(void) +{ + int ret; + int sock; +#define IFNUM_BASE 32 + int ifnum; + struct ifreq *ifreq; + struct ifconf ifconf; + struct interface *ifp; + int n; + int lastlen; + + /* Normally SIOCGIFCONF works with AF_INET socket. */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + flog_err_sys(EC_LIB_SOCKET, + "Can't make AF_INET socket stream: %s", + safe_strerror(errno)); + return -1; + } + +/* Set initial ifreq count. This will be double when SIOCGIFCONF + fail. Solaris has SIOCGIFNUM. */ +#ifdef SIOCGIFNUM + ret = ioctl(sock, SIOCGIFNUM, &ifnum); + if (ret < 0) + ifnum = IFNUM_BASE; + else + ifnum++; +#else + ifnum = IFNUM_BASE; +#endif /* SIOCGIFNUM */ + + ifconf.ifc_buf = NULL; + + lastlen = 0; + /* Loop until SIOCGIFCONF success. */ + for (;;) { + ifconf.ifc_len = sizeof(struct ifreq) * ifnum; + ifconf.ifc_buf = + XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len); + + ret = ioctl(sock, SIOCGIFCONF, &ifconf); + + if (ret < 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, "SIOCGIFCONF: %s", + safe_strerror(errno)); + goto end; + } + /* Repeatedly get info til buffer fails to grow. */ + if (ifconf.ifc_len > lastlen) { + lastlen = ifconf.ifc_len; + ifnum += 10; + continue; + } + /* Success. */ + break; + } + + /* Allocate interface. */ + ifreq = ifconf.ifc_req; + +#ifdef OPEN_BSD + for (n = 0; n < ifconf.ifc_len;) { + unsigned int size; + + ifreq = (struct ifreq *)((caddr_t)ifconf.ifc_req + n); + ifp = if_get_by_name(ifreq->ifr_name, VRF_DEFAULT, + VRF_DEFAULT_NAME); + if_add_update(ifp); + size = ifreq->ifr_addr.sa_len; + if (size < sizeof(ifreq->ifr_addr)) + size = sizeof(ifreq->ifr_addr); + size += sizeof(ifreq->ifr_name); + n += size; + } +#else + for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq)) { + ifp = if_get_by_name(ifreq->ifr_name, VRF_DEFAULT, + VRF_DEFAULT_NAME); + if_add_update(ifp); + ifreq++; + } +#endif /* OPEN_BSD */ + +end: + close(sock); + XFREE(MTYPE_TMP, ifconf.ifc_buf); + + return ret; +} + +/* Get interface's index by ioctl. */ +static int if_get_index(struct interface *ifp) +{ + if_set_index(ifp, if_nametoindex(ifp->name)); + return ifp->ifindex; +} + +#ifdef SIOCGIFHWADDR +static int if_get_hwaddr(struct interface *ifp) +{ + int ret; + struct ifreq ifreq; + int i; + + strlcpy(ifreq.ifr_name, ifp->name, sizeof(ifreq.ifr_name)); + ifreq.ifr_addr.sa_family = AF_INET; + + /* Fetch Hardware address if available. */ + ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf->vrf_id); + if (ret < 0) + ifp->hw_addr_len = 0; + else { + memcpy(ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6); + + for (i = 0; i < 6; i++) + if (ifp->hw_addr[i] != 0) + break; + + if (i == 6) + ifp->hw_addr_len = 0; + else + ifp->hw_addr_len = 6; + } + return 0; +} +#endif /* SIOCGIFHWADDR */ + +static int if_getaddrs(void) +{ + int ret; + struct ifaddrs *ifap; + struct ifaddrs *ifapfree; + struct interface *ifp; + int prefixlen; + + ret = getifaddrs(&ifap); + if (ret != 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, "getifaddrs(): %s", + safe_strerror(errno)); + return -1; + } + + for (ifapfree = ifap; ifap; ifap = ifap->ifa_next) { + if (ifap->ifa_addr == NULL) { + flog_err( + EC_LIB_INTERFACE, + "%s: nonsensical ifaddr with NULL ifa_addr, ifname %s", + __func__, + (ifap->ifa_name ? ifap->ifa_name : "(null)")); + continue; + } + + ifp = if_lookup_by_name(ifap->ifa_name, VRF_DEFAULT); + if (ifp == NULL) { + flog_err(EC_LIB_INTERFACE, + "%s: Can't lookup interface %s", __func__, + ifap->ifa_name); + continue; + } + + if (ifap->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *addr; + struct sockaddr_in *mask; + struct sockaddr_in *dest; + struct in_addr *dest_pnt; + int flags = 0; + + addr = (struct sockaddr_in *)ifap->ifa_addr; + mask = (struct sockaddr_in *)ifap->ifa_netmask; + prefixlen = ip_masklen(mask->sin_addr); + + dest_pnt = NULL; + + if (if_is_pointopoint(ifp) && ifap->ifa_dstaddr + && !IPV4_ADDR_SAME(&addr->sin_addr, + &((struct sockaddr_in *) + ifap->ifa_dstaddr) + ->sin_addr)) { + dest = (struct sockaddr_in *)ifap->ifa_dstaddr; + dest_pnt = &dest->sin_addr; + flags = ZEBRA_IFA_PEER; + } else if (ifap->ifa_broadaddr + && !IPV4_ADDR_SAME( + &addr->sin_addr, + &((struct sockaddr_in *) + ifap->ifa_broadaddr) + ->sin_addr)) { + dest = (struct sockaddr_in *) + ifap->ifa_broadaddr; + dest_pnt = &dest->sin_addr; + } + + connected_add_ipv4(ifp, flags, &addr->sin_addr, + prefixlen, dest_pnt, NULL, + METRIC_MAX); + } + if (ifap->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *addr; + struct sockaddr_in6 *mask; + int flags = 0; + + addr = (struct sockaddr_in6 *)ifap->ifa_addr; + mask = (struct sockaddr_in6 *)ifap->ifa_netmask; + prefixlen = ip6_masklen(mask->sin6_addr); + +#if defined(KAME) + if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { + addr->sin6_scope_id = + ntohs(*(uint16_t *)&addr->sin6_addr + .s6_addr[2]); + addr->sin6_addr.s6_addr[2] = + addr->sin6_addr.s6_addr[3] = 0; + } +#endif + + connected_add_ipv6(ifp, flags, &addr->sin6_addr, NULL, + prefixlen, NULL, METRIC_MAX); + } + } + + freeifaddrs(ifapfree); + + return 0; +} + +/* Fetch interface information via ioctl(). */ +static void interface_info_ioctl() +{ + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) { + if_get_index(ifp); +#ifdef SIOCGIFHWADDR + if_get_hwaddr(ifp); +#endif /* SIOCGIFHWADDR */ + if_get_flags(ifp); + if_get_mtu(ifp); + if_get_metric(ifp); + } +} + +/* Lookup all interface information. */ +void interface_list(struct zebra_ns *zns) +{ + + zlog_info("%s: NS %u", __func__, zns->ns_id); + +/* Linux can do both proc & ioctl, ioctl is the only way to get + interface aliases in 2.2 series kernels. */ +#ifdef HAVE_PROC_NET_DEV + interface_list_proc(); +#endif /* HAVE_PROC_NET_DEV */ + interface_list_ioctl(); + + /* After listing is done, get index, address, flags and other + interface's information. */ + interface_info_ioctl(); + + if_getaddrs(); + +#if defined(HAVE_PROC_NET_IF_INET6) + /* Linux provides interface's IPv6 address via + /proc/net/if_inet6. */ + ifaddr_proc_ipv6(); +#endif /* HAVE_PROC_NET_IF_INET6 */ +} + +#endif /* OPEN_BSD */ |