xref: /netbsd-src/sys/net/npf/npf_handler.c (revision 6cf6fe02a981b55727c49c3d37b0d8191a98c0ee)
1 /*	$NetBSD: npf_handler.c,v 1.33 2014/07/23 01:25:34 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This material is based upon work partially supported by The
8  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * NPF packet handler.
34  *
35  * Note: pfil(9) hooks are currently locked by softnet_lock and kernel-lock.
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.33 2014/07/23 01:25:34 rmind Exp $");
40 
41 #include <sys/types.h>
42 #include <sys/param.h>
43 
44 #include <sys/mbuf.h>
45 #include <sys/mutex.h>
46 #include <net/if.h>
47 #include <net/pfil.h>
48 #include <sys/socketvar.h>
49 
50 #include <netinet/in_systm.h>
51 #include <netinet/in.h>
52 #include <netinet/ip_var.h>
53 #include <netinet/ip6.h>
54 #include <netinet6/ip6_var.h>
55 
56 #include "npf_impl.h"
57 #include "npf_conn.h"
58 
59 static bool		pfil_registered = false;
60 static pfil_head_t *	npf_ph_if = NULL;
61 static pfil_head_t *	npf_ph_inet = NULL;
62 static pfil_head_t *	npf_ph_inet6 = NULL;
63 
64 #ifndef INET6
65 #define ip6_reass_packet(x, y)	ENOTSUP
66 #endif
67 
68 /*
69  * npf_ifhook: hook handling interface changes.
70  */
71 static int
72 npf_ifhook(void *arg, struct mbuf **mp, ifnet_t *ifp, int di)
73 {
74 	u_long cmd = (u_long)mp;
75 
76 	if (di == PFIL_IFNET) {
77 		switch (cmd) {
78 		case PFIL_IFNET_ATTACH:
79 			npf_ifmap_attach(ifp);
80 			break;
81 		case PFIL_IFNET_DETACH:
82 			npf_ifmap_detach(ifp);
83 			break;
84 		}
85 	}
86 	return 0;
87 }
88 
89 static int
90 npf_reassembly(npf_cache_t *npc, struct mbuf **mp)
91 {
92 	nbuf_t *nbuf = npc->npc_nbuf;
93 	int error = EINVAL;
94 
95 	/* Reset the mbuf as it may have changed. */
96 	*mp = nbuf_head_mbuf(nbuf);
97 	nbuf_reset(nbuf);
98 
99 	if (npf_iscached(npc, NPC_IP4)) {
100 		struct ip *ip = nbuf_dataptr(nbuf);
101 		error = ip_reass_packet(mp, ip);
102 	} else if (npf_iscached(npc, NPC_IP6)) {
103 		/*
104 		 * Note: ip6_reass_packet() offset is the start of
105 		 * the fragment header.
106 		 */
107 		error = ip6_reass_packet(mp, npc->npc_hlen);
108 		if (error && *mp == NULL) {
109 			memset(nbuf, 0, sizeof(nbuf_t));
110 		}
111 	}
112 	if (error) {
113 		npf_stats_inc(NPF_STAT_REASSFAIL);
114 		return error;
115 	}
116 	if (*mp == NULL) {
117 		/* More fragments should come. */
118 		npf_stats_inc(NPF_STAT_FRAGMENTS);
119 		return 0;
120 	}
121 
122 	/*
123 	 * Reassembly is complete, we have the final packet.
124 	 * Cache again, since layer 4 data is accessible now.
125 	 */
126 	nbuf_init(nbuf, *mp, nbuf->nb_ifp);
127 	npc->npc_info = 0;
128 
129 	if (npf_cache_all(npc) & NPC_IPFRAG) {
130 		return EINVAL;
131 	}
132 	npf_stats_inc(NPF_STAT_REASSEMBLY);
133 	return 0;
134 }
135 
136 /*
137  * npf_packet_handler: main packet handling routine for layer 3.
138  *
139  * Note: packet flow and inspection logic is in strict order.
140  */
141 int
142 npf_packet_handler(void *arg, struct mbuf **mp, ifnet_t *ifp, int di)
143 {
144 	nbuf_t nbuf;
145 	npf_cache_t npc;
146 	npf_conn_t *con;
147 	npf_rule_t *rl;
148 	npf_rproc_t *rp;
149 	int error, retfl;
150 	int decision;
151 
152 	/*
153 	 * Initialise packet information cache.
154 	 * Note: it is enough to clear the info bits.
155 	 */
156 	KASSERT(ifp != NULL);
157 	nbuf_init(&nbuf, *mp, ifp);
158 	npc.npc_nbuf = &nbuf;
159 	npc.npc_info = 0;
160 
161 	decision = NPF_DECISION_BLOCK;
162 	error = 0;
163 	retfl = 0;
164 	rp = NULL;
165 
166 	/* Cache everything.  Determine whether it is an IP fragment. */
167 	if (__predict_false(npf_cache_all(&npc) & NPC_IPFRAG)) {
168 		/*
169 		 * Pass to IPv4 or IPv6 reassembly mechanism.
170 		 */
171 		error = npf_reassembly(&npc, mp);
172 		if (error) {
173 			con = NULL;
174 			goto out;
175 		}
176 		if (*mp == NULL) {
177 			/* More fragments should come; return. */
178 			return 0;
179 		}
180 	}
181 
182 	/* Inspect the list of connections (if found, acquires a reference). */
183 	con = npf_conn_inspect(&npc, di, &error);
184 
185 	/* If "passing" connection found - skip the ruleset inspection. */
186 	if (con && npf_conn_pass(con, &rp)) {
187 		npf_stats_inc(NPF_STAT_PASS_CONN);
188 		KASSERT(error == 0);
189 		goto pass;
190 	}
191 	if (__predict_false(error)) {
192 		if (error == ENETUNREACH)
193 			goto block;
194 		goto out;
195 	}
196 
197 	/* Acquire the lock, inspect the ruleset using this packet. */
198 	int slock = npf_config_read_enter();
199 	npf_ruleset_t *rlset = npf_config_ruleset();
200 
201 	rl = npf_ruleset_inspect(&npc, rlset, di, NPF_LAYER_3);
202 	if (__predict_false(rl == NULL)) {
203 		const bool pass = npf_default_pass();
204 		npf_config_read_exit(slock);
205 
206 		if (pass) {
207 			npf_stats_inc(NPF_STAT_PASS_DEFAULT);
208 			goto pass;
209 		}
210 		npf_stats_inc(NPF_STAT_BLOCK_DEFAULT);
211 		goto block;
212 	}
213 
214 	/*
215 	 * Get the rule procedure (acquires a reference) for association
216 	 * with a connection (if any) and execution.
217 	 */
218 	KASSERT(rp == NULL);
219 	rp = npf_rule_getrproc(rl);
220 
221 	/* Conclude with the rule and release the lock. */
222 	error = npf_rule_conclude(rl, &retfl);
223 	npf_config_read_exit(slock);
224 
225 	if (error) {
226 		npf_stats_inc(NPF_STAT_BLOCK_RULESET);
227 		goto block;
228 	}
229 	npf_stats_inc(NPF_STAT_PASS_RULESET);
230 
231 	/*
232 	 * Establish a "pass" connection, if required.  Just proceed if
233 	 * connection creation fails (e.g. due to unsupported protocol).
234 	 */
235 	if ((retfl & NPF_RULE_STATEFUL) != 0 && !con) {
236 		con = npf_conn_establish(&npc, di,
237 		    (retfl & NPF_RULE_MULTIENDS) == 0);
238 		if (con) {
239 			/*
240 			 * Note: the reference on the rule procedure is
241 			 * transfered to the connection.  It will be
242 			 * released on connection destruction.
243 			 */
244 			npf_conn_setpass(con, rp);
245 		}
246 	}
247 pass:
248 	decision = NPF_DECISION_PASS;
249 	KASSERT(error == 0);
250 	/*
251 	 * Perform NAT.
252 	 */
253 	error = npf_do_nat(&npc, con, di);
254 block:
255 	/*
256 	 * Execute the rule procedure, if any is associated.
257 	 * It may reverse the decision from pass to block.
258 	 */
259 	if (rp && !npf_rproc_run(&npc, rp, &decision)) {
260 		if (con) {
261 			npf_conn_release(con);
262 		}
263 		npf_rproc_release(rp);
264 		*mp = NULL;
265 		return 0;
266 	}
267 out:
268 	/*
269 	 * Release the reference on a connection.  Release the reference
270 	 * on a rule procedure only if there was no association.
271 	 */
272 	if (con) {
273 		npf_conn_release(con);
274 	} else if (rp) {
275 		npf_rproc_release(rp);
276 	}
277 
278 	/* Reset mbuf pointer before returning to the caller. */
279 	if ((*mp = nbuf_head_mbuf(&nbuf)) == NULL) {
280 		return error ? error : ENOMEM;
281 	}
282 
283 	/* Pass the packet if decided and there is no error. */
284 	if (decision == NPF_DECISION_PASS && !error) {
285 		/*
286 		 * XXX: Disable for now, it will be set accordingly later,
287 		 * for optimisations (to reduce inspection).
288 		 */
289 		(*mp)->m_flags &= ~M_CANFASTFWD;
290 		return 0;
291 	}
292 
293 	/*
294 	 * Block the packet.  ENETUNREACH is used to indicate blocking.
295 	 * Depending on the flags and protocol, return TCP reset (RST) or
296 	 * ICMP destination unreachable.
297 	 */
298 	if (retfl && npf_return_block(&npc, retfl)) {
299 		*mp = NULL;
300 	}
301 
302 	if (!error) {
303 		error = ENETUNREACH;
304 	}
305 
306 	if (*mp) {
307 		m_freem(*mp);
308 		*mp = NULL;
309 	}
310 	return error;
311 }
312 
313 /*
314  * npf_pfil_register: register pfil(9) hooks.
315  */
316 int
317 npf_pfil_register(bool init)
318 {
319 	int error = 0;
320 
321 	mutex_enter(softnet_lock);
322 	KERNEL_LOCK(1, NULL);
323 
324 	/* Init: interface re-config and attach/detach hook. */
325 	if (!npf_ph_if) {
326 		npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0);
327 		if (!npf_ph_if) {
328 			error = ENOENT;
329 			goto out;
330 		}
331 		error = pfil_add_hook(npf_ifhook, NULL,
332 		    PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
333 		KASSERT(error == 0);
334 	}
335 	if (init) {
336 		goto out;
337 	}
338 
339 	/* Check if pfil hooks are not already registered. */
340 	if (pfil_registered) {
341 		error = EEXIST;
342 		goto out;
343 	}
344 
345 	/* Capture points of the activity in the IP layer. */
346 	npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET);
347 	npf_ph_inet6 = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET6);
348 	if (!npf_ph_inet && !npf_ph_inet6) {
349 		error = ENOENT;
350 		goto out;
351 	}
352 
353 	/* Packet IN/OUT handlers for IP layer. */
354 	if (npf_ph_inet) {
355 		error = pfil_add_hook(npf_packet_handler, NULL,
356 		    PFIL_ALL, npf_ph_inet);
357 		KASSERT(error == 0);
358 	}
359 	if (npf_ph_inet6) {
360 		error = pfil_add_hook(npf_packet_handler, NULL,
361 		    PFIL_ALL, npf_ph_inet6);
362 		KASSERT(error == 0);
363 	}
364 	pfil_registered = true;
365 out:
366 	KERNEL_UNLOCK_ONE(NULL);
367 	mutex_exit(softnet_lock);
368 
369 	return error;
370 }
371 
372 /*
373  * npf_pfil_unregister: unregister pfil(9) hooks.
374  */
375 void
376 npf_pfil_unregister(bool fini)
377 {
378 	mutex_enter(softnet_lock);
379 	KERNEL_LOCK(1, NULL);
380 
381 	if (fini && npf_ph_if) {
382 		(void)pfil_remove_hook(npf_ifhook, NULL,
383 		    PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
384 	}
385 	if (npf_ph_inet) {
386 		(void)pfil_remove_hook(npf_packet_handler, NULL,
387 		    PFIL_ALL, npf_ph_inet);
388 	}
389 	if (npf_ph_inet6) {
390 		(void)pfil_remove_hook(npf_packet_handler, NULL,
391 		    PFIL_ALL, npf_ph_inet6);
392 	}
393 	pfil_registered = false;
394 
395 	KERNEL_UNLOCK_ONE(NULL);
396 	mutex_exit(softnet_lock);
397 }
398 
399 bool
400 npf_pfil_registered_p(void)
401 {
402 	return pfil_registered;
403 }
404