2 * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: src/sys/netinet/ip_fw_pfil.c,v 1.25.2.2 2008/04/25 10:26:30 oleg Exp $");
30 #if !defined(KLD_MODULE)
35 #error IPFIREWALL requires INET.
37 #endif /* KLD_MODULE */
38 #include "opt_inet6.h"
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
44 #include <sys/module.h>
45 #include <sys/kernel.h>
47 #include <sys/rwlock.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/sysctl.h>
51 #include <sys/ucred.h>
54 #include <net/route.h>
58 #include <netinet/in.h>
59 #include <netinet/in_systm.h>
60 #include <netinet/ip.h>
61 #include <netinet/ip_var.h>
62 #include <netinet/ip_fw.h>
63 #include <netinet/ip_divert.h>
64 #include <netinet/ip_dummynet.h>
66 #include <netgraph/ng_ipfw.h>
68 #include <machine/in_cksum.h>
70 VNET_DEFINE(int, fw_enable) = 1;
72 VNET_DEFINE(int, fw6_enable) = 1;
75 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
78 ip_divert_packet_t *ip_divert_ptr = NULL;
81 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
83 /* Forward declarations. */
84 static int ipfw_divert(struct mbuf **, int, int);
89 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
92 struct ip_fw_args args;
93 struct ng_ipfw_tag *ng_tag;
98 #ifdef IPFIREWALL_FORWARD
99 struct m_tag *fwd_tag;
102 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
104 bzero(&args, sizeof(args));
106 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
108 if (ng_tag != NULL) {
109 KASSERT(ng_tag->dir == NG_IPFW_IN,
110 ("ng_ipfw tag with wrong direction"));
111 args.rule = ng_tag->rule;
112 args.rule_id = ng_tag->rule_id;
113 args.chain_id = ng_tag->chain_id;
114 m_tag_delete(*m0, (struct m_tag *)ng_tag);
118 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
120 struct dn_pkt_tag *dt;
122 dt = (struct dn_pkt_tag *)(dn_tag+1);
123 args.rule = dt->rule;
124 args.rule_id = dt->rule_id;
125 args.chain_id = dt->chain_id;
127 m_tag_delete(*m0, dn_tag);
134 if (V_fw_one_pass == 0 || args.rule == NULL) {
135 ipfw = ipfw_chk(&args);
140 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
145 if (args.next_hop == NULL)
148 #ifdef IPFIREWALL_FORWARD
149 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
150 sizeof(struct sockaddr_in), M_NOWAIT);
153 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
154 m_tag_prepend(*m0, fwd_tag);
156 if (in_localip(args.next_hop->sin_addr))
157 (*m0)->m_flags |= M_FASTFWD_OURS;
160 break; /* not reached */
164 break; /* not reached */
167 if (ip_dn_io_ptr == NULL)
169 if (mtod(*m0, struct ip *)->ip_v == 4)
170 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args);
171 else if (mtod(*m0, struct ip *)->ip_v == 6)
172 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args);
175 return 0; /* packet consumed */
182 divert = ipfw_divert(m0, DIV_DIR_IN, tee);
185 return 0; /* packet consumed */
188 goto again; /* continue with packet */
194 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
195 goto again; /* continue with packet */
200 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
203 goto again; /* continue with packet */
209 KASSERT(0, ("%s: unknown retval", __func__));
218 return 0; /* not filtered */
222 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
225 struct ip_fw_args args;
226 struct ng_ipfw_tag *ng_tag;
227 struct m_tag *dn_tag;
231 #ifdef IPFIREWALL_FORWARD
232 struct m_tag *fwd_tag;
235 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
237 bzero(&args, sizeof(args));
239 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
241 if (ng_tag != NULL) {
242 KASSERT(ng_tag->dir == NG_IPFW_OUT,
243 ("ng_ipfw tag with wrong direction"));
244 args.rule = ng_tag->rule;
245 args.rule_id = ng_tag->rule_id;
246 args.chain_id = ng_tag->chain_id;
247 m_tag_delete(*m0, (struct m_tag *)ng_tag);
251 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
252 if (dn_tag != NULL) {
253 struct dn_pkt_tag *dt;
255 dt = (struct dn_pkt_tag *)(dn_tag+1);
256 args.rule = dt->rule;
257 args.rule_id = dt->rule_id;
258 args.chain_id = dt->chain_id;
260 m_tag_delete(*m0, dn_tag);
268 if (V_fw_one_pass == 0 || args.rule == NULL) {
269 ipfw = ipfw_chk(&args);
274 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
279 if (args.next_hop == NULL)
281 #ifdef IPFIREWALL_FORWARD
282 /* Overwrite existing tag. */
283 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
284 if (fwd_tag == NULL) {
285 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
286 sizeof(struct sockaddr_in), M_NOWAIT);
290 m_tag_unlink(*m0, fwd_tag);
291 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
292 m_tag_prepend(*m0, fwd_tag);
294 if (in_localip(args.next_hop->sin_addr))
295 (*m0)->m_flags |= M_FASTFWD_OURS;
298 break; /* not reached */
302 break; /* not reached */
305 if (ip_dn_io_ptr == NULL)
307 if (mtod(*m0, struct ip *)->ip_v == 4)
308 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args);
309 else if (mtod(*m0, struct ip *)->ip_v == 6)
310 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args);
313 return 0; /* packet consumed */
322 divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
325 return 0; /* packet consumed */
328 goto again; /* continue with packet */
334 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
335 goto again; /* continue with packet */
340 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
343 goto again; /* continue with packet */
349 KASSERT(0, ("%s: unknown retval", __func__));
358 return 0; /* not filtered */
362 ipfw_divert(struct mbuf **m, int incoming, int tee)
365 * ipfw_chk() has already tagged the packet with the divert tag.
366 * If tee is set, copy packet and return original.
367 * If not tee, consume packet and send it to divert socket.
369 struct mbuf *clone, *reass;
375 /* Is divert module loaded? */
376 if (ip_divert_ptr == NULL)
379 /* Cloning needed for tee? */
381 clone = m_dup(*m, M_DONTWAIT);
385 /* In case m_dup was unable to allocate mbufs. */
390 * Divert listeners can only handle non-fragmented packets.
391 * However when tee is set we will *not* de-fragment the packets;
392 * Doing do would put the reassembly into double-jeopardy. On top
393 * of that someone doing a tee will probably want to get the packet
394 * in its original form.
396 ip = mtod(clone, struct ip *);
397 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
399 /* Reassemble packet. */
400 reass = ip_reass(clone);
403 * IP header checksum fixup after reassembly and leave header
404 * in network byte order.
407 ip = mtod(reass, struct ip *);
408 hlen = ip->ip_hl << 2;
409 ip->ip_len = htons(ip->ip_len);
410 ip->ip_off = htons(ip->ip_off);
412 if (hlen == sizeof(struct ip))
413 ip->ip_sum = in_cksum_hdr(ip);
415 ip->ip_sum = in_cksum(reass, hlen);
420 /* Convert header to network byte order. */
421 ip->ip_len = htons(ip->ip_len);
422 ip->ip_off = htons(ip->ip_off);
425 /* Do the dirty job... */
426 if (clone && ip_divert_ptr != NULL)
427 ip_divert_ptr(clone, incoming);
431 * For tee we leave the divert tag attached to original packet.
432 * It will then continue rule evaluation after the tee rule.
437 /* Packet diverted and consumed */
448 struct pfil_head *pfh_inet;
450 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
451 if (pfh_inet == NULL)
454 (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
456 (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
465 struct pfil_head *pfh_inet;
467 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
468 if (pfh_inet == NULL)
471 (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
473 (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
483 struct pfil_head *pfh_inet6;
485 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
486 if (pfh_inet6 == NULL)
489 (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
491 (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
500 struct pfil_head *pfh_inet6;
502 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
503 if (pfh_inet6 == NULL)
506 (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK,
508 (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
516 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
522 if (arg1 == &VNET_NAME(fw_enable)) {
523 enable = V_fw_enable;
526 else if (arg1 == &VNET_NAME(fw6_enable)) {
527 enable = V_fw6_enable;
535 error = sysctl_handle_int(oidp, &enable, 0, req);
540 enable = (enable) ? 1 : 0;
542 if (enable == oldenable)
545 if (arg1 == &VNET_NAME(fw_enable)) {
549 error = ipfw_unhook();
552 V_fw_enable = enable;
555 else if (arg1 == &VNET_NAME(fw6_enable)) {
557 error = ipfw6_hook();
559 error = ipfw6_unhook();
562 V_fw6_enable = enable;
570 ipfw_modevent(module_t mod, int type, void *unused)
576 if ((err = ipfw_init()) != 0) {
577 printf("ipfw_init() error\n");
580 if ((err = ipfw_hook()) != 0) {
581 printf("ipfw_hook() error\n");
585 if ((err = ipfw6_hook()) != 0) {
586 printf("ipfw_hook() error\n");
593 if ((err = ipfw_unhook()) > 0)
596 if ((err = ipfw6_unhook()) > 0)
609 static moduledata_t ipfwmod = {
614 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256);
615 MODULE_VERSION(ipfw, 2);