npf_handler.c revision 1.3.2.2 1 /* $NetBSD: npf_handler.c,v 1.3.2.2 2010/10/22 09:23:14 uebayasi Exp $ */
2
3 /*-
4 * Copyright (c) 2009-2010 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
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.3.2.2 2010/10/22 09:23:14 uebayasi Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41
42 #include <sys/mbuf.h>
43 #include <sys/mutex.h>
44 #include <net/if.h>
45 #include <net/pfil.h>
46 #include <sys/socketvar.h>
47
48 #include "npf_impl.h"
49
50 /*
51 * If npf_ph_if != NULL, pfil hooks are registers. If NULL, not registered.
52 * Used to check the state. Locked by: softnet_lock + KERNEL_LOCK (XXX).
53 */
54 static struct pfil_head * npf_ph_if = NULL;
55 static struct pfil_head * npf_ph_inet = NULL;
56
57 static bool default_pass = true;
58
59 int npf_packet_handler(void *, struct mbuf **, struct ifnet *, int);
60
61 /*
62 * npf_ifhook: hook handling interface changes.
63 */
64 static int
65 npf_ifhook(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
66 {
67
68 return 0;
69 }
70
71 /*
72 * npf_packet_handler: main packet handling routine for layer 3.
73 *
74 * Note: packet flow and inspection logic is in strict order.
75 */
76 int
77 npf_packet_handler(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
78 {
79 nbuf_t *nbuf = *mp;
80 npf_cache_t npc;
81 npf_session_t *se;
82 npf_rule_t *rl;
83 bool keepstate;
84 int retfl, error;
85
86 /*
87 * Initialise packet information cache.
88 * Note: it is enough to clear the info bits.
89 */
90 npc.npc_info = 0;
91 error = 0;
92 retfl = 0;
93
94 /* Inspect the list of sessions. */
95 se = npf_session_inspect(&npc, nbuf, ifp, di);
96
97 /* If "passing" session found - skip the ruleset inspection. */
98 if (se && npf_session_pass(se)) {
99 goto pass;
100 }
101
102 /* Inspect the ruleset using this packet. */
103 rl = npf_ruleset_inspect(&npc, nbuf, ifp, di, NPF_LAYER_3);
104 if (rl == NULL) {
105 if (default_pass) {
106 goto pass;
107 }
108 error = ENETUNREACH;
109 goto out;
110 }
111
112 /* Apply the rule. */
113 error = npf_rule_apply(&npc, rl, &keepstate, &retfl);
114 if (error) {
115 goto out;
116 }
117
118 /* Establish a "pass" session, if required. */
119 if (keepstate && !se) {
120 se = npf_session_establish(&npc, NULL, di);
121 if (se == NULL) {
122 error = ENOMEM;
123 goto out;
124 }
125 npf_session_setpass(se);
126 }
127 pass:
128 KASSERT(error == 0);
129 /*
130 * Perform NAT.
131 */
132 error = npf_do_nat(&npc, se, nbuf, ifp, di);
133 out:
134 /* Release reference on session. */
135 if (se != NULL) {
136 npf_session_release(se);
137 }
138
139 /*
140 * If error is set - drop the packet.
141 * Normally, ENETUNREACH is used for "block".
142 */
143 if (error) {
144 /*
145 * Depending on flags and protocol, return TCP reset (RST)
146 * or ICMP destination unreachable
147 */
148 if (retfl) {
149 npf_return_block(&npc, nbuf, retfl);
150 }
151 m_freem(*mp);
152 *mp = NULL;
153 } else {
154 /*
155 * XXX: Disable for now, it will be set accordingly later,
156 * for optimisations (to reduce inspection).
157 */
158 (*mp)->m_flags &= ~M_CANFASTFWD;
159 }
160 return error;
161 }
162
163 /*
164 * npf_register_pfil: register pfil(9) hooks.
165 */
166 int
167 npf_register_pfil(void)
168 {
169 int error;
170
171 mutex_enter(softnet_lock);
172 KERNEL_LOCK(1, NULL);
173
174 /* Check if pfil hooks are not already registered. */
175 if (npf_ph_if) {
176 error = EEXIST;
177 goto fail;
178 }
179
180 /* Capture point of any activity in interfaces and IP layer. */
181 npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0);
182 npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
183 if (npf_ph_if == NULL || npf_ph_inet == NULL) {
184 npf_ph_if = NULL;
185 error = ENOENT;
186 goto fail;
187 }
188
189 /* Interface re-config or attach/detach hook. */
190 error = pfil_add_hook(npf_ifhook, NULL,
191 PFIL_WAITOK | PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
192 KASSERT(error == 0);
193
194 /* Packet IN/OUT handler on all interfaces and IP layer. */
195 error = pfil_add_hook(npf_packet_handler, NULL,
196 PFIL_WAITOK | PFIL_ALL, npf_ph_inet);
197 KASSERT(error == 0);
198
199 fail:
200 KERNEL_UNLOCK_ONE(NULL);
201 mutex_exit(softnet_lock);
202
203 return error;
204 }
205
206 /*
207 * npf_unregister: unregister pfil(9) hooks.
208 */
209 void
210 npf_unregister_pfil(void)
211 {
212
213 mutex_enter(softnet_lock);
214 KERNEL_LOCK(1, NULL);
215
216 if (npf_ph_if) {
217 (void)pfil_remove_hook(npf_packet_handler, NULL,
218 PFIL_ALL, npf_ph_inet);
219 (void)pfil_remove_hook(npf_ifhook, NULL,
220 PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
221
222 npf_ph_if = NULL;
223 }
224
225 KERNEL_UNLOCK_ONE(NULL);
226 mutex_exit(softnet_lock);
227 }
228