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