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