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/5.3/sys/netinet/ip_fw_pfil.c 136588 2004-10-16 08:43:07Z cvs2svn $
27 */
28
29 #if !defined(KLD_MODULE)
30 #include "opt_ipfw.h"
31 #include "opt_ipdn.h"
32 #include "opt_ipdivert.h"
33 #include "opt_inet.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 <machine/in_cksum.h>
64
65 static int ipfw_pfil_hooked = 0;
66
67 /* Dummynet hooks. */
68 ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;
69
70 #define DIV_DIR_IN 1
71 #define DIV_DIR_OUT 0
72
73 static int ipfw_divert(struct mbuf **, int, int);
74
75 int
76 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
77 struct inpcb *inp)
78 {
79 struct ip_fw_args args;
80 struct m_tag *dn_tag;
81 int ipfw = 0;
82 int divert;
83 #ifdef IPFIREWALL_FORWARD
84 struct m_tag *fwd_tag;
85 #endif
86
87 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
88
89 if (!fw_enable)
90 goto pass;
91
92 bzero(&args, sizeof(args));
93
94 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
95 if (dn_tag != NULL){
96 struct dn_pkt_tag *dt;
97
98 dt = (struct dn_pkt_tag *)(dn_tag+1);
99 args.rule = dt->rule;
100
101 m_tag_delete(*m0, dn_tag);
102 }
103
104 again:
105 args.m = *m0;
106 args.inp = inp;
107 ipfw = ipfw_chk(&args);
108 *m0 = args.m;
109
110 if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)
111 goto drop;
112
113 if (ipfw == 0 && args.next_hop == NULL)
114 goto pass;
115
116 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
117 ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args);
118 *m0 = NULL;
119 return 0; /* packet consumed */
120 }
121
122 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
123 if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
124 divert = ipfw_divert(m0, DIV_DIR_IN, 1);
125 else
126 divert = ipfw_divert(m0, DIV_DIR_IN, 0);
127
128 /* tee should continue again with the firewall. */
129 if (divert) {
130 *m0 = NULL;
131 return 0; /* packet consumed */
132 } else
133 goto again; /* continue with packet */
134 }
135
136 #ifdef IPFIREWALL_FORWARD
137 if (ipfw == 0 && args.next_hop != NULL) {
138 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
139 sizeof(struct sockaddr_in), M_NOWAIT);
140 if (fwd_tag == NULL)
141 goto drop;
142 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
143 m_tag_prepend(*m0, fwd_tag);
144
145 if (in_localip(args.next_hop->sin_addr))
146 (*m0)->m_flags |= M_FASTFWD_OURS;
147 goto pass;
148 }
149 #endif
150
151 drop:
152 if (*m0)
153 m_freem(*m0);
154 *m0 = NULL;
155 return (EACCES);
156 pass:
157 return 0; /* not filtered */
158 }
159
160 int
161 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
162 struct inpcb *inp)
163 {
164 struct ip_fw_args args;
165 struct m_tag *dn_tag;
166 int ipfw = 0;
167 int divert;
168 #ifdef IPFIREWALL_FORWARD
169 struct m_tag *fwd_tag;
170 #endif
171
172 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
173
174 if (!fw_enable)
175 goto pass;
176
177 bzero(&args, sizeof(args));
178
179 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
180 if (dn_tag != NULL) {
181 struct dn_pkt_tag *dt;
182
183 dt = (struct dn_pkt_tag *)(dn_tag+1);
184 args.rule = dt->rule;
185
186 m_tag_delete(*m0, dn_tag);
187 }
188
189 again:
190 args.m = *m0;
191 args.oif = ifp;
192 args.inp = inp;
193 ipfw = ipfw_chk(&args);
194 *m0 = args.m;
195
196 if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)
197 goto drop;
198
199 if (ipfw == 0 && args.next_hop == NULL)
200 goto pass;
201
202 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
203 ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args);
204 *m0 = NULL;
205 return 0; /* packet consumed */
206 }
207
208 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
209 if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
210 divert = ipfw_divert(m0, DIV_DIR_OUT, 1);
211 else
212 divert = ipfw_divert(m0, DIV_DIR_OUT, 0);
213
214 if (divert) {
215 *m0 = NULL;
216 return 0; /* packet consumed */
217 } else
218 goto again; /* continue with packet */
219 }
220
221 #ifdef IPFIREWALL_FORWARD
222 if (ipfw == 0 && args.next_hop != NULL) {
223 /* Overwrite existing tag. */
224 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
225 if (fwd_tag == NULL) {
226 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
227 sizeof(struct sockaddr_in), M_NOWAIT);
228 if (fwd_tag == NULL)
229 goto drop;
230 } else
231 m_tag_unlink(*m0, fwd_tag);
232 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
233 m_tag_prepend(*m0, fwd_tag);
234
235 if (in_localip(args.next_hop->sin_addr))
236 (*m0)->m_flags |= M_FASTFWD_OURS;
237 goto pass;
238 }
239 #endif
240
241 drop:
242 if (*m0)
243 m_freem(*m0);
244 *m0 = NULL;
245 return (EACCES);
246 pass:
247 return 0; /* not filtered */
248 }
249
250 static int
251 ipfw_divert(struct mbuf **m, int incoming, int tee)
252 {
253 /*
254 * ipfw_chk() has already tagged the packet with the divert tag.
255 * If tee is set, copy packet and return original.
256 * If not tee, consume packet and send it to divert socket.
257 */
258 #ifdef IPDIVERT
259 struct mbuf *clone, *reass;
260 struct ip *ip;
261 int hlen;
262
263 reass = NULL;
264
265 /* Cloning needed for tee? */
266 if (tee)
267 clone = m_dup(*m, M_DONTWAIT);
268 else
269 clone = *m;
270
271 /* In case m_dup was unable to allocate mbufs. */
272 if (clone == NULL)
273 goto teeout;
274
275 /*
276 * Divert listeners can only handle non-fragmented packets.
277 * However when tee is set we will *not* de-fragment the packets;
278 * Doing do would put the reassembly into double-jeopardy. On top
279 * of that someone doing a tee will probably want to get the packet
280 * in its original form.
281 */
282 ip = mtod(clone, struct ip *);
283 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
284
285 /* Reassemble packet. */
286 reass = ip_reass(clone);
287
288 /*
289 * IP header checksum fixup after reassembly and leave header
290 * in network byte order.
291 */
292 if (reass != NULL) {
293 ip = mtod(reass, struct ip *);
294 hlen = ip->ip_hl << 2;
295 ip->ip_len = htons(ip->ip_len);
296 ip->ip_off = htons(ip->ip_off);
297 ip->ip_sum = 0;
298 if (hlen == sizeof(struct ip))
299 ip->ip_sum = in_cksum_hdr(ip);
300 else
301 ip->ip_sum = in_cksum(reass, hlen);
302 clone = reass;
303 } else
304 clone = NULL;
305 } else {
306 /* Convert header to network byte order. */
307 ip->ip_len = htons(ip->ip_len);
308 ip->ip_off = htons(ip->ip_off);
309 }
310
311 /* Do the dirty job... */
312 if (clone)
313 divert_packet(clone, incoming);
314
315 teeout:
316 /*
317 * For tee we leave the divert tag attached to original packet.
318 * It will then continue rule evaluation after the tee rule.
319 */
320 if (tee)
321 return 0;
322
323 /* Packet diverted and consumed */
324 return 1;
325 #else
326 m_freem(*m);
327 return 1;
328 #endif /* ipdivert */
329 }
330
331 static int
332 ipfw_hook(void)
333 {
334 struct pfil_head *pfh_inet;
335
336 if (ipfw_pfil_hooked)
337 return EEXIST;
338
339 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
340 if (pfh_inet == NULL)
341 return ENOENT;
342
343 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
344 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
345
346 return 0;
347 }
348
349 static int
350 ipfw_unhook(void)
351 {
352 struct pfil_head *pfh_inet;
353
354 if (!ipfw_pfil_hooked)
355 return ENOENT;
356
357 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
358 if (pfh_inet == NULL)
359 return ENOENT;
360
361 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
362 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
363
364 return 0;
365 }
366
367 static int
368 ipfw_modevent(module_t mod, int type, void *unused)
369 {
370 int err = 0;
371
372 switch (type) {
373 case MOD_LOAD:
374 if (ipfw_pfil_hooked) {
375 printf("IP firewall already loaded\n");
376 err = EEXIST;
377 } else {
378 if ((err = ipfw_init()) != 0) {
379 printf("ipfw_init() error\n");
380 break;
381 }
382 if ((err = ipfw_hook()) != 0) {
383 printf("ipfw_hook() error\n");
384 break;
385 }
386 ipfw_pfil_hooked = 1;
387 }
388 break;
389
390 case MOD_UNLOAD:
391 if (ipfw_pfil_hooked) {
392 if ((err = ipfw_unhook()) > 0)
393 break;
394 ipfw_destroy();
395 ipfw_pfil_hooked = 0;
396 } else {
397 printf("IP firewall already unloaded\n");
398 }
399 break;
400
401 default:
402 return EOPNOTSUPP;
403 break;
404 }
405 return err;
406 }
407
408 static moduledata_t ipfwmod = {
409 "ipfw",
410 ipfw_modevent,
411 0
412 };
413 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
414 MODULE_VERSION(ipfw, 2);
Cache object: a863d459ed86b3d65c81027769f609c7
|