mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-02 03:33:35 +00:00

It allows FRR to read the interface config even when the necessary VRFs are not yet created and interfaces are in "wrong" VRFs. Currently, such config is rejected. For VRF-lite backend, we don't care at all about the VRF of the inactive interface. When the interface is created in the OS and becomes active, we always use its actual VRF instead of the configured one. So there's no need to reject the config. For netns backend, we may have multiple interfaces with the same name in different VRFs. So we care about the VRF of inactive interfaces. And we must allow to preconfigure the interface in a VRF even before it is moved to the corresponding netns. From now on, we allow to create multiple configs for the same interface name in different VRFs and the necessary config is applied once the OS interface is moved to the corresponding netns. Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
316 lines
7.3 KiB
C
316 lines
7.3 KiB
C
/*
|
|
* 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_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,
|
|
"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 (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("interface_list: NS %u", 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 */
|