Browse Source

Don't use pcap_open_live

Instead, only activate the pcap handle once all relevant options
have been set. Also, increate the timeout length.

This *might* fix issues where users were reporting their routers
not responding[1] on Windows or macOS, whereas everything worked
on Linux (such as #111).

[1] "No response after 60 seconds. Bailing out."
Joseph C. Lehner 1 year ago
parent
commit
f6990ac433
2 changed files with 93 additions and 27 deletions
  1. 1 0
      Makefile
  2. 92 27
      ethsock.c

+ 1 - 0
Makefile

@@ -3,6 +3,7 @@ PKG_CONFIG ?= pkg-config
 PREFIX ?= /usr/local
 VERSION := $(shell if [ -d .git ] && which git 2>&1 > /dev/null; then git describe --always | tail -c +2; else echo $$STANDALONE_VERSION; fi)
 CFLAGS += -Wall -g -DNMRPFLASH_VERSION=\"$(VERSION)\"
+LDFLAGS += -ldl
 SUFFIX ?=
 MACOS_SDK ?= macosx11.1
 

+ 92 - 27
ethsock.c

@@ -44,6 +44,7 @@
 #include <linux/if_packet.h>
 #include <netlink/route/addr.h>
 #include <netlink/route/neighbour.h>
+#include <dlfcn.h>
 #else
 #define NMRPFLASH_AF_PACKET AF_LINK
 #include <net/if_types.h>
@@ -92,6 +93,41 @@ static int x_pcap_findalldevs(pcap_if_t **devs)
 	return 0;
 }
 
+static int (*f_pcap_set_immediate_mode)(pcap_t*, int) = NULL;
+
+static int x_pcap_set_immediate_mode(pcap_t *p, int immediate_mode)
+{
+	if (!f_pcap_set_immediate_mode) {
+		f_pcap_set_immediate_mode = dlsym(NULL, "pcap_set_immediate_mode");
+	}
+
+	if (verbosity > 2) {
+		fprintf(stderr, "pcap_set_immediate_mode = %p\n", f_pcap_set_immediate_mode);
+	}
+
+	if (f_pcap_set_immediate_mode) {
+		return f_pcap_set_immediate_mode(p, immediate_mode);
+	} else {
+		// silently ignore
+		return 0;
+	}
+}
+
+#ifdef NMRPFLASH_BSD
+static int bsd_set_immediate_mode(pcap_t *p, int immediate_mode)
+{
+	// if we have "pcap_set_immediate_mode", then there's no need to call this function. the
+	// reason for having both, is that this function must be called *after* activation, whereas
+	// pcap_set_immediate_mode must be called *before* activation!
+
+	if (f_pcap_set_immediate_mode) {
+		return 0;
+	}
+
+	return ioctl(pcap_fileno(p), BIOCIMMEDIATE, 1);
+}
+#endif
+
 static bool intf_get_pcap_flags(const char *intf, bpf_u_int32 *flags)
 {
 	pcap_if_t *devs, *dev;
@@ -608,7 +644,6 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
 	struct ethsock *sock;
 	bool is_bridge = false;
 	int err;
-	int promisc;
 
 #ifdef NMRPFLASH_WINDOWS
 	intf = intf_name_to_wpcap(intf);
@@ -624,28 +659,56 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
 	}
 
 	buf[0] = '\0';
-
 	sock->intf = intf;
-	promisc = true;
-
-	do {
-		sock->pcap = pcap_open_live(sock->intf, BUFSIZ, promisc, 1, buf);
-		if (!sock->pcap) {
-			if (!promisc) {
-				fprintf(stderr, "Error: %s.\n", buf);
-				goto cleanup;
-			} else {
-				fprintf(stderr, "Warning: failed to enable promiscous mode.\n");
-				promisc = false;
-				continue;
-			}
-		}
-	} while (!sock->pcap);
+	sock->pcap = pcap_create(sock->intf, buf);
+	if (!sock->pcap) {
+		fprintf(stderr, "pcap_create: %s\n", buf);
+	}
 
 	if (*buf) {
 		fprintf(stderr, "Warning: %s.\n", buf);
 	}
 
+	err = pcap_set_snaplen(sock->pcap, BUFSIZ);
+	if (err) {
+		pcap_perror(sock->pcap, "pcap_set_snaplen");
+		goto cleanup;
+	}
+
+	err = pcap_set_promisc(sock->pcap, 1);
+	if (err) {
+		pcap_perror(sock->pcap, "pcap_set_promisc");
+		goto cleanup;
+	}
+
+	err = pcap_set_timeout(sock->pcap, 200);
+	if (err) {
+		pcap_perror(sock->pcap, "pcap_set_timeout");
+		goto cleanup;
+	}
+
+	err = x_pcap_set_immediate_mode(sock->pcap, 1);
+	if (err) {
+		pcap_perror(sock->pcap, "pcap_set_immediate_mode");
+		goto cleanup;
+	}
+
+#ifdef NMRPFLASH_WINDOWS
+	err = pcap_setmintocopy(sock->pcap, 0);
+	if (err) {
+		pcap_perror(sock->pcap, "pcap_setmintocopy");
+		goto cleanup;
+	}
+#endif
+
+	err = pcap_activate(sock->pcap);
+	if (err < 0) {
+		pcap_perror(sock->pcap, "pcap_activate");
+		goto cleanup;
+	} else if (err > 0) {
+		fprintf(stderr, "Warning: %s.\n", pcap_geterr(sock->pcap));
+	}
+
 	if (pcap_datalink(sock->pcap) != DLT_EN10MB) {
 		fprintf(stderr, "%s is not an ethernet interface.\n",
 				intf);
@@ -662,25 +725,27 @@ struct ethsock *ethsock_create(const char *intf, uint16_t protocol)
 		goto cleanup;
 	}
 
-#ifndef NMRPFLASH_WINDOWS
-	sock->fd = pcap_get_selectable_fd(sock->pcap);
-	if (sock->fd == -1) {
-		pcap_perror(sock->pcap, "pcap_get_selectable_fd");
-		goto cleanup;
-	}
-#else
+#ifdef NMRPFLASH_WINDOWS
 	sock->handle = pcap_getevent(sock->pcap);
 	if (!sock->handle) {
 		pcap_perror(sock->pcap, "pcap_getevent");
 		goto cleanup;
 	}
+#else
+	sock->fd = pcap_get_selectable_fd(sock->pcap);
+	if (sock->fd == -1) {
+		pcap_perror(sock->pcap, "pcap_get_selectable_fd");
+		goto cleanup;
+	}
 
-	err = pcap_setmintocopy(sock->pcap, 1);
+#ifdef NMRPFLASH_BSD
+	err = bsd_set_immediate_mode(sock->pcap, 1);
 	if (err) {
-		pcap_perror(sock->pcap, "pcap_setmintocopy");
+		fprintf(stderr, "Warning: setting immediate mode failed: %s.\n", strerror(errno));
 		goto cleanup;
 	}
-#endif
+#endif // NMRPFLASH_BSD
+#endif // NMRPFLASH_WINDOWS
 
 	snprintf(buf, sizeof(buf), "ether proto 0x%04x and not ether src %s",
 			protocol, mac_to_str(sock->hwaddr));