mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-28 17:44:38 +00:00

2006-12-12 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * if.h: (struct connected) Add new ZEBRA_IFA_PEER flag indicating whether a peer address has been configured. Comment now shows the new interpretation of the destination addr: if ZEBRA_IFA_PEER is set, then it must contain the destination address, otherwise it may contain the broadcast address or be NULL. (CONNECTED_DEST_HOST,CONNECTED_POINTOPOINT_HOST) Remove obsolete macros that were specific to IPv4 and not fully general. (CONNECTED_PEER) New macro to check ZEBRA_IFA_PEER flag. (CONNECTED_PREFIX) New macro giving the prefix to insert into the RIB: if CONNECTED_PEER, then use the destination (peer) address, else use the address field. (CONNECTED_ID) New macro to come up with an identifying address for the struct connected. * if.c: (if_lookup_address, connected_lookup_address) Streamline logic with new CONNECTED_PREFIX macro. * prefix.h: (PREFIX_COPY_IPV4, PREFIX_COPY_IPV6) New macros for better performance than the general prefix_copy function. * zclient.c: (zebra_interface_address_read) For non-null destination addresses, set prefixlen to equal the address prefixlen. This is needed to get the new CONNECTED_PREFIX macro to work properly. * connected.c: (connected_up_ipv4, connected_down_ipv4, connected_up_ipv6, connected_down_ipv6) Simplify logic using the new CONNECTED_PREFIX macro. (connected_add_ipv4) Set prefixlen in destination addresses (required by the CONNECTED_PREFIX macro). Use CONNECTED_PEER macro instead of testing for IFF_POINTOPOINT. Delete invalid warning message. Warn about cases where the ZEBRA_IFA_PEER is set but no destination address has been supplied (and turn off the flag). (connected_add_ipv6) Add new flags argument so callers may set the ZEBRA_IFA_PEER flag. If peer/broadcast address satisfies IN6_IS_ADDR_UNSPECIFIED, then reject it with a warning. Set prefixlen in destination address so CONNECTED_PREFIX will work. * connected.h: (connected_add_ipv6) Add new flags argument so callers may set the ZEBRA_IFA_PEER flag. * interface.c: (connected_dump_vty) Use CONNECTED_PEER macro to decide whether the destination address is a peer or broadcast address (instead of checking IFF_BROADCAST and IFF_POINTOPOINT). * if_ioctl.c: (if_getaddrs) Instead of setting a peer address only when the IFF_POINTOPOINT is set, we now accept a peer address whenever it is available and not the same as the local address. Otherwise (no peer address assigned), we check for a broadcast address (regardless of the IFF_BROADCAST flag). And must now pass a flags value of ZEBRA_IFA_PEER to connected_add_ipv4 when a peer address is assigned. The same new logic is used with the IPv6 code as well (and we pass the new flags argument to connected_add_ipv6). (if_get_addr) Do not bother to check IFF_POINTOPOINT: just issue the SIOCGIFDSTADDR ioctl and see if we get back a peer address not matching the local address (and set the ZEBRA_IFA_PEER in that case). If there's no peer address, try to grab SIOCGIFBRDADDR regardless of whether IFF_BROADCAST is set. * if_ioctl_solaris.c: (if_get_addr) Just try the SIOCGLIFDSTADDR ioctl without bothering to check the IFF_POINTOPOINT flag. And if no peer address was found, just try the SIOCGLIFBRDADDR ioctl without checking the IFF_BROADCAST flag. Call connected_add_ipv4 and connected_add_ipv6 with appropriate flags. * if_proc.c: (ifaddr_proc_ipv6) Must pass new flags argument to connected_add_ipv6. * kernel_socket.c: (ifam_read) Must pass new flags argument to connected_add_ipv6. * rt_netlink.c: (netlink_interface_addr) Copy logic from iproute2 to determine local and possible peer address (so there's no longer a test for IFF_POINTOPOINT). Set ZEBRA_IFA_PEER flag appropriately. Pass new flags argument to connected_add_ipv6. (netlink_address) Test !CONNECTED_PEER instead of if_is_broadcast to determine whether the connected destination address is a broadcast address. * bgp_nexthop.c: (bgp_connected_add, bgp_connected_delete) Simplify logic by using new CONNECTED_PREFIX macro. * ospf_interface.c: (ospf_if_is_configured, ospf_if_lookup_by_prefix, ospf_if_lookup_recv_if) Simplify logic using new CONNECTED_PREFIX macro. * ospf_lsa.c: (lsa_link_ptop_set) Using the new CONNECTED_PREFIX macro, both options collapse into the same code. * ospf_snmp.c: (ospf_snmp_if_update) Simplify logic using new CONNECTED_ID macro. (ospf_snmp_is_if_have_addr) Simplify logic using new CONNECTED_PREFIX macro. * ospf_vty.c: (show_ip_ospf_interface_sub) Use new CONNECTED_PEER macro instead of testing the IFF_POINTOPOINT flag. * ospfd.c: (ospf_network_match_iface) Use new CONNECTED_PEER macro instead of testing with if_is_pointopoint. And add commented-out code to implement alternative (in my opinion) more elegant behavior that has no special-case treatment for PtP addresses. (ospf_network_run) Use new CONNECTED_ID macro to simplify logic. * rip_interface.c: (rip_interface_multicast_set) Use new CONNECTED_ID macro to simplify logic. (rip_request_interface_send) Fix minor bug: ipv4_broadcast_addr does not give a useful result if prefixlen is 32 (we require a peer address in such cases). * ripd.c: (rip_update_interface) Fix same bug as above.
384 lines
10 KiB
C
384 lines
10 KiB
C
/*
|
|
* Interface looking up by ioctl () on Solaris.
|
|
* 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 "privs.h"
|
|
|
|
#include "zebra/interface.h"
|
|
|
|
void lifreq_set_name (struct lifreq *, const char *);
|
|
int if_get_flags_direct (const char *, uint64_t *, unsigned int af);
|
|
static int if_get_addr (struct interface *, struct sockaddr *, const char *);
|
|
static void interface_info_ioctl (struct interface *);
|
|
extern struct zebra_privs_t zserv_privs;
|
|
|
|
int
|
|
interface_list_ioctl (int af)
|
|
{
|
|
int ret;
|
|
int sock;
|
|
#define IFNUM_BASE 32
|
|
struct lifnum lifn;
|
|
int ifnum;
|
|
struct lifreq *lifreq;
|
|
struct lifconf lifconf;
|
|
struct interface *ifp;
|
|
int n;
|
|
int save_errno;
|
|
size_t needed, lastneeded = 0;
|
|
char *buf = NULL;
|
|
|
|
if (zserv_privs.change(ZPRIVS_RAISE))
|
|
zlog (NULL, LOG_ERR, "Can't raise privileges");
|
|
|
|
sock = socket (af, SOCK_DGRAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
zlog_warn ("Can't make %s socket stream: %s",
|
|
(af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
|
|
|
|
if (zserv_privs.change(ZPRIVS_LOWER))
|
|
zlog (NULL, LOG_ERR, "Can't lower privileges");
|
|
|
|
return -1;
|
|
}
|
|
|
|
calculate_lifc_len: /* must hold privileges to enter here */
|
|
lifn.lifn_family = af;
|
|
lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
|
|
ret = ioctl (sock, SIOCGLIFNUM, &lifn);
|
|
save_errno = errno;
|
|
|
|
if (zserv_privs.change(ZPRIVS_LOWER))
|
|
zlog (NULL, LOG_ERR, "Can't lower privileges");
|
|
|
|
if (ret < 0)
|
|
{
|
|
zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
|
|
safe_strerror (save_errno));
|
|
close (sock);
|
|
return -1;
|
|
}
|
|
ifnum = lifn.lifn_count;
|
|
|
|
/*
|
|
* When calculating the buffer size needed, add a small number
|
|
* of interfaces to those we counted. We do this to capture
|
|
* the interface status of potential interfaces which may have
|
|
* been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
|
|
*/
|
|
needed = (ifnum + 4) * sizeof (struct lifreq);
|
|
if (needed > lastneeded || needed < lastneeded / 2)
|
|
{
|
|
if (buf != NULL)
|
|
XFREE (MTYPE_TMP, buf);
|
|
if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
|
|
{
|
|
zlog_warn ("interface_list_ioctl: malloc failed");
|
|
close (sock);
|
|
return -1;
|
|
}
|
|
}
|
|
lastneeded = needed;
|
|
|
|
lifconf.lifc_family = af;
|
|
lifconf.lifc_flags = LIFC_NOXMIT;
|
|
lifconf.lifc_len = needed;
|
|
lifconf.lifc_buf = buf;
|
|
|
|
if (zserv_privs.change(ZPRIVS_RAISE))
|
|
zlog (NULL, LOG_ERR, "Can't raise privileges");
|
|
|
|
ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
|
|
|
|
if (ret < 0)
|
|
{
|
|
if (errno == EINVAL)
|
|
goto calculate_lifc_len; /* deliberately hold privileges */
|
|
|
|
zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
|
|
|
|
if (zserv_privs.change(ZPRIVS_LOWER))
|
|
zlog (NULL, LOG_ERR, "Can't lower privileges");
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (zserv_privs.change(ZPRIVS_LOWER))
|
|
zlog (NULL, LOG_ERR, "Can't lower privileges");
|
|
|
|
/* Allocate interface. */
|
|
lifreq = lifconf.lifc_req;
|
|
|
|
for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
|
|
{
|
|
/* we treat Solaris logical interfaces as addresses, because that is
|
|
* how PF_ROUTE on Solaris treats them. Hence we can not directly use
|
|
* the lifreq_name to get the ifp. We need to normalise the name
|
|
* before attempting get.
|
|
*
|
|
* Solaris logical interface names are in the form of:
|
|
* <interface name>:<logical interface id>
|
|
*/
|
|
unsigned int normallen = 0;
|
|
uint64_t lifflags;
|
|
|
|
/* We should exclude ~IFF_UP interfaces, as we'll find out about them
|
|
* coming up later through RTM_NEWADDR message on the route socket.
|
|
*/
|
|
if (if_get_flags_direct (lifreq->lifr_name, &lifflags,
|
|
lifreq->lifr_addr.ss_family)
|
|
|| !CHECK_FLAG (lifflags, IFF_UP))
|
|
{
|
|
lifreq++;
|
|
continue;
|
|
}
|
|
|
|
/* Find the normalised name */
|
|
while ( (normallen < sizeof(lifreq->lifr_name))
|
|
&& ( *(lifreq->lifr_name + normallen) != '\0')
|
|
&& ( *(lifreq->lifr_name + normallen) != ':') )
|
|
normallen++;
|
|
|
|
ifp = if_get_by_name_len(lifreq->lifr_name, normallen);
|
|
|
|
if (lifreq->lifr_addr.ss_family == AF_INET)
|
|
ifp->flags |= IFF_IPV4;
|
|
|
|
if (lifreq->lifr_addr.ss_family == AF_INET6)
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
ifp->flags |= IFF_IPV6;
|
|
#else
|
|
lifreq++;
|
|
continue;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
|
|
if_add_update (ifp);
|
|
|
|
interface_info_ioctl (ifp);
|
|
|
|
/* If a logical interface pass the full name so it can be
|
|
* as a label on the address
|
|
*/
|
|
if ( *(lifreq->lifr_name + normallen) != '\0')
|
|
if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
|
|
lifreq->lifr_name);
|
|
else
|
|
if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
|
|
|
|
/* Poke the interface flags. Lets IFF_UP mangling kick in */
|
|
if_flags_update (ifp, ifp->flags);
|
|
|
|
lifreq++;
|
|
}
|
|
|
|
end:
|
|
close (sock);
|
|
XFREE (MTYPE_TMP, lifconf.lifc_buf);
|
|
return ret;
|
|
}
|
|
|
|
/* Get interface's index by ioctl. */
|
|
int
|
|
if_get_index (struct interface *ifp)
|
|
{
|
|
int ret;
|
|
struct lifreq lifreq;
|
|
|
|
lifreq_set_name (&lifreq, ifp);
|
|
|
|
if (ifp->flags & IFF_IPV4)
|
|
ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
|
|
else if (ifp->flags & IFF_IPV6)
|
|
ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
|
|
else
|
|
ret = -1;
|
|
|
|
if (ret < 0)
|
|
{
|
|
zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
|
|
return ret;
|
|
}
|
|
|
|
/* OK we got interface index. */
|
|
#ifdef ifr_ifindex
|
|
ifp->ifindex = lifreq.lifr_ifindex;
|
|
#else
|
|
ifp->ifindex = lifreq.lifr_index;
|
|
#endif
|
|
return ifp->ifindex;
|
|
|
|
}
|
|
|
|
|
|
/* Interface address lookup by ioctl. This function only looks up
|
|
IPv4 address. */
|
|
#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
|
|
sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
|
|
|
|
#define SIN(s) ((struct sockaddr_in *)(s))
|
|
#define SIN6(s) ((struct sockaddr_in6 *)(s))
|
|
|
|
/* Retrieve address information for the given ifp */
|
|
static int
|
|
if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
|
|
{
|
|
int ret;
|
|
struct lifreq lifreq;
|
|
struct sockaddr_storage mask, dest;
|
|
char *dest_pnt = NULL;
|
|
u_char prefixlen = 0;
|
|
afi_t af;
|
|
int flags = 0;
|
|
|
|
/* Interface's name and address family.
|
|
* We need to use the logical interface name / label, if we've been
|
|
* given one, in order to get the right address
|
|
*/
|
|
strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
|
|
|
|
/* Interface's address. */
|
|
memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
|
|
af = addr->sa_family;
|
|
|
|
/* Point to point or broad cast address pointer init. */
|
|
dest_pnt = NULL;
|
|
|
|
if (AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq) >= 0)
|
|
{
|
|
memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
|
|
if (af == AF_INET)
|
|
dest_pnt = (char *) &(SIN (&dest)->sin_addr);
|
|
else
|
|
dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
|
|
flags = ZEBRA_IFA_PEER;
|
|
}
|
|
|
|
if (af == AF_INET)
|
|
{
|
|
ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
|
|
|
|
if (ret < 0)
|
|
{
|
|
if (errno != EADDRNOTAVAIL)
|
|
{
|
|
zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
|
|
safe_strerror (errno));
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
|
|
|
|
prefixlen = ip_masklen (SIN (&mask)->sin_addr);
|
|
if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0))
|
|
{
|
|
memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
|
|
dest_pnt = (char *) &SIN (&dest)->sin_addr;
|
|
}
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else if (af == AF_INET6)
|
|
{
|
|
if (ifp->flags & IFF_POINTOPOINT)
|
|
{
|
|
prefixlen = IPV6_MAX_BITLEN;
|
|
}
|
|
else
|
|
{
|
|
ret = if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq);
|
|
if (ret < 0)
|
|
{
|
|
zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
|
|
ifp->name, safe_strerror (errno));
|
|
}
|
|
else
|
|
{
|
|
prefixlen = lifreq.lifr_addrlen;
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
/* Set address to the interface. */
|
|
if (af == AF_INET)
|
|
connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen,
|
|
(struct in_addr *) dest_pnt, label);
|
|
#ifdef HAVE_IPV6
|
|
else if (af == AF_INET6)
|
|
connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen,
|
|
(struct in6_addr *) dest_pnt, label);
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Fetch interface information via ioctl(). */
|
|
static void
|
|
interface_info_ioctl (struct interface *ifp)
|
|
{
|
|
if_get_index (ifp);
|
|
if_get_flags (ifp);
|
|
if_get_mtu (ifp);
|
|
if_get_metric (ifp);
|
|
}
|
|
|
|
/* Lookup all interface information. */
|
|
void
|
|
interface_list ()
|
|
{
|
|
interface_list_ioctl (AF_INET);
|
|
interface_list_ioctl (AF_INET6);
|
|
interface_list_ioctl (AF_UNSPEC);
|
|
}
|
|
|
|
struct connected *
|
|
if_lookup_linklocal (struct interface *ifp)
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
struct listnode *node;
|
|
struct connected *ifc;
|
|
|
|
if (ifp == NULL)
|
|
return NULL;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
|
|
{
|
|
if ((ifc->address->family == AF_INET6) &&
|
|
(IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
|
|
return ifc;
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
return NULL;
|
|
}
|