1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-29 09:39:03 +00:00
Files
nmap/libdnet-stripped/src/intf-win32.c
david 48c6e7b820 Move the body of eth_get_pcap_devname back into intf_get_pcap_devname, leaving
eth_get_pcap_devname as a wrapper.

In addition to the hardware address check, add a check of the textual interface
descriptions in order better to distinguish interfaces. It appears to me that
the pcap description (pdev->description) is the same as what is returned by a
call to PacketRequest with an OID of OID_GEN_FRIENDLY_NAME, so that's what I'm
comparing. That differs from OID_GEN_VENDOR_NAME, which is what you get in
ifrow.bDescr from GetIfTable.

We've found that simply comparing hardware addresses is not enough when using
Windows "teamed" (link-aggregated) interfaces. In a simple example, two NICs
are teamed together, leading to three interfaces visible to libdnet: the two
physical NICs and the virtual teamed interface. All three of these have the
same MAC address. What was happening was the eth0 interface was being assigned
to one of the physical NICs, packets were sent over it, but the replies were
not necessarily coming back to the same physical NIC.
2010-05-13 04:06:53 +00:00

476 lines
11 KiB
C

/*
* intf-win32.c
*
* Copyright (c) 2002 Dug Song <dugsong@monkey.org>
*
* $Id: intf-win32.c 632 2006-08-10 04:36:52Z dugsong $
*/
#ifdef _WIN32
#include "dnet_winconfig.h"
#else
#include "config.h"
#endif
#include <iphlpapi.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dnet.h"
#include "pcap.h"
#include <Packet32.h>
#include <Ntddndis.h>
struct ifcombo {
DWORD *idx;
int cnt;
int max;
};
/* XXX - ipifcons.h incomplete, use IANA ifTypes MIB */
#define MIB_IF_TYPE_TUNNEL 131
#define MIB_IF_TYPE_MAX MAX_IF_TYPE
struct intf_handle {
struct ifcombo ifcombo[MIB_IF_TYPE_MAX];
MIB_IFTABLE *iftable;
MIB_IPADDRTABLE *iptable;
};
static char *
_ifcombo_name(int type)
{
char *name = "eth"; /* XXX */
if (type == MIB_IF_TYPE_TOKENRING) {
name = "tr";
} else if (type == MIB_IF_TYPE_FDDI) {
name = "fddi";
} else if (type == MIB_IF_TYPE_PPP) {
name = "ppp";
} else if (type == MIB_IF_TYPE_LOOPBACK) {
name = "lo";
} else if (type == MIB_IF_TYPE_SLIP) {
name = "sl";
} else if (type == MIB_IF_TYPE_TUNNEL) {
name = "tun";
}
return (name);
}
static int
_ifcombo_type(const char *device)
{
int type = INTF_TYPE_OTHER;
if (strncmp(device, "eth", 3) == 0) {
type = INTF_TYPE_ETH;
} else if (strncmp(device, "tr", 2) == 0) {
type = INTF_TYPE_TOKENRING;
} else if (strncmp(device, "fd", 2) == 0) {
type = INTF_TYPE_FDDI;
} else if (strncmp(device, "ppp", 3) == 0) {
type = INTF_TYPE_PPP;
} else if (strncmp(device, "lo", 2) == 0) {
type = INTF_TYPE_LOOPBACK;
} else if (strncmp(device, "sl", 2) == 0) {
type = INTF_TYPE_SLIP;
} else if (strncmp(device, "tun", 3) == 0) {
type = INTF_TYPE_TUN;
}
return (type);
}
static void
_ifcombo_add(struct ifcombo *ifc, DWORD idx)
{
if (ifc->cnt == ifc->max) {
if (ifc->idx) {
ifc->max *= 2;
ifc->idx = realloc(ifc->idx,
sizeof(ifc->idx[0]) * ifc->max);
} else {
ifc->max = 8;
ifc->idx = malloc(sizeof(ifc->idx[0]) * ifc->max);
}
}
ifc->idx[ifc->cnt++] = idx;
}
/* Map an MIB_IFROW.dwType interface type into an internal interface
type. The internal types are never exposed to users of this library;
they exist only for the sake of ordering interface types within an
intf_handle, which has an array of ifcombo structures ordered by
type. Entries in an intf_handle must not be stored or accessed by a
raw MIB_IFROW.dwType number because they will not be able to be found
by a device name such as "net0" if the device name does not map
exactly to the dwType. */
static int
_if_type_canonicalize(int type)
{
return _ifcombo_type(_ifcombo_name(type));
}
static void
_ifrow_to_entry(intf_t *intf, MIB_IFROW *ifrow, struct intf_entry *entry)
{
struct addr *ap, *lap;
int i;
int type;
/* The total length of the entry may be passed inside entry.
Remember it and clear the entry. */
u_int intf_len = entry->intf_len;
memset(entry, 0, sizeof(*entry));
entry->intf_len = intf_len;
type = _if_type_canonicalize(ifrow->dwType);
for (i = 0; i < intf->ifcombo[type].cnt; i++) {
if (intf->ifcombo[type].idx[i] == ifrow->dwIndex)
break;
}
/* XXX - type matches MIB-II ifType. */
snprintf(entry->intf_name, sizeof(entry->intf_name), "%s%lu",
_ifcombo_name(type), i);
entry->intf_type = (uint16_t)type;
/* Get interface flags. */
entry->intf_flags = 0;
if (ifrow->dwAdminStatus == MIB_IF_ADMIN_STATUS_UP &&
(ifrow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL ||
ifrow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED))
entry->intf_flags |= INTF_FLAG_UP;
if (ifrow->dwType == MIB_IF_TYPE_LOOPBACK)
entry->intf_flags |= INTF_FLAG_LOOPBACK;
else
entry->intf_flags |= INTF_FLAG_MULTICAST;
/* Get interface MTU. */
entry->intf_mtu = ifrow->dwMtu;
/* Get hardware address. */
if (ifrow->dwPhysAddrLen == ETH_ADDR_LEN) {
entry->intf_link_addr.addr_type = ADDR_TYPE_ETH;
entry->intf_link_addr.addr_bits = ETH_ADDR_BITS;
memcpy(&entry->intf_link_addr.addr_eth, ifrow->bPhysAddr,
ETH_ADDR_LEN);
}
/* Get addresses. */
ap = entry->intf_alias_addrs;
lap = ap + ((entry->intf_len - sizeof(*entry)) /
sizeof(entry->intf_alias_addrs[0]));
for (i = 0; i < (int)intf->iptable->dwNumEntries; i++) {
if (intf->iptable->table[i].dwIndex == ifrow->dwIndex &&
intf->iptable->table[i].dwAddr != 0) {
if (entry->intf_addr.addr_type == ADDR_TYPE_NONE) {
/* Set primary address if unset. */
entry->intf_addr.addr_type = ADDR_TYPE_IP;
entry->intf_addr.addr_ip =
intf->iptable->table[i].dwAddr;
addr_mtob(&intf->iptable->table[i].dwMask,
IP_ADDR_LEN, &entry->intf_addr.addr_bits);
} else if (ap < lap) {
/* Set aliases. */
ap->addr_type = ADDR_TYPE_IP;
ap->addr_ip = intf->iptable->table[i].dwAddr;
addr_mtob(&intf->iptable->table[i].dwMask,
IP_ADDR_LEN, &ap->addr_bits);
ap++, entry->intf_alias_num++;
}
}
}
entry->intf_len = (u_int) ((u_char *)ap - (u_char *)entry);
}
static int
_refresh_tables(intf_t *intf)
{
MIB_IFROW *ifrow;
ULONG len;
u_int i, ret;
/* Get interface table. */
for (len = sizeof(intf->iftable[0]); ; ) {
if (intf->iftable)
free(intf->iftable);
intf->iftable = malloc(len);
ret = GetIfTable(intf->iftable, &len, FALSE);
if (ret == NO_ERROR)
break;
else if (ret != ERROR_INSUFFICIENT_BUFFER)
return (-1);
}
/* Get IP address table. */
for (len = sizeof(intf->iptable[0]); ; ) {
if (intf->iptable)
free(intf->iptable);
intf->iptable = malloc(len);
ret = GetIpAddrTable(intf->iptable, &len, FALSE);
if (ret == NO_ERROR)
break;
else if (ret != ERROR_INSUFFICIENT_BUFFER)
return (-1);
}
/*
* Map "unfriendly" win32 interface indices to ours.
* XXX - like IP_ADAPTER_INFO ComboIndex
*/
for (i = 0; i < intf->iftable->dwNumEntries; i++) {
int type;
ifrow = &intf->iftable->table[i];
type = _if_type_canonicalize(ifrow->dwType);
if (type < MIB_IF_TYPE_MAX) {
_ifcombo_add(&intf->ifcombo[type], ifrow->dwIndex);
} else
return (-1);
}
return (0);
}
static int
_find_ifindex(intf_t *intf, const char *device)
{
char *p = (char *)device;
int n, type = _ifcombo_type(device);
while (isalpha((int) (unsigned char) *p)) p++;
n = atoi(p);
return (intf->ifcombo[type].idx[n]);
}
intf_t *
intf_open(void)
{
return (calloc(1, sizeof(intf_t)));
}
int
intf_get(intf_t *intf, struct intf_entry *entry)
{
MIB_IFROW ifrow;
if (_refresh_tables(intf) < 0)
return (-1);
ifrow.dwIndex = _find_ifindex(intf, entry->intf_name);
if (GetIfEntry(&ifrow) != NO_ERROR)
return (-1);
_ifrow_to_entry(intf, &ifrow, entry);
return (0);
}
int
intf_get_src(intf_t *intf, struct intf_entry *entry, struct addr *src)
{
MIB_IFROW ifrow;
MIB_IPADDRROW *iprow;
int i;
if (src->addr_type != ADDR_TYPE_IP) {
errno = EINVAL;
return (-1);
}
if (_refresh_tables(intf) < 0)
return (-1);
for (i = 0; i < (int)intf->iptable->dwNumEntries; i++) {
iprow = &intf->iptable->table[i];
if (iprow->dwAddr == src->addr_ip) {
ifrow.dwIndex = iprow->dwIndex;
if (GetIfEntry(&ifrow) != NO_ERROR)
return (-1);
_ifrow_to_entry(intf, &ifrow, entry);
return (0);
}
}
errno = ENXIO;
return (-1);
}
int
intf_get_dst(intf_t *intf, struct intf_entry *entry, struct addr *dst)
{
MIB_IFROW ifrow;
if (dst->addr_type != ADDR_TYPE_IP) {
errno = EINVAL;
return (-1);
}
if (GetBestInterface(dst->addr_ip, &ifrow.dwIndex) != NO_ERROR)
return (-1);
if (GetIfEntry(&ifrow) != NO_ERROR)
return (-1);
if (_refresh_tables(intf) < 0)
return (-1);
_ifrow_to_entry(intf, &ifrow, entry);
return (0);
}
int
intf_set(intf_t *intf, const struct intf_entry *entry)
{
/*
* XXX - could set interface up/down via SetIfEntry(),
* but what about the rest of the configuration? :-(
* {Add,Delete}IPAddress for 2000/XP only
*/
#if 0
/* Set interface address. XXX - 2000/XP only? */
if (entry->intf_addr.addr_type == ADDR_TYPE_IP) {
ULONG ctx = 0, inst = 0;
UINT ip, mask;
memcpy(&ip, &entry->intf_addr.addr_ip, IP_ADDR_LEN);
addr_btom(entry->intf_addr.addr_bits, &mask, IP_ADDR_LEN);
if (AddIPAddress(ip, mask,
_find_ifindex(intf, entry->intf_name),
&ctx, &inst) != NO_ERROR) {
return (-1);
}
return (0);
}
#endif
errno = ENOSYS;
SetLastError(ERROR_NOT_SUPPORTED);
return (-1);
}
int
intf_loop(intf_t *intf, intf_handler callback, void *arg)
{
struct intf_entry *entry;
u_char ebuf[1024];
int i, ret = 0;
if (_refresh_tables(intf) < 0)
return (-1);
entry = (struct intf_entry *)ebuf;
for (i = 0; i < (int)intf->iftable->dwNumEntries; i++) {
entry->intf_len = sizeof(ebuf);
_ifrow_to_entry(intf, &intf->iftable->table[i], entry);
if ((ret = (*callback)(entry, arg)) != 0)
break;
}
return (ret);
}
intf_t *
intf_close(intf_t *intf)
{
int i;
if (intf != NULL) {
for (i = 0; i < MIB_IF_TYPE_MAX; i++) {
if (intf->ifcombo[i].idx)
free(intf->ifcombo[i].idx);
}
if (intf->iftable)
free(intf->iftable);
if (intf->iptable)
free(intf->iptable);
free(intf);
}
return (NULL);
}
/* Converts a libdnet interface name to its pcap equivalent. The pcap name is
stored in pcapdev up to a length of pcapdevlen, including the terminating
'\0'. Returns -1 on error. */
int
intf_get_pcap_devname(const char *intf_name, char *pcapdev, int pcapdevlen)
{
wchar_t descr_wc[512];
pcap_if_t *pcapdevs;
pcap_if_t *pdev;
intf_t *intf;
MIB_IFROW ifrow;
if ((intf = intf_open()) == NULL)
return (-1);
if (_refresh_tables(intf) < 0) {
intf_close(intf);
return (-1);
}
ifrow.dwIndex = _find_ifindex(intf, intf_name);
intf_close(intf);
if (GetIfEntry(&ifrow) != NO_ERROR)
return (-1);
/* OID_GEN_FRIENDLY_NAME returns a wide-character string, so convert
the description to wide characters for string comparison. */
mbstowcs(descr_wc, ifrow.bDescr, sizeof(descr_wc) / sizeof(descr_wc[0]) - 1);
descr_wc[sizeof(descr_wc) / sizeof(descr_wc[0]) - 1] = L'\0';
if (pcap_findalldevs(&pcapdevs, NULL) == -1)
return (-1);
/* Loop through all the pcap devices until we find a match. pcap gets
its interface list from the registry; dnet gets it from GetIfList.
We must match them up using values common to both data sets. We do
it by comparing hardware addresses and interface descriptions. */
for (pdev = pcapdevs; pdev != NULL; pdev = pdev->next) {
PACKET_OID_DATA *data;
u_char buf[512];
LPADAPTER lpa;
lpa = PacketOpenAdapter(pdev->name);
if (lpa == NULL)
continue;
if (lpa->hFile == INVALID_HANDLE_VALUE)
goto close_adapter;
data = (PACKET_OID_DATA *) buf;
/* Check the MAC address if available. */
data->Oid = OID_802_3_CURRENT_ADDRESS;
data->Length = sizeof(buf) - sizeof(*data);
if (PacketRequest(lpa, FALSE, data) == TRUE) {
if (data->Length != ifrow.dwPhysAddrLen)
goto close_adapter;
if (memcmp(ifrow.bPhysAddr, data->Data, data->Length) != 0)
goto close_adapter;
}
/* Distinct interfaces can have the same MAC address in the
case of "teamed" interfaces. Additionally check the
description string. */
data->Oid = OID_GEN_FRIENDLY_NAME;
data->Length = sizeof(buf) - sizeof(*data);
if (PacketRequest(lpa, FALSE, data) != TRUE)
goto close_adapter;
if (wcscmp(descr_wc, (wchar_t *) data->Data) != 0)
goto close_adapter;
/* Found it. */
PacketCloseAdapter(lpa);
break;
close_adapter:
PacketCloseAdapter(lpa);
}
if (pdev != NULL)
strlcpy(pcapdev, pdev->name, pcapdevlen);
pcap_freealldevs(pcapdevs);
if (pdev == NULL)
return -1;
else
return 0;
}