Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * dhcpcd: BPF arp and bootp filtering
      3  * SPDX-License-Identifier: BSD-2-Clause
      4  * Copyright (c) 2006-2025 Roy Marples <roy (at) marples.name>
      5  * All rights reserved
      6 
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/ioctl.h>
     30 #include <sys/socket.h>
     31 
     32 #include <net/if.h>
     33 #include <netinet/in.h>
     34 #include <netinet/if_ether.h>
     35 
     36 #include <arpa/inet.h>
     37 #include <errno.h>
     38 #include <fcntl.h>
     39 #include <stddef.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 
     43 #include "config.h" // IWYU pragma: keep
     44 #ifdef USE_LIBPCAP
     45 #include <pcap/bpf.h>
     46 #elif defined(__linux__)
     47 #include <linux/filter.h>
     48 #define bpf_insn sock_filter
     49 #else
     50 #include <net/bpf.h>
     51 #endif
     52 
     53 #include "arp.h"
     54 #include "bpf.h"
     55 #include "common.h"
     56 #include "dhcp.h"
     57 #include "if.h"
     58 #include "logerr.h"
     59 
     60 /* BPF helper macros */
     61 #ifdef __linux__
     62 #define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */
     63 #else
     64 #define BPF_WHOLEPACKET ~0U
     65 #endif
     66 
     67 /* Macros to update the BPF structure */
     68 #define BPF_SET_STMT(insn, c, v)           \
     69 	{                                  \
     70 		(insn)->code = (c);        \
     71 		(insn)->jt = 0;            \
     72 		(insn)->jf = 0;            \
     73 		(insn)->k = (uint32_t)(v); \
     74 	}
     75 
     76 #define BPF_SET_JUMP(insn, c, v, t, f)     \
     77 	{                                  \
     78 		(insn)->code = (c);        \
     79 		(insn)->jt = (t);          \
     80 		(insn)->jf = (f);          \
     81 		(insn)->k = (uint32_t)(v); \
     82 	}
     83 
     84 size_t
     85 bpf_frame_header_len(const struct interface *ifp)
     86 {
     87 	switch (ifp->hwtype) {
     88 	case ARPHRD_ETHER:
     89 		return sizeof(struct ether_header);
     90 	default:
     91 		return 0;
     92 	}
     93 }
     94 
     95 void *
     96 bpf_frame_header_src(const struct interface *ifp, void *fh, size_t *len)
     97 {
     98 	uint8_t *f = fh;
     99 
    100 	switch (ifp->hwtype) {
    101 	case ARPHRD_ETHER:
    102 		*len = sizeof(((struct ether_header *)0)->ether_shost);
    103 		return f + offsetof(struct ether_header, ether_shost);
    104 	default:
    105 		*len = 0;
    106 		errno = ENOTSUP;
    107 		return NULL;
    108 	}
    109 }
    110 
    111 void *
    112 bpf_frame_header_dst(const struct interface *ifp, void *fh, size_t *len)
    113 {
    114 	uint8_t *f = fh;
    115 
    116 	switch (ifp->hwtype) {
    117 	case ARPHRD_ETHER:
    118 		*len = sizeof(((struct ether_header *)0)->ether_dhost);
    119 		return f + offsetof(struct ether_header, ether_dhost);
    120 	default:
    121 		*len = 0;
    122 		errno = ENOTSUP;
    123 		return NULL;
    124 	}
    125 }
    126 
    127 static const uint8_t etherbcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
    128 
    129 int
    130 bpf_frame_bcast(const struct interface *ifp, const void *frame)
    131 {
    132 	switch (ifp->hwtype) {
    133 	case ARPHRD_ETHER:
    134 		return memcmp((const char *)frame +
    135 			offsetof(struct ether_header, ether_dhost),
    136 		    etherbcastaddr, sizeof(etherbcastaddr));
    137 	default:
    138 		return -1;
    139 	}
    140 }
    141 
    142 ssize_t
    143 bpf_send(const struct bpf *bpf, uint16_t protocol, const void *data, size_t len)
    144 {
    145 	struct iovec iov[2];
    146 	struct ether_header eh;
    147 
    148 	switch (bpf->bpf_ifp->hwtype) {
    149 	case ARPHRD_ETHER:
    150 		memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
    151 		memcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,
    152 		    sizeof(eh.ether_shost));
    153 		eh.ether_type = htons(protocol);
    154 		iov[0].iov_base = &eh;
    155 		iov[0].iov_len = sizeof(eh);
    156 		break;
    157 	default:
    158 		iov[0].iov_base = NULL;
    159 		iov[0].iov_len = 0;
    160 		break;
    161 	}
    162 	iov[1].iov_base = UNCONST(data);
    163 	iov[1].iov_len = len;
    164 
    165 	return bpf_writev(bpf, iov, __arraycount(iov));
    166 }
    167 
    168 #ifdef ARP
    169 #define BPF_CMP_HWADDR_LEN ((((HWADDR_LEN / 4) + 2) * 2) + 1)
    170 static unsigned int
    171 bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off, bool equal,
    172     const uint8_t *hwaddr, size_t hwaddr_len)
    173 {
    174 	struct bpf_insn *bp;
    175 	size_t maclen, nlft, njmps;
    176 	uint32_t mac32;
    177 	uint16_t mac16;
    178 	uint8_t jt, jf;
    179 
    180 	/* Calc the number of jumps */
    181 	if ((hwaddr_len / 4) >= 128) {
    182 		errno = EINVAL;
    183 		return 0;
    184 	}
    185 	njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
    186 	/* We jump after the 1st check. */
    187 	if (njmps)
    188 		njmps -= 2;
    189 	nlft = hwaddr_len % 4;
    190 	if (nlft) {
    191 		njmps += (nlft / 2) * 2;
    192 		nlft = nlft % 2;
    193 		if (nlft)
    194 			njmps += 2;
    195 	}
    196 
    197 	/* Skip to positive finish. */
    198 	njmps++;
    199 	if (equal) {
    200 		jt = (uint8_t)njmps;
    201 		jf = 0;
    202 	} else {
    203 		jt = 0;
    204 		jf = (uint8_t)njmps;
    205 	}
    206 
    207 	bp = bpf;
    208 	for (; hwaddr_len > 0;
    209 	    hwaddr += maclen, hwaddr_len -= maclen, off += maclen) {
    210 		if (bpf_len < 3) {
    211 			errno = ENOBUFS;
    212 			return 0;
    213 		}
    214 		bpf_len -= 3;
    215 
    216 		if (hwaddr_len >= 4) {
    217 			maclen = sizeof(mac32);
    218 			memcpy(&mac32, hwaddr, maclen);
    219 			BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);
    220 			bp++;
    221 			BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
    222 			    htonl(mac32), jt, jf);
    223 		} else if (hwaddr_len >= 2) {
    224 			maclen = sizeof(mac16);
    225 			memcpy(&mac16, hwaddr, maclen);
    226 			BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);
    227 			bp++;
    228 			BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
    229 			    htons(mac16), jt, jf);
    230 		} else {
    231 			maclen = sizeof(*hwaddr);
    232 			BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);
    233 			bp++;
    234 			BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, *hwaddr, jt,
    235 			    jf);
    236 		}
    237 		if (jt)
    238 			jt = (uint8_t)(jt - 2);
    239 		if (jf)
    240 			jf = (uint8_t)(jf - 2);
    241 		bp++;
    242 	}
    243 
    244 	/* Last step is always return failure.
    245 	 * Next step is a positive finish. */
    246 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
    247 	bp++;
    248 
    249 	return (unsigned int)(bp - bpf);
    250 }
    251 #endif
    252 
    253 #ifdef ARP
    254 static const struct bpf_insn bpf_arp_ether[] = {
    255 	/* Check this is an ARP packet. */
    256 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
    257 	    offsetof(struct ether_header, ether_type)),
    258 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
    259 	BPF_STMT(BPF_RET + BPF_K, 0),
    260 
    261 	/* Load frame header length into X */
    262 	BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
    263 
    264 	/* Make sure the hardware type matches. */
    265 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
    266 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
    267 	BPF_STMT(BPF_RET + BPF_K, 0),
    268 
    269 	/* Make sure the hardware length matches. */
    270 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
    271 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
    272 	    sizeof(((struct ether_header *)0)->ether_shost), 1, 0),
    273 	BPF_STMT(BPF_RET + BPF_K, 0),
    274 };
    275 #define BPF_ARP_ETHER_LEN __arraycount(bpf_arp_ether)
    276 
    277 static const struct bpf_insn bpf_arp_filter[] = {
    278 	/* Make sure this is for IP. */
    279 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),
    280 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
    281 	BPF_STMT(BPF_RET + BPF_K, 0),
    282 	/* Make sure this is an ARP REQUEST. */
    283 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
    284 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
    285 	/* or ARP REPLY. */
    286 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),
    287 	BPF_STMT(BPF_RET + BPF_K, 0),
    288 	/* Make sure the protocol length matches. */
    289 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
    290 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
    291 	BPF_STMT(BPF_RET + BPF_K, 0),
    292 };
    293 #define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter)
    294 
    295 /* One address is two checks of two statements. */
    296 #define BPF_NADDRS	  1
    297 #define BPF_ARP_ADDRS_LEN 5 + ((BPF_NADDRS * 2) * 2)
    298 
    299 #define BPF_ARP_LEN                                                   \
    300 	BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + BPF_CMP_HWADDR_LEN + \
    301 	    BPF_ARP_ADDRS_LEN
    302 
    303 static int
    304 bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv)
    305 {
    306 	const struct interface *ifp = bpf->bpf_ifp;
    307 	struct bpf_insn buf[BPF_ARP_LEN + 1];
    308 	struct bpf_insn *bp;
    309 	uint16_t arp_len;
    310 	unsigned int len;
    311 
    312 	bp = buf;
    313 	/* Check frame header. */
    314 	switch (ifp->hwtype) {
    315 	case ARPHRD_ETHER:
    316 		memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
    317 		bp += BPF_ARP_ETHER_LEN;
    318 		arp_len = sizeof(struct ether_header) + sizeof(struct arphdr) +
    319 		    ((sizeof(((struct ether_header *)0)->ether_shost) +
    320 			 sizeof(uint32_t)) *
    321 			2);
    322 		break;
    323 	default:
    324 		errno = EINVAL;
    325 		return -1;
    326 	}
    327 
    328 	/* Copy in the main filter. */
    329 	memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
    330 	bp += BPF_ARP_FILTER_LEN;
    331 
    332 	/* Ensure it's not from us. */
    333 	bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
    334 	    !recv, ifp->hwaddr, ifp->hwlen);
    335 
    336 	/* Match sender protocol address */
    337 	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
    338 	    sizeof(struct arphdr) + ifp->hwlen);
    339 	bp++;
    340 	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
    341 	bp++;
    342 	BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
    343 	bp++;
    344 
    345 	/* If we didn't match sender, then we're only interested in
    346 	 * ARP probes to us, so check the null host sender. */
    347 	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
    348 	bp++;
    349 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
    350 	bp++;
    351 
    352 	/* Match target protocol address */
    353 	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
    354 	    (sizeof(struct arphdr) + (size_t)(ifp->hwlen * 2) +
    355 		sizeof(in_addr_t)));
    356 	bp++;
    357 	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
    358 	bp++;
    359 	BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
    360 	bp++;
    361 
    362 	/* No match, drop it */
    363 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
    364 	bp++;
    365 
    366 	len = (unsigned int)(bp - buf);
    367 	if (recv)
    368 		return bpf_setfilter(bpf, buf, len);
    369 	return bpf_setwfilter(bpf, buf, len);
    370 }
    371 
    372 int
    373 bpf_filter_arp(const struct bpf *bpf, const struct in_addr *ia)
    374 {
    375 	if (bpf_arp_rw(bpf, ia, true) == -1)
    376 		return -1;
    377 	if (bpf_arp_rw(bpf, ia, false) == -1 && errno != ENOSYS)
    378 		return -1;
    379 	if (bpf_lockfilter(bpf) == -1 && errno != ENOSYS)
    380 		return -1;
    381 	return 0;
    382 }
    383 #endif
    384 
    385 #ifdef ARPHRD_NONE
    386 static const struct bpf_insn bpf_bootp_none[] = {};
    387 #define BPF_BOOTP_NONE_LEN __arraycount(bpf_bootp_none)
    388 #endif
    389 
    390 static const struct bpf_insn bpf_bootp_ether[] = {
    391 	/* Make sure this is an IP packet. */
    392 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
    393 	    offsetof(struct ether_header, ether_type)),
    394 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
    395 	BPF_STMT(BPF_RET + BPF_K, 0),
    396 
    397 	/* Advance to the IP header. */
    398 	BPF_STMT(BPF_LDX + BPF_K, sizeof(struct ether_header)),
    399 };
    400 #define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether)
    401 
    402 static const struct bpf_insn bpf_bootp_base[] = {
    403 	/* Make sure it's an IPv4 packet. */
    404 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
    405 	BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
    406 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0),
    407 	BPF_STMT(BPF_RET + BPF_K, 0),
    408 
    409 	/* Make sure it's a UDP packet. */
    410 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
    411 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
    412 	BPF_STMT(BPF_RET + BPF_K, 0),
    413 
    414 	/* Make sure this isn't a fragment. */
    415 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),
    416 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
    417 	BPF_STMT(BPF_RET + BPF_K, 0),
    418 
    419 	/* Advance to the UDP header. */
    420 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
    421 	BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f),
    422 	BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
    423 	BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
    424 	BPF_STMT(BPF_MISC + BPF_TAX, 0),
    425 };
    426 #define BPF_BOOTP_BASE_LEN __arraycount(bpf_bootp_base)
    427 
    428 static const struct bpf_insn bpf_bootp_read[] = {
    429 	/* Make sure it's to the right port.
    430 	 * RFC2131 makes no mention of enforcing a source port. */
    431 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_dport)),
    432 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),
    433 	BPF_STMT(BPF_RET + BPF_K, 0),
    434 };
    435 #define BPF_BOOTP_READ_LEN __arraycount(bpf_bootp_read)
    436 
    437 static const struct bpf_insn bpf_bootp_write[] = {
    438 	/* Make sure it's from and to the right port.
    439 	 * RFC2131 makes no mention of encforcing a source port,
    440 	 * but dhcpcd does enforce it for sending. */
    441 	BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
    442 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),
    443 	BPF_STMT(BPF_RET + BPF_K, 0),
    444 };
    445 #define BPF_BOOTP_WRITE_LEN  __arraycount(bpf_bootp_write)
    446 
    447 #define BPF_BOOTP_CHADDR_LEN ((BOOTP_CHADDR_LEN / 4) * 3)
    448 #define BPF_BOOTP_XID_LEN    4 /* BOUND check is 4 instructions */
    449 
    450 #define BPF_BOOTP_LEN                                                   \
    451 	BPF_BOOTP_ETHER_LEN + BPF_BOOTP_BASE_LEN + BPF_BOOTP_READ_LEN + \
    452 	    BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
    453 
    454 static int
    455 bpf_bootp_rw(const struct bpf *bpf, bool read)
    456 {
    457 	struct bpf_insn buf[BPF_BOOTP_LEN + 1];
    458 	struct bpf_insn *bp;
    459 
    460 	bp = buf;
    461 	/* Check frame header. */
    462 	switch (bpf->bpf_ifp->hwtype) {
    463 #ifdef ARPHRD_NONE
    464 	case ARPHRD_NONE:
    465 		memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));
    466 		bp += BPF_BOOTP_NONE_LEN;
    467 		break;
    468 #endif
    469 	case ARPHRD_ETHER:
    470 		memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));
    471 		bp += BPF_BOOTP_ETHER_LEN;
    472 		break;
    473 	default:
    474 		errno = EINVAL;
    475 		return -1;
    476 	}
    477 
    478 	/* Copy in the main filter. */
    479 	memcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base));
    480 	bp += BPF_BOOTP_BASE_LEN;
    481 
    482 	if (!read) {
    483 		memcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write));
    484 		bp += BPF_BOOTP_WRITE_LEN;
    485 
    486 		/* All passed, return the packet. */
    487 		BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
    488 		bp++;
    489 
    490 		return bpf_setwfilter(bpf, buf, (unsigned int)(bp - buf));
    491 	}
    492 
    493 	memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read));
    494 	bp += BPF_BOOTP_READ_LEN;
    495 
    496 	/* All passed, return the packet. */
    497 	BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
    498 	bp++;
    499 
    500 	return bpf_setfilter(bpf, buf, (unsigned int)(bp - buf));
    501 }
    502 
    503 int
    504 bpf_filter_bootp(const struct bpf *bpf, __unused const struct in_addr *ia)
    505 {
    506 	if (bpf_bootp_rw(bpf, true) == -1)
    507 		return -1;
    508 	if (bpf_bootp_rw(bpf, false) == -1 && errno != ENOSYS)
    509 		return -1;
    510 	if (bpf_lockfilter(bpf) == -1 && errno != ENOSYS)
    511 		return -1;
    512 	return 0;
    513 }
    514