| /* |
| * 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 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 "sockunion.h" |
| #include "prefix.h" |
| #include "ioctl.h" |
| #include "connected.h" |
| #include "memory.h" |
| #include "log.h" |
| #include "vrf.h" |
| #include "vty.h" |
| |
| #include "zebra/interface.h" |
| #include "zebra/rib.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) |
| { |
| zlog_warn ("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) |
| { |
| zlog_warn ("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; ) |
| { |
| int size; |
| |
| ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n); |
| ifp = if_get_by_name_len(ifreq->ifr_name, |
| strnlen(ifreq->ifr_name, |
| sizeof(ifreq->ifr_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_len(ifreq->ifr_name, |
| strnlen(ifreq->ifr_name, |
| sizeof(ifreq->ifr_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 defined(HAVE_IF_NAMETOINDEX) |
| /* Modern systems should have if_nametoindex(3). */ |
| ifp->ifindex = if_nametoindex(ifp->name); |
| #elif defined(SIOCGIFINDEX) && !defined(HAVE_BROKEN_ALIASES) |
| /* Fall-back for older linuxes. */ |
| int ret; |
| struct ifreq ifreq; |
| static int if_fake_index; |
| |
| ifreq_set_name (&ifreq, ifp); |
| |
| ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq); |
| if (ret < 0) |
| { |
| /* Linux 2.0.X does not have interface index. */ |
| ifp->ifindex = if_fake_index++; |
| return ifp->ifindex; |
| } |
| |
| /* OK we got interface index. */ |
| #ifdef ifr_ifindex |
| ifp->ifindex = ifreq.ifr_ifindex; |
| #else |
| ifp->ifindex = ifreq.ifr_index; |
| #endif |
| |
| #else |
| /* Linux 2.2.X does not provide individual interface index |
| for aliases and we know it. For others issue a warning. */ |
| #if !defined(HAVE_BROKEN_ALIASES) |
| #warning "Using if_fake_index. You may want to add appropriate" |
| #warning "mapping from ifname to ifindex for your system..." |
| #endif |
| /* This branch probably won't provide usable results, but anyway... */ |
| static int if_fake_index = 1; |
| ifp->ifindex = if_fake_index++; |
| #endif |
| |
| return ifp->ifindex; |
| } |
| |
| #ifdef SIOCGIFHWADDR |
| static int |
| if_get_hwaddr (struct interface *ifp) |
| { |
| int ret; |
| struct ifreq ifreq; |
| int i; |
| |
| strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); |
| ifreq.ifr_addr.sa_family = AF_INET; |
| |
| /* Fetch Hardware address if available. */ |
| ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq); |
| 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 */ |
| |
| #ifdef HAVE_GETIFADDRS |
| #include <ifaddrs.h> |
| |
| static int |
| if_getaddrs (void) |
| { |
| int ret; |
| struct ifaddrs *ifap; |
| struct ifaddrs *ifapfree; |
| struct interface *ifp; |
| int prefixlen; |
| |
| ret = getifaddrs (&ifap); |
| if (ret != 0) |
| { |
| zlog_err ("getifaddrs(): %s", safe_strerror (errno)); |
| return -1; |
| } |
| |
| for (ifapfree = ifap; ifap; ifap = ifap->ifa_next) |
| { |
| if (ifap->ifa_addr == NULL) |
| { |
| zlog_err ("%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); |
| if (ifp == NULL) |
| { |
| zlog_err ("if_getaddrs(): Can't lookup interface %s\n", |
| 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 (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); |
| } |
| #ifdef HAVE_IPV6 |
| if (ifap->ifa_addr->sa_family == AF_INET6) |
| { |
| struct sockaddr_in6 *addr; |
| struct sockaddr_in6 *mask; |
| struct sockaddr_in6 *dest; |
| struct in6_addr *dest_pnt; |
| int flags = 0; |
| |
| addr = (struct sockaddr_in6 *) ifap->ifa_addr; |
| mask = (struct sockaddr_in6 *) ifap->ifa_netmask; |
| prefixlen = ip6_masklen (mask->sin6_addr); |
| |
| dest_pnt = NULL; |
| |
| if (ifap->ifa_dstaddr && |
| !IPV6_ADDR_SAME(&addr->sin6_addr, |
| &((struct sockaddr_in6 *) |
| ifap->ifa_dstaddr)->sin6_addr)) |
| { |
| dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr; |
| dest_pnt = &dest->sin6_addr; |
| flags = ZEBRA_IFA_PEER; |
| } |
| else if (ifap->ifa_broadaddr && |
| !IPV6_ADDR_SAME(&addr->sin6_addr, |
| &((struct sockaddr_in6 *) |
| ifap->ifa_broadaddr)->sin6_addr)) |
| { |
| dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr; |
| dest_pnt = &dest->sin6_addr; |
| } |
| |
| #if defined(KAME) |
| if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) |
| { |
| addr->sin6_scope_id = |
| ntohs(*(u_int16_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, prefixlen, |
| dest_pnt, NULL); |
| } |
| #endif /* HAVE_IPV6 */ |
| } |
| |
| freeifaddrs (ifapfree); |
| |
| return 0; |
| } |
| #else /* HAVE_GETIFADDRS */ |
| /* Interface address lookup by ioctl. This function only looks up |
| IPv4 address. */ |
| int |
| if_get_addr (struct interface *ifp) |
| { |
| int ret; |
| struct ifreq ifreq; |
| struct sockaddr_in addr; |
| struct sockaddr_in mask; |
| struct sockaddr_in dest; |
| struct in_addr *dest_pnt; |
| u_char prefixlen; |
| int flags = 0; |
| |
| /* Interface's name and address family. */ |
| strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); |
| ifreq.ifr_addr.sa_family = AF_INET; |
| |
| /* Interface's address. */ |
| ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq); |
| if (ret < 0) |
| { |
| if (errno != EADDRNOTAVAIL) |
| { |
| zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno)); |
| return ret; |
| } |
| return 0; |
| } |
| memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); |
| |
| /* Interface's network mask. */ |
| ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq); |
| if (ret < 0) |
| { |
| if (errno != EADDRNOTAVAIL) |
| { |
| zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno)); |
| return ret; |
| } |
| return 0; |
| } |
| #ifdef ifr_netmask |
| memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in)); |
| #else |
| memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); |
| #endif /* ifr_netmask */ |
| prefixlen = ip_masklen (mask.sin_addr); |
| |
| /* Point to point or borad cast address pointer init. */ |
| dest_pnt = NULL; |
| |
| ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq); |
| if (ret < 0) |
| { |
| if (errno != EADDRNOTAVAIL) |
| zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno)); |
| } |
| else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_dstaddr.sin_addr)) |
| { |
| memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in)); |
| dest_pnt = &dest.sin_addr; |
| flags = ZEBRA_IFA_PEER; |
| } |
| if (!dest_pnt) |
| { |
| ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq); |
| if (ret < 0) |
| { |
| if (errno != EADDRNOTAVAIL) |
| zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno)); |
| } |
| else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_broadaddr.sin_addr)) |
| { |
| memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in)); |
| dest_pnt = &dest.sin_addr; |
| } |
| } |
| |
| |
| /* Set address to the interface. */ |
| connected_add_ipv4 (ifp, flags, &addr.sin_addr, prefixlen, dest_pnt, NULL); |
| |
| return 0; |
| } |
| #endif /* HAVE_GETIFADDRS */ |
| |
| /* Fetch interface information via ioctl(). */ |
| static void |
| interface_info_ioctl () |
| { |
| struct listnode *node, *nnode; |
| struct interface *ifp; |
| |
| for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) |
| { |
| if_get_index (ifp); |
| #ifdef SIOCGIFHWADDR |
| if_get_hwaddr (ifp); |
| #endif /* SIOCGIFHWADDR */ |
| if_get_flags (ifp); |
| #ifndef HAVE_GETIFADDRS |
| if_get_addr (ifp); |
| #endif /* ! HAVE_GETIFADDRS */ |
| if_get_mtu (ifp); |
| if_get_metric (ifp); |
| } |
| } |
| |
| /* Lookup all interface information. */ |
| void |
| interface_list (struct zebra_vrf *zvrf) |
| { |
| if (zvrf->vrf_id != VRF_DEFAULT) |
| { |
| zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id); |
| return; |
| } |
| /* 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 (); |
| |
| #ifdef HAVE_GETIFADDRS |
| if_getaddrs (); |
| #endif /* HAVE_GETIFADDRS */ |
| |
| #if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) |
| /* Linux provides interface's IPv6 address via |
| /proc/net/if_inet6. */ |
| ifaddr_proc_ipv6 (); |
| #endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */ |
| } |