mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 03:29:06 +00:00 
			
		
		
		
	Practically no-one uses this and ioctls are pretty much wrappered. Further wrappering could make this even better. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
		
			
				
	
	
		
			302 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * Interface looking up by ioctl ().
 | 
						|
 * Copyright (C) 1997, 98 Kunihiro Ishiguro
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
#include <sys/ioctl.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 */
 |