-
Notifications
You must be signed in to change notification settings - Fork 144
BPF: Split OS specific code out into own files and add libpcap support #607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rsmarples
wants to merge
4
commits into
master
Choose a base branch
from
bpf
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
542c426
BPF: Simplify by splitting OS specifics into own files
rsmarples 79a49d0
BPF: Fixup comment to indicate this could be used outside of dhcpcd
rsmarples 6ae0742
BPF: Add support for libpcap
jlledom 157f7c7
BPF: Address review comments
rsmarples File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,261 @@ | ||
| /* | ||
| * BPF BSD interface | ||
| * SPDX-License-Identifier: BSD-2-Clause | ||
| * Copyright (c) 2006-2025 Roy Marples <roy@marples.name> | ||
| * 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 <sys/ioctl.h> | ||
| #include <sys/socket.h> | ||
|
|
||
| #include <net/bpf.h> | ||
|
|
||
| #include <errno.h> | ||
| #include <fcntl.h> | ||
| #include <stddef.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
||
| #include "bpf.h" | ||
| #include "logerr.h" | ||
|
|
||
| const char *bpf_name = "Berkeley Packet Filter"; | ||
|
|
||
| struct bpf * | ||
| bpf_open(const struct interface *ifp, | ||
| int (*filter)(const struct bpf *, const struct in_addr *), | ||
| const struct in_addr *ia) | ||
| { | ||
| struct bpf *bpf; | ||
| struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 }; | ||
| struct ifreq ifr = { .ifr_flags = 0 }; | ||
| int ibuf_len = 0; | ||
| #ifdef O_CLOEXEC | ||
| #define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK | O_CLOEXEC | ||
| #else | ||
| #define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK | ||
| #endif | ||
| #ifdef BIOCIMMEDIATE | ||
| unsigned int flags; | ||
| #endif | ||
| #ifndef O_CLOEXEC | ||
| int fd_opts; | ||
| #endif | ||
|
|
||
| bpf = calloc(1, sizeof(*bpf)); | ||
| if (bpf == NULL) | ||
| return NULL; | ||
| bpf->bpf_ifp = ifp; | ||
| bpf->bpf_flags = BPF_EOF; | ||
|
|
||
| /* /dev/bpf is a cloner on modern kernels */ | ||
| bpf->bpf_fd = open("/dev/bpf", BPF_OPEN_FLAGS); | ||
|
|
||
| /* Support older kernels where /dev/bpf is not a cloner */ | ||
| if (bpf->bpf_fd == -1) { | ||
| char device[32]; | ||
| int n = 0; | ||
|
|
||
| do { | ||
| snprintf(device, sizeof(device), "/dev/bpf%d", n++); | ||
| bpf->bpf_fd = open(device, BPF_OPEN_FLAGS); | ||
| } while (bpf->bpf_fd == -1 && errno == EBUSY); | ||
| } | ||
|
|
||
| if (bpf->bpf_fd == -1) | ||
| goto eexit; | ||
|
|
||
| #ifndef O_CLOEXEC | ||
| if ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 || | ||
| fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) | ||
| goto eexit; | ||
| #endif | ||
|
|
||
| if (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1) | ||
| goto eexit; | ||
| if (pv.bv_major != BPF_MAJOR_VERSION || | ||
| pv.bv_minor < BPF_MINOR_VERSION) { | ||
| logerrx("BPF version mismatch - recompile"); | ||
| goto eexit; | ||
| } | ||
|
|
||
| strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); | ||
| if (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1) | ||
| goto eexit; | ||
|
|
||
| #ifdef BIOCIMMEDIATE | ||
| flags = 1; | ||
| if (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1) | ||
| goto eexit; | ||
| #endif | ||
|
|
||
| if (filter(bpf, ia) != 0) | ||
| goto eexit; | ||
|
|
||
| /* Get the required BPF buffer length from the kernel. */ | ||
| if (ioctl(bpf->bpf_fd, BIOCGBLEN, &ibuf_len) == -1) | ||
| goto eexit; | ||
|
|
||
| bpf->bpf_size = (size_t)ibuf_len; | ||
| bpf->bpf_buffer = malloc(bpf->bpf_size); | ||
| if (bpf->bpf_buffer == NULL) | ||
| goto eexit; | ||
|
|
||
| return bpf; | ||
|
|
||
| eexit: | ||
| if (bpf->bpf_fd != -1) | ||
| close(bpf->bpf_fd); | ||
| free(bpf); | ||
| return NULL; | ||
| } | ||
|
|
||
| /* BPF requires that we read the entire buffer. | ||
| * So we pass the buffer in the API so we can loop on >1 packet. */ | ||
| ssize_t | ||
| bpf_read(struct bpf *bpf, void *data, size_t len) | ||
| { | ||
| ssize_t bytes; | ||
| struct bpf_hdr packet; | ||
| size_t hdr_max; | ||
| const uint8_t *payload; | ||
|
|
||
| bpf->bpf_flags &= ~BPF_EOF; | ||
| for (;;) { | ||
| if (bpf->bpf_len == 0) { | ||
| bytes = read(bpf->bpf_fd, bpf->bpf_buffer, | ||
| bpf->bpf_size); | ||
| #if defined(__sun) | ||
| /* After 2^31 bytes, the kernel offset overflows. | ||
| * To work around this bug, lseek 0. */ | ||
| if (bytes == -1 && errno == EINVAL) { | ||
| lseek(bpf->bpf_fd, 0, SEEK_SET); | ||
| continue; | ||
| } | ||
| #endif | ||
| if (bytes == -1 || bytes == 0) | ||
| return bytes; | ||
| bpf->bpf_len = (size_t)bytes; | ||
| bpf->bpf_pos = 0; | ||
| } | ||
| bytes = -1; | ||
| if (bpf->bpf_pos + sizeof(packet) > bpf->bpf_len) { | ||
| errno = EINVAL; | ||
| goto err; | ||
| } | ||
| payload = (const uint8_t *)bpf->bpf_buffer + bpf->bpf_pos; | ||
| memcpy(&packet, payload, sizeof(packet)); | ||
| hdr_max = SIZE_MAX - packet.bh_caplen; | ||
| if (packet.bh_hdrlen > hdr_max) { | ||
| errno = EOVERFLOW; | ||
| goto err; | ||
| } | ||
| if (packet.bh_hdrlen + packet.bh_caplen > | ||
| bpf->bpf_len - bpf->bpf_pos) { | ||
| errno = EBADMSG; | ||
| goto err; | ||
| } | ||
| payload += packet.bh_hdrlen; | ||
| if (packet.bh_caplen > len) | ||
| bytes = (ssize_t)len; | ||
| else | ||
| bytes = (ssize_t)packet.bh_caplen; | ||
| if (bpf_frame_bcast(bpf->bpf_ifp, payload) == 0) | ||
| bpf->bpf_flags |= BPF_BCAST; | ||
| else | ||
| bpf->bpf_flags &= ~BPF_BCAST; | ||
| memcpy(data, payload, (size_t)bytes); | ||
| bpf->bpf_pos += BPF_WORDALIGN( | ||
| packet.bh_hdrlen + packet.bh_caplen); | ||
| if (bpf->bpf_pos >= bpf->bpf_len) { | ||
| bpf->bpf_len = bpf->bpf_pos = 0; | ||
| bpf->bpf_flags |= BPF_EOF; | ||
| } | ||
| if (bytes != -1) | ||
| return bytes; | ||
| } | ||
|
|
||
| /* NOTREACHED */ | ||
|
|
||
| err: | ||
| bpf->bpf_len = bpf->bpf_pos = 0; | ||
| bpf->bpf_flags |= BPF_EOF; | ||
| return -1; | ||
| } | ||
|
|
||
| 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 ioctl(bpf->bpf_fd, BIOCSETF, &pf); | ||
| } | ||
|
|
||
| int | ||
| bpf_setwfilter(const struct bpf *bpf, void *filter, unsigned int filter_len) | ||
| { | ||
| #ifdef BIOCSETWF | ||
| struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len }; | ||
|
|
||
| /* Install the filter. */ | ||
| return ioctl(bpf->bpf_fd, BIOCSETWF, &pf); | ||
| #else | ||
| #warning No BIOCSETWF support - a compromised BPF can be used as a raw socket | ||
| UNUSED(bpf); | ||
| UNUSED(filter); | ||
| UNUSED(filter_len); | ||
| errno = ENOSYS; | ||
| return -1; | ||
| #endif | ||
| } | ||
|
|
||
| int | ||
| bpf_lock(const struct bpf *bpf) | ||
| { | ||
| #ifdef BIOCLOCK | ||
| return ioctl(bpf->bpf_fd, BIOCLOCK); | ||
| #else | ||
| UNUSED(bpf); | ||
| errno = ENOSYS; | ||
| return -1; | ||
| #endif | ||
| } | ||
|
|
||
| #if !defined(__sun) | ||
| /* SunOS is special too - sending via BPF goes nowhere. */ | ||
| ssize_t | ||
| bpf_writev(const struct bpf *bpf, struct iovec *iov, int iovcnt) | ||
| { | ||
| return writev(bpf->bpf_fd, iov, iovcnt); | ||
| } | ||
| #endif | ||
|
|
||
| void | ||
| bpf_close(struct bpf *bpf) | ||
| { | ||
| close(bpf->bpf_fd); | ||
| free(bpf->bpf_buffer); | ||
| free(bpf); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add libpcap options to
./configure --help.Line 123 and Line 124 add new flags, but the help text block does not list them. This makes feature discovery harder for users relying on
--help.Suggested help text addition
🤖 Prompt for AI Agents