1 /*-
2 * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: releng/5.4/sys/netinet/ip_fw_pfil.c 144804 2005-04-08 12:37:59Z glebius $
27 */
28
29 #if !defined(KLD_MODULE)
30 #include "opt_ipfw.h"
31 #include "opt_ipdn.h"
32 #include "opt_ipdivert.h"
33 #include "opt_inet.h"
34 #ifndef INET
35 #error IPFIREWALL requires INET.
36 #endif /* INET */
37 #endif /* KLD_MODULE */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/mbuf.h>
43 #include <sys/module.h>
44 #include <sys/kernel.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/sysctl.h>
48 #include <sys/ucred.h>
49
50 #include <net/if.h>
51 #include <net/route.h>
52 #include <net/pfil.h>
53
54 #include <netinet/in.h>
55 #include <netinet/in_systm.h>
56 #include <netinet/in_var.h>
57 #include <netinet/ip.h>
58 #include <netinet/ip_var.h>
59 #include <netinet/ip_fw.h>
60 #include <netinet/ip_divert.h>
61 #include <netinet/ip_dummynet.h>
62
63 #include <machine/in_cksum.h>
64
65 static int ipfw_pfil_hooked = 0;
66
67 /* Dummynet hooks. */
68 ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;
69
70 #define DIV_DIR_IN 1
71 #define DIV_DIR_OUT 0
72
73 static int ipfw_divert(struct mbuf **, int, int);
74
75 int
76 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
77 struct inpcb *inp)
78 {
79 struct ip_fw_args args;
80 struct m_tag *dn_tag;
81 int ipfw = 0;
82 int divert;
83 #ifdef IPFIREWALL_FORWARD
84 struct m_tag *fwd_tag;
85 #endif
86
87 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
88
89 if (!fw_enable)
90 goto pass;
91
92 bzero(&args, sizeof(args));
93
94 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
95 if (dn_tag != NULL){
96 struct dn_pkt_tag *dt;
97
98 dt = (struct dn_pkt_tag *)(dn_tag+1);
99 args.rule = dt->rule;
100
101 m_tag_delete(*m0, dn_tag);
102 }
103
104 again:
105 args.m = *m0;
106 args.inp = inp;
107 ipfw = ipfw_chk(&args);
108 *m0 = args.m;
109
110 if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)
111 goto drop;
112
113 if (ipfw == 0 && args.next_hop == NULL)
114 goto pass;
115
116 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
117 ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args);
118 *m0 = NULL;
119 return 0; /* packet consumed */
120 }
121
122 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
123 if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
124 divert = ipfw_divert(m0, DIV_DIR_IN, 1);
125 else
126 divert = ipfw_divert(m0, DIV_DIR_IN, 0);
127
128 /* tee should continue again with the firewall. */
129 if (divert) {
130 *m0 = NULL;
131 return 0; /* packet consumed */
132 } else {
133 args.rule = NULL;
134 goto again; /* continue with packet */
135 }
136 }
137
138 #ifdef IPFIREWALL_FORWARD
139 if (ipfw == 0 && args.next_hop != NULL) {
140 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
141 sizeof(struct sockaddr_in), M_NOWAIT);
142 if (fwd_tag == NULL)
143 goto drop;
144 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
145 m_tag_prepend(*m0, fwd_tag);
146
147 if (in_localip(args.next_hop->sin_addr))
148 (*m0)->m_flags |= M_FASTFWD_OURS;
149 goto pass;
150 }
151 #endif
152
153 drop:
154 if (*m0)
155 m_freem(*m0);
156 *m0 = NULL;
157 return (EACCES);
158 pass:
159 return 0; /* not filtered */
160 }
161
162 int
163 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
164 struct inpcb *inp)
165 {
166 struct ip_fw_args args;
167 struct m_tag *dn_tag;
168 int ipfw = 0;
169 int divert;
170 #ifdef IPFIREWALL_FORWARD
171 struct m_tag *fwd_tag;
172 #endif
173
174 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
175
176 if (!fw_enable)
177 goto pass;
178
179 bzero(&args, sizeof(args));
180
181 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
182 if (dn_tag != NULL) {
183 struct dn_pkt_tag *dt;
184
185 dt = (struct dn_pkt_tag *)(dn_tag+1);
186 args.rule = dt->rule;
187
188 m_tag_delete(*m0, dn_tag);
189 }
190
191 again:
192 args.m = *m0;
193 args.oif = ifp;
194 args.inp = inp;
195 ipfw = ipfw_chk(&args);
196 *m0 = args.m;
197
198 if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)
199 goto drop;
200
201 if (ipfw == 0 && args.next_hop == NULL)
202 goto pass;
203
204 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
205 ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args);
206 *m0 = NULL;
207 return 0; /* packet consumed */
208 }
209
210 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
211 if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
212 divert = ipfw_divert(m0, DIV_DIR_OUT, 1);
213 else
214 divert = ipfw_divert(m0, DIV_DIR_OUT, 0);
215
216 if (divert) {
217 *m0 = NULL;
218 return 0; /* packet consumed */
219 } else {
220 args.rule = NULL;
221 goto again; /* continue with packet */
222 }
223 }
224
225 #ifdef IPFIREWALL_FORWARD
226 if (ipfw == 0 && args.next_hop != NULL) {
227 /* Overwrite existing tag. */
228 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
229 if (fwd_tag == NULL) {
230 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
231 sizeof(struct sockaddr_in), M_NOWAIT);
232 if (fwd_tag == NULL)
233 goto drop;
234 } else
235 m_tag_unlink(*m0, fwd_tag);
236 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
237 m_tag_prepend(*m0, fwd_tag);
238
239 if (in_localip(args.next_hop->sin_addr))
240 (*m0)->m_flags |= M_FASTFWD_OURS;
241 goto pass;
242 }
243 #endif
244
245 drop:
246 if (*m0)
247 m_freem(*m0);
248 *m0 = NULL;
249 return (EACCES);
250 pass:
251 return 0; /* not filtered */
252 }
253
254 static int
255 ipfw_divert(struct mbuf **m, int incoming, int tee)
256 {
257 /*
258 * ipfw_chk() has already tagged the packet with the divert tag.
259 * If tee is set, copy packet and return original.
260 * If not tee, consume packet and send it to divert socket.
261 */
262 #ifdef IPDIVERT
263 struct mbuf *clone, *reass;
264 struct ip *ip;
265 int hlen;
266
267 reass = NULL;
268
269 /* Cloning needed for tee? */
270 if (tee)
271 clone = m_dup(*m, M_DONTWAIT);
272 else
273 clone = *m;
274
275 /* In case m_dup was unable to allocate mbufs. */
276 if (clone == NULL)
277 goto teeout;
278
279 /*
280 * Divert listeners can only handle non-fragmented packets.
281 * However when tee is set we will *not* de-fragment the packets;
282 * Doing do would put the reassembly into double-jeopardy. On top
283 * of that someone doing a tee will probably want to get the packet
284 * in its original form.
285 */
286 ip = mtod(clone, struct ip *);
287 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
288
289 /* Reassemble packet. */
290 reass = ip_reass(clone);
291
292 /*
293 * IP header checksum fixup after reassembly and leave header
294 * in network byte order.
295 */
296 if (reass != NULL) {
297 ip = mtod(reass, struct ip *);
298 hlen = ip->ip_hl << 2;
299 ip->ip_len = htons(ip->ip_len);
300 ip->ip_off = htons(ip->ip_off);
301 ip->ip_sum = 0;
302 if (hlen == sizeof(struct ip))
303 ip->ip_sum = in_cksum_hdr(ip);
304 else
305 ip->ip_sum = in_cksum(reass, hlen);
306 clone = reass;
307 } else
308 clone = NULL;
309 } else {
310 /* Convert header to network byte order. */
311 ip->ip_len = htons(ip->ip_len);
312 ip->ip_off = htons(ip->ip_off);
313 }
314
315 /* Do the dirty job... */
316 if (clone)
317 divert_packet(clone, incoming);
318
319 teeout:
320 /*
321 * For tee we leave the divert tag attached to original packet.
322 * It will then continue rule evaluation after the tee rule.
323 */
324 if (tee)
325 return 0;
326
327 /* Packet diverted and consumed */
328 return 1;
329 #else
330 m_freem(*m);
331 return 1;
332 #endif /* ipdivert */
333 }
334
335 static int
336 ipfw_hook(void)
337 {
338 struct pfil_head *pfh_inet;
339
340 if (ipfw_pfil_hooked)
341 return EEXIST;
342
343 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
344 if (pfh_inet == NULL)
345 return ENOENT;
346
347 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
348 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
349
350 return 0;
351 }
352
353 static int
354 ipfw_unhook(void)
355 {
356 struct pfil_head *pfh_inet;
357
358 if (!ipfw_pfil_hooked)
359 return ENOENT;
360
361 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
362 if (pfh_inet == NULL)
363 return ENOENT;
364
365 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
366 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
367
368 return 0;
369 }
370
371 static int
372 ipfw_modevent(module_t mod, int type, void *unused)
373 {
374 int err = 0;
375
376 switch (type) {
377 case MOD_LOAD:
378 if (ipfw_pfil_hooked) {
379 printf("IP firewall already loaded\n");
380 err = EEXIST;
381 } else {
382 if ((err = ipfw_init()) != 0) {
383 printf("ipfw_init() error\n");
384 break;
385 }
386 if ((err = ipfw_hook()) != 0) {
387 printf("ipfw_hook() error\n");
388 break;
389 }
390 ipfw_pfil_hooked = 1;
391 }
392 break;
393
394 case MOD_UNLOAD:
395 if (ipfw_pfil_hooked) {
396 if ((err = ipfw_unhook()) > 0)
397 break;
398 ipfw_destroy();
399 ipfw_pfil_hooked = 0;
400 } else {
401 printf("IP firewall already unloaded\n");
402 }
403 break;
404
405 default:
406 return EOPNOTSUPP;
407 break;
408 }
409 return err;
410 }
411
412 static moduledata_t ipfwmod = {
413 "ipfw",
414 ipfw_modevent,
415 0
416 };
417 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
418 MODULE_VERSION(ipfw, 2);
Cache object: 7d233227fba6d606309940ce88286855
|