165 lines
3.9 KiB
C
165 lines
3.9 KiB
C
/*
|
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* use this file except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
*/
|
|
|
|
#include "recv.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <assert.h>
|
|
|
|
#include "../lib/includes.h"
|
|
#include "../lib/logger.h"
|
|
|
|
#include <pcap.h>
|
|
#include <pcap/pcap.h>
|
|
#if defined __linux__ && __linux__
|
|
#include <pcap/sll.h>
|
|
#endif
|
|
|
|
#include "recv-internal.h"
|
|
#include "state.h"
|
|
|
|
#include "probe_modules/probe_modules.h"
|
|
|
|
#define PCAP_PROMISC 1
|
|
#define PCAP_TIMEOUT 1000
|
|
|
|
static pcap_t *pc = NULL;
|
|
|
|
void packet_cb(u_char __attribute__((__unused__)) * user,
|
|
const struct pcap_pkthdr *p, const u_char *bytes)
|
|
{
|
|
struct timespec ts;
|
|
if (!p) {
|
|
return;
|
|
}
|
|
if (zrecv.filter_success >= zconf.max_results) {
|
|
// Libpcap can process multiple packets per pcap_dispatch;
|
|
// we need to throw out results once we've
|
|
// gotten our --max-results worth.
|
|
return;
|
|
}
|
|
|
|
// length of entire packet captured by libpcap
|
|
uint32_t buflen = (uint32_t)p->caplen;
|
|
ts.tv_sec = p->ts.tv_sec;
|
|
ts.tv_nsec = p->ts.tv_usec * 1000;
|
|
handle_packet(buflen, bytes, ts);
|
|
}
|
|
|
|
#define BPFLEN 1024
|
|
|
|
void recv_init(void)
|
|
{
|
|
char bpftmp[BPFLEN];
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
|
|
pc = pcap_open_live(zconf.iface, zconf.probe_module->pcap_snaplen,
|
|
PCAP_PROMISC, PCAP_TIMEOUT, errbuf);
|
|
if (pc == NULL) {
|
|
log_fatal("recv", "could not open device %s: %s", zconf.iface,
|
|
errbuf);
|
|
}
|
|
switch (pcap_datalink(pc)) {
|
|
case DLT_NULL:
|
|
// utun on macOS
|
|
log_debug("recv", "BSD loopback encapsulation");
|
|
zconf.data_link_size = 4;
|
|
break;
|
|
case DLT_EN10MB:
|
|
log_debug("recv", "Data link layer Ethernet");
|
|
zconf.data_link_size = sizeof(struct ether_header);
|
|
break;
|
|
case DLT_RAW:
|
|
log_info("recv", "Data link RAW");
|
|
zconf.data_link_size = 0;
|
|
break;
|
|
#if defined __linux__ && __linux__
|
|
case DLT_LINUX_SLL:
|
|
log_info("recv", "Data link cooked socket");
|
|
zconf.data_link_size = SLL_HDR_LEN;
|
|
break;
|
|
#endif
|
|
default:
|
|
log_error("recv", "unknown data link layer: %u", pcap_datalink(pc));
|
|
}
|
|
|
|
struct bpf_program bpf;
|
|
|
|
if (!zconf.send_ip_pkts) {
|
|
snprintf(bpftmp, sizeof(bpftmp) - 1,
|
|
"not ether src %02x:%02x:%02x:%02x:%02x:%02x",
|
|
zconf.hw_mac[0], zconf.hw_mac[1], zconf.hw_mac[2],
|
|
zconf.hw_mac[3], zconf.hw_mac[4], zconf.hw_mac[5]);
|
|
assert(strlen(zconf.probe_module->pcap_filter) + 10 <
|
|
(BPFLEN - strlen(bpftmp)));
|
|
} else {
|
|
bpftmp[0] = 0;
|
|
}
|
|
if (zconf.probe_module->pcap_filter) {
|
|
if (!zconf.send_ip_pkts) {
|
|
strcat(bpftmp, " and (");
|
|
} else {
|
|
strcat(bpftmp, "(");
|
|
}
|
|
strcat(bpftmp, zconf.probe_module->pcap_filter);
|
|
strcat(bpftmp, ")");
|
|
}
|
|
if (strcmp(bpftmp, "")) {
|
|
if (pcap_compile(pc, &bpf, bpftmp, 1, 0) < 0) {
|
|
log_fatal("recv", "couldn't compile filter");
|
|
}
|
|
if (pcap_setfilter(pc, &bpf) < 0) {
|
|
log_fatal("recv", "couldn't install filter");
|
|
}
|
|
}
|
|
// set pcap_dispatch to not hang if it never receives any packets
|
|
// this could occur if you ever scan a small number of hosts as
|
|
// documented in issue #74.
|
|
if (pcap_setnonblock(pc, 1, errbuf) == -1) {
|
|
log_fatal("recv", "pcap_setnonblock error:%s", errbuf);
|
|
}
|
|
}
|
|
|
|
void recv_packets(void)
|
|
{
|
|
int ret = pcap_dispatch(pc, -1, packet_cb, NULL);
|
|
if (ret == -1) {
|
|
log_fatal("recv", "pcap_dispatch error");
|
|
} else if (ret == 0) {
|
|
usleep(1000);
|
|
}
|
|
}
|
|
|
|
void recv_cleanup(void)
|
|
{
|
|
pcap_close(pc);
|
|
pc = NULL;
|
|
}
|
|
|
|
int recv_update_stats(void)
|
|
{
|
|
if (!pc) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
struct pcap_stat pcst;
|
|
if (pcap_stats(pc, &pcst)) {
|
|
log_error("recv", "unable to retrieve pcap statistics: %s",
|
|
pcap_geterr(pc));
|
|
return EXIT_FAILURE;
|
|
} else {
|
|
zrecv.pcap_recv = pcst.ps_recv;
|
|
zrecv.pcap_drop = pcst.ps_drop;
|
|
zrecv.pcap_ifdrop = pcst.ps_ifdrop;
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|