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)