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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: releng/7.3/sys/netinet/ip_fw_pfil.c 188589 2009-02-13 18:31:35Z luigi $");
29
30 #if !defined(KLD_MODULE)
31 #include "opt_ipfw.h"
32 #include "opt_ipdn.h"
33 #include "opt_inet.h"
34 #ifndef INET
35 #error IPFIREWALL requires INET.
36 #endif /* INET */
37 #endif /* KLD_MODULE */
38 #include "opt_inet6.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
43 #include <sys/mbuf.h>
44 #include <sys/module.h>
45 #include <sys/kernel.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/sysctl.h>
49
50 #include <net/if.h>
51 #include <net/pfil.h>
52
53 #include <netinet/in.h>
54 #include <netinet/ip.h>
55 #include <netinet/ip_var.h>
56 #include <netinet/ip_fw.h>
57 #include <netinet/ip_divert.h>
58 #include <netinet/ip_dummynet.h>
59
60 #include <netgraph/ng_ipfw.h>
61
62 #include <machine/in_cksum.h>
63
64 int fw_enable = 1;
65 #ifdef INET6
66 int fw6_enable = 1;
67 #endif
68
69 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
70
71 /* Dummynet hooks. */
72 ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;
73
74 /* Divert hooks. */
75 ip_divert_packet_t *ip_divert_ptr = NULL;
76
77 /* ng_ipfw hooks. */
78 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
79
80 /* Forward declarations. */
81 static int ipfw_divert(struct mbuf **, int, int);
82 #define DIV_DIR_IN 1
83 #define DIV_DIR_OUT 0
84
85 int
86 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
87 struct inpcb *inp)
88 {
89 struct ip_fw_args args;
90 struct ng_ipfw_tag *ng_tag;
91 struct m_tag *dn_tag;
92 int ipfw = 0;
93 int divert;
94 int tee;
95 #ifdef IPFIREWALL_FORWARD
96 struct m_tag *fwd_tag;
97 #endif
98
99 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
100
101 bzero(&args, sizeof(args));
102
103 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
104 NULL);
105 if (ng_tag != NULL) {
106 KASSERT(ng_tag->dir == NG_IPFW_IN,
107 ("ng_ipfw tag with wrong direction"));
108 args.rule = ng_tag->rule;
109 m_tag_delete(*m0, (struct m_tag *)ng_tag);
110 }
111
112 again:
113 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
114 if (dn_tag != NULL){
115 struct dn_pkt_tag *dt;
116
117 dt = (struct dn_pkt_tag *)(dn_tag+1);
118 args.rule = dt->rule;
119
120 m_tag_delete(*m0, dn_tag);
121 }
122
123 args.m = *m0;
124 args.inp = inp;
125 ipfw = ipfw_chk(&args);
126 *m0 = args.m;
127 tee = 0;
128
129 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
130 __func__));
131
132 switch (ipfw) {
133 case IP_FW_PASS:
134 if (args.next_hop == NULL)
135 goto pass;
136
137 #ifdef IPFIREWALL_FORWARD
138 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
139 sizeof(struct sockaddr_in), M_NOWAIT);
140 if (fwd_tag == NULL)
141 goto drop;
142 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
143 m_tag_prepend(*m0, fwd_tag);
144
145 if (in_localip(args.next_hop->sin_addr))
146 (*m0)->m_flags |= M_FASTFWD_OURS;
147 goto pass;
148 #endif
149 break; /* not reached */
150
151 case IP_FW_DENY:
152 goto drop;
153 break; /* not reached */
154
155 case IP_FW_DUMMYNET:
156 if (!DUMMYNET_LOADED)
157 goto drop;
158 if (mtod(*m0, struct ip *)->ip_v == 4)
159 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args);
160 else if (mtod(*m0, struct ip *)->ip_v == 6)
161 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args);
162 if (*m0 != NULL)
163 goto again;
164 return 0; /* packet consumed */
165
166 case IP_FW_TEE:
167 tee = 1;
168 /* fall through */
169
170 case IP_FW_DIVERT:
171 divert = ipfw_divert(m0, DIV_DIR_IN, tee);
172 if (divert) {
173 *m0 = NULL;
174 return 0; /* packet consumed */
175 } else {
176 args.rule = NULL;
177 goto again; /* continue with packet */
178 }
179
180 case IP_FW_NGTEE:
181 if (!NG_IPFW_LOADED)
182 goto drop;
183 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
184 goto again; /* continue with packet */
185
186 case IP_FW_NETGRAPH:
187 if (!NG_IPFW_LOADED)
188 goto drop;
189 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
190
191 case IP_FW_NAT:
192 goto again; /* continue with packet */
193
194 default:
195 KASSERT(0, ("%s: unknown retval", __func__));
196 }
197
198 drop:
199 if (*m0)
200 m_freem(*m0);
201 *m0 = NULL;
202 return (EACCES);
203 pass:
204 return 0; /* not filtered */
205 }
206
207 int
208 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
209 struct inpcb *inp)
210 {
211 struct ip_fw_args args;
212 struct ng_ipfw_tag *ng_tag;
213 struct m_tag *dn_tag;
214 int ipfw = 0;
215 int divert;
216 int tee;
217 #ifdef IPFIREWALL_FORWARD
218 struct m_tag *fwd_tag;
219 #endif
220
221 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
222
223 bzero(&args, sizeof(args));
224
225 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
226 NULL);
227 if (ng_tag != NULL) {
228 KASSERT(ng_tag->dir == NG_IPFW_OUT,
229 ("ng_ipfw tag with wrong direction"));
230 args.rule = ng_tag->rule;
231 m_tag_delete(*m0, (struct m_tag *)ng_tag);
232 }
233
234 again:
235 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
236 if (dn_tag != NULL) {
237 struct dn_pkt_tag *dt;
238
239 dt = (struct dn_pkt_tag *)(dn_tag+1);
240 args.rule = dt->rule;
241
242 m_tag_delete(*m0, dn_tag);
243 }
244
245 args.m = *m0;
246 args.oif = ifp;
247 args.inp = inp;
248 ipfw = ipfw_chk(&args);
249 *m0 = args.m;
250 tee = 0;
251
252 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
253 __func__));
254
255 switch (ipfw) {
256 case IP_FW_PASS:
257 if (args.next_hop == NULL)
258 goto pass;
259 #ifdef IPFIREWALL_FORWARD
260 /* Overwrite existing tag. */
261 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
262 if (fwd_tag == NULL) {
263 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
264 sizeof(struct sockaddr_in), M_NOWAIT);
265 if (fwd_tag == NULL)
266 goto drop;
267 } else
268 m_tag_unlink(*m0, fwd_tag);
269 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
270 m_tag_prepend(*m0, fwd_tag);
271
272 if (in_localip(args.next_hop->sin_addr))
273 (*m0)->m_flags |= M_FASTFWD_OURS;
274 goto pass;
275 #endif
276 break; /* not reached */
277
278 case IP_FW_DENY:
279 goto drop;
280 break; /* not reached */
281
282 case IP_FW_DUMMYNET:
283 if (!DUMMYNET_LOADED)
284 break;
285 if (mtod(*m0, struct ip *)->ip_v == 4)
286 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args);
287 else if (mtod(*m0, struct ip *)->ip_v == 6)
288 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args);
289 if (*m0 != NULL)
290 goto again;
291 return 0; /* packet consumed */
292
293 break;
294
295 case IP_FW_TEE:
296 tee = 1;
297 /* fall through */
298
299 case IP_FW_DIVERT:
300 divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
301 if (divert) {
302 *m0 = NULL;
303 return 0; /* packet consumed */
304 } else {
305 args.rule = NULL;
306 goto again; /* continue with packet */
307 }
308
309 case IP_FW_NGTEE:
310 if (!NG_IPFW_LOADED)
311 goto drop;
312 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
313 goto again; /* continue with packet */
314
315 case IP_FW_NETGRAPH:
316 if (!NG_IPFW_LOADED)
317 goto drop;
318 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
319
320 case IP_FW_NAT:
321 goto again; /* continue with packet */
322
323 default:
324 KASSERT(0, ("%s: unknown retval", __func__));
325 }
326
327 drop:
328 if (*m0)
329 m_freem(*m0);
330 *m0 = NULL;
331 return (EACCES);
332 pass:
333 return 0; /* not filtered */
334 }
335
336 static int
337 ipfw_divert(struct mbuf **m, int incoming, int tee)
338 {
339 /*
340 * ipfw_chk() has already tagged the packet with the divert tag.
341 * If tee is set, copy packet and return original.
342 * If not tee, consume packet and send it to divert socket.
343 */
344 struct mbuf *clone, *reass;
345 struct ip *ip;
346 int hlen;
347
348 reass = NULL;
349
350 /* Is divert module loaded? */
351 if (ip_divert_ptr == NULL)
352 goto nodivert;
353
354 /* Cloning needed for tee? */
355 if (tee)
356 clone = m_dup(*m, M_DONTWAIT);
357 else
358 clone = *m;
359
360 /* In case m_dup was unable to allocate mbufs. */
361 if (clone == NULL)
362 goto teeout;
363
364 /*
365 * Divert listeners can only handle non-fragmented packets.
366 * However when tee is set we will *not* de-fragment the packets;
367 * Doing do would put the reassembly into double-jeopardy. On top
368 * of that someone doing a tee will probably want to get the packet
369 * in its original form.
370 */
371 ip = mtod(clone, struct ip *);
372 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
373
374 /* Reassemble packet. */
375 reass = ip_reass(clone);
376
377 /*
378 * IP header checksum fixup after reassembly and leave header
379 * in network byte order.
380 */
381 if (reass != NULL) {
382 ip = mtod(reass, struct ip *);
383 hlen = ip->ip_hl << 2;
384 ip->ip_len = htons(ip->ip_len);
385 ip->ip_off = htons(ip->ip_off);
386 ip->ip_sum = 0;
387 if (hlen == sizeof(struct ip))
388 ip->ip_sum = in_cksum_hdr(ip);
389 else
390 ip->ip_sum = in_cksum(reass, hlen);
391 clone = reass;
392 } else
393 clone = NULL;
394 } else {
395 /* Convert header to network byte order. */
396 ip->ip_len = htons(ip->ip_len);
397 ip->ip_off = htons(ip->ip_off);
398 }
399
400 /* Do the dirty job... */
401 if (clone && ip_divert_ptr != NULL)
402 ip_divert_ptr(clone, incoming);
403
404 teeout:
405 /*
406 * For tee we leave the divert tag attached to original packet.
407 * It will then continue rule evaluation after the tee rule.
408 */
409 if (tee)
410 return 0;
411
412 /* Packet diverted and consumed */
413 return 1;
414
415 nodivert:
416 m_freem(*m);
417 return 1;
418 }
419
420 static int
421 ipfw_hook(void)
422 {
423 struct pfil_head *pfh_inet;
424
425 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
426 if (pfh_inet == NULL)
427 return ENOENT;
428
429 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
430 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
431
432 return 0;
433 }
434
435 static int
436 ipfw_unhook(void)
437 {
438 struct pfil_head *pfh_inet;
439
440 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
441 if (pfh_inet == NULL)
442 return ENOENT;
443
444 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
445 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
446
447 return 0;
448 }
449
450 #ifdef INET6
451 static int
452 ipfw6_hook(void)
453 {
454 struct pfil_head *pfh_inet6;
455
456 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
457 if (pfh_inet6 == NULL)
458 return ENOENT;
459
460 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
461 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
462
463 return 0;
464 }
465
466 static int
467 ipfw6_unhook(void)
468 {
469 struct pfil_head *pfh_inet6;
470
471 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
472 if (pfh_inet6 == NULL)
473 return ENOENT;
474
475 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
476 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
477
478 return 0;
479 }
480 #endif /* INET6 */
481
482 int
483 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
484 {
485 int enable = *(int *)arg1;
486 int error;
487
488 error = sysctl_handle_int(oidp, &enable, 0, req);
489 if (error)
490 return (error);
491
492 enable = (enable) ? 1 : 0;
493
494 if (enable == *(int *)arg1)
495 return (0);
496
497 if (arg1 == &fw_enable) {
498 if (enable)
499 error = ipfw_hook();
500 else
501 error = ipfw_unhook();
502 }
503 #ifdef INET6
504 if (arg1 == &fw6_enable) {
505 if (enable)
506 error = ipfw6_hook();
507 else
508 error = ipfw6_unhook();
509 }
510 #endif
511
512 if (error)
513 return (error);
514
515 *(int *)arg1 = enable;
516
517 return (0);
518 }
519
520 static int
521 ipfw_modevent(module_t mod, int type, void *unused)
522 {
523 int err = 0;
524
525 switch (type) {
526 case MOD_LOAD:
527 if ((err = ipfw_init()) != 0) {
528 printf("ipfw_init() error\n");
529 break;
530 }
531 if ((err = ipfw_hook()) != 0) {
532 printf("ipfw_hook() error\n");
533 break;
534 }
535 #ifdef INET6
536 if ((err = ipfw6_hook()) != 0) {
537 printf("ipfw_hook() error\n");
538 break;
539 }
540 #endif
541 break;
542
543 case MOD_UNLOAD:
544 if ((err = ipfw_unhook()) > 0)
545 break;
546 #ifdef INET6
547 if ((err = ipfw6_unhook()) > 0)
548 break;
549 #endif
550 ipfw_destroy();
551 break;
552
553 default:
554 return EOPNOTSUPP;
555 break;
556 }
557 return err;
558 }
559
560 static moduledata_t ipfwmod = {
561 "ipfw",
562 ipfw_modevent,
563 0
564 };
565 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256);
566 MODULE_VERSION(ipfw, 2);
Cache object: 2b05b45669beb1bddac88899b524e9cf
|