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
29 #if !defined(KLD_MODULE)
34 #error IPFIREWALL requires INET.
36 #endif /* KLD_MODULE */
37 #include "opt_inet6.h"
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.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>
51 #include <net/route.h>
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>
63 #include <netgraph/ng_ipfw.h>
65 #include <machine/in_cksum.h>
67 static int ipfw_pfil_hooked = 0;
70 ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;
73 ip_divert_packet_t *ip_divert_ptr = NULL;
76 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
78 /* Forward declarations. */
79 static int ipfw_divert(struct mbuf **, int, int);
84 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
87 struct ip_fw_args args;
88 struct ng_ipfw_tag *ng_tag;
93 #ifdef IPFIREWALL_FORWARD
94 struct m_tag *fwd_tag;
97 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
102 bzero(&args, sizeof(args));
104 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
106 if (ng_tag != NULL) {
107 KASSERT(ng_tag->dir == NG_IPFW_IN,
108 ("ng_ipfw tag with wrong direction"));
109 args.rule = ng_tag->rule;
110 m_tag_delete(*m0, (struct m_tag *)ng_tag);
114 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
116 struct dn_pkt_tag *dt;
118 dt = (struct dn_pkt_tag *)(dn_tag+1);
119 args.rule = dt->rule;
121 m_tag_delete(*m0, dn_tag);
126 ipfw = ipfw_chk(&args);
130 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
135 if (args.next_hop == NULL)
138 #ifdef IPFIREWALL_FORWARD
139 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
140 sizeof(struct sockaddr_in), M_NOWAIT);
143 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
144 m_tag_prepend(*m0, fwd_tag);
146 if (in_localip(args.next_hop->sin_addr))
147 (*m0)->m_flags |= M_FASTFWD_OURS;
150 break; /* not reached */
154 break; /* not reached */
157 if (!DUMMYNET_LOADED)
159 if (mtod(*m0, struct ip *)->ip_v == 4)
160 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args);
161 else if (mtod(*m0, struct ip *)->ip_v == 6)
162 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args);
165 return 0; /* packet consumed */
172 divert = ipfw_divert(m0, DIV_DIR_IN, tee);
175 return 0; /* packet consumed */
178 goto again; /* continue with packet */
184 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
185 goto again; /* continue with packet */
190 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
193 KASSERT(0, ("%s: unknown retval", __func__));
202 return 0; /* not filtered */
206 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
209 struct ip_fw_args args;
210 struct ng_ipfw_tag *ng_tag;
211 struct m_tag *dn_tag;
215 #ifdef IPFIREWALL_FORWARD
216 struct m_tag *fwd_tag;
219 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
224 bzero(&args, sizeof(args));
226 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
228 if (ng_tag != NULL) {
229 KASSERT(ng_tag->dir == NG_IPFW_OUT,
230 ("ng_ipfw tag with wrong direction"));
231 args.rule = ng_tag->rule;
232 m_tag_delete(*m0, (struct m_tag *)ng_tag);
236 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
237 if (dn_tag != NULL) {
238 struct dn_pkt_tag *dt;
240 dt = (struct dn_pkt_tag *)(dn_tag+1);
241 args.rule = dt->rule;
243 m_tag_delete(*m0, dn_tag);
249 ipfw = ipfw_chk(&args);
253 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
258 if (args.next_hop == NULL)
260 #ifdef IPFIREWALL_FORWARD
261 /* Overwrite existing tag. */
262 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
263 if (fwd_tag == NULL) {
264 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
265 sizeof(struct sockaddr_in), M_NOWAIT);
269 m_tag_unlink(*m0, fwd_tag);
270 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
271 m_tag_prepend(*m0, fwd_tag);
273 if (in_localip(args.next_hop->sin_addr))
274 (*m0)->m_flags |= M_FASTFWD_OURS;
277 break; /* not reached */
281 break; /* not reached */
284 if (!DUMMYNET_LOADED)
286 if (mtod(*m0, struct ip *)->ip_v == 4)
287 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args);
288 else if (mtod(*m0, struct ip *)->ip_v == 6)
289 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args);
292 return 0; /* packet consumed */
301 divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
304 return 0; /* packet consumed */
307 goto again; /* continue with packet */
313 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
314 goto again; /* continue with packet */
319 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
322 KASSERT(0, ("%s: unknown retval", __func__));
331 return 0; /* not filtered */
335 ipfw_divert(struct mbuf **m, int incoming, int tee)
338 * ipfw_chk() has already tagged the packet with the divert tag.
339 * If tee is set, copy packet and return original.
340 * If not tee, consume packet and send it to divert socket.
342 struct mbuf *clone, *reass;
348 /* Is divert module loaded? */
349 if (ip_divert_ptr == NULL)
352 /* Cloning needed for tee? */
354 clone = m_dup(*m, M_DONTWAIT);
358 /* In case m_dup was unable to allocate mbufs. */
363 * Divert listeners can only handle non-fragmented packets.
364 * However when tee is set we will *not* de-fragment the packets;
365 * Doing do would put the reassembly into double-jeopardy. On top
366 * of that someone doing a tee will probably want to get the packet
367 * in its original form.
369 ip = mtod(clone, struct ip *);
370 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
372 /* Reassemble packet. */
373 reass = ip_reass(clone);
376 * IP header checksum fixup after reassembly and leave header
377 * in network byte order.
380 ip = mtod(reass, struct ip *);
381 hlen = ip->ip_hl << 2;
382 ip->ip_len = htons(ip->ip_len);
383 ip->ip_off = htons(ip->ip_off);
385 if (hlen == sizeof(struct ip))
386 ip->ip_sum = in_cksum_hdr(ip);
388 ip->ip_sum = in_cksum(reass, hlen);
393 /* Convert header to network byte order. */
394 ip->ip_len = htons(ip->ip_len);
395 ip->ip_off = htons(ip->ip_off);
398 /* Do the dirty job... */
399 if (clone && ip_divert_ptr != NULL)
400 ip_divert_ptr(clone, incoming);
404 * For tee we leave the divert tag attached to original packet.
405 * It will then continue rule evaluation after the tee rule.
410 /* Packet diverted and consumed */
421 struct pfil_head *pfh_inet;
423 struct pfil_head *pfh_inet6;
426 if (ipfw_pfil_hooked)
429 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
430 if (pfh_inet == NULL)
433 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
434 if (pfh_inet6 == NULL)
438 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
439 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
441 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
442 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
451 struct pfil_head *pfh_inet;
453 struct pfil_head *pfh_inet6;
456 if (!ipfw_pfil_hooked)
459 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
460 if (pfh_inet == NULL)
463 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
464 if (pfh_inet6 == NULL)
468 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
469 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
471 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
472 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
479 ipfw_modevent(module_t mod, int type, void *unused)
485 if (ipfw_pfil_hooked) {
486 printf("IP firewall already loaded\n");
489 if ((err = ipfw_init()) != 0) {
490 printf("ipfw_init() error\n");
493 if ((err = ipfw_hook()) != 0) {
494 printf("ipfw_hook() error\n");
497 ipfw_pfil_hooked = 1;
502 if (ipfw_pfil_hooked) {
503 if ((err = ipfw_unhook()) > 0)
506 ipfw_pfil_hooked = 0;
508 printf("IP firewall already unloaded\n");
519 static moduledata_t ipfwmod = {
524 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
525 MODULE_VERSION(ipfw, 2);