Linux packet filter bug fixes and improvements
Please find a patch for the ISCP DHCP Server regarding the Linux packet filter below, which provides the following modifications:
-
Optimization: The BPF program order has been reorganized to reduce the filter processing workload in the kernel. Furthermore, it is superfluous to BPF_LD the port twice in the relay filter, so the second BPF_LD has been removed.
-
Bugfix: The test for fragmented packets did not drop the first fragment.
-
Bugfix: Non-DHCP packets may be received on the socket in the period from the socket was created until the filter was attached to it. So drain the socket after the filter has been attached.
PS: I have previously contributed a similar patch to KEA.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or
(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or
(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.
(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
Signed-off-by: Morten Brørup mb@smartsharesystems.com
$ diff -u bpf.c.orig bpf.c
--- bpf.c.orig 2023-09-03 15:05:06.481202014 +0200
+++ bpf.c 2023-09-03 17:48:26.057021797 +0200
@@ -5,6 +5,7 @@
/*
* Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
+ * Copyright (C) 2023 SmartShare Systems
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -168,28 +169,31 @@
#if defined (USE_BPF_RECEIVE) || defined (USE_LPF_RECEIVE)
/* Packet filter program...
+ Most packets are not DHCP packets. By designing the filter to detect
+ non-DHCP packets as early as possible, we reduce the kernel's BPF
+ processing workload.
XXX Changes to the filter program may require changes to the constant
offsets used in if_register_send to patch the BPF program! XXX */
struct bpf_insn dhcp_bpf_filter [] = {
+ /* Get the IP header length... */
+ BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's to the right port... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 7), /* patch */
+
/* Make sure this is an IP packet... */
BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 5),
/* Make sure it's a UDP packet... */
BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 3),
/* Make sure this isn't a fragment... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
- BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
-
- /* Get the IP header length... */
- BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
-
- /* Make sure it's to the right port... */
- BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP (BPF_JMP + BPF_JSET + BPF_K, 0x3fff, 1, 0),
/* If we passed all the tests, ask for the whole packet. */
BPF_STMT (BPF_RET + BPF_K, (u_int)-1),
@@ -203,28 +207,27 @@
* For relay port extension
*/
struct bpf_insn dhcp_bpf_relay_filter [] = {
- /* Make sure this is an IP packet... */
- BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
-
- /* Make sure it's a UDP packet... */
- BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
-
- /* Make sure this isn't a fragment... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
- BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0),
-
/* Get the IP header length... */
BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),
/* Make sure it's to the right port... */
BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 2, 0), /* patch */
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 1, 0), /* patch */
/* relay can have an alternative port... */
- BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 7), /* patch */
+
+ /* Make sure this is an IP packet... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 5),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 3),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP (BPF_JMP + BPF_JSET + BPF_K, 0x3fff, 1, 0),
/* If we passed all the tests, ask for the whole packet. */
BPF_STMT (BPF_RET + BPF_K, (u_int)-1),
@@ -357,10 +360,11 @@
p.bf_len = dhcp_bpf_relay_filter_len;
p.bf_insns = dhcp_bpf_relay_filter;
- dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
+ dhcp_bpf_relay_filter [2].k = ntohs (local_port);
+ dhcp_bpf_relay_filter [3].k = ntohs (relay_port);
}
#endif
- p.bf_insns [8].k = ntohs (local_port);
+ dhcp_bpf_filter [2].k = ntohs (local_port);
if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0)
log_fatal ("Can't install packet filter program: %m");
$ diff -u lpf.c.orig lpf.c
--- lpf.c.orig 2023-09-03 15:50:27.305763266 +0200
+++ lpf.c 2023-09-03 17:16:42.999729478 +0200
@@ -6,6 +6,7 @@
/*
* Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
+ * Copyright (C) 2023 SmartShare Systems
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -249,6 +250,8 @@
static void lpf_gen_filter_setup (info)
struct interface_info *info;
{
+ unsigned char ibuf [1536];
+ int length;
struct sock_fprog p;
memset(&p, 0, sizeof(p));
@@ -270,10 +273,11 @@
p.len = dhcp_bpf_relay_filter_len;
p.filter = dhcp_bpf_relay_filter;
- dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
+ dhcp_bpf_relay_filter [2].k = ntohs (local_port);
+ dhcp_bpf_relay_filter [3].k = ntohs (relay_port);
}
#endif
- dhcp_bpf_filter [8].k = ntohs (local_port);
+ dhcp_bpf_filter [2].k = ntohs (local_port);
if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
sizeof p) < 0) {
@@ -289,6 +293,13 @@
}
log_fatal ("Can't install packet filter program: %m");
}
+
+ /* Non-DHCP packets may have been received before the filter was
+ attached, so drain the socket. */
+ do {
+ length = recv (info -> rfdesc, ibuf, sizeof ibuf,
+ MSG_DONTWAIT | MSG_TRUNC);
+ } while (length > 0);
}
#if defined (HAVE_TR_SUPPORT)