npf_handler.c revision 1.3.2.2 1 1.3.2.2 uebayasi /* $NetBSD: npf_handler.c,v 1.3.2.2 2010/10/22 09:23:14 uebayasi Exp $ */
2 1.3.2.2 uebayasi
3 1.3.2.2 uebayasi /*-
4 1.3.2.2 uebayasi * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
5 1.3.2.2 uebayasi * All rights reserved.
6 1.3.2.2 uebayasi *
7 1.3.2.2 uebayasi * This material is based upon work partially supported by The
8 1.3.2.2 uebayasi * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9 1.3.2.2 uebayasi *
10 1.3.2.2 uebayasi * Redistribution and use in source and binary forms, with or without
11 1.3.2.2 uebayasi * modification, are permitted provided that the following conditions
12 1.3.2.2 uebayasi * are met:
13 1.3.2.2 uebayasi * 1. Redistributions of source code must retain the above copyright
14 1.3.2.2 uebayasi * notice, this list of conditions and the following disclaimer.
15 1.3.2.2 uebayasi * 2. Redistributions in binary form must reproduce the above copyright
16 1.3.2.2 uebayasi * notice, this list of conditions and the following disclaimer in the
17 1.3.2.2 uebayasi * documentation and/or other materials provided with the distribution.
18 1.3.2.2 uebayasi *
19 1.3.2.2 uebayasi * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.3.2.2 uebayasi * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.3.2.2 uebayasi * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.3.2.2 uebayasi * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.3.2.2 uebayasi * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.3.2.2 uebayasi * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.3.2.2 uebayasi * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.3.2.2 uebayasi * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.3.2.2 uebayasi * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.3.2.2 uebayasi * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.3.2.2 uebayasi * POSSIBILITY OF SUCH DAMAGE.
30 1.3.2.2 uebayasi */
31 1.3.2.2 uebayasi
32 1.3.2.2 uebayasi /*
33 1.3.2.2 uebayasi * NPF packet handler.
34 1.3.2.2 uebayasi */
35 1.3.2.2 uebayasi
36 1.3.2.2 uebayasi #include <sys/cdefs.h>
37 1.3.2.2 uebayasi __KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.3.2.2 2010/10/22 09:23:14 uebayasi Exp $");
38 1.3.2.2 uebayasi
39 1.3.2.2 uebayasi #include <sys/param.h>
40 1.3.2.2 uebayasi #include <sys/systm.h>
41 1.3.2.2 uebayasi
42 1.3.2.2 uebayasi #include <sys/mbuf.h>
43 1.3.2.2 uebayasi #include <sys/mutex.h>
44 1.3.2.2 uebayasi #include <net/if.h>
45 1.3.2.2 uebayasi #include <net/pfil.h>
46 1.3.2.2 uebayasi #include <sys/socketvar.h>
47 1.3.2.2 uebayasi
48 1.3.2.2 uebayasi #include "npf_impl.h"
49 1.3.2.2 uebayasi
50 1.3.2.2 uebayasi /*
51 1.3.2.2 uebayasi * If npf_ph_if != NULL, pfil hooks are registers. If NULL, not registered.
52 1.3.2.2 uebayasi * Used to check the state. Locked by: softnet_lock + KERNEL_LOCK (XXX).
53 1.3.2.2 uebayasi */
54 1.3.2.2 uebayasi static struct pfil_head * npf_ph_if = NULL;
55 1.3.2.2 uebayasi static struct pfil_head * npf_ph_inet = NULL;
56 1.3.2.2 uebayasi
57 1.3.2.2 uebayasi static bool default_pass = true;
58 1.3.2.2 uebayasi
59 1.3.2.2 uebayasi int npf_packet_handler(void *, struct mbuf **, struct ifnet *, int);
60 1.3.2.2 uebayasi
61 1.3.2.2 uebayasi /*
62 1.3.2.2 uebayasi * npf_ifhook: hook handling interface changes.
63 1.3.2.2 uebayasi */
64 1.3.2.2 uebayasi static int
65 1.3.2.2 uebayasi npf_ifhook(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
66 1.3.2.2 uebayasi {
67 1.3.2.2 uebayasi
68 1.3.2.2 uebayasi return 0;
69 1.3.2.2 uebayasi }
70 1.3.2.2 uebayasi
71 1.3.2.2 uebayasi /*
72 1.3.2.2 uebayasi * npf_packet_handler: main packet handling routine for layer 3.
73 1.3.2.2 uebayasi *
74 1.3.2.2 uebayasi * Note: packet flow and inspection logic is in strict order.
75 1.3.2.2 uebayasi */
76 1.3.2.2 uebayasi int
77 1.3.2.2 uebayasi npf_packet_handler(void *arg, struct mbuf **mp, struct ifnet *ifp, int di)
78 1.3.2.2 uebayasi {
79 1.3.2.2 uebayasi nbuf_t *nbuf = *mp;
80 1.3.2.2 uebayasi npf_cache_t npc;
81 1.3.2.2 uebayasi npf_session_t *se;
82 1.3.2.2 uebayasi npf_rule_t *rl;
83 1.3.2.2 uebayasi bool keepstate;
84 1.3.2.2 uebayasi int retfl, error;
85 1.3.2.2 uebayasi
86 1.3.2.2 uebayasi /*
87 1.3.2.2 uebayasi * Initialise packet information cache.
88 1.3.2.2 uebayasi * Note: it is enough to clear the info bits.
89 1.3.2.2 uebayasi */
90 1.3.2.2 uebayasi npc.npc_info = 0;
91 1.3.2.2 uebayasi error = 0;
92 1.3.2.2 uebayasi retfl = 0;
93 1.3.2.2 uebayasi
94 1.3.2.2 uebayasi /* Inspect the list of sessions. */
95 1.3.2.2 uebayasi se = npf_session_inspect(&npc, nbuf, ifp, di);
96 1.3.2.2 uebayasi
97 1.3.2.2 uebayasi /* If "passing" session found - skip the ruleset inspection. */
98 1.3.2.2 uebayasi if (se && npf_session_pass(se)) {
99 1.3.2.2 uebayasi goto pass;
100 1.3.2.2 uebayasi }
101 1.3.2.2 uebayasi
102 1.3.2.2 uebayasi /* Inspect the ruleset using this packet. */
103 1.3.2.2 uebayasi rl = npf_ruleset_inspect(&npc, nbuf, ifp, di, NPF_LAYER_3);
104 1.3.2.2 uebayasi if (rl == NULL) {
105 1.3.2.2 uebayasi if (default_pass) {
106 1.3.2.2 uebayasi goto pass;
107 1.3.2.2 uebayasi }
108 1.3.2.2 uebayasi error = ENETUNREACH;
109 1.3.2.2 uebayasi goto out;
110 1.3.2.2 uebayasi }
111 1.3.2.2 uebayasi
112 1.3.2.2 uebayasi /* Apply the rule. */
113 1.3.2.2 uebayasi error = npf_rule_apply(&npc, rl, &keepstate, &retfl);
114 1.3.2.2 uebayasi if (error) {
115 1.3.2.2 uebayasi goto out;
116 1.3.2.2 uebayasi }
117 1.3.2.2 uebayasi
118 1.3.2.2 uebayasi /* Establish a "pass" session, if required. */
119 1.3.2.2 uebayasi if (keepstate && !se) {
120 1.3.2.2 uebayasi se = npf_session_establish(&npc, NULL, di);
121 1.3.2.2 uebayasi if (se == NULL) {
122 1.3.2.2 uebayasi error = ENOMEM;
123 1.3.2.2 uebayasi goto out;
124 1.3.2.2 uebayasi }
125 1.3.2.2 uebayasi npf_session_setpass(se);
126 1.3.2.2 uebayasi }
127 1.3.2.2 uebayasi pass:
128 1.3.2.2 uebayasi KASSERT(error == 0);
129 1.3.2.2 uebayasi /*
130 1.3.2.2 uebayasi * Perform NAT.
131 1.3.2.2 uebayasi */
132 1.3.2.2 uebayasi error = npf_do_nat(&npc, se, nbuf, ifp, di);
133 1.3.2.2 uebayasi out:
134 1.3.2.2 uebayasi /* Release reference on session. */
135 1.3.2.2 uebayasi if (se != NULL) {
136 1.3.2.2 uebayasi npf_session_release(se);
137 1.3.2.2 uebayasi }
138 1.3.2.2 uebayasi
139 1.3.2.2 uebayasi /*
140 1.3.2.2 uebayasi * If error is set - drop the packet.
141 1.3.2.2 uebayasi * Normally, ENETUNREACH is used for "block".
142 1.3.2.2 uebayasi */
143 1.3.2.2 uebayasi if (error) {
144 1.3.2.2 uebayasi /*
145 1.3.2.2 uebayasi * Depending on flags and protocol, return TCP reset (RST)
146 1.3.2.2 uebayasi * or ICMP destination unreachable
147 1.3.2.2 uebayasi */
148 1.3.2.2 uebayasi if (retfl) {
149 1.3.2.2 uebayasi npf_return_block(&npc, nbuf, retfl);
150 1.3.2.2 uebayasi }
151 1.3.2.2 uebayasi m_freem(*mp);
152 1.3.2.2 uebayasi *mp = NULL;
153 1.3.2.2 uebayasi } else {
154 1.3.2.2 uebayasi /*
155 1.3.2.2 uebayasi * XXX: Disable for now, it will be set accordingly later,
156 1.3.2.2 uebayasi * for optimisations (to reduce inspection).
157 1.3.2.2 uebayasi */
158 1.3.2.2 uebayasi (*mp)->m_flags &= ~M_CANFASTFWD;
159 1.3.2.2 uebayasi }
160 1.3.2.2 uebayasi return error;
161 1.3.2.2 uebayasi }
162 1.3.2.2 uebayasi
163 1.3.2.2 uebayasi /*
164 1.3.2.2 uebayasi * npf_register_pfil: register pfil(9) hooks.
165 1.3.2.2 uebayasi */
166 1.3.2.2 uebayasi int
167 1.3.2.2 uebayasi npf_register_pfil(void)
168 1.3.2.2 uebayasi {
169 1.3.2.2 uebayasi int error;
170 1.3.2.2 uebayasi
171 1.3.2.2 uebayasi mutex_enter(softnet_lock);
172 1.3.2.2 uebayasi KERNEL_LOCK(1, NULL);
173 1.3.2.2 uebayasi
174 1.3.2.2 uebayasi /* Check if pfil hooks are not already registered. */
175 1.3.2.2 uebayasi if (npf_ph_if) {
176 1.3.2.2 uebayasi error = EEXIST;
177 1.3.2.2 uebayasi goto fail;
178 1.3.2.2 uebayasi }
179 1.3.2.2 uebayasi
180 1.3.2.2 uebayasi /* Capture point of any activity in interfaces and IP layer. */
181 1.3.2.2 uebayasi npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0);
182 1.3.2.2 uebayasi npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
183 1.3.2.2 uebayasi if (npf_ph_if == NULL || npf_ph_inet == NULL) {
184 1.3.2.2 uebayasi npf_ph_if = NULL;
185 1.3.2.2 uebayasi error = ENOENT;
186 1.3.2.2 uebayasi goto fail;
187 1.3.2.2 uebayasi }
188 1.3.2.2 uebayasi
189 1.3.2.2 uebayasi /* Interface re-config or attach/detach hook. */
190 1.3.2.2 uebayasi error = pfil_add_hook(npf_ifhook, NULL,
191 1.3.2.2 uebayasi PFIL_WAITOK | PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
192 1.3.2.2 uebayasi KASSERT(error == 0);
193 1.3.2.2 uebayasi
194 1.3.2.2 uebayasi /* Packet IN/OUT handler on all interfaces and IP layer. */
195 1.3.2.2 uebayasi error = pfil_add_hook(npf_packet_handler, NULL,
196 1.3.2.2 uebayasi PFIL_WAITOK | PFIL_ALL, npf_ph_inet);
197 1.3.2.2 uebayasi KASSERT(error == 0);
198 1.3.2.2 uebayasi
199 1.3.2.2 uebayasi fail:
200 1.3.2.2 uebayasi KERNEL_UNLOCK_ONE(NULL);
201 1.3.2.2 uebayasi mutex_exit(softnet_lock);
202 1.3.2.2 uebayasi
203 1.3.2.2 uebayasi return error;
204 1.3.2.2 uebayasi }
205 1.3.2.2 uebayasi
206 1.3.2.2 uebayasi /*
207 1.3.2.2 uebayasi * npf_unregister: unregister pfil(9) hooks.
208 1.3.2.2 uebayasi */
209 1.3.2.2 uebayasi void
210 1.3.2.2 uebayasi npf_unregister_pfil(void)
211 1.3.2.2 uebayasi {
212 1.3.2.2 uebayasi
213 1.3.2.2 uebayasi mutex_enter(softnet_lock);
214 1.3.2.2 uebayasi KERNEL_LOCK(1, NULL);
215 1.3.2.2 uebayasi
216 1.3.2.2 uebayasi if (npf_ph_if) {
217 1.3.2.2 uebayasi (void)pfil_remove_hook(npf_packet_handler, NULL,
218 1.3.2.2 uebayasi PFIL_ALL, npf_ph_inet);
219 1.3.2.2 uebayasi (void)pfil_remove_hook(npf_ifhook, NULL,
220 1.3.2.2 uebayasi PFIL_IFADDR | PFIL_IFNET, npf_ph_if);
221 1.3.2.2 uebayasi
222 1.3.2.2 uebayasi npf_ph_if = NULL;
223 1.3.2.2 uebayasi }
224 1.3.2.2 uebayasi
225 1.3.2.2 uebayasi KERNEL_UNLOCK_ONE(NULL);
226 1.3.2.2 uebayasi mutex_exit(softnet_lock);
227 1.3.2.2 uebayasi }
228