1005 lines
30 KiB
C
1005 lines
30 KiB
C
|
/*
|
||
|
portable interface to "raw sockets"
|
||
|
|
||
|
This uses both "libpcap" on systems, but on Linux, we try to use the
|
||
|
basic raw sockets, bypassing libpcap for better performance.
|
||
|
*/
|
||
|
#include "rawsock.h"
|
||
|
#include "templ-pkt.h"
|
||
|
#include "util-logger.h"
|
||
|
#include "main-ptrace.h"
|
||
|
#include "util-safefunc.h"
|
||
|
#include "stub-pcap.h"
|
||
|
#include "stub-pfring.h"
|
||
|
#include "pixie-timer.h"
|
||
|
#include "main-globals.h"
|
||
|
#include "proto-preprocess.h"
|
||
|
#include "stack-arpv4.h"
|
||
|
#include "stack-ndpv6.h"
|
||
|
|
||
|
#include "unusedparm.h"
|
||
|
#include "util-malloc.h"
|
||
|
#include <assert.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
static int is_pcap_file = 0;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
#include <winsock.h>
|
||
|
#include <iphlpapi.h>
|
||
|
|
||
|
#if defined(_MSC_VER)
|
||
|
#pragma comment(lib, "IPHLPAPI.lib")
|
||
|
#endif
|
||
|
|
||
|
#elif defined(__GNUC__)
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <net/if.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
#else
|
||
|
#endif
|
||
|
|
||
|
#include "rawsock-adapter.h"
|
||
|
|
||
|
#define SENDQ_SIZE 65536 * 8
|
||
|
|
||
|
|
||
|
struct AdapterNames
|
||
|
{
|
||
|
char *easy_name;
|
||
|
char *hard_name;
|
||
|
};
|
||
|
|
||
|
struct AdapterNames adapter_names[64];
|
||
|
unsigned adapter_name_count = 0;
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
#ifdef WIN32
|
||
|
int pcap_setdirection(pcap_t *pcap, pcap_direction_t direction)
|
||
|
{
|
||
|
static int (*real_setdirection)(pcap_t *, pcap_direction_t) = 0;
|
||
|
|
||
|
if (real_setdirection == 0) {
|
||
|
void* h = LoadLibraryA("wpcap.dll");
|
||
|
if (h == NULL) {
|
||
|
fprintf(stderr, "couldn't load wpcap.dll: %u\n",
|
||
|
(unsigned)GetLastError());
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
real_setdirection = (int (*)(pcap_t*,pcap_direction_t))
|
||
|
GetProcAddress(h, "pcap_setdirection");
|
||
|
if (real_setdirection == 0) {
|
||
|
fprintf(stderr, "couldn't find pcap_setdirection(): %u\n",
|
||
|
(unsigned)GetLastError());
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return real_setdirection(pcap, direction);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
void
|
||
|
rawsock_init(void)
|
||
|
{
|
||
|
#ifdef WIN32
|
||
|
/* Declare and initialize variables */
|
||
|
|
||
|
// It is possible for an adapter to have multiple
|
||
|
// IPv4 addresses, gateways, and secondary WINS servers
|
||
|
// assigned to the adapter.
|
||
|
//
|
||
|
// Note that this sample code only prints out the
|
||
|
// first entry for the IP address/mask, and gateway, and
|
||
|
// the primary and secondary WINS server for each adapter.
|
||
|
|
||
|
PIP_ADAPTER_INFO pAdapterInfo;
|
||
|
PIP_ADAPTER_INFO pAdapter = NULL;
|
||
|
DWORD dwRetVal = 0;
|
||
|
UINT i;
|
||
|
|
||
|
/* variables used to print DHCP time info */
|
||
|
//struct tm newtime;
|
||
|
//char buffer[32];
|
||
|
|
||
|
ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
|
||
|
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof (IP_ADAPTER_INFO));
|
||
|
if (pAdapterInfo == NULL) {
|
||
|
printf("Error allocating memory needed to call GetAdaptersinfo\n");
|
||
|
return;
|
||
|
}
|
||
|
// Make an initial call to GetAdaptersInfo to get
|
||
|
// the necessary size into the ulOutBufLen variable
|
||
|
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
|
||
|
free(pAdapterInfo);
|
||
|
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
|
||
|
if (pAdapterInfo == NULL) {
|
||
|
printf("Error allocating memory needed to call GetAdaptersinfo\n");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
|
||
|
for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) {
|
||
|
if (pAdapter->Type != MIB_IF_TYPE_ETHERNET)
|
||
|
continue;
|
||
|
|
||
|
//printf("\tComboIndex: \t%d\n", pAdapter->ComboIndex);
|
||
|
//printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName);
|
||
|
{
|
||
|
size_t name_len = strlen(pAdapter->AdapterName) + 12 + 1;
|
||
|
char *name = (char*)malloc(name_len);
|
||
|
size_t addr_len = pAdapter->AddressLength * 3 + 1;
|
||
|
char *addr = (char*)malloc(addr_len);
|
||
|
|
||
|
if (name == NULL || addr == NULL)
|
||
|
exit(1);
|
||
|
|
||
|
snprintf(name, name_len, "\\Device\\NPF_%s", pAdapter->AdapterName);
|
||
|
|
||
|
//printf("\tAdapter Desc: \t%s\n", pAdapter->Description);
|
||
|
//printf("\tAdapter Addr: \t");
|
||
|
for (i = 0; i < pAdapter->AddressLength; i++) {
|
||
|
if (i == (pAdapter->AddressLength - 1))
|
||
|
snprintf(addr+i*3, addr_len-i*3, "%.2X", pAdapter->Address[i]);
|
||
|
else
|
||
|
snprintf(addr+i*3, addr_len-i*3, "%.2X-", pAdapter->Address[i]);
|
||
|
}
|
||
|
//printf("%s -> %s\n", addr, name);
|
||
|
adapter_names[adapter_name_count].easy_name = addr;
|
||
|
adapter_names[adapter_name_count].hard_name = name;
|
||
|
adapter_name_count++;
|
||
|
}
|
||
|
|
||
|
//printf("\tIndex: \t%d\n", pAdapter->Index);
|
||
|
|
||
|
{
|
||
|
size_t name_len = strlen(pAdapter->AdapterName) + 12 + 1;
|
||
|
char *name = (char*)malloc(name_len);
|
||
|
size_t addr_len = strlen(pAdapter->IpAddressList.IpAddress.String) + 1;
|
||
|
char *addr = (char*)malloc(addr_len);
|
||
|
if (name == NULL || addr == NULL)
|
||
|
exit(1);
|
||
|
snprintf(name, name_len, "\\Device\\NPF_%s", pAdapter->AdapterName);
|
||
|
snprintf(addr, addr_len, "%s", pAdapter->IpAddressList.IpAddress.String);
|
||
|
//printf("%s -> %s\n", addr, name);
|
||
|
adapter_names[adapter_name_count].easy_name = addr;
|
||
|
adapter_names[adapter_name_count].hard_name = name;
|
||
|
adapter_name_count++;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
} else {
|
||
|
printf("GetAdaptersInfo failed with error: %u\n",
|
||
|
(unsigned)dwRetVal);
|
||
|
|
||
|
}
|
||
|
if (pAdapterInfo)
|
||
|
free(pAdapterInfo);
|
||
|
#else
|
||
|
PFRING_init();
|
||
|
#endif
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* This function prints to the command line a list of all the network
|
||
|
* interfaces/devices.
|
||
|
***************************************************************************/
|
||
|
void
|
||
|
rawsock_list_adapters(void)
|
||
|
{
|
||
|
pcap_if_t *alldevs;
|
||
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||
|
|
||
|
if (PCAP.findalldevs(&alldevs, errbuf) != -1) {
|
||
|
int i;
|
||
|
const pcap_if_t *d;
|
||
|
i=0;
|
||
|
|
||
|
if (alldevs == NULL) {
|
||
|
fprintf(stderr, "ERR:libpcap: no adapters found, are you sure you are root?\n");
|
||
|
}
|
||
|
/* Print the list */
|
||
|
for(d=alldevs; d; d=PCAP.dev_next(d)) {
|
||
|
fprintf(stderr, " %d %s \t", i++, PCAP.dev_name(d));
|
||
|
if (PCAP.dev_description(d))
|
||
|
fprintf(stderr, "(%s)\n", PCAP.dev_description(d));
|
||
|
else
|
||
|
fprintf(stderr, "(No description available)\n");
|
||
|
}
|
||
|
fprintf(stderr,"\n");
|
||
|
PCAP.freealldevs(alldevs);
|
||
|
} else {
|
||
|
fprintf(stderr, "%s\n", errbuf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static const char *
|
||
|
adapter_from_index(unsigned index)
|
||
|
{
|
||
|
pcap_if_t *alldevs;
|
||
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||
|
int x;
|
||
|
|
||
|
x = PCAP.findalldevs(&alldevs, errbuf);
|
||
|
if (x != -1) {
|
||
|
const pcap_if_t *d;
|
||
|
|
||
|
if (alldevs == NULL) {
|
||
|
fprintf(stderr, "ERR:libpcap: no adapters found, are you sure you are root?\n");
|
||
|
}
|
||
|
/* Print the list */
|
||
|
for(d=alldevs; d; d=PCAP.dev_next(d)) {
|
||
|
if (index-- == 0)
|
||
|
return PCAP.dev_name(d);
|
||
|
}
|
||
|
return 0;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Some methods of transmit queue multiple packets in a buffer then
|
||
|
* send all queued packets at once. At the end of a scan, we might have
|
||
|
* some pending packets that haven't been transmitted yet. Therefore,
|
||
|
* we'll have to flush them.
|
||
|
***************************************************************************/
|
||
|
void
|
||
|
rawsock_flush(struct Adapter *adapter)
|
||
|
{
|
||
|
if (adapter->sendq) {
|
||
|
PCAP.sendqueue_transmit(adapter->pcap, adapter->sendq, 0);
|
||
|
|
||
|
/* Dude, I totally forget why this step is necessary. I vaguely
|
||
|
* remember there's a good reason for it though */
|
||
|
PCAP.sendqueue_destroy(adapter->sendq);
|
||
|
adapter->sendq = PCAP.sendqueue_alloc(SENDQ_SIZE);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* wrapper for libpcap's sendpacket
|
||
|
*
|
||
|
* PORTABILITY: WINDOWS and PF_RING
|
||
|
* For performance, Windows and PF_RING can queue up multiple packets, then
|
||
|
* transmit them all in a chunk. If we stop and wait for a bit, we need
|
||
|
* to flush the queue to force packets to be transmitted immediately.
|
||
|
***************************************************************************/
|
||
|
int
|
||
|
rawsock_send_packet(
|
||
|
struct Adapter *adapter,
|
||
|
const unsigned char *packet,
|
||
|
unsigned length,
|
||
|
unsigned flush)
|
||
|
{
|
||
|
|
||
|
/* Why: this happens in "offline mode", when we are benchmarking the
|
||
|
* core algorithms without sending packets. */
|
||
|
if (adapter == 0)
|
||
|
return 0;
|
||
|
|
||
|
/* Print --packet-trace if debugging */
|
||
|
if (adapter->is_packet_trace) {
|
||
|
packet_trace(stdout, adapter->pt_start, packet, length, 1);
|
||
|
}
|
||
|
|
||
|
/* PF_RING */
|
||
|
if (adapter->ring) {
|
||
|
int err = PF_RING_ERROR_NO_TX_SLOT_AVAILABLE;
|
||
|
|
||
|
while (err == PF_RING_ERROR_NO_TX_SLOT_AVAILABLE) {
|
||
|
err = PFRING.send(adapter->ring, packet, length, (unsigned char)flush);
|
||
|
}
|
||
|
if (err < 0)
|
||
|
LOG(1, "pfring:xmit: ERROR %d\n", err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/* WINDOWS PCAP */
|
||
|
if (adapter->sendq) {
|
||
|
int err;
|
||
|
struct pcap_pkthdr hdr;
|
||
|
hdr.len = length;
|
||
|
hdr.caplen = length;
|
||
|
|
||
|
err = PCAP.sendqueue_queue(adapter->sendq, &hdr, packet);
|
||
|
if (err) {
|
||
|
rawsock_flush(adapter);
|
||
|
PCAP.sendqueue_queue(adapter->sendq, &hdr, packet);
|
||
|
}
|
||
|
|
||
|
if (flush) {
|
||
|
rawsock_flush(adapter);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* LIBPCAP */
|
||
|
if (adapter->pcap)
|
||
|
return PCAP.sendpacket(adapter->pcap, packet, length);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
int rawsock_recv_packet(
|
||
|
struct Adapter *adapter,
|
||
|
unsigned *length,
|
||
|
unsigned *secs,
|
||
|
unsigned *usecs,
|
||
|
const unsigned char **packet)
|
||
|
{
|
||
|
|
||
|
if (adapter->ring) {
|
||
|
/* This is for doing libpfring instead of libpcap */
|
||
|
struct pfring_pkthdr hdr;
|
||
|
int err;
|
||
|
|
||
|
again:
|
||
|
err = PFRING.recv(adapter->ring,
|
||
|
(unsigned char**)packet,
|
||
|
0, /* zero-copy */
|
||
|
&hdr,
|
||
|
0 /* return immediately */
|
||
|
);
|
||
|
if (err == PF_RING_ERROR_NO_PKT_AVAILABLE || hdr.caplen == 0) {
|
||
|
PFRING.poll(adapter->ring, 1);
|
||
|
if (is_tx_done)
|
||
|
return 1;
|
||
|
goto again;
|
||
|
}
|
||
|
if (err)
|
||
|
return 1;
|
||
|
|
||
|
*length = hdr.caplen;
|
||
|
*secs = (unsigned)hdr.ts.tv_sec;
|
||
|
*usecs = (unsigned)hdr.ts.tv_usec;
|
||
|
|
||
|
} else if (adapter->pcap) {
|
||
|
struct pcap_pkthdr hdr;
|
||
|
|
||
|
*packet = PCAP.next(adapter->pcap, &hdr);
|
||
|
|
||
|
if (*packet == NULL) {
|
||
|
if (is_pcap_file) {
|
||
|
//pixie_time_set_offset(10*100000);
|
||
|
is_tx_done = 1;
|
||
|
is_rx_done = 1;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
*length = hdr.caplen;
|
||
|
*secs = (unsigned)hdr.ts.tv_sec;
|
||
|
*usecs = (unsigned)hdr.ts.tv_usec;
|
||
|
}
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Sends the TCP SYN probe packet.
|
||
|
*
|
||
|
* Step 1: format the packet
|
||
|
* Step 2: send it in a portable manner
|
||
|
***************************************************************************/
|
||
|
void
|
||
|
rawsock_send_probe_ipv4(
|
||
|
struct Adapter *adapter,
|
||
|
ipv4address ip_them, unsigned port_them,
|
||
|
ipv4address ip_me, unsigned port_me,
|
||
|
unsigned seqno, unsigned flush,
|
||
|
struct TemplateSet *tmplset)
|
||
|
{
|
||
|
unsigned char px[2048];
|
||
|
size_t packet_length;
|
||
|
|
||
|
/*
|
||
|
* Construct the destination packet
|
||
|
*/
|
||
|
template_set_target_ipv4(tmplset, ip_them, port_them, ip_me, port_me, seqno,
|
||
|
px, sizeof(px), &packet_length);
|
||
|
|
||
|
/*
|
||
|
* Send it
|
||
|
*/
|
||
|
rawsock_send_packet(adapter, px, (unsigned)packet_length, flush);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
rawsock_send_probe_ipv6(
|
||
|
struct Adapter *adapter,
|
||
|
ipv6address ip_them, unsigned port_them,
|
||
|
ipv6address ip_me, unsigned port_me,
|
||
|
unsigned seqno, unsigned flush,
|
||
|
struct TemplateSet *tmplset)
|
||
|
{
|
||
|
unsigned char px[2048];
|
||
|
size_t packet_length;
|
||
|
|
||
|
/*
|
||
|
* Construct the destination packet
|
||
|
*/
|
||
|
template_set_target_ipv6(tmplset, ip_them, port_them, ip_me, port_me, seqno,
|
||
|
px, sizeof(px), &packet_length);
|
||
|
|
||
|
/*
|
||
|
* Send it
|
||
|
*/
|
||
|
rawsock_send_packet(adapter, px, (unsigned)packet_length, flush);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Used on Windows: network adapters have horrible names, so therefore we
|
||
|
* use numeric indexes instead. You can which adapter you are looking for
|
||
|
* by typing "--iflist" as an option.
|
||
|
***************************************************************************/
|
||
|
static int
|
||
|
is_numeric_index(const char *ifname)
|
||
|
{
|
||
|
int result = 1;
|
||
|
int i;
|
||
|
|
||
|
/* empty strings aren't numbers */
|
||
|
if (ifname[0] == '\0')
|
||
|
return 0;
|
||
|
|
||
|
/* 'true' if all digits */
|
||
|
for (i=0; ifname[i]; i++) {
|
||
|
char c = ifname[i];
|
||
|
|
||
|
if (c < '0' || '9' < c)
|
||
|
result = 0;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Used on Windows: if the adapter name is a numeric index, convert it to
|
||
|
* the full name.
|
||
|
***************************************************************************/
|
||
|
const char *
|
||
|
rawsock_win_name(const char *ifname)
|
||
|
{
|
||
|
if (is_numeric_index(ifname)) {
|
||
|
const char *new_adapter_name;
|
||
|
|
||
|
new_adapter_name = adapter_from_index(atoi(ifname));
|
||
|
if (new_adapter_name)
|
||
|
return new_adapter_name;
|
||
|
}
|
||
|
|
||
|
return ifname;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Configure the socket to not capture transmitted packets. This is needed
|
||
|
* because we transmit packets at a rate of millions per second, which will
|
||
|
* overwhelm the receive thread.
|
||
|
*
|
||
|
* PORTABILITY: Windows doesn't seem to support this feature, so instead
|
||
|
* what we do is apply a BPF filter to ignore the transmits, so that they
|
||
|
* still get filtered at a low level.
|
||
|
***************************************************************************/
|
||
|
void
|
||
|
rawsock_ignore_transmits(struct Adapter *adapter, const char *ifname)
|
||
|
{
|
||
|
if (adapter->ring) {
|
||
|
/* PORTABILITY: don't do anything for PF_RING, because it's
|
||
|
* actually done when we create the adapter, because we can't
|
||
|
* reconfigure the adapter after it's been activated. */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (adapter->pcap) {
|
||
|
int err;
|
||
|
err = PCAP.setdirection(adapter->pcap, PCAP_D_IN);
|
||
|
if (err) {
|
||
|
; //PCAP.perror(adapter->pcap, "if: pcap_setdirection(IN)");
|
||
|
} else {
|
||
|
LOG(2, "if:%s: not receiving transmits\n", ifname);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
static void
|
||
|
rawsock_close_adapter(struct Adapter *adapter)
|
||
|
{
|
||
|
if (adapter->ring) {
|
||
|
PFRING.close(adapter->ring);
|
||
|
}
|
||
|
if (adapter->pcap) {
|
||
|
PCAP.close(adapter->pcap);
|
||
|
}
|
||
|
if (adapter->sendq) {
|
||
|
PCAP.sendqueue_destroy(adapter->sendq);
|
||
|
}
|
||
|
|
||
|
free(adapter);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Does the name look like a PF_RING DNA adapter? Common names are:
|
||
|
* dna0
|
||
|
* dna1
|
||
|
* dna0@1
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
static int
|
||
|
is_pfring_dna(const char *name)
|
||
|
{
|
||
|
if (strlen(name) < 4)
|
||
|
return 0;
|
||
|
if (memcmp(name, "zc:", 3) == 0)
|
||
|
return 1;
|
||
|
if (memcmp(name, "dna", 3) != 0)
|
||
|
return 0;
|
||
|
|
||
|
name +=3;
|
||
|
|
||
|
if (!isdigit(name[0]&0xFF))
|
||
|
return 0;
|
||
|
while (isdigit(name[0]&0xFF))
|
||
|
name++;
|
||
|
|
||
|
if (name[0] == '\0')
|
||
|
return 1;
|
||
|
|
||
|
if (name[0] != '@')
|
||
|
return 0;
|
||
|
else
|
||
|
name++;
|
||
|
|
||
|
if (!isdigit(name[0]&0xFF))
|
||
|
return 0;
|
||
|
while (isdigit(name[0]&0xFF))
|
||
|
name++;
|
||
|
|
||
|
if (name[0] == '\0')
|
||
|
return 1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
struct Adapter *
|
||
|
rawsock_init_adapter(const char *adapter_name,
|
||
|
unsigned is_pfring,
|
||
|
unsigned is_sendq,
|
||
|
unsigned is_packet_trace,
|
||
|
unsigned is_offline,
|
||
|
const char *bpf_filter,
|
||
|
unsigned is_vlan,
|
||
|
unsigned vlan_id)
|
||
|
{
|
||
|
struct Adapter *adapter;
|
||
|
char errbuf[PCAP_ERRBUF_SIZE] = "pcap";
|
||
|
|
||
|
/* BPF filter not supported on some platforms, so ignore this compiler
|
||
|
* warning when unused */
|
||
|
UNUSEDPARM(bpf_filter);
|
||
|
|
||
|
adapter = CALLOC(1, sizeof(*adapter));
|
||
|
adapter->is_packet_trace = is_packet_trace;
|
||
|
adapter->pt_start = 1.0 * pixie_gettime() / 1000000.0;
|
||
|
|
||
|
adapter->is_vlan = is_vlan;
|
||
|
adapter->vlan_id = vlan_id;
|
||
|
|
||
|
if (is_offline)
|
||
|
return adapter;
|
||
|
|
||
|
/*----------------------------------------------------------------
|
||
|
* PORTABILITY: WINDOWS
|
||
|
* If is all digits index, then look in indexed list
|
||
|
*----------------------------------------------------------------*/
|
||
|
if (is_numeric_index(adapter_name)) {
|
||
|
const char *new_adapter_name;
|
||
|
|
||
|
new_adapter_name = adapter_from_index(atoi(adapter_name));
|
||
|
if (new_adapter_name == 0) {
|
||
|
fprintf(stderr, "pcap_open_live(%s) error: bad index\n",
|
||
|
adapter_name);
|
||
|
return 0;
|
||
|
} else
|
||
|
adapter_name = new_adapter_name;
|
||
|
}
|
||
|
|
||
|
/*----------------------------------------------------------------
|
||
|
* PORTABILITY: PF_RING
|
||
|
* If we've been told to use --pfring, then attempt to open the
|
||
|
* network adapter using the PF_RING API rather than libpcap.
|
||
|
* Since a lot of things can go wrong, we do a lot of extra
|
||
|
* logging here.
|
||
|
*----------------------------------------------------------------*/
|
||
|
if(is_pfring && !is_pfring_dna(adapter_name)){ /*First ensure pfring dna adapter is available*/
|
||
|
fprintf(stderr,"No pfring adapter available. Please install pfring or run masscan without the --pfring option.\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (is_pfring_dna(adapter_name)) {
|
||
|
int err;
|
||
|
unsigned version;
|
||
|
|
||
|
/*
|
||
|
* Open
|
||
|
*
|
||
|
* TODO: Do we need the PF_RING_REENTRANT flag? We only have one
|
||
|
* transmit and one receive thread, so I don't think we need it.
|
||
|
* Also, this reduces performance in half, from 12-mpps to
|
||
|
* 6-mpps.
|
||
|
* NOTE: I don't think it needs the "re-entrant" flag, because it
|
||
|
* transmit and receive are separate functions?
|
||
|
*/
|
||
|
LOG(2, "pfring:'%s': opening...\n", adapter_name);
|
||
|
adapter->ring = PFRING.open(adapter_name, 1500, 0);//PF_RING_REENTRANT);
|
||
|
adapter->pcap = (pcap_t*)adapter->ring;
|
||
|
adapter->link_type = 1;
|
||
|
if (adapter->ring == NULL) {
|
||
|
LOG(0, "pfring:'%s': OPEN ERROR: %s\n",
|
||
|
adapter_name, strerror(errno));
|
||
|
return 0;
|
||
|
} else
|
||
|
LOG(1, "pfring:'%s': successfully opened\n", adapter_name);
|
||
|
|
||
|
/*
|
||
|
* Housekeeping
|
||
|
*/
|
||
|
PFRING.set_application_name(adapter->ring, "masscan");
|
||
|
PFRING.version(adapter->ring, &version);
|
||
|
LOG(1, "pfring: version %d.%d.%d\n",
|
||
|
(version >> 16) & 0xFFFF,
|
||
|
(version >> 8) & 0xFF,
|
||
|
(version >> 0) & 0xFF);
|
||
|
|
||
|
LOG(2, "pfring:'%s': setting direction\n", adapter_name);
|
||
|
err = PFRING.set_direction(adapter->ring, rx_only_direction);
|
||
|
if (err) {
|
||
|
fprintf(stderr, "pfring:'%s': setdirection = %d\n",
|
||
|
adapter_name, err);
|
||
|
} else
|
||
|
LOG(2, "pfring:'%s': direction success\n", adapter_name);
|
||
|
|
||
|
/*
|
||
|
* Activate
|
||
|
*
|
||
|
* PF_RING requires a separate activation step.
|
||
|
*/
|
||
|
LOG(2, "pfring:'%s': activating\n", adapter_name);
|
||
|
err = PFRING.enable_ring(adapter->ring);
|
||
|
if (err != 0) {
|
||
|
LOG(0, "pfring: '%s': ENABLE ERROR: %s\n",
|
||
|
adapter_name, strerror(errno));
|
||
|
PFRING.close(adapter->ring);
|
||
|
adapter->ring = 0;
|
||
|
return 0;
|
||
|
} else
|
||
|
LOG(1, "pfring:'%s': successfully enabled\n", adapter_name);
|
||
|
|
||
|
return adapter;
|
||
|
}
|
||
|
|
||
|
/*----------------------------------------------------------------
|
||
|
* Kludge: for using files
|
||
|
*----------------------------------------------------------------*/
|
||
|
if (memcmp(adapter_name, "file:", 5) == 0) {
|
||
|
LOG(1, "pcap: file: %s\n", adapter_name+5);
|
||
|
is_pcap_file = 1;
|
||
|
|
||
|
adapter->pcap = PCAP.open_offline(adapter_name+5, errbuf);
|
||
|
adapter->link_type = PCAP.datalink(adapter->pcap);
|
||
|
}
|
||
|
/*----------------------------------------------------------------
|
||
|
* PORTABILITY: LIBPCAP
|
||
|
*
|
||
|
* This is the standard that should work everywhere.
|
||
|
*----------------------------------------------------------------*/
|
||
|
{
|
||
|
int err;
|
||
|
LOG(1, "[+] if(%s): pcap: %s\n", adapter_name, PCAP.lib_version());
|
||
|
LOG(2, "[+] if(%s): opening...\n", adapter_name);
|
||
|
|
||
|
/* This reserves resources, but doesn't actually open the
|
||
|
* adapter until we call pcap_activate */
|
||
|
adapter->pcap = PCAP.create(adapter_name, errbuf);
|
||
|
if (adapter->pcap == NULL) {
|
||
|
adapter->pcap = PCAP.open_live(
|
||
|
adapter_name, /* interface name */
|
||
|
65536, /* max packet size */
|
||
|
8, /* promiscuous mode */
|
||
|
1000, /* read timeout in milliseconds */
|
||
|
errbuf);
|
||
|
if (adapter->pcap == NULL) {
|
||
|
LOG(0, "FAIL:%s: can't open adapter: %s\n", adapter_name, errbuf);
|
||
|
if (strstr(errbuf, "perm")) {
|
||
|
LOG(0, "FAIL: permission denied\n");
|
||
|
LOG(0, " [hint] need to sudo or run as root or something\n");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
} else {
|
||
|
err = PCAP.set_snaplen(adapter->pcap, 65536);
|
||
|
if (err) {
|
||
|
PCAP.perror(adapter->pcap, "if: set_snaplen");
|
||
|
goto pcap_error;
|
||
|
}
|
||
|
|
||
|
err = PCAP.set_promisc(adapter->pcap, 8);
|
||
|
if (err) {
|
||
|
PCAP.perror(adapter->pcap, "if: set_promisc");
|
||
|
goto pcap_error;
|
||
|
}
|
||
|
|
||
|
err = PCAP.set_timeout(adapter->pcap, 1000);
|
||
|
if (err) {
|
||
|
PCAP.perror(adapter->pcap, "if: set_timeout");
|
||
|
goto pcap_error;
|
||
|
}
|
||
|
|
||
|
err = PCAP.set_immediate_mode(adapter->pcap, 1);
|
||
|
if (err) {
|
||
|
PCAP.perror(adapter->pcap, "if: set_immediate_mode");
|
||
|
goto pcap_error;
|
||
|
}
|
||
|
|
||
|
/* If errors happen, they aren't likely to happen above, but will
|
||
|
* happen where when they are applied */
|
||
|
err = PCAP.activate(adapter->pcap);
|
||
|
switch (err) {
|
||
|
case 0:
|
||
|
/* drop down below */
|
||
|
break;
|
||
|
case PCAP_ERROR_PERM_DENIED:
|
||
|
LOG(0, "[-] FAIL: permission denied\n");
|
||
|
LOG(0, " [hint] need to sudo or run as root or something\n");
|
||
|
goto pcap_error;
|
||
|
default:
|
||
|
LOG(0, "[-] if(%s): activate:%d: %s\n", adapter_name, err, PCAP.geterr(adapter->pcap));
|
||
|
if (err < 0)
|
||
|
goto pcap_error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOG(1, "[+] if(%s): successfully opened\n", adapter_name);
|
||
|
|
||
|
|
||
|
|
||
|
/* Figure out the link-type. We suport Ethernet and IP */
|
||
|
adapter->link_type = PCAP.datalink(adapter->pcap);
|
||
|
switch (adapter->link_type) {
|
||
|
case -1:
|
||
|
PCAP.perror(adapter->pcap, "if: datalink");
|
||
|
goto pcap_error;
|
||
|
case 0: /* Null/Loopback [VPN tunnel] */
|
||
|
LOG(1, "[+] if(%s): VPN tunnel interface found\n", adapter_name);
|
||
|
break;
|
||
|
case 1: /* Ethernet */
|
||
|
case 12: /* IP Raw */
|
||
|
break;
|
||
|
default:
|
||
|
LOG(0, "[-] if(%s): unknown data link type: %u(%s)\n",
|
||
|
adapter_name,
|
||
|
adapter->link_type,
|
||
|
PCAP.datalink_val_to_name(adapter->link_type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*----------------------------------------------------------------
|
||
|
* PORTABILITY: WINDOWS
|
||
|
*
|
||
|
* The transmit rate on Windows is really slow, like 40-kpps.
|
||
|
* The speed can be increased by using the "sendqueue" feature
|
||
|
* to roughly 300-kpps.
|
||
|
*----------------------------------------------------------------*/
|
||
|
adapter->sendq = 0;
|
||
|
#if defined(WIN32)
|
||
|
if (is_sendq)
|
||
|
adapter->sendq = PCAP.sendqueue_alloc(SENDQ_SIZE);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
return adapter;
|
||
|
pcap_error:
|
||
|
if (adapter->pcap) {
|
||
|
PCAP.close(adapter->pcap);
|
||
|
adapter->pcap = NULL;
|
||
|
}
|
||
|
if (adapter->pcap == NULL) {
|
||
|
if (strcmp(adapter_name, "vmnet1") == 0) {
|
||
|
LOG(0, " [hint] VMware on Macintosh doesn't support masscan\n");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* for testing when two Windows adapters have the same name. Sometimes
|
||
|
* the \Device\NPF_ string is prepended, sometimes not.
|
||
|
***************************************************************************/
|
||
|
int
|
||
|
rawsock_is_adapter_names_equal(const char *lhs, const char *rhs)
|
||
|
{
|
||
|
if (memcmp(lhs, "\\Device\\NPF_", 12) == 0)
|
||
|
lhs += 12;
|
||
|
if (memcmp(rhs, "\\Device\\NPF_", 12) == 0)
|
||
|
rhs += 12;
|
||
|
return strcmp(lhs, rhs) == 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Runs some tests when the "--debug if" option is given on the
|
||
|
* command-line. This is useful to figure out why the interface you
|
||
|
* are accessing doesn't work.
|
||
|
***************************************************************************/
|
||
|
int
|
||
|
rawsock_selftest_if(const char *ifname)
|
||
|
{
|
||
|
int err;
|
||
|
ipv4address_t ipv4 = 0;
|
||
|
ipv6address_t ipv6;
|
||
|
ipv4address_t router_ipv4 = 0;
|
||
|
macaddress_t source_mac = {{0,0,0,0,0,0}};
|
||
|
struct Adapter *adapter;
|
||
|
char ifname2[246];
|
||
|
ipaddress_formatted_t fmt;
|
||
|
|
||
|
/*
|
||
|
* Get the interface
|
||
|
*/
|
||
|
if (ifname == NULL || ifname[0] == 0) {
|
||
|
err = rawsock_get_default_interface(ifname2, sizeof(ifname2));
|
||
|
if (err) {
|
||
|
printf("[-] if = not found (err=%d)\n", err);
|
||
|
return -1;
|
||
|
}
|
||
|
ifname = ifname2;
|
||
|
}
|
||
|
printf("[+] if = %s\n", ifname);
|
||
|
|
||
|
/*
|
||
|
* Initialize the adapter.
|
||
|
*/
|
||
|
adapter = rawsock_init_adapter(ifname, 0, 0, 0, 0, 0, 0, 0);
|
||
|
if (adapter == 0) {
|
||
|
printf("[-] pcap = failed\n");
|
||
|
return -1;
|
||
|
} else {
|
||
|
printf("[+] pcap = opened\n");
|
||
|
}
|
||
|
|
||
|
/* IPv4 address */
|
||
|
ipv4 = rawsock_get_adapter_ip(ifname);
|
||
|
if (ipv4 == 0) {
|
||
|
printf("[-] source-ipv4 = not found (err)\n");
|
||
|
} else {
|
||
|
fmt = ipv4address_fmt(ipv4);
|
||
|
printf("[+] source-ipv4 = %s\n", fmt.string);
|
||
|
}
|
||
|
|
||
|
/* IPv6 address */
|
||
|
ipv6 = rawsock_get_adapter_ipv6(ifname);
|
||
|
if (ipv6address_is_zero(ipv6)) {
|
||
|
printf("[-] source-ipv6 = not found\n");
|
||
|
} else {
|
||
|
fmt = ipv6address_fmt(ipv6);
|
||
|
printf("[+] source-ipv6 = [%s]\n", fmt.string);
|
||
|
}
|
||
|
|
||
|
/* MAC address */
|
||
|
err = rawsock_get_adapter_mac(ifname, source_mac.addr);
|
||
|
if (err) {
|
||
|
printf("[-] source-mac = not found (err=%d)\n", err);
|
||
|
} else {
|
||
|
fmt = macaddress_fmt(source_mac);
|
||
|
printf("[+] source-mac = %s\n", fmt.string);
|
||
|
}
|
||
|
|
||
|
switch (adapter->link_type) {
|
||
|
case 0:
|
||
|
printf("[+] router-ip = implicit\n");
|
||
|
printf("[+] router-mac = implicit\n");
|
||
|
break;
|
||
|
default:
|
||
|
/* IPv4 router IP address */
|
||
|
err = rawsock_get_default_gateway(ifname, &router_ipv4);
|
||
|
if (err) {
|
||
|
fprintf(stderr, "[-] router-ip = not found(err=%d)\n", err);
|
||
|
} else {
|
||
|
fmt = ipv4address_fmt(router_ipv4);
|
||
|
printf("[+] router-ip = %s\n", fmt.string);
|
||
|
}
|
||
|
|
||
|
/* IPv4 router MAC address */
|
||
|
{
|
||
|
macaddress_t router_mac = {{0,0,0,0,0,0}};
|
||
|
|
||
|
stack_arp_resolve(
|
||
|
adapter,
|
||
|
ipv4,
|
||
|
source_mac,
|
||
|
router_ipv4,
|
||
|
&router_mac);
|
||
|
|
||
|
if (macaddress_is_zero(router_mac)) {
|
||
|
printf("[-] router-mac-ipv4 = not found\n");
|
||
|
} else {
|
||
|
fmt = macaddress_fmt(router_mac);
|
||
|
printf("[+] router-mac-ipv4 = %s\n", fmt.string);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* IPv6 router MAC address.
|
||
|
* If it's not configured, then we need to send a (synchronous) query
|
||
|
* to the network in order to discover the location of routers on
|
||
|
* the local network
|
||
|
*/
|
||
|
if (!ipv6address_is_zero(ipv6)) {
|
||
|
macaddress_t router_mac = {{0,0,0,0,0,0}};
|
||
|
|
||
|
stack_ndpv6_resolve(
|
||
|
adapter,
|
||
|
ipv6,
|
||
|
source_mac,
|
||
|
&router_mac);
|
||
|
|
||
|
if (macaddress_is_zero(router_mac)) {
|
||
|
printf("[-] router-mac-ipv6 = not found\n");
|
||
|
} else {
|
||
|
fmt = macaddress_fmt(router_mac);
|
||
|
printf("[+] router-mac-ipv6 = %s\n", fmt.string);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rawsock_close_adapter(adapter);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
***************************************************************************/
|
||
|
int
|
||
|
rawsock_selftest()
|
||
|
{
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|