mirror of
https://github.com/nmap/nmap.git
synced 2025-12-20 14:39:02 +00:00
1019 lines
25 KiB
C
1019 lines
25 KiB
C
/*
|
|
* intf.c
|
|
*
|
|
* Copyright (c) 2001 Dug Song <dugsong@monkey.org>
|
|
*
|
|
* $Id: intf.c 616 2006-01-09 07:09:49Z dugsong $
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
#include "dnet_winconfig.h"
|
|
#else
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#ifdef HAVE_SYS_SOCKIO_H
|
|
# include <sys/sockio.h>
|
|
#endif
|
|
/* XXX - AIX */
|
|
#ifndef IP_MULTICAST
|
|
# define IP_MULTICAST
|
|
#endif
|
|
#include <net/if.h>
|
|
#ifdef HAVE_NET_IF_VAR_H
|
|
# include <net/if_var.h>
|
|
#endif
|
|
#undef IP_MULTICAST
|
|
/* XXX - IPv6 ioctls */
|
|
#ifdef HAVE_NETINET_IN_VAR_H
|
|
# include <netinet/in.h>
|
|
# include <netinet/in_var.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN6_VAR_H
|
|
# include <sys/protosw.h>
|
|
# include <netinet/in6_var.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "dnet.h"
|
|
|
|
/* XXX - Tru64 */
|
|
#if defined(SIOCRIPMTU) && defined(SIOCSIPMTU)
|
|
# define SIOCGIFMTU SIOCRIPMTU
|
|
# define SIOCSIFMTU SIOCSIPMTU
|
|
#endif
|
|
|
|
/* XXX - HP-UX */
|
|
#if defined(SIOCADDIFADDR) && defined(SIOCDELIFADDR)
|
|
# define SIOCAIFADDR SIOCADDIFADDR
|
|
# define SIOCDIFADDR SIOCDELIFADDR
|
|
#endif
|
|
|
|
/* XXX - HP-UX, Solaris */
|
|
#if !defined(ifr_mtu) && defined(ifr_metric)
|
|
# define ifr_mtu ifr_metric
|
|
#endif
|
|
|
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
|
# define NEXTIFR(i) ((struct ifreq *)((u_char *)&i->ifr_addr + \
|
|
(i->ifr_addr.sa_len ? i->ifr_addr.sa_len : \
|
|
sizeof(i->ifr_addr))))
|
|
#else
|
|
# define NEXTIFR(i) (i + 1)
|
|
#endif
|
|
|
|
#define NEXTLIFR(i) (i + 1)
|
|
|
|
/* XXX - superset of ifreq, for portable SIOC{A,D}IFADDR */
|
|
struct dnet_ifaliasreq {
|
|
char ifra_name[IFNAMSIZ];
|
|
struct sockaddr ifra_addr;
|
|
struct sockaddr ifra_brdaddr;
|
|
struct sockaddr ifra_mask;
|
|
int ifra_cookie; /* XXX - IRIX!@#$ */
|
|
};
|
|
|
|
struct intf_handle {
|
|
int fd;
|
|
int fd6;
|
|
struct ifconf ifc;
|
|
#ifdef SIOCGLIFCONF
|
|
struct lifconf lifc;
|
|
#endif
|
|
u_char ifcbuf[4192];
|
|
};
|
|
|
|
static int
|
|
intf_flags_to_iff(u_short flags, int iff)
|
|
{
|
|
if (flags & INTF_FLAG_UP)
|
|
iff |= IFF_UP;
|
|
else
|
|
iff &= ~IFF_UP;
|
|
if (flags & INTF_FLAG_NOARP)
|
|
iff |= IFF_NOARP;
|
|
else
|
|
iff &= ~IFF_NOARP;
|
|
|
|
return (iff);
|
|
}
|
|
|
|
static u_int
|
|
intf_iff_to_flags(uint64_t iff)
|
|
{
|
|
u_int n = 0;
|
|
|
|
if (iff & IFF_UP)
|
|
n |= INTF_FLAG_UP;
|
|
if (iff & IFF_LOOPBACK)
|
|
n |= INTF_FLAG_LOOPBACK;
|
|
if (iff & IFF_POINTOPOINT)
|
|
n |= INTF_FLAG_POINTOPOINT;
|
|
if (iff & IFF_NOARP)
|
|
n |= INTF_FLAG_NOARP;
|
|
if (iff & IFF_BROADCAST)
|
|
n |= INTF_FLAG_BROADCAST;
|
|
if (iff & IFF_MULTICAST)
|
|
n |= INTF_FLAG_MULTICAST;
|
|
#ifdef IFF_IPMP
|
|
/* Unset the BROADCAST and MULTICAST flags from Solaris IPMP interfaces,
|
|
* otherwise _intf_set_type will think they are INTF_TYPE_ETH. */
|
|
if (iff & IFF_IPMP)
|
|
n &= ~(INTF_FLAG_BROADCAST | INTF_FLAG_MULTICAST);
|
|
#endif
|
|
|
|
return (n);
|
|
}
|
|
|
|
intf_t *
|
|
intf_open(void)
|
|
{
|
|
intf_t *intf;
|
|
int one = 1;
|
|
|
|
if ((intf = calloc(1, sizeof(*intf))) != NULL) {
|
|
intf->fd = intf->fd6 = -1;
|
|
|
|
if ((intf->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
return (intf_close(intf));
|
|
|
|
setsockopt(intf->fd, SOL_SOCKET, SO_BROADCAST,
|
|
(const char *) &one, sizeof(one));
|
|
|
|
#if defined(SIOCGLIFCONF) || defined(SIOCGIFNETMASK_IN6) || defined(SIOCGIFNETMASK6)
|
|
if ((intf->fd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
|
|
# ifdef EPROTONOSUPPORT
|
|
if (errno != EPROTONOSUPPORT)
|
|
# endif
|
|
return (intf_close(intf));
|
|
}
|
|
#endif
|
|
}
|
|
return (intf);
|
|
}
|
|
|
|
static int
|
|
_intf_delete_addrs(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
#if defined(SIOCDIFADDR)
|
|
struct dnet_ifaliasreq ifra;
|
|
|
|
memset(&ifra, 0, sizeof(ifra));
|
|
strlcpy(ifra.ifra_name, entry->intf_name, sizeof(ifra.ifra_name));
|
|
if (entry->intf_addr.addr_type == ADDR_TYPE_IP) {
|
|
addr_ntos(&entry->intf_addr, &ifra.ifra_addr);
|
|
ioctl(intf->fd, SIOCDIFADDR, &ifra);
|
|
}
|
|
if (entry->intf_dst_addr.addr_type == ADDR_TYPE_IP) {
|
|
addr_ntos(&entry->intf_dst_addr, &ifra.ifra_addr);
|
|
ioctl(intf->fd, SIOCDIFADDR, &ifra);
|
|
}
|
|
#elif defined(SIOCLIFREMOVEIF)
|
|
struct ifreq ifr;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strlcpy(ifr.ifr_name, entry->intf_name, sizeof(ifr.ifr_name));
|
|
/* XXX - overloading Solaris lifreq with ifreq */
|
|
ioctl(intf->fd, SIOCLIFREMOVEIF, &ifr);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
_intf_delete_aliases(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
int i;
|
|
#if defined(SIOCDIFADDR) && !defined(__linux__) /* XXX - see Linux below */
|
|
struct dnet_ifaliasreq ifra;
|
|
|
|
memset(&ifra, 0, sizeof(ifra));
|
|
strlcpy(ifra.ifra_name, entry->intf_name, sizeof(ifra.ifra_name));
|
|
|
|
for (i = 0; i < (int)entry->intf_alias_num; i++) {
|
|
addr_ntos(&entry->intf_alias_addrs[i], &ifra.ifra_addr);
|
|
ioctl(intf->fd, SIOCDIFADDR, &ifra);
|
|
}
|
|
#else
|
|
struct ifreq ifr;
|
|
|
|
for (i = 0; i < entry->intf_alias_num; i++) {
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s:%d",
|
|
entry->intf_name, i + 1);
|
|
# ifdef SIOCLIFREMOVEIF
|
|
/* XXX - overloading Solaris lifreq with ifreq */
|
|
ioctl(intf->fd, SIOCLIFREMOVEIF, &ifr);
|
|
# else
|
|
/* XXX - only need to set interface down on Linux */
|
|
ifr.ifr_flags = 0;
|
|
ioctl(intf->fd, SIOCSIFFLAGS, &ifr);
|
|
# endif
|
|
}
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
_intf_add_aliases(intf_t *intf, const struct intf_entry *entry)
|
|
{
|
|
int i;
|
|
#ifdef SIOCAIFADDR
|
|
struct dnet_ifaliasreq ifra;
|
|
struct addr bcast;
|
|
|
|
memset(&ifra, 0, sizeof(ifra));
|
|
strlcpy(ifra.ifra_name, entry->intf_name, sizeof(ifra.ifra_name));
|
|
|
|
for (i = 0; i < (int)entry->intf_alias_num; i++) {
|
|
if (entry->intf_alias_addrs[i].addr_type != ADDR_TYPE_IP)
|
|
continue;
|
|
|
|
if (addr_ntos(&entry->intf_alias_addrs[i],
|
|
&ifra.ifra_addr) < 0)
|
|
return (-1);
|
|
addr_bcast(&entry->intf_alias_addrs[i], &bcast);
|
|
addr_ntos(&bcast, &ifra.ifra_brdaddr);
|
|
addr_btos(entry->intf_alias_addrs[i].addr_bits,
|
|
&ifra.ifra_mask);
|
|
|
|
if (ioctl(intf->fd, SIOCAIFADDR, &ifra) < 0)
|
|
return (-1);
|
|
}
|
|
#else
|
|
struct ifreq ifr;
|
|
int n = 1;
|
|
|
|
for (i = 0; i < entry->intf_alias_num; i++) {
|
|
if (entry->intf_alias_addrs[i].addr_type != ADDR_TYPE_IP)
|
|
continue;
|
|
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s:%d",
|
|
entry->intf_name, n++);
|
|
# ifdef SIOCLIFADDIF
|
|
if (ioctl(intf->fd, SIOCLIFADDIF, &ifr) < 0)
|
|
return (-1);
|
|
# endif
|
|
if (addr_ntos(&entry->intf_alias_addrs[i], &ifr.ifr_addr) < 0)
|
|
return (-1);
|
|
if (ioctl(intf->fd, SIOCSIFADDR, &ifr) < 0)
|
|
return (-1);
|
|
}
|
|
strlcpy(ifr.ifr_name, entry->intf_name, sizeof(ifr.ifr_name));
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
intf_set(intf_t *intf, const struct intf_entry *entry)
|
|
{
|
|
struct ifreq ifr;
|
|
struct intf_entry *orig;
|
|
struct addr bcast;
|
|
u_char buf[BUFSIZ];
|
|
|
|
orig = (struct intf_entry *)buf;
|
|
orig->intf_len = sizeof(buf);
|
|
strcpy(orig->intf_name, entry->intf_name);
|
|
|
|
if (intf_get(intf, orig) < 0)
|
|
return (-1);
|
|
|
|
/* Delete any existing aliases. */
|
|
if (_intf_delete_aliases(intf, orig) < 0)
|
|
return (-1);
|
|
|
|
/* Delete any existing addrs. */
|
|
if (_intf_delete_addrs(intf, orig) < 0)
|
|
return (-1);
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strlcpy(ifr.ifr_name, entry->intf_name, sizeof(ifr.ifr_name));
|
|
|
|
/* Set interface MTU. */
|
|
if (entry->intf_mtu != 0) {
|
|
ifr.ifr_mtu = entry->intf_mtu;
|
|
#ifdef SIOCSIFMTU
|
|
if (ioctl(intf->fd, SIOCSIFMTU, &ifr) < 0)
|
|
#endif
|
|
return (-1);
|
|
}
|
|
/* Set interface address. */
|
|
if (entry->intf_addr.addr_type == ADDR_TYPE_IP) {
|
|
#ifdef BSD
|
|
/* XXX - why must this happen before SIOCSIFADDR? */
|
|
if (addr_btos(entry->intf_addr.addr_bits,
|
|
&ifr.ifr_addr) == 0) {
|
|
if (ioctl(intf->fd, SIOCSIFNETMASK, &ifr) < 0)
|
|
return (-1);
|
|
}
|
|
#endif
|
|
if (addr_ntos(&entry->intf_addr, &ifr.ifr_addr) < 0)
|
|
return (-1);
|
|
if (ioctl(intf->fd, SIOCSIFADDR, &ifr) < 0 && errno != EEXIST)
|
|
return (-1);
|
|
|
|
if (addr_btos(entry->intf_addr.addr_bits, &ifr.ifr_addr) == 0
|
|
#ifdef __linux__
|
|
&& entry->intf_addr.addr_ip != 0
|
|
#endif
|
|
) {
|
|
if (ioctl(intf->fd, SIOCSIFNETMASK, &ifr) < 0)
|
|
return (-1);
|
|
}
|
|
if (addr_bcast(&entry->intf_addr, &bcast) == 0) {
|
|
if (addr_ntos(&bcast, &ifr.ifr_broadaddr) == 0) {
|
|
/* XXX - ignore error from non-broadcast ifs */
|
|
ioctl(intf->fd, SIOCSIFBRDADDR, &ifr);
|
|
}
|
|
}
|
|
}
|
|
/* Set link-level address. */
|
|
if (entry->intf_link_addr.addr_type == ADDR_TYPE_ETH &&
|
|
addr_cmp(&entry->intf_link_addr, &orig->intf_link_addr) != 0) {
|
|
#if defined(SIOCSIFHWADDR)
|
|
if (addr_ntos(&entry->intf_link_addr, &ifr.ifr_hwaddr) < 0)
|
|
return (-1);
|
|
if (ioctl(intf->fd, SIOCSIFHWADDR, &ifr) < 0)
|
|
return (-1);
|
|
#elif defined (SIOCSIFLLADDR)
|
|
memcpy(ifr.ifr_addr.sa_data, &entry->intf_link_addr.addr_eth,
|
|
ETH_ADDR_LEN);
|
|
ifr.ifr_addr.sa_len = ETH_ADDR_LEN;
|
|
if (ioctl(intf->fd, SIOCSIFLLADDR, &ifr) < 0)
|
|
return (-1);
|
|
#else
|
|
eth_t *eth;
|
|
|
|
if ((eth = eth_open(entry->intf_name)) == NULL)
|
|
return (-1);
|
|
if (eth_set(eth, &entry->intf_link_addr.addr_eth) < 0) {
|
|
eth_close(eth);
|
|
return (-1);
|
|
}
|
|
eth_close(eth);
|
|
#endif
|
|
}
|
|
/* Set point-to-point destination. */
|
|
if (entry->intf_dst_addr.addr_type == ADDR_TYPE_IP) {
|
|
if (addr_ntos(&entry->intf_dst_addr, &ifr.ifr_dstaddr) < 0)
|
|
return (-1);
|
|
if (ioctl(intf->fd, SIOCSIFDSTADDR, &ifr) < 0 &&
|
|
errno != EEXIST)
|
|
return (-1);
|
|
}
|
|
/* Add aliases. */
|
|
if (_intf_add_aliases(intf, entry) < 0)
|
|
return (-1);
|
|
|
|
/* Set interface flags. */
|
|
if (ioctl(intf->fd, SIOCGIFFLAGS, &ifr) < 0)
|
|
return (-1);
|
|
|
|
ifr.ifr_flags = intf_flags_to_iff(entry->intf_flags, ifr.ifr_flags);
|
|
|
|
if (ioctl(intf->fd, SIOCSIFFLAGS, &ifr) < 0)
|
|
return (-1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* XXX - this is total crap. how to do this without walking ifnet? */
|
|
static void
|
|
_intf_set_type(struct intf_entry *entry)
|
|
{
|
|
if ((entry->intf_flags & INTF_FLAG_LOOPBACK) != 0)
|
|
entry->intf_type = INTF_TYPE_LOOPBACK;
|
|
else if ((entry->intf_flags & INTF_FLAG_BROADCAST) != 0)
|
|
entry->intf_type = INTF_TYPE_ETH;
|
|
else if ((entry->intf_flags & INTF_FLAG_POINTOPOINT) != 0)
|
|
entry->intf_type = INTF_TYPE_TUN;
|
|
else
|
|
entry->intf_type = INTF_TYPE_OTHER;
|
|
}
|
|
|
|
#ifdef SIOCGLIFCONF
|
|
int
|
|
_intf_get_noalias(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
struct lifreq lifr;
|
|
int fd;
|
|
|
|
/* Get interface index. */
|
|
entry->intf_index = if_nametoindex(entry->intf_name);
|
|
if (entry->intf_index == 0)
|
|
return (-1);
|
|
|
|
strlcpy(lifr.lifr_name, entry->intf_name, sizeof(lifr.lifr_name));
|
|
|
|
/* Get interface flags. Here he also check whether we need to use fd or
|
|
* fd6 in the rest of the function. Using the wrong address family in
|
|
* the ioctls gives ENXIO on Solaris. */
|
|
if (ioctl(intf->fd, SIOCGLIFFLAGS, &lifr) >= 0)
|
|
fd = intf->fd;
|
|
else if (intf->fd6 == -1 || ioctl(intf->fd6, SIOCGLIFFLAGS, &lifr) >= 0)
|
|
fd = intf->fd6;
|
|
else
|
|
return (-1);
|
|
|
|
entry->intf_flags = intf_iff_to_flags(lifr.lifr_flags);
|
|
_intf_set_type(entry);
|
|
|
|
/* Get interface MTU. */
|
|
#ifdef SIOCGLIFMTU
|
|
if (ioctl(fd, SIOCGLIFMTU, &lifr) < 0)
|
|
#endif
|
|
return (-1);
|
|
entry->intf_mtu = lifr.lifr_mtu;
|
|
|
|
entry->intf_addr.addr_type = entry->intf_dst_addr.addr_type =
|
|
entry->intf_link_addr.addr_type = ADDR_TYPE_NONE;
|
|
|
|
/* Get primary interface address. */
|
|
if (ioctl(fd, SIOCGLIFADDR, &lifr) == 0) {
|
|
addr_ston((struct sockaddr *)&lifr.lifr_addr, &entry->intf_addr);
|
|
if (ioctl(fd, SIOCGLIFNETMASK, &lifr) < 0)
|
|
return (-1);
|
|
addr_stob((struct sockaddr *)&lifr.lifr_addr, &entry->intf_addr.addr_bits);
|
|
}
|
|
/* Get other addresses. */
|
|
if (entry->intf_type == INTF_TYPE_TUN) {
|
|
if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == 0) {
|
|
if (addr_ston((struct sockaddr *)&lifr.lifr_addr,
|
|
&entry->intf_dst_addr) < 0)
|
|
return (-1);
|
|
}
|
|
} else if (entry->intf_type == INTF_TYPE_ETH) {
|
|
eth_t *eth;
|
|
|
|
if ((eth = eth_open(entry->intf_name)) != NULL) {
|
|
if (!eth_get(eth, &entry->intf_link_addr.addr_eth)) {
|
|
entry->intf_link_addr.addr_type =
|
|
ADDR_TYPE_ETH;
|
|
entry->intf_link_addr.addr_bits =
|
|
ETH_ADDR_BITS;
|
|
}
|
|
eth_close(eth);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
#else
|
|
static int
|
|
_intf_get_noalias(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
struct ifreq ifr;
|
|
|
|
/* Get interface index. */
|
|
entry->intf_index = if_nametoindex(entry->intf_name);
|
|
if (entry->intf_index == 0)
|
|
return (-1);
|
|
|
|
strlcpy(ifr.ifr_name, entry->intf_name, sizeof(ifr.ifr_name));
|
|
|
|
/* Get interface flags. */
|
|
if (ioctl(intf->fd, SIOCGIFFLAGS, &ifr) < 0)
|
|
return (-1);
|
|
|
|
entry->intf_flags = intf_iff_to_flags(ifr.ifr_flags);
|
|
_intf_set_type(entry);
|
|
|
|
/* Get interface MTU. */
|
|
#ifdef SIOCGIFMTU
|
|
if (ioctl(intf->fd, SIOCGIFMTU, &ifr) < 0)
|
|
#endif
|
|
return (-1);
|
|
entry->intf_mtu = ifr.ifr_mtu;
|
|
|
|
entry->intf_addr.addr_type = entry->intf_dst_addr.addr_type =
|
|
entry->intf_link_addr.addr_type = ADDR_TYPE_NONE;
|
|
|
|
/* Get primary interface address. */
|
|
if (ioctl(intf->fd, SIOCGIFADDR, &ifr) == 0) {
|
|
addr_ston(&ifr.ifr_addr, &entry->intf_addr);
|
|
if (ioctl(intf->fd, SIOCGIFNETMASK, &ifr) < 0)
|
|
return (-1);
|
|
addr_stob(&ifr.ifr_addr, &entry->intf_addr.addr_bits);
|
|
}
|
|
/* Get other addresses. */
|
|
if (entry->intf_type == INTF_TYPE_TUN) {
|
|
if (ioctl(intf->fd, SIOCGIFDSTADDR, &ifr) == 0) {
|
|
if (addr_ston(&ifr.ifr_addr,
|
|
&entry->intf_dst_addr) < 0)
|
|
return (-1);
|
|
}
|
|
} else if (entry->intf_type == INTF_TYPE_ETH) {
|
|
#if defined(SIOCGIFHWADDR)
|
|
if (ioctl(intf->fd, SIOCGIFHWADDR, &ifr) < 0)
|
|
return (-1);
|
|
if (addr_ston(&ifr.ifr_addr, &entry->intf_link_addr) < 0)
|
|
return (-1);
|
|
#elif defined(SIOCRPHYSADDR)
|
|
/* Tru64 */
|
|
struct ifdevea *ifd = (struct ifdevea *)𝔦 /* XXX */
|
|
|
|
if (ioctl(intf->fd, SIOCRPHYSADDR, ifd) < 0)
|
|
return (-1);
|
|
addr_pack(&entry->intf_link_addr, ADDR_TYPE_ETH, ETH_ADDR_BITS,
|
|
ifd->current_pa, ETH_ADDR_LEN);
|
|
#else
|
|
eth_t *eth;
|
|
|
|
if ((eth = eth_open(entry->intf_name)) != NULL) {
|
|
if (!eth_get(eth, &entry->intf_link_addr.addr_eth)) {
|
|
entry->intf_link_addr.addr_type =
|
|
ADDR_TYPE_ETH;
|
|
entry->intf_link_addr.addr_bits =
|
|
ETH_ADDR_BITS;
|
|
}
|
|
eth_close(eth);
|
|
}
|
|
#endif
|
|
}
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef SIOCLIFADDR
|
|
/* XXX - aliases on IRIX don't show up in SIOCGIFCONF */
|
|
static int
|
|
_intf_get_aliases(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
struct dnet_ifaliasreq ifra;
|
|
struct addr *ap, *lap;
|
|
|
|
strlcpy(ifra.ifra_name, entry->intf_name, sizeof(ifra.ifra_name));
|
|
addr_ntos(&entry->intf_addr, &ifra.ifra_addr);
|
|
addr_btos(entry->intf_addr.addr_bits, &ifra.ifra_mask);
|
|
memset(&ifra.ifra_brdaddr, 0, sizeof(ifra.ifra_brdaddr));
|
|
ifra.ifra_cookie = 1;
|
|
|
|
ap = entry->intf_alias_addrs;
|
|
lap = (struct addr *)((u_char *)entry + entry->intf_len);
|
|
|
|
while (ioctl(intf->fd, SIOCLIFADDR, &ifra) == 0 &&
|
|
ifra.ifra_cookie > 0 && (ap + 1) < lap) {
|
|
if (addr_ston(&ifra.ifra_addr, ap) < 0)
|
|
break;
|
|
ap++, entry->intf_alias_num++;
|
|
}
|
|
entry->intf_len = (u_char *)ap - (u_char *)entry;
|
|
|
|
return (0);
|
|
}
|
|
#elif defined(SIOCGLIFCONF)
|
|
static int
|
|
_intf_get_aliases(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
struct lifreq *lifr, *llifr;
|
|
struct lifreq tmplifr;
|
|
struct addr *ap, *lap;
|
|
char *p;
|
|
|
|
if (intf->lifc.lifc_len < (int)sizeof(*lifr)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
entry->intf_alias_num = 0;
|
|
ap = entry->intf_alias_addrs;
|
|
llifr = (struct lifreq *)intf->lifc.lifc_buf +
|
|
(intf->lifc.lifc_len / sizeof(*llifr));
|
|
lap = (struct addr *)((u_char *)entry + entry->intf_len);
|
|
|
|
/* Get addresses for this interface. */
|
|
for (lifr = intf->lifc.lifc_req; lifr < llifr && (ap + 1) < lap;
|
|
lifr = NEXTLIFR(lifr)) {
|
|
/* XXX - Linux, Solaris ifaliases */
|
|
if ((p = strchr(lifr->lifr_name, ':')) != NULL)
|
|
*p = '\0';
|
|
|
|
if (strcmp(lifr->lifr_name, entry->intf_name) != 0) {
|
|
if (p) *p = ':';
|
|
continue;
|
|
}
|
|
|
|
/* Fix the name back up */
|
|
if (p) *p = ':';
|
|
|
|
if (addr_ston((struct sockaddr *)&lifr->lifr_addr, ap) < 0)
|
|
continue;
|
|
|
|
/* XXX */
|
|
if (ap->addr_type == ADDR_TYPE_ETH) {
|
|
memcpy(&entry->intf_link_addr, ap, sizeof(*ap));
|
|
continue;
|
|
} else if (ap->addr_type == ADDR_TYPE_IP) {
|
|
if (ap->addr_ip == entry->intf_addr.addr_ip ||
|
|
ap->addr_ip == entry->intf_dst_addr.addr_ip)
|
|
continue;
|
|
strlcpy(tmplifr.lifr_name, lifr->lifr_name, sizeof(tmplifr.lifr_name));
|
|
if (ioctl(intf->fd, SIOCGIFNETMASK, &tmplifr) == 0)
|
|
addr_stob((struct sockaddr *)&tmplifr.lifr_addr, &ap->addr_bits);
|
|
} else if (ap->addr_type == ADDR_TYPE_IP6 && intf->fd6 != -1) {
|
|
if (memcmp(&ap->addr_ip6, &entry->intf_addr.addr_ip6, IP6_ADDR_LEN) == 0 ||
|
|
memcmp(&ap->addr_ip6, &entry->intf_dst_addr.addr_ip6, IP6_ADDR_LEN) == 0)
|
|
continue;
|
|
strlcpy(tmplifr.lifr_name, lifr->lifr_name, sizeof(tmplifr.lifr_name));
|
|
if (ioctl(intf->fd6, SIOCGLIFNETMASK, &tmplifr) == 0) {
|
|
addr_stob((struct sockaddr *)&tmplifr.lifr_addr,
|
|
&ap->addr_bits);
|
|
}
|
|
else perror("SIOCGLIFNETMASK");
|
|
}
|
|
ap++, entry->intf_alias_num++;
|
|
}
|
|
entry->intf_len = (u_char *)ap - (u_char *)entry;
|
|
|
|
return (0);
|
|
}
|
|
#else
|
|
static int
|
|
_intf_get_aliases(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
struct ifreq *ifr, *lifr;
|
|
struct ifreq tmpifr;
|
|
struct addr *ap, *lap;
|
|
char *p;
|
|
|
|
if (intf->ifc.ifc_len < (int)sizeof(*ifr)) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
entry->intf_alias_num = 0;
|
|
ap = entry->intf_alias_addrs;
|
|
lifr = (struct ifreq *)intf->ifc.ifc_buf +
|
|
(intf->ifc.ifc_len / sizeof(*lifr));
|
|
lap = (struct addr *)((u_char *)entry + entry->intf_len);
|
|
|
|
/* Get addresses for this interface. */
|
|
for (ifr = intf->ifc.ifc_req; ifr < lifr && (ap + 1) < lap;
|
|
ifr = NEXTIFR(ifr)) {
|
|
/* XXX - Linux, Solaris ifaliases */
|
|
if ((p = strchr(ifr->ifr_name, ':')) != NULL)
|
|
*p = '\0';
|
|
|
|
if (strcmp(ifr->ifr_name, entry->intf_name) != 0) {
|
|
if (p) *p = ':';
|
|
continue;
|
|
}
|
|
|
|
/* Fix the name back up */
|
|
if (p) *p = ':';
|
|
|
|
if (addr_ston(&ifr->ifr_addr, ap) < 0)
|
|
continue;
|
|
|
|
/* XXX */
|
|
if (ap->addr_type == ADDR_TYPE_ETH) {
|
|
memcpy(&entry->intf_link_addr, ap, sizeof(*ap));
|
|
continue;
|
|
} else if (ap->addr_type == ADDR_TYPE_IP) {
|
|
if (ap->addr_ip == entry->intf_addr.addr_ip ||
|
|
ap->addr_ip == entry->intf_dst_addr.addr_ip)
|
|
continue;
|
|
strlcpy(tmpifr.ifr_name, ifr->ifr_name, sizeof(tmpifr.ifr_name));
|
|
if (ioctl(intf->fd, SIOCGIFNETMASK, &tmpifr) == 0)
|
|
addr_stob(&tmpifr.ifr_addr, &ap->addr_bits);
|
|
}
|
|
#ifdef SIOCGIFNETMASK_IN6
|
|
else if (ap->addr_type == ADDR_TYPE_IP6 && intf->fd6 != -1) {
|
|
struct in6_ifreq ifr6;
|
|
|
|
/* XXX - sizeof(ifr) < sizeof(ifr6) */
|
|
memcpy(&ifr6, ifr, sizeof(ifr6));
|
|
|
|
if (ioctl(intf->fd6, SIOCGIFNETMASK_IN6, &ifr6) == 0) {
|
|
addr_stob((struct sockaddr *)&ifr6.ifr_addr,
|
|
&ap->addr_bits);
|
|
}
|
|
else perror("SIOCGIFNETMASK_IN6");
|
|
}
|
|
#else
|
|
#ifdef SIOCGIFNETMASK6
|
|
else if (ap->addr_type == ADDR_TYPE_IP6 && intf->fd6 != -1) {
|
|
struct in6_ifreq ifr6;
|
|
|
|
/* XXX - sizeof(ifr) < sizeof(ifr6) */
|
|
memcpy(&ifr6, ifr, sizeof(ifr6));
|
|
|
|
if (ioctl(intf->fd6, SIOCGIFNETMASK6, &ifr6) == 0) {
|
|
/* For some reason this is 0 after the ioctl. */
|
|
ifr6.ifr_Addr.sin6_family = AF_INET6;
|
|
addr_stob((struct sockaddr *)&ifr6.ifr_Addr,
|
|
&ap->addr_bits);
|
|
}
|
|
else perror("SIOCGIFNETMASK6");
|
|
}
|
|
#endif
|
|
#endif
|
|
ap++, entry->intf_alias_num++;
|
|
}
|
|
#ifdef HAVE_LINUX_PROCFS
|
|
#define PROC_INET6_FILE "/proc/net/if_inet6"
|
|
{
|
|
FILE *f;
|
|
char buf[256], s[8][5], name[INTF_NAME_LEN];
|
|
u_int idx, bits, scope, flags;
|
|
|
|
if ((f = fopen(PROC_INET6_FILE, "r")) != NULL) {
|
|
while (ap < lap &&
|
|
fgets(buf, sizeof(buf), f) != NULL) {
|
|
sscanf(buf, "%04s%04s%04s%04s%04s%04s%04s%04s %x %02x %02x %02x %32s\n",
|
|
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
|
|
&idx, &bits, &scope, &flags, name);
|
|
if (strcmp(name, entry->intf_name) == 0) {
|
|
snprintf(buf, sizeof(buf), "%s:%s:%s:%s:%s:%s:%s:%s/%d",
|
|
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], bits);
|
|
addr_aton(buf, ap);
|
|
ap++, entry->intf_alias_num++;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
}
|
|
#endif
|
|
entry->intf_len = (u_char *)ap - (u_char *)entry;
|
|
|
|
return (0);
|
|
}
|
|
#endif /* SIOCLIFADDR */
|
|
|
|
int
|
|
intf_get(intf_t *intf, struct intf_entry *entry)
|
|
{
|
|
if (_intf_get_noalias(intf, entry) < 0)
|
|
return (-1);
|
|
#ifndef SIOCLIFADDR
|
|
intf->ifc.ifc_buf = (caddr_t)intf->ifcbuf;
|
|
intf->ifc.ifc_len = sizeof(intf->ifcbuf);
|
|
|
|
if (ioctl(intf->fd, SIOCGIFCONF, &intf->ifc) < 0)
|
|
return (-1);
|
|
#endif
|
|
return (_intf_get_aliases(intf, entry));
|
|
}
|
|
|
|
/* Look up an interface from an index, such as a sockaddr_in6.sin6_scope_id. */
|
|
int
|
|
intf_get_index(intf_t *intf, struct intf_entry *entry, int af, unsigned int index)
|
|
{
|
|
char namebuf[IFNAMSIZ];
|
|
char *devname;
|
|
|
|
/* af is ignored; only used in intf-win32.c. */
|
|
devname = if_indextoname(index, namebuf);
|
|
if (devname == NULL)
|
|
return (-1);
|
|
strlcpy(entry->intf_name, devname, sizeof(entry->intf_name));
|
|
return intf_get(intf, entry);
|
|
}
|
|
|
|
static int
|
|
_match_intf_src(const struct intf_entry *entry, void *arg)
|
|
{
|
|
struct intf_entry *save = (struct intf_entry *)arg;
|
|
int matched = 0, cnt;
|
|
|
|
if (entry->intf_addr.addr_type == ADDR_TYPE_IP &&
|
|
entry->intf_addr.addr_ip == save->intf_addr.addr_ip)
|
|
matched = 1;
|
|
|
|
for (cnt = 0; !matched && cnt < (int) entry->intf_alias_num; cnt++) {
|
|
if (entry->intf_alias_addrs[cnt].addr_type != ADDR_TYPE_IP)
|
|
continue;
|
|
if (entry->intf_alias_addrs[cnt].addr_ip == save->intf_addr.addr_ip)
|
|
matched = 1;
|
|
}
|
|
|
|
if (matched) {
|
|
/* XXX - truncated result if entry is too small. */
|
|
if (save->intf_len < entry->intf_len)
|
|
memcpy(save, entry, save->intf_len);
|
|
else
|
|
memcpy(save, entry, entry->intf_len);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
intf_get_src(intf_t *intf, struct intf_entry *entry, struct addr *src)
|
|
{
|
|
memcpy(&entry->intf_addr, src, sizeof(*src));
|
|
|
|
if (intf_loop(intf, _match_intf_src, entry) != 1) {
|
|
errno = ENXIO;
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
intf_get_dst(intf_t *intf, struct intf_entry *entry, struct addr *dst)
|
|
{
|
|
struct sockaddr_in sin;
|
|
socklen_t n;
|
|
|
|
if (dst->addr_type != ADDR_TYPE_IP) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
addr_ntos(dst, (struct sockaddr *)&sin);
|
|
sin.sin_port = htons(666);
|
|
|
|
if (connect(intf->fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
|
|
return (-1);
|
|
|
|
n = sizeof(sin);
|
|
if (getsockname(intf->fd, (struct sockaddr *)&sin, &n) < 0)
|
|
return (-1);
|
|
|
|
addr_ston((struct sockaddr *)&sin, &entry->intf_addr);
|
|
|
|
if (intf_loop(intf, _match_intf_src, entry) != 1)
|
|
return (-1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#ifdef HAVE_LINUX_PROCFS
|
|
#define PROC_DEV_FILE "/proc/net/dev"
|
|
|
|
int
|
|
intf_loop(intf_t *intf, intf_handler callback, void *arg)
|
|
{
|
|
FILE *fp;
|
|
struct intf_entry *entry;
|
|
char *p, buf[BUFSIZ], ebuf[BUFSIZ];
|
|
int ret;
|
|
|
|
entry = (struct intf_entry *)ebuf;
|
|
|
|
if ((fp = fopen(PROC_DEV_FILE, "r")) == NULL)
|
|
return (-1);
|
|
|
|
intf->ifc.ifc_buf = (caddr_t)intf->ifcbuf;
|
|
intf->ifc.ifc_len = sizeof(intf->ifcbuf);
|
|
|
|
if (ioctl(intf->fd, SIOCGIFCONF, &intf->ifc) < 0) {
|
|
fclose(fp);
|
|
return (-1);
|
|
}
|
|
|
|
ret = 0;
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
|
if ((p = strchr(buf, ':')) == NULL)
|
|
continue;
|
|
*p = '\0';
|
|
for (p = buf; *p == ' '; p++)
|
|
;
|
|
|
|
memset(ebuf, 0, sizeof(ebuf));
|
|
strlcpy(entry->intf_name, p, sizeof(entry->intf_name));
|
|
entry->intf_len = sizeof(ebuf);
|
|
|
|
if (_intf_get_noalias(intf, entry) < 0) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
if (_intf_get_aliases(intf, entry) < 0) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
if ((ret = (*callback)(entry, arg)) != 0)
|
|
break;
|
|
}
|
|
if (ferror(fp))
|
|
ret = -1;
|
|
|
|
fclose(fp);
|
|
|
|
return (ret);
|
|
}
|
|
#elif defined(SIOCGLIFCONF)
|
|
int
|
|
intf_loop(intf_t *intf, intf_handler callback, void *arg)
|
|
{
|
|
struct intf_entry *entry;
|
|
struct lifreq *lifr, *llifr, *plifr;
|
|
char *p, ebuf[BUFSIZ];
|
|
int ret;
|
|
|
|
entry = (struct intf_entry *)ebuf;
|
|
|
|
/* http://www.unix.com/man-page/opensolaris/7p/if_tcp */
|
|
intf->lifc.lifc_family = AF_UNSPEC;
|
|
intf->lifc.lifc_flags = 0;
|
|
#ifdef LIFC_UNDER_IPMP
|
|
intf->lifc.lifc_flags |= LIFC_UNDER_IPMP;
|
|
#endif
|
|
intf->lifc.lifc_buf = (caddr_t)intf->ifcbuf;
|
|
intf->lifc.lifc_len = sizeof(intf->ifcbuf);
|
|
|
|
if (ioctl(intf->fd, SIOCGLIFCONF, &intf->lifc) < 0)
|
|
return (-1);
|
|
|
|
llifr = (struct lifreq *)&intf->lifc.lifc_buf[intf->lifc.lifc_len];
|
|
|
|
for (lifr = intf->lifc.lifc_req; lifr < llifr; lifr = NEXTLIFR(lifr)) {
|
|
/* XXX - Linux, Solaris ifaliases */
|
|
if ((p = strchr(lifr->lifr_name, ':')) != NULL)
|
|
*p = '\0';
|
|
|
|
for (plifr = intf->lifc.lifc_req; plifr < lifr; plifr = NEXTLIFR(lifr)) {
|
|
if (strcmp(lifr->lifr_name, plifr->lifr_name) == 0)
|
|
break;
|
|
}
|
|
if (lifr > intf->lifc.lifc_req && plifr < lifr)
|
|
continue;
|
|
|
|
memset(ebuf, 0, sizeof(ebuf));
|
|
strlcpy(entry->intf_name, lifr->lifr_name,
|
|
sizeof(entry->intf_name));
|
|
entry->intf_len = sizeof(ebuf);
|
|
|
|
/* Repair the alias name back up */
|
|
if (p) *p = ':';
|
|
|
|
if (_intf_get_noalias(intf, entry) < 0)
|
|
return (-1);
|
|
if (_intf_get_aliases(intf, entry) < 0)
|
|
return (-1);
|
|
|
|
if ((ret = (*callback)(entry, arg)) != 0)
|
|
return (ret);
|
|
}
|
|
return (0);
|
|
}
|
|
#else
|
|
int
|
|
intf_loop(intf_t *intf, intf_handler callback, void *arg)
|
|
{
|
|
struct intf_entry *entry;
|
|
struct ifreq *ifr, *lifr, *pifr;
|
|
char *p, ebuf[BUFSIZ];
|
|
int ret;
|
|
|
|
entry = (struct intf_entry *)ebuf;
|
|
|
|
intf->ifc.ifc_buf = (caddr_t)intf->ifcbuf;
|
|
intf->ifc.ifc_len = sizeof(intf->ifcbuf);
|
|
|
|
if (ioctl(intf->fd, SIOCGIFCONF, &intf->ifc) < 0)
|
|
return (-1);
|
|
|
|
pifr = NULL;
|
|
lifr = (struct ifreq *)&intf->ifc.ifc_buf[intf->ifc.ifc_len];
|
|
|
|
for (ifr = intf->ifc.ifc_req; ifr < lifr; ifr = NEXTIFR(ifr)) {
|
|
/* XXX - Linux, Solaris ifaliases */
|
|
if ((p = strchr(ifr->ifr_name, ':')) != NULL)
|
|
*p = '\0';
|
|
|
|
if (pifr != NULL && strcmp(ifr->ifr_name, pifr->ifr_name) == 0) {
|
|
if (p) *p = ':';
|
|
continue;
|
|
}
|
|
|
|
memset(ebuf, 0, sizeof(ebuf));
|
|
strlcpy(entry->intf_name, ifr->ifr_name,
|
|
sizeof(entry->intf_name));
|
|
entry->intf_len = sizeof(ebuf);
|
|
|
|
/* Repair the alias name back up */
|
|
if (p) *p = ':';
|
|
|
|
if (_intf_get_noalias(intf, entry) < 0)
|
|
return (-1);
|
|
if (_intf_get_aliases(intf, entry) < 0)
|
|
return (-1);
|
|
|
|
if ((ret = (*callback)(entry, arg)) != 0)
|
|
return (ret);
|
|
|
|
pifr = ifr;
|
|
}
|
|
return (0);
|
|
}
|
|
#endif /* !HAVE_LINUX_PROCFS */
|
|
|
|
intf_t *
|
|
intf_close(intf_t *intf)
|
|
{
|
|
if (intf != NULL) {
|
|
if (intf->fd >= 0)
|
|
close(intf->fd);
|
|
if (intf->fd6 >= 0)
|
|
close(intf->fd6);
|
|
free(intf);
|
|
}
|
|
return (NULL);
|
|
}
|