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