xref: /freebsd/sys/netpfil/ipfw/ip_fw_bpf.c (revision 76af334737b1f388163dab6d63aab90de7a46c40)
1 /*-
2  * Copyright (c) 2016 Yandex LLC
3  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
4  * Copyright (c) 2025-2026 Gleb Smirnoff <glebius@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/mbuf.h>
31 #include <sys/kernel.h>
32 #include <sys/lock.h>
33 #include <sys/sx.h>
34 #include <sys/socket.h>
35 #include <sys/tree.h>
36 #include <net/ethernet.h>
37 #include <net/if.h>
38 #include <net/if_pflog.h>
39 #include <net/vnet.h>
40 #include <net/bpf.h>
41 
42 #include <netinet/ip.h>
43 #include <netinet/ip_fw.h>
44 #include <netinet/ip_var.h>
45 #include <netpfil/ipfw/ip_fw_private.h>
46 
47 static bool
bpf_ipfw_chkdir(void * arg __unused,const struct mbuf * m,int dir)48 bpf_ipfw_chkdir(void *arg __unused, const struct mbuf *m, int dir)
49 {
50 	return ((dir == BPF_D_IN && m_rcvif(m) == NULL) ||
51 	    (dir == BPF_D_OUT && m_rcvif(m) != NULL));
52 }
53 
54 static const struct bif_methods bpf_ipfw_methods = {
55 	.bif_chkdir = bpf_ipfw_chkdir,
56 };
57 
58 struct ipfw_tap {
59 	RB_ENTRY(ipfw_tap)	entry;
60 	uint32_t		rule;
61 	u_int			refs;
62 	struct bpf_if		*bpf;
63 	char 			name[sizeof("ipfw4294967295")];
64 };
65 
66 static inline int
tap_compare(const struct ipfw_tap * a,const struct ipfw_tap * b)67 tap_compare(const struct ipfw_tap *a, const struct ipfw_tap *b)
68 {
69 	return (a->rule != b->rule ? (a->rule < b->rule ? -1 : 1) : 0);
70 }
71 RB_GENERATE_STATIC(tap_tree, ipfw_tap, entry, tap_compare);
72 VNET_DEFINE_STATIC(struct ipfw_tap, default_tap) = { .name = "ipfw0" };
73 #define	V_default_tap	VNET(default_tap)
74 
75 void
ipfw_tap_alloc(struct ip_fw_chain * ch,uint32_t rule)76 ipfw_tap_alloc(struct ip_fw_chain *ch, uint32_t rule)
77 {
78 	struct ipfw_tap	*tap, key = { .rule = rule };
79 	int n __diagused;
80 
81 	MPASS(rule > 0 && rule < IPFW_DEFAULT_RULE);
82 	IPFW_UH_WLOCK_ASSERT(ch);
83 
84 	tap = RB_FIND(tap_tree, &ch->taps, &key);
85 	if (tap != NULL) {
86 		MPASS(tap->rule == rule);
87 		tap->refs++;
88 		return;
89 	}
90 	tap = malloc(sizeof(*tap), M_IPFW, M_WAITOK);
91 	tap->rule = rule;
92 	tap->refs = 1;
93 	n = snprintf(tap->name, sizeof(tap->name), "ipfw%u", rule);
94 	MPASS(n > 4 && n < sizeof("ipfw4294967295"));
95 	tap->bpf = bpf_attach(tap->name, DLT_EN10MB, PFLOG_HDRLEN,
96 	    &bpf_ipfw_methods, NULL);
97 	tap = RB_INSERT(tap_tree, &ch->taps, tap);
98 	MPASS(tap == NULL);
99 }
100 
101 void
ipfw_tap_free(struct ip_fw_chain * ch,uint32_t rule)102 ipfw_tap_free(struct ip_fw_chain *ch, uint32_t rule)
103 {
104 	struct ipfw_tap	*tap, key = { .rule = rule };
105 
106 	MPASS(rule > 0 && rule < IPFW_DEFAULT_RULE);
107 	IPFW_UH_WLOCK_ASSERT(ch);
108 
109 	tap = RB_FIND(tap_tree, &ch->taps, &key);
110 	MPASS(tap != NULL);
111 	if (--tap->refs == 0) {
112 		bpf_detach(tap->bpf);
113 		RB_REMOVE(tap_tree, &ch->taps, tap);
114 		free(tap, M_IPFW);
115 	}
116 }
117 
118 void
ipfw_bpf_tap(struct ip_fw_chain * ch,struct ip_fw_args * args,struct ip * ip,uint32_t rulenum)119 ipfw_bpf_tap(struct ip_fw_chain *ch, struct ip_fw_args *args,
120     struct ip *ip, uint32_t rulenum)
121 {
122 	struct ipfw_tap *tap;
123 
124 	if (rulenum == IPFW_DEFAULT_RULE) {
125 		tap = &V_default_tap;
126 	} else {
127 		struct ipfw_tap key = { .rule = rulenum };
128 
129 		tap = RB_FIND(tap_tree, &ch->taps, &key);
130 		MPASS(tap != NULL);
131 		/*
132 		 * Compatibility: if user is not using per-rule taps, fallback
133 		 * to the default tap.
134 		 */
135 		if (!bpf_peers_present(tap->bpf))
136 			tap = &V_default_tap;
137 	}
138 	if (args->flags & IPFW_ARGS_LENMASK) {
139 		bpf_tap(tap->bpf, args->mem, IPFW_ARGS_LENGTH(args->flags));
140 	} else if (args->flags & IPFW_ARGS_ETHER) {
141 		/* layer2, use orig hdr */
142 		bpf_mtap(tap->bpf, args->m);
143 	} else {
144 		char *fakehdr;
145 
146 		/* Add fake header. Later we will store
147 		 * more info in the header.
148 		 */
149 		if (ip->ip_v == 4)
150 			fakehdr = "DDDDDDSSSSSS\x08\x00";
151 		else if (ip->ip_v == 6)
152 			fakehdr = "DDDDDDSSSSSS\x86\xdd";
153 		else
154 			/* Obviously bogus EtherType. */
155 			fakehdr = "DDDDDDSSSSSS\xff\xff";
156 
157 		bpf_mtap2(tap->bpf, fakehdr, ETHER_HDR_LEN, args->m);
158 	}
159 }
160 
161 VNET_DEFINE_STATIC(struct bpf_if *, bpf_pflog);
162 #define	V_bpf_pflog	VNET(bpf_pflog)
163 void
ipfw_pflog_tap(void * data,struct mbuf * m)164 ipfw_pflog_tap(void *data, struct mbuf *m)
165 {
166 	bpf_mtap2(V_bpf_pflog, data, PFLOG_HDRLEN, m);
167 }
168 
169 void
ipfw_bpf_init(int first __unused)170 ipfw_bpf_init(int first __unused)
171 {
172 	V_default_tap.bpf = bpf_attach(V_default_tap.name, DLT_EN10MB,
173 	    PFLOG_HDRLEN, &bpf_ipfw_methods, NULL);
174 	V_bpf_pflog = bpf_attach("ipfwlog0", DLT_PFLOG, PFLOG_HDRLEN,
175 	    &bpf_ipfw_methods, NULL);
176 }
177 
178 void
ipfw_bpf_uninit(int last __unused)179 ipfw_bpf_uninit(int last __unused)
180 {
181 	bpf_detach(V_default_tap.bpf);
182 	bpf_detach(V_bpf_pflog);
183 }
184