ng_ipfw.c revision 209633
1/*-
2 * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
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/netgraph/ng_ipfw.c 209633 2010-07-01 17:46:12Z glebius $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/lock.h>
33#include <sys/mbuf.h>
34#include <sys/malloc.h>
35#include <sys/ctype.h>
36#include <sys/errno.h>
37#include <sys/rwlock.h>
38#include <sys/socket.h>
39#include <sys/syslog.h>
40
41#include <net/if.h>
42
43#include <netinet/in.h>
44#include <netinet/in_systm.h>
45#include <netinet/in_var.h>
46#include <netinet/ip_var.h>
47#include <netinet/ip_fw.h>
48#include <netinet/ipfw/ip_fw_private.h>
49#include <netinet/ip.h>
50
51#include <netgraph/ng_message.h>
52#include <netgraph/ng_parse.h>
53#include <netgraph/ng_ipfw.h>
54#include <netgraph/netgraph.h>
55
56static int		ng_ipfw_mod_event(module_t mod, int event, void *data);
57static ng_constructor_t	ng_ipfw_constructor;
58static ng_shutdown_t	ng_ipfw_shutdown;
59static ng_newhook_t	ng_ipfw_newhook;
60static ng_connect_t	ng_ipfw_connect;
61static ng_findhook_t	ng_ipfw_findhook;
62static ng_rcvdata_t	ng_ipfw_rcvdata;
63static ng_disconnect_t	ng_ipfw_disconnect;
64
65static hook_p		ng_ipfw_findhook1(node_p, u_int16_t );
66static int		ng_ipfw_input(struct mbuf **, int, struct ip_fw_args *,
67			    int);
68
69/* We have only one node */
70static node_p	fw_node;
71
72/* Netgraph node type descriptor */
73static struct ng_type ng_ipfw_typestruct = {
74	.version =	NG_ABI_VERSION,
75	.name =		NG_IPFW_NODE_TYPE,
76	.mod_event =	ng_ipfw_mod_event,
77	.constructor =	ng_ipfw_constructor,
78	.shutdown =	ng_ipfw_shutdown,
79	.newhook =	ng_ipfw_newhook,
80	.connect =	ng_ipfw_connect,
81	.findhook =	ng_ipfw_findhook,
82	.rcvdata =	ng_ipfw_rcvdata,
83	.disconnect =	ng_ipfw_disconnect,
84};
85NETGRAPH_INIT(ipfw, &ng_ipfw_typestruct);
86MODULE_DEPEND(ng_ipfw, ipfw, 2, 2, 2);
87
88/* Information we store for each hook */
89struct ng_ipfw_hook_priv {
90        hook_p		hook;
91	u_int16_t	rulenum;
92};
93typedef struct ng_ipfw_hook_priv *hpriv_p;
94
95static int
96ng_ipfw_mod_event(module_t mod, int event, void *data)
97{
98	int error = 0;
99
100	switch (event) {
101	case MOD_LOAD:
102
103		if (ng_ipfw_input_p != NULL) {
104			error = EEXIST;
105			break;
106		}
107
108		/* Setup node without any private data */
109		if ((error = ng_make_node_common(&ng_ipfw_typestruct, &fw_node))
110		    != 0) {
111			log(LOG_ERR, "%s: can't create ng_ipfw node", __func__);
112                	break;
113		};
114
115		/* Try to name node */
116		if (ng_name_node(fw_node, "ipfw") != 0)
117			log(LOG_WARNING, "%s: failed to name node \"ipfw\"",
118			    __func__);
119
120		/* Register hook */
121		ng_ipfw_input_p = ng_ipfw_input;
122		break;
123
124	case MOD_UNLOAD:
125		 /*
126		  * This won't happen if a node exists.
127		  * ng_ipfw_input_p is already cleared.
128		  */
129		break;
130
131	default:
132		error = EOPNOTSUPP;
133		break;
134	}
135
136	return (error);
137}
138
139static int
140ng_ipfw_constructor(node_p node)
141{
142	return (EINVAL);	/* Only one node */
143}
144
145static int
146ng_ipfw_newhook(node_p node, hook_p hook, const char *name)
147{
148	hpriv_p	hpriv;
149	u_int16_t rulenum;
150	const char *cp;
151	char *endptr;
152
153	/* Protect from leading zero */
154	if (name[0] == '0' && name[1] != '\0')
155		return (EINVAL);
156
157	/* Check that name contains only digits */
158	for (cp = name; *cp != '\0'; cp++)
159		if (!isdigit(*cp))
160			return (EINVAL);
161
162	/* Convert it to integer */
163	rulenum = (u_int16_t)strtol(name, &endptr, 10);
164	if (*endptr != '\0')
165		return (EINVAL);
166
167	/* Allocate memory for this hook's private data */
168	hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
169	if (hpriv== NULL)
170		return (ENOMEM);
171
172	hpriv->hook = hook;
173	hpriv->rulenum = rulenum;
174
175	NG_HOOK_SET_PRIVATE(hook, hpriv);
176
177	return(0);
178}
179
180/*
181 * Set hooks into queueing mode, to avoid recursion between
182 * netgraph layer and ip_{input,output}.
183 */
184static int
185ng_ipfw_connect(hook_p hook)
186{
187	NG_HOOK_FORCE_QUEUE(hook);
188	return (0);
189}
190
191/* Look up hook by name */
192hook_p
193ng_ipfw_findhook(node_p node, const char *name)
194{
195	u_int16_t n;	/* numeric representation of hook */
196	char *endptr;
197
198	n = (u_int16_t)strtol(name, &endptr, 10);
199	if (*endptr != '\0')
200		return NULL;
201	return ng_ipfw_findhook1(node, n);
202}
203
204/* Look up hook by rule number */
205static hook_p
206ng_ipfw_findhook1(node_p node, u_int16_t rulenum)
207{
208	hook_p	hook;
209	hpriv_p	hpriv;
210
211	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
212		hpriv = NG_HOOK_PRIVATE(hook);
213		if (NG_HOOK_IS_VALID(hook) && (hpriv->rulenum == rulenum))
214                        return (hook);
215	}
216
217	return (NULL);
218}
219
220
221static int
222ng_ipfw_rcvdata(hook_p hook, item_p item)
223{
224	struct m_tag *tag;
225	struct ipfw_rule_ref *r;
226	struct mbuf *m;
227
228	NGI_GET_M(item, m);
229	NG_FREE_ITEM(item);
230
231	tag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL);
232	if (tag == NULL) {
233		NG_FREE_M(m);
234		return (EINVAL);	/* XXX: find smth better */
235	};
236
237	r = (struct ipfw_rule_ref *)(tag + 1);
238	if (r->info & IPFW_INFO_IN) {
239		ip_input(m);
240		return (0);
241	} else {
242		struct ip *ip;
243
244		if (m->m_len < sizeof(struct ip) &&
245		    (m = m_pullup(m, sizeof(struct ip))) == NULL)
246			return (EINVAL);
247
248		ip = mtod(m, struct ip *);
249
250		SET_HOST_IPLEN(ip);
251
252		return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
253	}
254}
255
256static int
257ng_ipfw_input(struct mbuf **m0, int dir, struct ip_fw_args *fwa, int tee)
258{
259	struct mbuf *m;
260	struct ip *ip;
261	hook_p	hook;
262	int error = 0;
263
264	/*
265	 * Node must be loaded and corresponding hook must be present.
266	 */
267	if (fw_node == NULL ||
268	   (hook = ng_ipfw_findhook1(fw_node, fwa->rule.info)) == NULL) {
269		if (tee == 0)
270			m_freem(*m0);
271		return (ESRCH);		/* no hook associated with this rule */
272	}
273
274	/*
275	 * We have two modes: in normal mode we add a tag to packet, which is
276	 * important to return packet back to IP stack. In tee mode we make
277	 * a copy of a packet and forward it into netgraph without a tag.
278	 */
279	if (tee == 0) {
280		struct m_tag *tag;
281		struct ipfw_rule_ref *r;
282		m = *m0;
283		*m0 = NULL;	/* it belongs now to netgraph */
284
285		tag = m_tag_alloc(MTAG_IPFW_RULE, 0, sizeof(*r),
286			M_NOWAIT|M_ZERO);
287		if (tag == NULL) {
288			m_freem(m);
289			return (ENOMEM);
290		}
291		r = (struct ipfw_rule_ref *)(tag + 1);
292		*r = fwa->rule;
293		r->info = dir ? IPFW_INFO_IN : IPFW_INFO_OUT;
294		m_tag_prepend(m, tag);
295
296	} else
297		if ((m = m_dup(*m0, M_DONTWAIT)) == NULL)
298			return (ENOMEM);	/* which is ignored */
299
300	if (m->m_len < sizeof(struct ip) &&
301	    (m = m_pullup(m, sizeof(struct ip))) == NULL)
302		return (EINVAL);
303
304	ip = mtod(m, struct ip *);
305
306	NG_SEND_DATA_ONLY(error, hook, m);
307
308	return (error);
309}
310
311static int
312ng_ipfw_shutdown(node_p node)
313{
314
315	/*
316	 * After our single node has been removed,
317	 * the only thing that can be done is
318	 * 'kldunload ng_ipfw.ko'
319	 */
320	ng_ipfw_input_p = NULL;
321	NG_NODE_UNREF(node);
322	return (0);
323}
324
325static int
326ng_ipfw_disconnect(hook_p hook)
327{
328	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
329
330	free(hpriv, M_NETGRAPH);
331	NG_HOOK_SET_PRIVATE(hook, NULL);
332
333	return (0);
334}
335