When -i is not given, iterate all available devices until a usable one is found instead of just trying the first one and giving up if it is not usable. See RHBZ #842660. Additionally, handle the case where a device provides both supported and unsupported datalink types. diff -Naur arpwatch-3.1-original/arpwatch.c arpwatch-3.1/arpwatch.c --- arpwatch-3.1-original/arpwatch.c 2019-11-30 13:35:23.000000000 -0500 +++ arpwatch-3.1/arpwatch.c 2021-04-24 09:02:50.762535242 -0400 @@ -161,6 +161,8 @@ RETSIGTYPE die(int); int isbogon(u_int32_t); int main(int, char **); +int try_open_live(pcap_t ** pd_ptr, char const * interface_name, + int promiscuous_enable); void process_ether(u_char *, const struct pcap_pkthdr *, const u_char *); void process_fddi(u_char *, const struct pcap_pkthdr *, const u_char *); int readsnmp(char *); @@ -177,7 +179,7 @@ int op, snaplen, timeout, linktype, status; pcap_t *pd; FILE *fp; - pcap_if_t *alldevs; + pcap_if_t *alldevs, *dev; char *interface, *rfilename; struct bpf_program code; char errbuf[PCAP_ERRBUF_SIZE]; @@ -309,13 +311,18 @@ "%s: pcap_findalldevs: %s\n", prog, errbuf); exit(1); } - if (alldevs == NULL) { + for (dev = alldevs; dev; dev = dev->next) { + if (try_open_live(&pd, dev->name, promisc)) { + interface = savestr(alldevs->name); + break; + } + } + pcap_freealldevs(alldevs); + if (interface == NULL) { (void)fprintf(stderr, "%s: pcap_findalldevs:" " no suitable devices found\n", prog); exit(1); } - interface = savestr(alldevs->name); - pcap_freealldevs(alldevs); #else if (interface = pcap_lookupdev(errbuf)) == NULL) { (void)fprintf(stderr, @@ -354,15 +361,12 @@ } swapped = pcap_is_swapped(pd); } else { - snaplen = max(sizeof(struct ether_header), - sizeof(struct fddi_header)) + sizeof(struct ether_arp); - timeout = 1000; - pd = pcap_open_live(interface, snaplen, promisc, timeout, - errbuf); if (pd == NULL) { - lg(LOG_ERR, "pcap open %s: %s", interface, errbuf); - exit(1); + if (!try_open_live(&pd, interface, promisc)) { + exit(1); + } } + /* else pd was already opened based on pcap_findalldevs */ #ifdef WORDS_BIGENDIAN swapped = 1; #endif @@ -452,6 +456,74 @@ exit(0); } +int +try_open_live(pcap_t ** pd_ptr, char const * interface_name, int promiscuous_enable) { + /* Attempt to open an interface and set up a supported datalink type; + * return nonzero on success and zero on failure (and log a message). + */ + int snaplen, timeout, n_datalinks, datalink_i; + int * datalinks, datalink; + char errbuf[PCAP_ERRBUF_SIZE]; + + snaplen = max(sizeof(struct ether_header), + sizeof(struct fddi_header)) + sizeof(struct ether_arp); + timeout = 1000; + datalinks = NULL; + + /* Just in case... */ + if (*pd_ptr != NULL) { + pcap_close(*pd_ptr); + *pd_ptr = NULL; + } + + *pd_ptr = pcap_open_live(interface_name, snaplen, promiscuous_enable, + timeout, errbuf); + if (*pd_ptr == NULL) { + lg(LOG_ERR, "pcap open %s: %s", interface_name, errbuf); + goto fail; + } + + /* Must be able to select an ethernet or fddi datalink */ + n_datalinks = pcap_list_datalinks(*pd_ptr, &datalinks); + if (n_datalinks < 0) { + lg(LOG_ERR, "pcap_list_datalinks %s: %s", interface_name, + pcap_geterr(*pd_ptr)); + goto fail; + } + for (datalink_i = 0; datalink_i < n_datalinks; ++datalink_i) { + switch (datalinks[datalink_i]) { + case DLT_EN10MB: + case DLT_FDDI: + break; + default: + continue; /* unsupported; try the next datalink */ + } + if (pcap_set_datalink(*pd_ptr, datalinks[datalink_i]) != 0) { + lg(LOG_ERR, "pcap_set_datalink %s %d: %s", + interface_name, datalinks[datalink_i], + pcap_geterr(*pd_ptr)); + continue; + } + break; /* success */ + } + if (datalink_i >= n_datalinks) { + lg(LOG_ERR, "no ethernet or fddi datalink for %s", + interface_name); + goto fail; + } + + free(datalinks); + return 1; /* success */ + +fail: + if (*pd_ptr != NULL) { + pcap_close(*pd_ptr); + *pd_ptr = NULL; + } + free(datalinks); + return 0; /* failure */ +} + /* Process an ethernet arp/rarp packet */ void process_ether(u_char *u, const struct pcap_pkthdr *h, const u_char *p)