xref: /freebsd/sys/netpfil/ipfw/ip_fw_bpf.c (revision 129e15d4994311958db59a1718d4ff42d440ce2b)
1 /*-
2  * Copyright (c) 2016 Yandex LLC
3  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
4  * Copyright (c) 2025 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/rwlock.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_HEAD(tap_tree, ipfw_tap);
72 VNET_DEFINE_STATIC(struct tap_tree, tap_tree);
73 #define	V_tap_tree	VNET(tap_tree)
74 RB_GENERATE_STATIC(tap_tree, ipfw_tap, entry, tap_compare);
75 VNET_DEFINE_STATIC(struct ipfw_tap *, default_tap);
76 #define	V_default_tap	VNET(default_tap)
77 
78 void
ipfw_tap_alloc(uint32_t rule)79 ipfw_tap_alloc(uint32_t rule)
80 {
81 	struct ipfw_tap	*tap, key = { .rule = rule };
82 	int n __diagused;
83 
84 	tap = RB_FIND(tap_tree, &V_tap_tree, &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 	/* Note: the default rule logs to "ipfw0". */
94 	if (__predict_false(rule == IPFW_DEFAULT_RULE)) {
95 		V_default_tap = tap;
96 		rule = 0;
97 	}
98 	n = snprintf(tap->name, sizeof(tap->name), "ipfw%u", rule);
99 	MPASS(n > 4 && n < sizeof("ipfw4294967295"));
100 	tap->bpf = bpf_attach(tap->name, DLT_EN10MB, PFLOG_HDRLEN,
101 	    &bpf_ipfw_methods, NULL);
102 	tap = RB_INSERT(tap_tree, &V_tap_tree, tap);
103 	MPASS(tap == NULL);
104 }
105 
106 void
ipfw_tap_free(uint32_t rule)107 ipfw_tap_free(uint32_t rule)
108 {
109 
110 	struct ipfw_tap	*tap, key = { .rule = rule };
111 
112 	tap = RB_FIND(tap_tree, &V_tap_tree, &key);
113 	MPASS(tap != NULL);
114 	if (--tap->refs == 0) {
115 		bpf_detach(tap->bpf);
116 		RB_REMOVE(tap_tree, &V_tap_tree, tap);
117 		free(tap, M_IPFW);
118 	}
119 }
120 
121 void
ipfw_bpf_tap(struct ip_fw_args * args,struct ip * ip,uint32_t rulenum)122 ipfw_bpf_tap(struct ip_fw_args *args, struct ip *ip, uint32_t rulenum)
123 {
124 	struct ipfw_tap *tap, key = { .rule = rulenum };
125 
126 	tap = RB_FIND(tap_tree, &V_tap_tree, &key);
127 	MPASS(tap != NULL);
128 	if (!bpf_peers_present(tap->bpf))
129 		tap = V_default_tap;
130 	if (args->flags & IPFW_ARGS_LENMASK) {
131 		bpf_tap(tap->bpf, args->mem, IPFW_ARGS_LENGTH(args->flags));
132 	} else if (args->flags & IPFW_ARGS_ETHER) {
133 		/* layer2, use orig hdr */
134 		bpf_mtap(tap->bpf, args->m);
135 	} else {
136 		char *fakehdr;
137 
138 		/* Add fake header. Later we will store
139 		 * more info in the header.
140 		 */
141 		if (ip->ip_v == 4)
142 			fakehdr = "DDDDDDSSSSSS\x08\x00";
143 		else if (ip->ip_v == 6)
144 			fakehdr = "DDDDDDSSSSSS\x86\xdd";
145 		else
146 			/* Obviously bogus EtherType. */
147 			fakehdr = "DDDDDDSSSSSS\xff\xff";
148 
149 		bpf_mtap2(tap->bpf, fakehdr, ETHER_HDR_LEN, args->m);
150 	}
151 }
152 
153 VNET_DEFINE_STATIC(struct bpf_if *, bpf_pflog);
154 #define	V_bpf_pflog	VNET(bpf_pflog)
155 void
ipfw_pflog_tap(void * data,struct mbuf * m)156 ipfw_pflog_tap(void *data, struct mbuf *m)
157 {
158 	bpf_mtap2(V_bpf_pflog, data, PFLOG_HDRLEN, m);
159 }
160 
161 void
ipfw_bpf_init(int first __unused)162 ipfw_bpf_init(int first __unused)
163 {
164 	ipfw_tap_alloc(IPFW_DEFAULT_RULE);
165 	V_bpf_pflog = bpf_attach("ipfwlog0", DLT_PFLOG, PFLOG_HDRLEN,
166 	    &bpf_ipfw_methods, NULL);
167 }
168 
169 void
ipfw_bpf_uninit(int last __unused)170 ipfw_bpf_uninit(int last __unused)
171 {
172 
173 	ipfw_tap_free(IPFW_DEFAULT_RULE);
174 	bpf_detach(V_bpf_pflog);
175 }
176