summaryrefslogtreecommitdiffstats
path: root/zebra/if_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/if_ioctl.c')
-rw-r--r--zebra/if_ioctl.c315
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 */