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