ip_fw_pfil.c revision 135154
1133920Sandre/*
2133920Sandre * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
3133920Sandre * All rights reserved.
4133920Sandre *
5133920Sandre * Redistribution and use in source and binary forms, with or without
6133920Sandre * modification, are permitted provided that the following conditions
7133920Sandre * are met:
8133920Sandre * 1. Redistributions of source code must retain the above copyright
9133920Sandre *    notice, this list of conditions and the following disclaimer.
10133920Sandre * 2. Redistributions in binary form must reproduce the above copyright
11133920Sandre *    notice, this list of conditions and the following disclaimer in the
12133920Sandre *    documentation and/or other materials provided with the distribution.
13133920Sandre *
14133920Sandre * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15133920Sandre * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16133920Sandre * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17133920Sandre * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18133920Sandre * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19133920Sandre * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20133920Sandre * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21133920Sandre * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22133920Sandre * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23133920Sandre * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24133920Sandre * SUCH DAMAGE.
25133920Sandre *
26133920Sandre * $FreeBSD: head/sys/netinet/ip_fw_pfil.c 135154 2004-09-13 16:46:05Z andre $
27133920Sandre */
28133920Sandre
29134346Sru#if !defined(KLD_MODULE)
30133920Sandre#include "opt_ipfw.h"
31133920Sandre#include "opt_ipdn.h"
32133920Sandre#include "opt_ipdivert.h"
33133920Sandre#include "opt_inet.h"
34133920Sandre#ifndef INET
35133920Sandre#error IPFIREWALL requires INET.
36133920Sandre#endif /* INET */
37134383Sandre#endif /* KLD_MODULE */
38133920Sandre
39133920Sandre#include <sys/param.h>
40133920Sandre#include <sys/systm.h>
41133920Sandre#include <sys/malloc.h>
42133920Sandre#include <sys/mbuf.h>
43133920Sandre#include <sys/module.h>
44133920Sandre#include <sys/kernel.h>
45133920Sandre#include <sys/socket.h>
46133920Sandre#include <sys/socketvar.h>
47133920Sandre#include <sys/sysctl.h>
48133920Sandre#include <sys/ucred.h>
49133920Sandre
50133920Sandre#include <net/if.h>
51133920Sandre#include <net/route.h>
52133920Sandre#include <net/pfil.h>
53133920Sandre
54133920Sandre#include <netinet/in.h>
55133920Sandre#include <netinet/in_systm.h>
56133920Sandre#include <netinet/in_var.h>
57133920Sandre#include <netinet/ip.h>
58133920Sandre#include <netinet/ip_var.h>
59133920Sandre#include <netinet/ip_fw.h>
60133920Sandre#include <netinet/ip_divert.h>
61133920Sandre#include <netinet/ip_dummynet.h>
62133920Sandre
63133920Sandre#include <machine/in_cksum.h>
64133920Sandre
65133920Sandrestatic	int ipfw_pfil_hooked = 0;
66133920Sandre
67133920Sandre/* Dummynet hooks. */
68133920Sandreip_dn_ruledel_t	*ip_dn_ruledel_ptr = NULL;
69133920Sandre
70133920Sandre#define	DIV_DIR_IN	1
71133920Sandre#define	DIV_DIR_OUT	0
72133920Sandre
73133920Sandrestatic int	ipfw_divert(struct mbuf **, int, int);
74133920Sandre
75133920Sandreint
76133920Sandreipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir)
77133920Sandre{
78133920Sandre	struct ip_fw_args args;
79133920Sandre	struct m_tag *dn_tag;
80133920Sandre	int ipfw = 0;
81133920Sandre	int divert;
82133920Sandre#ifdef IPFIREWALL_FORWARD
83133920Sandre	struct m_tag *fwd_tag;
84133920Sandre#endif
85133920Sandre
86133920Sandre	KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
87133920Sandre
88134022Sandre	if (!fw_enable)
89134022Sandre		goto pass;
90134022Sandre
91133920Sandre	bzero(&args, sizeof(args));
92133920Sandre
93133920Sandre	dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
94133920Sandre	if (dn_tag != NULL){
95133920Sandre		struct dn_pkt_tag *dt;
96133920Sandre
97133920Sandre		dt = (struct dn_pkt_tag *)(dn_tag+1);
98133920Sandre		args.rule = dt->rule;
99133920Sandre
100133920Sandre		m_tag_delete(*m0, dn_tag);
101133920Sandre	}
102133920Sandre
103135154Sandreagain:
104133920Sandre	args.m = *m0;
105133920Sandre	ipfw = ipfw_chk(&args);
106133920Sandre	*m0 = args.m;
107133920Sandre
108133920Sandre	if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)
109133920Sandre		goto drop;
110133920Sandre
111133920Sandre	if (ipfw == 0 && args.next_hop == NULL)
112133920Sandre		goto pass;
113133920Sandre
114133920Sandre	if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
115133920Sandre		ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args);
116133920Sandre		*m0 = NULL;
117133920Sandre		return 0;		/* packet consumed */
118133920Sandre	}
119133920Sandre
120133920Sandre	if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
121133920Sandre		if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
122133920Sandre			divert = ipfw_divert(m0, DIV_DIR_IN, 1);
123133920Sandre		else
124133920Sandre			divert = ipfw_divert(m0, DIV_DIR_IN, 0);
125133920Sandre
126133920Sandre		/* tee should continue again with the firewall. */
127133920Sandre		if (divert) {
128133920Sandre			*m0 = NULL;
129133920Sandre			return 0;	/* packet consumed */
130133920Sandre		} else
131135154Sandre			goto again;	/* continue with packet */
132133920Sandre	}
133133920Sandre
134133920Sandre#ifdef IPFIREWALL_FORWARD
135133920Sandre	if (ipfw == 0 && args.next_hop != NULL) {
136133920Sandre		fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
137133920Sandre				sizeof(struct sockaddr_in), M_NOWAIT);
138133920Sandre		if (fwd_tag == NULL)
139133920Sandre			goto drop;
140133920Sandre		bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
141133920Sandre		m_tag_prepend(*m0, fwd_tag);
142133920Sandre
143133920Sandre		if (in_localip(args.next_hop->sin_addr))
144133920Sandre			(*m0)->m_flags |= M_FASTFWD_OURS;
145133920Sandre		goto pass;
146133920Sandre	}
147133920Sandre#endif
148133920Sandre
149133920Sandredrop:
150133920Sandre	if (*m0)
151133920Sandre		m_freem(*m0);
152133920Sandre	*m0 = NULL;
153133920Sandre	return (EACCES);
154133920Sandrepass:
155133920Sandre	return 0;	/* not filtered */
156133920Sandre}
157133920Sandre
158133920Sandreint
159133920Sandreipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir)
160133920Sandre{
161133920Sandre	struct ip_fw_args args;
162133920Sandre	struct m_tag *dn_tag;
163133920Sandre	int ipfw = 0;
164133920Sandre	int divert;
165133920Sandre#ifdef IPFIREWALL_FORWARD
166133920Sandre	struct m_tag *fwd_tag;
167133920Sandre#endif
168133920Sandre
169133920Sandre	KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
170133920Sandre
171134022Sandre	if (!fw_enable)
172134022Sandre		goto pass;
173134022Sandre
174133920Sandre	bzero(&args, sizeof(args));
175133920Sandre
176133920Sandre	dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
177133920Sandre	if (dn_tag != NULL) {
178133920Sandre		struct dn_pkt_tag *dt;
179133920Sandre
180133920Sandre		dt = (struct dn_pkt_tag *)(dn_tag+1);
181133920Sandre		args.rule = dt->rule;
182133920Sandre
183133920Sandre		m_tag_delete(*m0, dn_tag);
184133920Sandre	}
185133920Sandre
186135154Sandreagain:
187133920Sandre	args.m = *m0;
188133920Sandre	args.oif = ifp;
189133920Sandre	ipfw = ipfw_chk(&args);
190133920Sandre	*m0 = args.m;
191133920Sandre
192133920Sandre	if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL)
193133920Sandre		goto drop;
194133920Sandre
195133920Sandre	if (ipfw == 0 && args.next_hop == NULL)
196133920Sandre		goto pass;
197133920Sandre
198133920Sandre	if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
199133920Sandre		ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args);
200133920Sandre		*m0 = NULL;
201133920Sandre		return 0;		/* packet consumed */
202133920Sandre	}
203133920Sandre
204133920Sandre	if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
205133920Sandre		if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
206133920Sandre			divert = ipfw_divert(m0, DIV_DIR_OUT, 1);
207133920Sandre		else
208133920Sandre			divert = ipfw_divert(m0, DIV_DIR_OUT, 0);
209133920Sandre
210133920Sandre		if (divert) {
211133920Sandre			*m0 = NULL;
212133920Sandre			return 0;	/* packet consumed */
213133920Sandre		} else
214135154Sandre			goto again;	/* continue with packet */
215133920Sandre        }
216133920Sandre
217133920Sandre#ifdef IPFIREWALL_FORWARD
218133920Sandre	if (ipfw == 0 && args.next_hop != NULL) {
219133920Sandre		/* Overwrite existing tag. */
220133920Sandre		fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
221133920Sandre		if (fwd_tag == NULL)
222133920Sandre			fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
223133920Sandre				sizeof(struct sockaddr_in), M_NOWAIT);
224133920Sandre		if (fwd_tag == NULL)
225133920Sandre			goto drop;
226133920Sandre		bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
227133920Sandre		m_tag_prepend(*m0, fwd_tag);
228133920Sandre
229133920Sandre		if (in_localip(args.next_hop->sin_addr))
230133920Sandre			(*m0)->m_flags |= M_FASTFWD_OURS;
231133920Sandre		goto pass;
232133920Sandre	}
233133920Sandre#endif
234133920Sandre
235133920Sandredrop:
236133920Sandre	if (*m0)
237133920Sandre		m_freem(*m0);
238133920Sandre	*m0 = NULL;
239133920Sandre	return (EACCES);
240133920Sandrepass:
241133920Sandre	return 0;	/* not filtered */
242133920Sandre}
243133920Sandre
244133920Sandrestatic int
245133920Sandreipfw_divert(struct mbuf **m, int incoming, int tee)
246133920Sandre{
247133920Sandre	/*
248135154Sandre	 * ipfw_chk() has already tagged the packet with the divert tag.
249133920Sandre	 * If tee is set, copy packet and return original.
250133920Sandre	 * If not tee, consume packet and send it to divert socket.
251133920Sandre	 */
252133920Sandre#ifdef IPDIVERT
253133920Sandre	struct mbuf *clone, *reass;
254133920Sandre	struct ip *ip;
255133920Sandre	int hlen;
256133920Sandre
257133920Sandre	reass = NULL;
258133920Sandre
259133920Sandre	/* Cloning needed for tee? */
260133920Sandre	if (tee)
261133920Sandre		clone = m_dup(*m, M_DONTWAIT);
262133920Sandre	else
263133920Sandre		clone = *m;
264133920Sandre
265133920Sandre	/* In case m_dup was unable to allocate mbufs. */
266133920Sandre	if (clone == NULL)
267133920Sandre		goto teeout;
268133920Sandre
269133920Sandre	/*
270133920Sandre	 * Divert listeners can only handle non-fragmented packets.
271133920Sandre	 * However when tee is set we will *not* de-fragment the packets;
272133920Sandre	 * Doing do would put the reassembly into double-jeopardy.  On top
273133920Sandre	 * of that someone doing a tee will probably want to get the packet
274133920Sandre	 * in its original form.
275133920Sandre	 */
276133920Sandre	ip = mtod(clone, struct ip *);
277133920Sandre	if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
278133920Sandre
279133920Sandre		/* Reassemble packet. */
280133920Sandre		reass = ip_reass(clone);
281133920Sandre
282133920Sandre		/*
283133920Sandre		 * IP header checksum fixup after reassembly and leave header
284133920Sandre		 * in network byte order.
285133920Sandre		 */
286133920Sandre		if (reass != NULL) {
287133920Sandre			ip = mtod(reass, struct ip *);
288133920Sandre			hlen = ip->ip_hl << 2;
289133920Sandre			ip->ip_len = htons(ip->ip_len);
290133920Sandre			ip->ip_off = htons(ip->ip_off);
291133920Sandre			ip->ip_sum = 0;
292133920Sandre			if (hlen == sizeof(struct ip))
293133920Sandre				ip->ip_sum = in_cksum_hdr(ip);
294133920Sandre			else
295133920Sandre				ip->ip_sum = in_cksum(reass, hlen);
296133920Sandre			clone = reass;
297133920Sandre		} else
298133920Sandre			clone = NULL;
299133920Sandre	} else {
300133920Sandre		/* Convert header to network byte order. */
301133920Sandre		ip->ip_len = htons(ip->ip_len);
302133920Sandre		ip->ip_off = htons(ip->ip_off);
303133920Sandre	}
304133920Sandre
305133920Sandre	/* Do the dirty job... */
306133920Sandre	if (clone)
307133920Sandre		divert_packet(clone, incoming);
308133920Sandre
309133920Sandreteeout:
310135154Sandre	/*
311135154Sandre	 * For tee we leave the divert tag attached to original packet.
312135154Sandre	 * It will then continue rule evaluation after the tee rule.
313135154Sandre	 */
314135154Sandre	if (tee)
315135154Sandre		return 0;
316133920Sandre
317133920Sandre	/* Packet diverted and consumed */
318133920Sandre	return 1;
319133920Sandre#else
320133920Sandre	m_freem(*m);
321133920Sandre	return 1;
322133920Sandre#endif	/* ipdivert */
323133920Sandre}
324133920Sandre
325133920Sandrestatic int
326133920Sandreipfw_hook(void)
327133920Sandre{
328133920Sandre	struct pfil_head *pfh_inet;
329133920Sandre
330133920Sandre	if (ipfw_pfil_hooked)
331133920Sandre		return EEXIST;
332133920Sandre
333133920Sandre	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
334133920Sandre	if (pfh_inet == NULL)
335133920Sandre		return ENOENT;
336133920Sandre
337133920Sandre	pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
338133920Sandre	pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
339133920Sandre
340133920Sandre	return 0;
341133920Sandre}
342133920Sandre
343133920Sandrestatic int
344133920Sandreipfw_unhook(void)
345133920Sandre{
346133920Sandre	struct pfil_head *pfh_inet;
347133920Sandre
348133920Sandre	if (!ipfw_pfil_hooked)
349133920Sandre		return ENOENT;
350133920Sandre
351133920Sandre	pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
352133920Sandre	if (pfh_inet == NULL)
353133920Sandre		return ENOENT;
354133920Sandre
355133920Sandre	pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
356133920Sandre	pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
357133920Sandre
358133920Sandre	return 0;
359133920Sandre}
360133920Sandre
361133920Sandrestatic int
362133920Sandreipfw_modevent(module_t mod, int type, void *unused)
363133920Sandre{
364133920Sandre	int err = 0;
365133920Sandre
366133920Sandre	switch (type) {
367133920Sandre	case MOD_LOAD:
368133920Sandre		if (ipfw_pfil_hooked) {
369133920Sandre			printf("IP firewall already loaded\n");
370133920Sandre			err = EEXIST;
371133920Sandre		} else {
372133920Sandre			if ((err = ipfw_init()) != 0) {
373133920Sandre				printf("ipfw_init() error\n");
374133920Sandre				break;
375133920Sandre			}
376133920Sandre			if ((err = ipfw_hook()) != 0) {
377133920Sandre				printf("ipfw_hook() error\n");
378133920Sandre				break;
379133920Sandre			}
380133920Sandre			ipfw_pfil_hooked = 1;
381133920Sandre		}
382133920Sandre		break;
383133920Sandre
384133920Sandre	case MOD_UNLOAD:
385133920Sandre		if (ipfw_pfil_hooked) {
386134055Sandre			if ((err = ipfw_unhook()) > 0)
387133920Sandre				break;
388133920Sandre			ipfw_destroy();
389133920Sandre			ipfw_pfil_hooked = 0;
390133920Sandre		} else {
391133920Sandre			printf("IP firewall already unloaded\n");
392133920Sandre		}
393133920Sandre		break;
394133920Sandre
395133920Sandre	default:
396133920Sandre		return EOPNOTSUPP;
397133920Sandre		break;
398133920Sandre	}
399133920Sandre	return err;
400133920Sandre}
401133920Sandre
402133920Sandrestatic moduledata_t ipfwmod = {
403133920Sandre	"ipfw",
404133920Sandre	ipfw_modevent,
405133920Sandre	0
406133920Sandre};
407133920SandreDECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
408133920SandreMODULE_VERSION(ipfw, 2);
409