From: Joan Lledó <[email protected]>
Required for the Hurd.
GNU/Hurd doesn't provide any BPF implementation itself. Instead, it expects
user programs to use libpcap, which includes a Hurd module to interact with
GNUMach BPF backend.
The changes include modifying `bpf_attach` signature to be able to work with
both pcap handle and file descriptor
---
src/bpf.c | 8 +--
src/bpf.h | 3 +-
src/if-linux.c | 6 +-
src/if-pcap.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 197 insertions(+), 8 deletions(-)
create mode 100644 src/if-pcap.c
diff --git a/src/bpf.c b/src/bpf.c
index 7c521261..08b8fa68 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -292,12 +292,12 @@ next:
}
int
-bpf_attach(int fd, void *filter, unsigned int filter_len)
+bpf_attach(const struct bpf *bpf, void *filter, unsigned int filter_len)
{
struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
/* Install the filter. */
- return ioctl(fd, BIOCSETF, &pf);
+ return ioctl(bpf->bpf_fd, BIOCSETF, &pf);
}
#ifdef BIOCSETWF
@@ -549,7 +549,7 @@ bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia,
bool recv)
return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
#endif
- return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
+ return bpf_attach(bpf, buf, (unsigned int)(bp - buf));
}
int
@@ -690,7 +690,7 @@ bpf_bootp_rw(const struct bpf *bpf, bool read)
BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
bp++;
- return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
+ return bpf_attach(bpf, buf, (unsigned int)(bp - buf));
}
int
diff --git a/src/bpf.h b/src/bpf.h
index 1ffcd65a..f98ebafa 100644
--- a/src/bpf.h
+++ b/src/bpf.h
@@ -57,6 +57,7 @@
struct bpf {
const struct interface *bpf_ifp;
int bpf_fd;
+ void *bpf_handle;
unsigned int bpf_flags;
void *bpf_buffer;
size_t bpf_size;
@@ -73,7 +74,7 @@ struct bpf * bpf_open(const struct interface *,
int (*)(const struct bpf *, const struct in_addr *),
const struct in_addr *);
void bpf_close(struct bpf *);
-int bpf_attach(int, void *, unsigned int);
+int bpf_attach(const struct bpf *, void *, unsigned int);
ssize_t bpf_send(const struct bpf *, uint16_t, const void *, size_t);
ssize_t bpf_read(struct bpf *, void *, size_t);
int bpf_arp(const struct bpf *, const struct in_addr *);
diff --git a/src/if-linux.c b/src/if-linux.c
index 7e0e3c72..45047218 100644
--- a/src/if-linux.c
+++ b/src/if-linux.c
@@ -1958,7 +1958,7 @@ bpf_read(struct bpf *bpf, void *data, size_t len)
}
int
-bpf_attach(int s, void *filter, unsigned int filter_len)
+bpf_attach(const struct bpf *bpf, void *filter, unsigned int filter_len)
{
struct sock_fprog pf = {
.filter = filter,
@@ -1966,13 +1966,13 @@ bpf_attach(int s, void *filter, unsigned int filter_len)
};
/* Install the filter. */
- if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) == -1)
+ if (setsockopt(bpf->bpf_fd, SOL_SOCKET, SO_ATTACH_FILTER, &pf,
sizeof(pf)) == -1)
return -1;
#ifdef SO_LOCK_FILTER
int on = 1;
- if (setsockopt(s, SOL_SOCKET, SO_LOCK_FILTER, &on, sizeof(on)) == -1)
+ if (setsockopt(bpf->bpf_fd, SOL_SOCKET, SO_LOCK_FILTER, &on,
sizeof(on)) == -1)
return -1;
#endif
diff --git a/src/if-pcap.c b/src/if-pcap.c
new file mode 100644
index 00000000..f7851615
--- /dev/null
+++ b/src/if-pcap.c
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * libpcap interface driver for dhcpcd
+ * Copyright (c) 2025 Joan Lledó <[email protected]>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/if_ether.h>
+
+#include <pcap.h>
+
+#include "logerr.h"
+#include "bpf.h"
+
+#define PCAP_CHECK(call, name) do { \
+ int status = (call); \
+ if (status < 0) \
+ logerrx("%s: %s failed: %s", __func__, name,
pcap_statustostr(status)); \
+ else if (status > 0) \
+ logwarnx("%s: %s warning: %s", __func__, name,
pcap_statustostr(status)); \
+} while(0)
+
+#define ETH_MTU 1500
+
+const char *bpf_name = "BPF over libpcap";
+
+struct bpf *
+bpf_open(const struct interface *ifp,
+ int (*filter)(const struct bpf *, const struct in_addr *),
+ __unused const struct in_addr *ia) {
+ int err;
+ struct bpf *bpf;
+ pcap_t* handle;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ bpf = calloc(1, sizeof(*bpf));
+ if (bpf == NULL)
+ return NULL;
+
+ bpf->bpf_ifp = ifp;
+ bpf->bpf_size = 0; /* We use libpcap buffer */
+ bpf->bpf_len = 0;
+ bpf->bpf_pos = 0;
+ bpf->bpf_flags = BPF_EOF;
+
+ handle = pcap_create(ifp->name, errbuf);
+ if(handle == NULL) {
+ logerrx("%s: cannot open pcap handle: %s", __func__, errbuf);
+ goto eexit;
+ }
+
+ PCAP_CHECK(pcap_set_snaplen(handle, ETH_MTU), "pcap_set_snaplen");
+ PCAP_CHECK(pcap_set_promisc(handle, 0), "pcap_set_promisc");
+ PCAP_CHECK(pcap_set_immediate_mode(handle, 1), "pcap_set_immediate_mode");
+
+ err = pcap_activate(handle);
+ if (err != 0) {
+ if (err < 0) {
+ logerrx("%s: pcap_activate failed: %s", __func__,
pcap_statustostr(err));
+ goto eexit;
+ }
+ logwarnx("%s: pcap_activate warning: %s", __func__,
pcap_statustostr(err));
+ }
+
+ bpf->bpf_fd = pcap_get_selectable_fd(handle);
+ if(bpf->bpf_fd < 0) {
+ logerrx("%s: pcap_get_selectable_fd failed", __func__);
+ goto eexit;
+ }
+
+ bpf->bpf_handle = handle;
+
+ if (filter(bpf, ia) != 0)
+ goto eexit;
+
+ return bpf;
+
+eexit:
+ free(bpf);
+ return NULL;
+}
+
+ssize_t
+bpf_read(struct bpf *bpf, void *data, size_t len) {
+ struct pcap_pkthdr *pkt_header;
+ const u_char *pkt_data;
+ size_t cap_len;
+ int err;
+
+ bpf->bpf_flags |= BPF_EOF; /* We only read one packet per call */
+
+ err = pcap_next_ex(bpf->bpf_handle, &pkt_header, &pkt_data);
+
+ if (err < 0)
+ return -1;
+
+ /* Packet read successfully */
+ cap_len = pkt_header->caplen;
+ if (cap_len > len)
+ cap_len = len;
+ memcpy(data, pkt_data, cap_len);
+
+ return (ssize_t)cap_len;
+}
+
+ssize_t
+bpf_send(const struct bpf *bpf, uint16_t protocol,
+ const void *data, size_t len)
+{
+ struct iovec iov[2];
+ struct ether_header eh;
+ char *buffer;
+ size_t buflen;
+ int ret;
+
+ switch(bpf->bpf_ifp->hwtype) {
+ case ARPHRD_ETHER:
+ memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+ memcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,
+ sizeof(eh.ether_shost));
+ eh.ether_type = htons(protocol);
+ iov[0].iov_base = &eh;
+ iov[0].iov_len = sizeof(eh);
+ break;
+ default:
+ iov[0].iov_base = NULL;
+ iov[0].iov_len = 0;
+ break;
+ }
+ iov[1].iov_base = UNCONST(data);
+ iov[1].iov_len = len;
+
+ if (iov[0].iov_len > (SIZE_MAX - iov[1].iov_len)) {
+ logerrx("%s: buffer length overflow", __func__);
+ return -1;
+ }
+
+ buflen = iov[0].iov_len + iov[1].iov_len;
+ buffer = calloc(1, buflen);
+ if (buffer == NULL)
+ return -1;
+
+ memcpy(buffer, iov[0].iov_base, iov[0].iov_len);
+ memcpy(buffer + iov[0].iov_len, iov[1].iov_base, iov[1].iov_len);
+
+ ret = pcap_inject(bpf->bpf_handle, buffer, buflen);
+
+ free(buffer);
+ return ret;
+}
+
+int
+bpf_attach(const struct bpf *bpf, void *filter, unsigned int filter_len) {
+ struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
+
+ /* Install the filter. */
+ return pcap_setfilter(bpf->bpf_handle, &pf);
+}
+
+void
+bpf_close(struct bpf *bpf)
+{
+ pcap_close(bpf->bpf_handle);
+ free(bpf);
+}
--
2.50.1