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