SANITIZE=no
STATUSARG=
OPENSSL=
+LIBPCAP=
DHCPCD_DEFS=dhcpcd-definitions.conf
--with-poll) POLL="$var";;
--with-openssl) OPENSSL=yes;;
--without-openssl) OPENSSL=no;;
+ --with-libpcap) LIBPCAP=yes;;
+ --without-libpcap) LIBPCAP=no;;
--sanitise|--sanitize) SANITIZEADDRESS="yes";;
--serviceexists) SERVICEEXISTS=$var;;
--servicecmd) SERVICECMD=$var;;
if [ -z "$BPF_OS" ]; then
BPF_OS="bpf-bsd.c"
fi
+if [ "$LIBPCAP" = yes ]; then
+ BPF_OS="bpf-pcap.c"
+fi
if [ -n "${_DEFAULT_HOSTNAME+x}" ]; then
DEFAULT_HOSTNAME="${_DEFAULT_HOSTNAME}"
rm -f _openssl_sha.c _openssl_sha
fi
+if [ "$LIBPCAP" = yes ]; then
+ printf "Testing for libpcap ... "
+ if $PKG_CONFIG --exists libpcap 2>/dev/null; then
+ LIBPCAP_CFLAGS=$($PKG_CONFIG --cflags libpcap 2>/dev/null)
+ LIBPCAP_LIBS=$($PKG_CONFIG --libs libpcap 2>/dev/null)
+ echo "yes"
+ echo "CFLAGS+= $LIBPCAP_CFLAGS" >>$CONFIG_MK
+ echo "LDADD+= $LIBPCAP_LIBS" >>$CONFIG_MK
+ else
+ cat <<EOF >_libpcap.c
+#include <pcap.h>
+
+int main(void) {
+ return pcap_activate(NULL);
+}
+EOF
+ if $XCC _libpcap.c -o libpcap -lpcap 2>&3; then
+ echo "yes"
+ echo "LDADD+= -lpcap" >>$CONFIG_MK
+ abort=false
+ else
+ echo "no"
+ abort=true
+ fi
+ rm -f _libpcap.c libpcap
+ $abort && exit 1
+ fi
+ echo "#define USE_LIBPCAP" >>$CONFIG_H
+fi
+
# Workaround for DragonFlyBSD import
if [ "$OS" = dragonfly ]; then
echo "#ifdef USE_PRIVATECRYPTO" >>$CONFIG_H
--- /dev/null
+/*
+ * BPF libpcap interface
+ * SPDX-License-Identifier: BSD-2-Clause
+ * Copyright (c) 2025 Joan Lledó <jlledom@member.fsf.org>
+ * 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 <errno.h>
+#include <pcap.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bpf.h"
+#include "logerr.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 = "Berkley Packet Filter (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];
+ int snaplen;
+
+ bpf = calloc(1, sizeof(*bpf));
+ if (bpf == NULL)
+ return NULL;
+
+ snaplen = ifp->mtu ? ifp->mtu : ETH_MTU;
+ bpf->bpf_ifp = ifp;
+ bpf->bpf_size = bpf_frame_header_len(ifp) + (size_t)snaplen;
+ bpf->bpf_buffer = malloc(bpf->bpf_size);
+ if (bpf->bpf_buffer == NULL)
+ goto eexit;
+ bpf->bpf_len = 0;
+ bpf->bpf_pos = 0;
+ bpf->bpf_flags = BPF_EOF;
+
+ bpf->bpf_handle = handle = pcap_create(ifp->name, errbuf);
+ if (handle == NULL) {
+ logerrx("%s: pcap_create: %s", __func__, errbuf);
+ goto eexit;
+ }
+
+ PCAP_CHECK(pcap_set_snaplen(handle, snaplen), "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;
+ }
+
+ if (filter(bpf, ia) != 0)
+ goto eexit;
+
+ return bpf;
+
+eexit:
+ bpf_close(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_writev(const struct bpf *bpf, struct iovec *iov, int iovcnt)
+{
+ int i;
+ size_t len = 0;
+ uint8_t *bp = bpf->bpf_buffer;
+
+ for (i = 0; i < iovcnt; i++) {
+ len += iov[i].iov_len;
+ /* This should be impossible. */
+ if (bpf->bpf_size < len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(bp, iov[i].iov_base, iov[i].iov_len);
+ bp += iov[i].iov_len;
+ }
+
+ return pcap_inject(bpf->bpf_handle, bpf->bpf_buffer, len);
+}
+
+int
+bpf_setfilter(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);
+}
+
+int
+bpf_setwfilter(__unused const struct bpf *bpf, __unused void *filter,
+ __unused unsigned int filter_len)
+{
+#warning A compromised libpcap socket can be used as a raw socket
+
+ errno = ENOSYS;
+ return -1;
+}
+
+int
+bpf_lock(__unused const struct bpf *bpf)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+void
+bpf_close(struct bpf *bpf)
+{
+ if (bpf->bpf_handle != NULL)
+ pcap_close(bpf->bpf_handle);
+ free(bpf->bpf_buffer);
+ free(bpf);
+}