/* 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 #include static int is_pcap_file = 0; #ifdef WIN32 #include #include #if defined(_MSC_VER) #pragma comment(lib, "IPHLPAPI.lib") #endif #elif defined(__GNUC__) #include #include #include #include #include #include #include #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; }