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