diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc index 436582719..5bd0237b2 100644 --- a/libnetutil/netutil.cc +++ b/libnetutil/netutil.cc @@ -959,10 +959,91 @@ int pcap_select(pcap_t *p, long usecs) { return ret; } +struct netutil_eth_t { + union { + pcap_t *pt; + eth_t *ethsd; + }; + int datalink; +}; + +int netutil_eth_datalink(const netutil_eth_t *e) { + if (e) return e->datalink; + return -1; +} + +#ifdef WIN32 +#define eth_handle(_eth) (_eth->pt) +#define eth_handle_send pcap_inject +#define eth_handle_close eth_close +#else +#define eth_handle(_eth) (_eth->ethsd) +#define eth_handle_send eth_send +#define eth_handle_close eth_close +#endif + +netutil_eth_t *netutil_eth_open(const char *device) { + assert(device != NULL); + assert(device[0] != '\0'); + + netutil_eth_t *e = (netutil_eth_t *)safe_zalloc(sizeof(netutil_eth_t)); + e->datalink = -1; + +#ifdef WIN32 + char err0r[PCAP_ERRBUF_SIZE] = {0}; + char pcapdev[128] = {0}; + int failed = 0; + pcap_t *pt = NULL; + do { + if (!DnetName2PcapName(device, pcapdev, sizeof(pcapdev))) { + break; + } + pt = pcap_create(pcapdev, err0r); + if (!pt) { + netutil_error("pcap_create(%s) FAILED: %s.", pcapdev, err0r); + break; + } + failed = pcap_activate(pt); + if (failed < 0) { + // PCAP error + netutil_error("pcap_activate(%s) FAILED: %s.", pcapdev, pcap_geterr(pt)); + pcap_close(pt); + return NULL; + } + else if (failed > 0) { + // PCAP warning, report but assume it'll still work + netutil_error("pcap_activate(%s) WARNING: %s.", pcapdev, pcap_geterr(pt)); + } + eth_handle(e) = pt; + e->datalink = pcap_datalink(pt); + } while (0); +#else + eth_handle(e) = eth_open(device); + e->datalink = DLT_EN10MB; +#endif + + if (eth_handle(e) == NULL) { + free(e); + return NULL; + } + return e; +} + +void netutil_eth_close(netutil_eth_t *e) { + assert(e != NULL); + eth_handle_close(eth_handle(e)); + free(e); +} + +ssize_t netutil_eth_send(netutil_eth_t *e, const void *buf, size_t len) { + assert(e != NULL); + assert(eth_handle(e) != NULL); + return eth_handle_send(eth_handle(e), buf, len); +} /* These two are for eth_open_cached() and eth_close_cached() */ static char etht_cache_device_name[64]; -static eth_t *etht_cache_device = NULL; +static netutil_eth_t *etht_cache_device = NULL; /* A simple function that caches the eth_t from dnet for one device, to avoid opening, closing, and re-opening it thousands of tims. If @@ -972,7 +1053,7 @@ static eth_t *etht_cache_device = NULL; eth_close() A DEVICE OBTAINED FROM THIS FUNCTION. Instead, you can call eth_close_cached() to close whichever device (if any) is cached. Returns NULL if it fails to open the device. */ -eth_t *eth_open_cached(const char *device) { +netutil_eth_t *eth_open_cached(const char *device) { if (!device) netutil_fatal("%s() called with NULL device name!", __func__); if (!*device) @@ -984,12 +1065,12 @@ eth_t *eth_open_cached(const char *device) { } if (*etht_cache_device_name) { - eth_close(etht_cache_device); + netutil_eth_close(etht_cache_device); etht_cache_device_name[0] = '\0'; etht_cache_device = NULL; } - etht_cache_device = eth_open(device); + etht_cache_device = netutil_eth_open(device); if (etht_cache_device) Strncpy(etht_cache_device_name, device, sizeof(etht_cache_device_name)); @@ -1000,7 +1081,7 @@ eth_t *eth_open_cached(const char *device) { /* See the description for eth_open_cached */ void eth_close_cached() { if (etht_cache_device) { - eth_close(etht_cache_device); + netutil_eth_close(etht_cache_device); etht_cache_device = NULL; etht_cache_device_name[0] = '\0'; } @@ -3448,19 +3529,27 @@ int Sendto(const char *functionname, int sd, } +int netutil_eth_can_send(const netutil_eth_t *e) { + switch (netutil_eth_datalink(e)) { + case DLT_NULL: + case DLT_EN10MB: + case DLT_RAW: + return 1; + break; + default: + return 0; + break; + } +} /* Send an IP packet over an ethernet handle. */ static int send_ip_packet_eth(const struct eth_nfo *eth, const u8 *packet, unsigned int packetlen, int af) { - eth_t *ethsd; - u8 *eth_frame; + netutil_eth_t *ethsd; + u8 *eth_frame = NULL; int res; size_t framelen; uint16_t ethertype = (af == AF_INET6 ? ETH_TYPE_IPV6 : ETH_TYPE_IP); - framelen = 14 + packetlen; - eth_frame = (u8 *) safe_malloc(framelen); - memcpy(eth_frame + 14, packet, packetlen); - eth_pack_hdr(eth_frame, eth->dstmac, eth->srcmac, ethertype); if (!eth->ethsd) { ethsd = eth_open_cached(eth->devname); if (!ethsd) @@ -3468,9 +3557,42 @@ static int send_ip_packet_eth(const struct eth_nfo *eth, const u8 *packet, unsig } else { ethsd = eth->ethsd; } - res = eth_send(ethsd, eth_frame, framelen); + switch (ethsd->datalink) { + case DLT_EN10MB: + framelen = 14 + packetlen; + eth_frame = (u8 *) safe_malloc(framelen); + memcpy(eth_frame + 14, packet, packetlen); + eth_pack_hdr(eth_frame, eth->dstmac, eth->srcmac, ethertype); + break; + case DLT_NULL: + framelen = 4 + packetlen; + eth_frame = (u8 *) safe_malloc(framelen); + memcpy(eth_frame + 4, packet, packetlen); + if (af == AF_INET6) { + /* These values are per libpcap/gencode.c */ +#if defined(__APPLE__) + *(uint32_t *)eth_frame = 30; // macOS, iOS, other Darwin-based OSes +#elif defined(__FreeBSD__) + *(uint32_t *)eth_frame = 28; // FreeBSD +#else + *(uint32_t *)eth_frame = 24; // NetBSD, OpenBSD, BSD/OS, Npcap +#endif + } + else { + *(uint32_t *)eth_frame = AF_INET; + } + break; + case DLT_RAW: + framelen = packetlen; + break; + default: + netutil_fatal("%s: unsupported DLT for %s: %d", __func__, eth->devname, ethsd->datalink); + break; + } + res = netutil_eth_send(ethsd, eth_frame ? eth_frame : packet, framelen); /* No need to close ethsd due to caching */ - free(eth_frame); + if (eth_frame != packet) + free(eth_frame); return res; } @@ -4364,7 +4486,7 @@ bool doND(const char *dev, const u8 *srcmac, int timeouts[] = { 100000, 400000, 800000 }; int max_sends = 3; int num_sends = 0; // How many we have sent so far - eth_t *ethsd; + netutil_eth_t *ethsd; u8 frame[ETH_HDR_LEN + IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8]; struct timeval start, now, rcvdtime; int timeleft; @@ -4423,7 +4545,7 @@ bool doND(const char *dev, const u8 *srcmac, while (!foundit && num_sends < max_sends) { /* Send the sucker */ - rc = eth_send(ethsd, frame, sizeof(frame)); + rc = netutil_eth_send(ethsd, frame, sizeof(frame)); if (rc != sizeof(frame)) { netutil_error("WARNING: %s: eth_send of Neighbor Solicitation packet returned %u rather than expected %d bytes", __func__, rc, (int) sizeof(frame)); } @@ -4484,7 +4606,7 @@ bool doArp(const char *dev, const u8 *srcmac, int timeouts[] = { 100000, 400000, 800000 }; int max_sends = 3; int num_sends = 0; // How many we have sent so far - eth_t *ethsd; + netutil_eth_t *ethsd; u8 frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN]; const struct sockaddr_in *targetsin = (struct sockaddr_in *) targetip; const struct sockaddr_in *srcsin = (struct sockaddr_in *) srcip; @@ -4520,7 +4642,7 @@ bool doArp(const char *dev, const u8 *srcmac, while (!foundit && num_sends < max_sends) { /* Send the sucker */ - rc = eth_send(ethsd, frame, sizeof(frame)); + rc = netutil_eth_send(ethsd, frame, sizeof(frame)); if (rc != sizeof(frame)) { netutil_error("WARNING: %s: eth_send of ARP packet returned %u rather than expected %d bytes", __func__, rc, (int) sizeof(frame)); } diff --git a/libnetutil/netutil.h b/libnetutil/netutil.h index 4cc253457..8cf9c3646 100644 --- a/libnetutil/netutil.h +++ b/libnetutil/netutil.h @@ -74,8 +74,8 @@ extern "C" { } #endif -#include "dnet.h" #include +#include /* It is VERY important to never change the value of these two constants. * Specially, OP_FAILURE should never be positive, as some pieces of code take @@ -278,10 +278,12 @@ struct sys_route { int metric; }; +struct netutil_eth_t; + struct eth_nfo { char srcmac[6]; char dstmac[6]; - eth_t *ethsd; // Optional, but improves performance. Set to NULL if unavail + netutil_eth_t *ethsd; // Optional, but improves performance. Set to NULL if unavail char devname[16]; // Only needed if ethsd is NULL. }; @@ -293,7 +295,12 @@ struct eth_nfo { eth_close() A DEVICE OBTAINED FROM THIS FUNCTION. Instead, you can call eth_close_cached() to close whichever device (if any) is cached. Returns NULL if it fails to open the device. */ -eth_t *eth_open_cached(const char *device); +netutil_eth_t *eth_open_cached(const char *device); +netutil_eth_t *netutil_eth_open(const char *device); +void netutil_eth_close(netutil_eth_t *e); +ssize_t netutil_eth_send(netutil_eth_t *e, const void *buf, size_t len); +int netutil_eth_datalink(const netutil_eth_t *e); +int netutil_eth_can_send(const netutil_eth_t *e); /* See the description for eth_open_cached */ void eth_close_cached(); diff --git a/nping/utils_net.cc b/nping/utils_net.cc index f7d313b03..77f2b2af6 100644 --- a/nping/utils_net.cc +++ b/nping/utils_net.cc @@ -66,7 +66,6 @@ #include "output.h" #include "nbase.h" #include "pcap.h" -#include "dnet.h" #include extern NpingOps o; @@ -1111,8 +1110,8 @@ int send_packet(NpingTarget *target, int rawfd, u8 *pkt, size_t pktLen){ assert(pktLen > 0); if ( o.sendEth() ){ - eth_t *ethsd = eth_open_cached(o.getDevice()); - eth_send(ethsd, pkt, pktLen); + netutil_eth_t *ethsd = eth_open_cached(o.getDevice()); + netutil_eth_send(ethsd, pkt, pktLen); }else{ if( o.ipv6() ){ /* IPv6 */ memset(&s6, 0, sizeof(struct sockaddr_in6)); diff --git a/nse_dnet.cc b/nse_dnet.cc index bb387e244..41e3f4711 100644 --- a/nse_dnet.cc +++ b/nse_dnet.cc @@ -24,7 +24,7 @@ enum { typedef struct nse_dnet_udata { - eth_t *eth; + netutil_eth_t *eth; int sock; /* raw ip socket */ char devname[32]; /* libnetutil uses this len; dnet generally uses 16 */ } nse_dnet_udata; @@ -123,23 +123,23 @@ static int l_dnet_get_interface_info (lua_State *L) static int close_eth (lua_State *L) { - eth_t **eth = (eth_t **) nseU_checkudata(L, 1, DNET_ETHERNET_METATABLE, "ethernet"); + netutil_eth_t **eth = (netutil_eth_t **) nseU_checkudata(L, 1, DNET_ETHERNET_METATABLE, "ethernet"); assert(*eth != NULL); - eth_close(*eth); + netutil_eth_close(*eth); *eth = NULL; return nseU_success(L); } -static eth_t *open_eth_cached (lua_State *L, int dnet_index, const char *device) +static netutil_eth_t *open_eth_cached (lua_State *L, int dnet_index, const char *device) { - eth_t **eth; + netutil_eth_t **eth; lua_getfield(L, CACHE_DEVICE_ETHERNET, device); if (!lua_isuserdata(L, -1)) { lua_pop(L, 1); - eth = (eth_t **) lua_newuserdatauv(L, sizeof(eth_t *), 0); - *eth = eth_open(device); + eth = (netutil_eth_t **) lua_newuserdatauv(L, sizeof(netutil_eth_t *), 0); + *eth = netutil_eth_open(device); if (*eth == NULL) luaL_error(L, "unable to open dnet on ethernet interface %s", device); lua_pushvalue(L, DNET_ETHERNET_METATABLE); @@ -147,13 +147,13 @@ static eth_t *open_eth_cached (lua_State *L, int dnet_index, const char *device) lua_pushvalue(L, -1); lua_setfield(L, CACHE_DEVICE_ETHERNET, device); } - eth = (eth_t **) lua_touserdata(L, -1); + eth = (netutil_eth_t **) lua_touserdata(L, -1); lua_pushvalue(L, dnet_index); - lua_pushvalue(L, -2); /* eth_t userdata */ + lua_pushvalue(L, -2); /* netutil_eth_t userdata */ lua_rawset(L, CACHE_DNET_ETHERNET); - lua_pop(L, 1); /* eth_t userdata */ + lua_pop(L, 1); /* netutil_eth_t userdata */ return *eth; } @@ -213,11 +213,11 @@ static int ethernet_send (lua_State *L) log_write(LOG_STDOUT, "%s: Ethernet frame (%lu bytes) > %s\n", SCRIPT_ENGINE, len, udata->devname); } - size_t sent = eth_send(udata->eth, frame, len); + size_t sent = netutil_eth_send(udata->eth, frame, len); if (sent == len) return nseU_success(L); else - return nseU_safeerror(L, "eth_send error: %lu", sent); + return nseU_safeerror(L, "netutil_eth_send error: %lu", sent); } static int ip_open (lua_State *L) diff --git a/osscan2.cc b/osscan2.cc index 1328dc092..af44699f2 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -1172,7 +1172,7 @@ void HostOsScanStats::initScanStats() { /* Fill in an eth_nfo struct with the appropriate source and destination MAC addresses and a given Ethernet handle. The return value is suitable to pass to send_ip_packet: if ethsd is NULL, returns NULL; otherwise returns eth. */ -struct eth_nfo *HostOsScanStats::fill_eth_nfo(struct eth_nfo *eth, eth_t *ethsd) const { +struct eth_nfo *HostOsScanStats::fill_eth_nfo(struct eth_nfo *eth, netutil_eth_t *ethsd) const { if (ethsd == NULL) return NULL; diff --git a/osscan2.h b/osscan2.h index 82809ecfc..f1ec44ed0 100644 --- a/osscan2.h +++ b/osscan2.h @@ -64,7 +64,7 @@ #define OSSCAN2_H #include "nbase.h" -#include +#include "libnetutil/netutil.h" #include #include @@ -232,7 +232,7 @@ class HostOsScanStats { HostOsScanStats(Target *t); ~HostOsScanStats(); void initScanStats(); - struct eth_nfo *fill_eth_nfo(struct eth_nfo *eth, eth_t *ethsd) const; + struct eth_nfo *fill_eth_nfo(struct eth_nfo *eth, netutil_eth_t *ethsd) const; void addNewProbe(OFProbeType type, int subid); void removeActiveProbe(std::list::iterator probeI); /* Get an active probe from active probe list identified by probe type @@ -431,7 +431,7 @@ private: int get_tcpopt_string(const struct tcp_hdr *tcp, int mss, char *result, int maxlen) const; int rawsd; /* Raw socket descriptor */ - eth_t *ethsd; /* Ethernet handle */ + netutil_eth_t *ethsd; /* Ethernet handle */ unsigned int tcpSeqBase; /* Seq value used in TCP probes */ unsigned int tcpAck; /* Ack value used in TCP probes */ diff --git a/scan_engine.h b/scan_engine.h index 81e22c9ab..93613f6c8 100644 --- a/scan_engine.h +++ b/scan_engine.h @@ -68,7 +68,7 @@ #include "scan_lists.h" #include "probespec.h" -#include +#include "libnetutil/netutil.h" #include "timing.h" @@ -626,7 +626,7 @@ public: const struct scan_lists *ports; int rawsd; /* raw socket descriptor */ pcap_t *pd; - eth_t *ethsd; + netutil_eth_t *ethsd; u32 seqmask; /* This mask value is used to encode values in sequence numbers. It is set randomly in UltraScanInfo::Init() */ u16 base_port; diff --git a/scan_engine_raw.cc b/scan_engine_raw.cc index 8fd816edc..5497e5892 100644 --- a/scan_engine_raw.cc +++ b/scan_engine_raw.cc @@ -940,7 +940,7 @@ UltraProbe *sendArpScanProbe(UltraScanInfo *USI, HostScanStats *hss, gettimeofday(&USI->now, NULL); probe->sent = USI->now; hss->probeSent(sizeof(frame)); - if ((rc = eth_send(USI->ethsd, frame, sizeof(frame))) != sizeof(frame)) { + if ((rc = netutil_eth_send(USI->ethsd, frame, sizeof(frame))) != sizeof(frame)) { int err = socket_errno(); error("WARNING: eth_send of ARP packet returned %i rather than expected %d (errno=%i: %s)", rc, (int) sizeof(frame), err, strerror(err)); } diff --git a/traceroute.cc b/traceroute.cc index c7ccff44a..b4c7766e8 100644 --- a/traceroute.cc +++ b/traceroute.cc @@ -229,7 +229,7 @@ public: ~HostState(); bool has_more_probes() const; bool is_finished() const; - bool send_next_probe(int rawsd, eth_t *ethsd); + bool send_next_probe(int rawsd, netutil_eth_t *ethsd); void next_ttl(); void count_up(); int cancel_probe(std::list::iterator it); @@ -262,8 +262,8 @@ public: Probe(HostState *host, struct probespec pspec, u8 ttl); virtual ~Probe(); - void send(int rawsd, eth_t *ethsd, struct timeval *now = NULL); - void resend(int rawsd, eth_t *ethsd, struct timeval *now = NULL); + void send(int rawsd, netutil_eth_t *ethsd, struct timeval *now = NULL); + void resend(int rawsd, netutil_eth_t *ethsd, struct timeval *now = NULL); bool is_timedout(struct timeval *now = NULL) const; bool may_resend() const; virtual unsigned char *build_packet(const struct sockaddr_storage *source, @@ -292,7 +292,7 @@ public: double completion_fraction() const; private: - eth_t *ethsd; + netutil_eth_t *ethsd; int rawsd; pcap_t *pd; int num_active_probes; @@ -348,7 +348,7 @@ bool HostState::is_finished() const { && active_probes.empty() && pending_resends.empty(); } -bool HostState::send_next_probe(int rawsd, eth_t *ethsd) { +bool HostState::send_next_probe(int rawsd, netutil_eth_t *ethsd) { Probe *probe; /* Do a resend if possible. */ @@ -589,7 +589,7 @@ Probe::Probe(HostState *host, struct probespec pspec, u8 ttl) { Probe::~Probe() { } -void Probe::send(int rawsd, eth_t *ethsd, struct timeval *now) { +void Probe::send(int rawsd, netutil_eth_t *ethsd, struct timeval *now) { struct eth_nfo eth; struct eth_nfo *ethp; int decoy; @@ -623,7 +623,7 @@ void Probe::send(int rawsd, eth_t *ethsd, struct timeval *now) { } } -void Probe::resend(int rawsd, eth_t *ethsd, struct timeval *now) { +void Probe::resend(int rawsd, netutil_eth_t *ethsd, struct timeval *now) { num_resends++; this->send(rawsd, ethsd, now); }