npf_conn.c revision 1.35 1 1.1 rmind /*-
2 1.32 rmind * Copyright (c) 2014-2020 Mindaugas Rasiukevicius <rmind at noxt eu>
3 1.1 rmind * Copyright (c) 2010-2014 The NetBSD Foundation, Inc.
4 1.1 rmind * All rights reserved.
5 1.1 rmind *
6 1.1 rmind * This material is based upon work partially supported by The
7 1.1 rmind * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
8 1.1 rmind *
9 1.1 rmind * Redistribution and use in source and binary forms, with or without
10 1.1 rmind * modification, are permitted provided that the following conditions
11 1.1 rmind * are met:
12 1.1 rmind * 1. Redistributions of source code must retain the above copyright
13 1.1 rmind * notice, this list of conditions and the following disclaimer.
14 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 rmind * notice, this list of conditions and the following disclaimer in the
16 1.1 rmind * documentation and/or other materials provided with the distribution.
17 1.1 rmind *
18 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 1.1 rmind * POSSIBILITY OF SUCH DAMAGE.
29 1.1 rmind */
30 1.1 rmind
31 1.1 rmind /*
32 1.1 rmind * NPF connection tracking for stateful filtering and translation.
33 1.1 rmind *
34 1.1 rmind * Overview
35 1.1 rmind *
36 1.26 rmind * Packets can be incoming or outgoing with respect to an interface.
37 1.1 rmind * Connection direction is identified by the direction of its first
38 1.26 rmind * packet. The meaning of incoming/outgoing packet in the context of
39 1.26 rmind * connection direction can be confusing. Therefore, we will use the
40 1.26 rmind * terms "forwards stream" and "backwards stream", where packets in
41 1.26 rmind * the forwards stream mean the packets travelling in the direction
42 1.26 rmind * as the connection direction.
43 1.26 rmind *
44 1.26 rmind * All connections have two keys and thus two entries:
45 1.1 rmind *
46 1.27 rmind * - npf_conn_getforwkey(con) -- for the forwards stream;
47 1.27 rmind * - npf_conn_getbackkey(con, alen) -- for the backwards stream.
48 1.27 rmind *
49 1.27 rmind * Note: the keys are stored in npf_conn_t::c_keys[], which is used
50 1.27 rmind * to allocate variable-length npf_conn_t structures based on whether
51 1.32 rmind * the IPv4 or IPv6 addresses are used.
52 1.1 rmind *
53 1.32 rmind * The key is an n-tuple used to identify the connection flow: see the
54 1.32 rmind * npf_connkey.c source file for the description of the key layouts.
55 1.32 rmind * The key may be formed using translated values in a case of NAT.
56 1.1 rmind *
57 1.32 rmind * Connections can serve two purposes: for the implicit passing and/or
58 1.1 rmind * to accommodate the dynamic NAT. Connections for the former purpose
59 1.1 rmind * are created by the rules with "stateful" attribute and are used for
60 1.1 rmind * stateful filtering. Such connections indicate that the packet of
61 1.1 rmind * the backwards stream should be passed without inspection of the
62 1.1 rmind * ruleset. The other purpose is to associate a dynamic NAT mechanism
63 1.1 rmind * with a connection. Such connections are created by the NAT policies
64 1.1 rmind * and they have a relationship with NAT translation structure via
65 1.1 rmind * npf_conn_t::c_nat. A single connection can serve both purposes,
66 1.1 rmind * which is a common case.
67 1.1 rmind *
68 1.1 rmind * Connection life-cycle
69 1.1 rmind *
70 1.1 rmind * Connections are established when a packet matches said rule or
71 1.1 rmind * NAT policy. Both keys of the established connection are inserted
72 1.1 rmind * into the connection database. A garbage collection thread
73 1.1 rmind * periodically scans all connections and depending on connection
74 1.1 rmind * properties (e.g. last activity time, protocol) removes connection
75 1.1 rmind * entries and expires the actual connections.
76 1.1 rmind *
77 1.1 rmind * Each connection has a reference count. The reference is acquired
78 1.1 rmind * on lookup and should be released by the caller. It guarantees that
79 1.1 rmind * the connection will not be destroyed, although it may be expired.
80 1.1 rmind *
81 1.32 rmind * Synchronization
82 1.1 rmind *
83 1.32 rmind * Connection database is accessed in a lock-free manner by the main
84 1.1 rmind * routines: npf_conn_inspect() and npf_conn_establish(). Since they
85 1.1 rmind * are always called from a software interrupt, the database is
86 1.32 rmind * protected using EBR. The main place which can destroy a connection
87 1.32 rmind * is npf_conn_worker(). The database itself can be replaced and
88 1.32 rmind * destroyed in npf_conn_reload().
89 1.1 rmind *
90 1.1 rmind * ALG support
91 1.1 rmind *
92 1.1 rmind * Application-level gateways (ALGs) can override generic connection
93 1.1 rmind * inspection (npf_alg_conn() call in npf_conn_inspect() function) by
94 1.1 rmind * performing their own lookup using different key. Recursive call
95 1.1 rmind * to npf_conn_inspect() is not allowed. The ALGs ought to use the
96 1.1 rmind * npf_conn_lookup() function for this purpose.
97 1.1 rmind *
98 1.1 rmind * Lock order
99 1.1 rmind *
100 1.35 riastrad * npf_t::config_lock ->
101 1.6 rmind * conn_lock ->
102 1.6 rmind * npf_conn_t::c_lock
103 1.1 rmind */
104 1.1 rmind
105 1.22 christos #ifdef _KERNEL
106 1.1 rmind #include <sys/cdefs.h>
107 1.35 riastrad __KERNEL_RCSID(0, "$NetBSD: npf_conn.c,v 1.35 2023/01/22 18:39:35 riastradh Exp $");
108 1.1 rmind
109 1.1 rmind #include <sys/param.h>
110 1.1 rmind #include <sys/types.h>
111 1.1 rmind
112 1.1 rmind #include <netinet/in.h>
113 1.1 rmind #include <netinet/tcp.h>
114 1.1 rmind
115 1.1 rmind #include <sys/atomic.h>
116 1.1 rmind #include <sys/kmem.h>
117 1.1 rmind #include <sys/mutex.h>
118 1.1 rmind #include <net/pfil.h>
119 1.1 rmind #include <sys/pool.h>
120 1.1 rmind #include <sys/queue.h>
121 1.1 rmind #include <sys/systm.h>
122 1.22 christos #endif
123 1.1 rmind
124 1.1 rmind #define __NPF_CONN_PRIVATE
125 1.1 rmind #include "npf_conn.h"
126 1.1 rmind #include "npf_impl.h"
127 1.1 rmind
128 1.27 rmind /* A helper to select the IPv4 or IPv6 connection cache. */
129 1.27 rmind #define NPF_CONNCACHE(alen) (((alen) >> 4) & 0x1)
130 1.27 rmind
131 1.1 rmind /*
132 1.1 rmind * Connection flags: PFIL_IN and PFIL_OUT values are reserved for direction.
133 1.1 rmind */
134 1.1 rmind CTASSERT(PFIL_ALL == (0x001 | 0x002));
135 1.1 rmind #define CONN_ACTIVE 0x004 /* visible on inspection */
136 1.1 rmind #define CONN_PASS 0x008 /* perform implicit passing */
137 1.1 rmind #define CONN_EXPIRE 0x010 /* explicitly expire */
138 1.1 rmind #define CONN_REMOVED 0x020 /* "forw/back" entries removed */
139 1.1 rmind
140 1.6 rmind enum { CONN_TRACKING_OFF, CONN_TRACKING_ON };
141 1.1 rmind
142 1.32 rmind static int npf_conn_export(npf_t *, npf_conn_t *, nvlist_t *);
143 1.1 rmind
144 1.1 rmind /*
145 1.32 rmind * npf_conn_sys{init,fini}: initialize/destroy connection tracking.
146 1.1 rmind */
147 1.1 rmind
148 1.1 rmind void
149 1.29 christos npf_conn_init(npf_t *npf)
150 1.1 rmind {
151 1.32 rmind npf_conn_params_t *params = npf_param_allocgroup(npf,
152 1.32 rmind NPF_PARAMS_CONN, sizeof(npf_conn_params_t));
153 1.32 rmind npf_param_t param_map[] = {
154 1.32 rmind {
155 1.32 rmind "state.key.interface",
156 1.32 rmind ¶ms->connkey_interface,
157 1.32 rmind .default_val = 1, // true
158 1.32 rmind .min = 0, .max = 1
159 1.32 rmind },
160 1.32 rmind {
161 1.32 rmind "state.key.direction",
162 1.32 rmind ¶ms->connkey_direction,
163 1.32 rmind .default_val = 1, // true
164 1.32 rmind .min = 0, .max = 1
165 1.32 rmind },
166 1.32 rmind };
167 1.32 rmind npf_param_register(npf, param_map, __arraycount(param_map));
168 1.32 rmind
169 1.27 rmind npf->conn_cache[0] = pool_cache_init(
170 1.27 rmind offsetof(npf_conn_t, c_keys[NPF_CONNKEY_V4WORDS * 2]),
171 1.27 rmind 0, 0, 0, "npfcn4pl", NULL, IPL_NET, NULL, NULL, NULL);
172 1.27 rmind npf->conn_cache[1] = pool_cache_init(
173 1.27 rmind offsetof(npf_conn_t, c_keys[NPF_CONNKEY_V6WORDS * 2]),
174 1.27 rmind 0, 0, 0, "npfcn6pl", NULL, IPL_NET, NULL, NULL, NULL);
175 1.27 rmind
176 1.22 christos mutex_init(&npf->conn_lock, MUTEX_DEFAULT, IPL_NONE);
177 1.32 rmind atomic_store_relaxed(&npf->conn_tracking, CONN_TRACKING_OFF);
178 1.22 christos npf->conn_db = npf_conndb_create();
179 1.27 rmind npf_conndb_sysinit(npf);
180 1.32 rmind
181 1.32 rmind npf_worker_addfunc(npf, npf_conn_worker);
182 1.1 rmind }
183 1.1 rmind
184 1.1 rmind void
185 1.22 christos npf_conn_fini(npf_t *npf)
186 1.1 rmind {
187 1.32 rmind const size_t len = sizeof(npf_conn_params_t);
188 1.27 rmind
189 1.6 rmind /* Note: the caller should have flushed the connections. */
190 1.32 rmind KASSERT(atomic_load_relaxed(&npf->conn_tracking) == CONN_TRACKING_OFF);
191 1.1 rmind
192 1.22 christos npf_conndb_destroy(npf->conn_db);
193 1.27 rmind pool_cache_destroy(npf->conn_cache[0]);
194 1.27 rmind pool_cache_destroy(npf->conn_cache[1]);
195 1.22 christos mutex_destroy(&npf->conn_lock);
196 1.32 rmind
197 1.32 rmind npf_param_freegroup(npf, NPF_PARAMS_CONN, len);
198 1.32 rmind npf_conndb_sysfini(npf);
199 1.1 rmind }
200 1.1 rmind
201 1.1 rmind /*
202 1.6 rmind * npf_conn_load: perform the load by flushing the current connection
203 1.6 rmind * database and replacing it with the new one or just destroying.
204 1.1 rmind *
205 1.6 rmind * => The caller must disable the connection tracking and ensure that
206 1.6 rmind * there are no connection database lookups or references in-flight.
207 1.1 rmind */
208 1.6 rmind void
209 1.22 christos npf_conn_load(npf_t *npf, npf_conndb_t *ndb, bool track)
210 1.1 rmind {
211 1.6 rmind npf_conndb_t *odb = NULL;
212 1.1 rmind
213 1.22 christos KASSERT(npf_config_locked_p(npf));
214 1.1 rmind
215 1.1 rmind /*
216 1.6 rmind * The connection database is in the quiescent state.
217 1.6 rmind * Prevent G/C thread from running and install a new database.
218 1.1 rmind */
219 1.22 christos mutex_enter(&npf->conn_lock);
220 1.6 rmind if (ndb) {
221 1.32 rmind KASSERT(atomic_load_relaxed(&npf->conn_tracking)
222 1.32 rmind == CONN_TRACKING_OFF);
223 1.32 rmind odb = atomic_load_relaxed(&npf->conn_db);
224 1.34 riastrad atomic_store_release(&npf->conn_db, ndb);
225 1.6 rmind }
226 1.6 rmind if (track) {
227 1.6 rmind /* After this point lookups start flying in. */
228 1.32 rmind membar_producer();
229 1.32 rmind atomic_store_relaxed(&npf->conn_tracking, CONN_TRACKING_ON);
230 1.1 rmind }
231 1.22 christos mutex_exit(&npf->conn_lock);
232 1.1 rmind
233 1.1 rmind if (odb) {
234 1.6 rmind /*
235 1.6 rmind * Flush all, no sync since the caller did it for us.
236 1.6 rmind * Also, release the pool cache memory.
237 1.6 rmind */
238 1.26 rmind npf_conndb_gc(npf, odb, true, false);
239 1.1 rmind npf_conndb_destroy(odb);
240 1.27 rmind pool_cache_invalidate(npf->conn_cache[0]);
241 1.27 rmind pool_cache_invalidate(npf->conn_cache[1]);
242 1.1 rmind }
243 1.1 rmind }
244 1.1 rmind
245 1.1 rmind /*
246 1.1 rmind * npf_conn_tracking: enable/disable connection tracking.
247 1.1 rmind */
248 1.1 rmind void
249 1.22 christos npf_conn_tracking(npf_t *npf, bool track)
250 1.1 rmind {
251 1.22 christos KASSERT(npf_config_locked_p(npf));
252 1.32 rmind atomic_store_relaxed(&npf->conn_tracking,
253 1.32 rmind track ? CONN_TRACKING_ON : CONN_TRACKING_OFF);
254 1.1 rmind }
255 1.1 rmind
256 1.6 rmind static inline bool
257 1.1 rmind npf_conn_trackable_p(const npf_cache_t *npc)
258 1.1 rmind {
259 1.22 christos const npf_t *npf = npc->npc_ctx;
260 1.22 christos
261 1.1 rmind /*
262 1.1 rmind * Check if connection tracking is on. Also, if layer 3 and 4 are
263 1.1 rmind * not cached - protocol is not supported or packet is invalid.
264 1.1 rmind */
265 1.32 rmind if (atomic_load_relaxed(&npf->conn_tracking) != CONN_TRACKING_ON) {
266 1.1 rmind return false;
267 1.1 rmind }
268 1.1 rmind if (!npf_iscached(npc, NPC_IP46) || !npf_iscached(npc, NPC_LAYER4)) {
269 1.1 rmind return false;
270 1.1 rmind }
271 1.1 rmind return true;
272 1.1 rmind }
273 1.1 rmind
274 1.22 christos static inline void
275 1.22 christos conn_update_atime(npf_conn_t *con)
276 1.22 christos {
277 1.22 christos struct timespec tsnow;
278 1.22 christos
279 1.22 christos getnanouptime(&tsnow);
280 1.32 rmind atomic_store_relaxed(&con->c_atime, tsnow.tv_sec);
281 1.22 christos }
282 1.22 christos
283 1.1 rmind /*
284 1.27 rmind * npf_conn_check: check that:
285 1.27 rmind *
286 1.27 rmind * - the connection is active;
287 1.27 rmind *
288 1.27 rmind * - the packet is travelling in the right direction with the respect
289 1.27 rmind * to the connection direction (if interface-id is not zero);
290 1.27 rmind *
291 1.27 rmind * - the packet is travelling on the same interface as the
292 1.27 rmind * connection interface (if interface-id is not zero).
293 1.18 christos */
294 1.18 christos static bool
295 1.27 rmind npf_conn_check(const npf_conn_t *con, const nbuf_t *nbuf,
296 1.32 rmind const unsigned di, const npf_flow_t flow)
297 1.18 christos {
298 1.32 rmind const uint32_t flags = atomic_load_relaxed(&con->c_flags);
299 1.32 rmind const unsigned ifid = atomic_load_relaxed(&con->c_ifid);
300 1.32 rmind bool active;
301 1.18 christos
302 1.27 rmind active = (flags & (CONN_ACTIVE | CONN_EXPIRE)) == CONN_ACTIVE;
303 1.27 rmind if (__predict_false(!active)) {
304 1.18 christos return false;
305 1.18 christos }
306 1.27 rmind if (ifid && nbuf) {
307 1.32 rmind const bool match = (flags & PFIL_ALL) == di;
308 1.32 rmind npf_flow_t pflow = match ? NPF_FLOW_FORW : NPF_FLOW_BACK;
309 1.32 rmind
310 1.32 rmind if (__predict_false(flow != pflow)) {
311 1.27 rmind return false;
312 1.27 rmind }
313 1.27 rmind if (__predict_false(ifid != nbuf->nb_ifid)) {
314 1.27 rmind return false;
315 1.27 rmind }
316 1.18 christos }
317 1.18 christos return true;
318 1.18 christos }
319 1.18 christos
320 1.18 christos /*
321 1.1 rmind * npf_conn_lookup: lookup if there is an established connection.
322 1.1 rmind *
323 1.1 rmind * => If found, we will hold a reference for the caller.
324 1.1 rmind */
325 1.1 rmind npf_conn_t *
326 1.32 rmind npf_conn_lookup(const npf_cache_t *npc, const unsigned di, npf_flow_t *flow)
327 1.1 rmind {
328 1.22 christos npf_t *npf = npc->npc_ctx;
329 1.4 rmind const nbuf_t *nbuf = npc->npc_nbuf;
330 1.1 rmind npf_conn_t *con;
331 1.1 rmind npf_connkey_t key;
332 1.1 rmind
333 1.1 rmind /* Construct a key and lookup for a connection in the store. */
334 1.32 rmind if (!npf_conn_conkey(npc, &key, di, NPF_FLOW_FORW)) {
335 1.1 rmind return NULL;
336 1.1 rmind }
337 1.32 rmind con = npf_conndb_lookup(npf, &key, flow);
338 1.1 rmind if (con == NULL) {
339 1.1 rmind return NULL;
340 1.1 rmind }
341 1.32 rmind KASSERT(npc->npc_proto == atomic_load_relaxed(&con->c_proto));
342 1.1 rmind
343 1.27 rmind /* Extra checks for the connection and packet. */
344 1.32 rmind if (!npf_conn_check(con, nbuf, di, *flow)) {
345 1.1 rmind atomic_dec_uint(&con->c_refcnt);
346 1.1 rmind return NULL;
347 1.1 rmind }
348 1.1 rmind
349 1.1 rmind /* Update the last activity time. */
350 1.22 christos conn_update_atime(con);
351 1.1 rmind return con;
352 1.1 rmind }
353 1.1 rmind
354 1.1 rmind /*
355 1.1 rmind * npf_conn_inspect: lookup a connection and inspecting the protocol data.
356 1.1 rmind *
357 1.1 rmind * => If found, we will hold a reference for the caller.
358 1.1 rmind */
359 1.1 rmind npf_conn_t *
360 1.32 rmind npf_conn_inspect(npf_cache_t *npc, const unsigned di, int *error)
361 1.1 rmind {
362 1.4 rmind nbuf_t *nbuf = npc->npc_nbuf;
363 1.32 rmind npf_flow_t flow;
364 1.1 rmind npf_conn_t *con;
365 1.32 rmind bool ok;
366 1.1 rmind
367 1.1 rmind KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
368 1.1 rmind if (!npf_conn_trackable_p(npc)) {
369 1.1 rmind return NULL;
370 1.1 rmind }
371 1.1 rmind
372 1.1 rmind /* Query ALG which may lookup connection for us. */
373 1.4 rmind if ((con = npf_alg_conn(npc, di)) != NULL) {
374 1.1 rmind /* Note: reference is held. */
375 1.1 rmind return con;
376 1.1 rmind }
377 1.1 rmind if (nbuf_head_mbuf(nbuf) == NULL) {
378 1.1 rmind *error = ENOMEM;
379 1.1 rmind return NULL;
380 1.1 rmind }
381 1.1 rmind KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
382 1.1 rmind
383 1.32 rmind /* The main lookup of the connection (acquires a reference). */
384 1.32 rmind if ((con = npf_conn_lookup(npc, di, &flow)) == NULL) {
385 1.1 rmind return NULL;
386 1.1 rmind }
387 1.1 rmind
388 1.1 rmind /* Inspect the protocol data and handle state changes. */
389 1.1 rmind mutex_enter(&con->c_lock);
390 1.32 rmind ok = npf_state_inspect(npc, &con->c_state, flow);
391 1.1 rmind mutex_exit(&con->c_lock);
392 1.1 rmind
393 1.17 rmind /* If invalid state: let the rules deal with it. */
394 1.1 rmind if (__predict_false(!ok)) {
395 1.1 rmind npf_conn_release(con);
396 1.22 christos npf_stats_inc(npc->npc_ctx, NPF_STAT_INVALID_STATE);
397 1.17 rmind return NULL;
398 1.17 rmind }
399 1.32 rmind #if 0
400 1.17 rmind /*
401 1.32 rmind * TODO -- determine when this might be wanted/used.
402 1.32 rmind *
403 1.32 rmind * Note: skipping the connection lookup and ruleset inspection
404 1.32 rmind * on other interfaces will also bypass dynamic NAT.
405 1.17 rmind */
406 1.32 rmind if (atomic_load_relaxed(&con->c_flags) & CONN_GPASS) {
407 1.32 rmind /*
408 1.32 rmind * Note: if tagging fails, then give this packet a chance
409 1.32 rmind * to go through a regular ruleset.
410 1.32 rmind */
411 1.32 rmind (void)nbuf_add_tag(nbuf, NPF_NTAG_PASS);
412 1.1 rmind }
413 1.32 rmind #endif
414 1.1 rmind return con;
415 1.1 rmind }
416 1.1 rmind
417 1.1 rmind /*
418 1.1 rmind * npf_conn_establish: create a new connection, insert into the global list.
419 1.1 rmind *
420 1.1 rmind * => Connection is created with the reference held for the caller.
421 1.1 rmind * => Connection will be activated on the first reference release.
422 1.1 rmind */
423 1.1 rmind npf_conn_t *
424 1.32 rmind npf_conn_establish(npf_cache_t *npc, const unsigned di, bool global)
425 1.1 rmind {
426 1.22 christos npf_t *npf = npc->npc_ctx;
427 1.27 rmind const unsigned alen = npc->npc_alen;
428 1.27 rmind const unsigned idx = NPF_CONNCACHE(alen);
429 1.4 rmind const nbuf_t *nbuf = npc->npc_nbuf;
430 1.27 rmind npf_connkey_t *fw, *bk;
431 1.32 rmind npf_conndb_t *conn_db;
432 1.1 rmind npf_conn_t *con;
433 1.15 rmind int error = 0;
434 1.1 rmind
435 1.1 rmind KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
436 1.1 rmind
437 1.1 rmind if (!npf_conn_trackable_p(npc)) {
438 1.1 rmind return NULL;
439 1.1 rmind }
440 1.1 rmind
441 1.32 rmind /* Allocate and initialize the new connection. */
442 1.27 rmind con = pool_cache_get(npf->conn_cache[idx], PR_NOWAIT);
443 1.1 rmind if (__predict_false(!con)) {
444 1.22 christos npf_worker_signal(npf);
445 1.1 rmind return NULL;
446 1.1 rmind }
447 1.1 rmind NPF_PRINTF(("NPF: create conn %p\n", con));
448 1.22 christos npf_stats_inc(npf, NPF_STAT_CONN_CREATE);
449 1.1 rmind
450 1.1 rmind mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET);
451 1.32 rmind atomic_store_relaxed(&con->c_flags, di & PFIL_ALL);
452 1.32 rmind atomic_store_relaxed(&con->c_refcnt, 0);
453 1.1 rmind con->c_rproc = NULL;
454 1.1 rmind con->c_nat = NULL;
455 1.1 rmind
456 1.27 rmind con->c_proto = npc->npc_proto;
457 1.27 rmind CTASSERT(sizeof(con->c_proto) >= sizeof(npc->npc_proto));
458 1.29 christos con->c_alen = alen;
459 1.27 rmind
460 1.15 rmind /* Initialize the protocol state. */
461 1.4 rmind if (!npf_state_init(npc, &con->c_state)) {
462 1.29 christos npf_conn_destroy(npf, con);
463 1.15 rmind return NULL;
464 1.1 rmind }
465 1.27 rmind KASSERT(npf_iscached(npc, NPC_IP46));
466 1.1 rmind
467 1.27 rmind fw = npf_conn_getforwkey(con);
468 1.27 rmind bk = npf_conn_getbackkey(con, alen);
469 1.1 rmind
470 1.1 rmind /*
471 1.1 rmind * Construct "forwards" and "backwards" keys. Also, set the
472 1.1 rmind * interface ID for this connection (unless it is global).
473 1.1 rmind */
474 1.32 rmind if (!npf_conn_conkey(npc, fw, di, NPF_FLOW_FORW) ||
475 1.32 rmind !npf_conn_conkey(npc, bk, di ^ PFIL_ALL, NPF_FLOW_BACK)) {
476 1.29 christos npf_conn_destroy(npf, con);
477 1.15 rmind return NULL;
478 1.1 rmind }
479 1.27 rmind con->c_ifid = global ? nbuf->nb_ifid : 0;
480 1.1 rmind
481 1.15 rmind /*
482 1.15 rmind * Set last activity time for a new connection and acquire
483 1.15 rmind * a reference for the caller before we make it visible.
484 1.15 rmind */
485 1.22 christos conn_update_atime(con);
486 1.32 rmind atomic_store_relaxed(&con->c_refcnt, 1);
487 1.1 rmind
488 1.1 rmind /*
489 1.1 rmind * Insert both keys (entries representing directions) of the
490 1.15 rmind * connection. At this point it becomes visible, but we activate
491 1.15 rmind * the connection later.
492 1.1 rmind */
493 1.15 rmind mutex_enter(&con->c_lock);
494 1.34 riastrad conn_db = atomic_load_consume(&npf->conn_db);
495 1.32 rmind if (!npf_conndb_insert(conn_db, fw, con, NPF_FLOW_FORW)) {
496 1.15 rmind error = EISCONN;
497 1.1 rmind goto err;
498 1.1 rmind }
499 1.32 rmind if (!npf_conndb_insert(conn_db, bk, con, NPF_FLOW_BACK)) {
500 1.15 rmind npf_conn_t *ret __diagused;
501 1.32 rmind ret = npf_conndb_remove(conn_db, fw);
502 1.15 rmind KASSERT(ret == con);
503 1.15 rmind error = EISCONN;
504 1.15 rmind goto err;
505 1.15 rmind }
506 1.15 rmind err:
507 1.15 rmind /*
508 1.15 rmind * If we have hit the duplicate: mark the connection as expired
509 1.15 rmind * and let the G/C thread to take care of it. We cannot do it
510 1.15 rmind * here since there might be references acquired already.
511 1.15 rmind */
512 1.15 rmind if (error) {
513 1.16 rmind atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE);
514 1.16 rmind atomic_dec_uint(&con->c_refcnt);
515 1.22 christos npf_stats_inc(npf, NPF_STAT_RACE_CONN);
516 1.15 rmind } else {
517 1.15 rmind NPF_PRINTF(("NPF: establish conn %p\n", con));
518 1.1 rmind }
519 1.1 rmind
520 1.1 rmind /* Finally, insert into the connection list. */
521 1.32 rmind npf_conndb_enqueue(conn_db, con);
522 1.15 rmind mutex_exit(&con->c_lock);
523 1.15 rmind
524 1.15 rmind return error ? NULL : con;
525 1.1 rmind }
526 1.1 rmind
527 1.26 rmind void
528 1.22 christos npf_conn_destroy(npf_t *npf, npf_conn_t *con)
529 1.1 rmind {
530 1.29 christos const unsigned idx __unused = NPF_CONNCACHE(con->c_alen);
531 1.27 rmind
532 1.32 rmind KASSERT(atomic_load_relaxed(&con->c_refcnt) == 0);
533 1.15 rmind
534 1.1 rmind if (con->c_nat) {
535 1.1 rmind /* Release any NAT structures. */
536 1.32 rmind npf_nat_destroy(con, con->c_nat);
537 1.1 rmind }
538 1.1 rmind if (con->c_rproc) {
539 1.1 rmind /* Release the rule procedure. */
540 1.1 rmind npf_rproc_release(con->c_rproc);
541 1.1 rmind }
542 1.1 rmind
543 1.1 rmind /* Destroy the state. */
544 1.1 rmind npf_state_destroy(&con->c_state);
545 1.1 rmind mutex_destroy(&con->c_lock);
546 1.1 rmind
547 1.1 rmind /* Free the structure, increase the counter. */
548 1.27 rmind pool_cache_put(npf->conn_cache[idx], con);
549 1.22 christos npf_stats_inc(npf, NPF_STAT_CONN_DESTROY);
550 1.1 rmind NPF_PRINTF(("NPF: conn %p destroyed\n", con));
551 1.1 rmind }
552 1.1 rmind
553 1.1 rmind /*
554 1.1 rmind * npf_conn_setnat: associate NAT entry with the connection, update and
555 1.1 rmind * re-insert connection entry using the translation values.
556 1.16 rmind *
557 1.16 rmind * => The caller must be holding a reference.
558 1.1 rmind */
559 1.1 rmind int
560 1.1 rmind npf_conn_setnat(const npf_cache_t *npc, npf_conn_t *con,
561 1.27 rmind npf_nat_t *nt, unsigned ntype)
562 1.1 rmind {
563 1.32 rmind static const unsigned nat_type_which[] = {
564 1.32 rmind /* See the description in npf_nat_which(). */
565 1.1 rmind [NPF_NATOUT] = NPF_DST,
566 1.1 rmind [NPF_NATIN] = NPF_SRC,
567 1.1 rmind };
568 1.22 christos npf_t *npf = npc->npc_ctx;
569 1.2 rmind npf_conn_t *ret __diagused;
570 1.32 rmind npf_conndb_t *conn_db;
571 1.32 rmind npf_connkey_t *bk;
572 1.1 rmind npf_addr_t *taddr;
573 1.1 rmind in_port_t tport;
574 1.32 rmind uint32_t flags;
575 1.1 rmind
576 1.32 rmind KASSERT(atomic_load_relaxed(&con->c_refcnt) > 0);
577 1.1 rmind
578 1.1 rmind npf_nat_gettrans(nt, &taddr, &tport);
579 1.1 rmind KASSERT(ntype == NPF_NATOUT || ntype == NPF_NATIN);
580 1.1 rmind
581 1.1 rmind /* Acquire the lock and check for the races. */
582 1.1 rmind mutex_enter(&con->c_lock);
583 1.32 rmind flags = atomic_load_relaxed(&con->c_flags);
584 1.32 rmind if (__predict_false(flags & CONN_EXPIRE)) {
585 1.1 rmind /* The connection got expired. */
586 1.1 rmind mutex_exit(&con->c_lock);
587 1.1 rmind return EINVAL;
588 1.1 rmind }
589 1.32 rmind KASSERT((flags & CONN_REMOVED) == 0);
590 1.15 rmind
591 1.1 rmind if (__predict_false(con->c_nat != NULL)) {
592 1.1 rmind /* Race with a duplicate packet. */
593 1.1 rmind mutex_exit(&con->c_lock);
594 1.22 christos npf_stats_inc(npc->npc_ctx, NPF_STAT_RACE_NAT);
595 1.1 rmind return EISCONN;
596 1.1 rmind }
597 1.1 rmind
598 1.27 rmind /* Remove the "backwards" key. */
599 1.34 riastrad conn_db = atomic_load_consume(&npf->conn_db);
600 1.32 rmind bk = npf_conn_getbackkey(con, con->c_alen);
601 1.32 rmind ret = npf_conndb_remove(conn_db, bk);
602 1.1 rmind KASSERT(ret == con);
603 1.1 rmind
604 1.1 rmind /* Set the source/destination IDs to the translation values. */
605 1.32 rmind npf_conn_adjkey(bk, taddr, tport, nat_type_which[ntype]);
606 1.1 rmind
607 1.27 rmind /* Finally, re-insert the "backwards" key. */
608 1.32 rmind if (!npf_conndb_insert(conn_db, bk, con, NPF_FLOW_BACK)) {
609 1.1 rmind /*
610 1.1 rmind * Race: we have hit the duplicate, remove the "forwards"
611 1.27 rmind * key and expire our connection; it is no longer valid.
612 1.1 rmind */
613 1.32 rmind npf_connkey_t *fw = npf_conn_getforwkey(con);
614 1.32 rmind ret = npf_conndb_remove(conn_db, fw);
615 1.15 rmind KASSERT(ret == con);
616 1.15 rmind
617 1.1 rmind atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE);
618 1.1 rmind mutex_exit(&con->c_lock);
619 1.1 rmind
620 1.22 christos npf_stats_inc(npc->npc_ctx, NPF_STAT_RACE_NAT);
621 1.1 rmind return EISCONN;
622 1.1 rmind }
623 1.1 rmind
624 1.1 rmind /* Associate the NAT entry and release the lock. */
625 1.1 rmind con->c_nat = nt;
626 1.1 rmind mutex_exit(&con->c_lock);
627 1.1 rmind return 0;
628 1.1 rmind }
629 1.1 rmind
630 1.1 rmind /*
631 1.1 rmind * npf_conn_expire: explicitly mark connection as expired.
632 1.32 rmind *
633 1.32 rmind * => Must be called with: a) reference held b) the relevant lock held.
634 1.32 rmind * The relevant lock should prevent from connection destruction, e.g.
635 1.32 rmind * npf_t::conn_lock or npf_natpolicy_t::n_lock.
636 1.1 rmind */
637 1.1 rmind void
638 1.1 rmind npf_conn_expire(npf_conn_t *con)
639 1.1 rmind {
640 1.1 rmind atomic_or_uint(&con->c_flags, CONN_EXPIRE);
641 1.1 rmind }
642 1.1 rmind
643 1.1 rmind /*
644 1.1 rmind * npf_conn_pass: return true if connection is "pass" one, otherwise false.
645 1.1 rmind */
646 1.1 rmind bool
647 1.23 christos npf_conn_pass(const npf_conn_t *con, npf_match_info_t *mi, npf_rproc_t **rp)
648 1.1 rmind {
649 1.32 rmind KASSERT(atomic_load_relaxed(&con->c_refcnt) > 0);
650 1.32 rmind if (__predict_true(atomic_load_relaxed(&con->c_flags) & CONN_PASS)) {
651 1.32 rmind mi->mi_retfl = atomic_load_relaxed(&con->c_retfl);
652 1.24 rmind mi->mi_rid = con->c_rid;
653 1.1 rmind *rp = con->c_rproc;
654 1.1 rmind return true;
655 1.1 rmind }
656 1.1 rmind return false;
657 1.1 rmind }
658 1.1 rmind
659 1.1 rmind /*
660 1.1 rmind * npf_conn_setpass: mark connection as a "pass" one and associate the
661 1.1 rmind * rule procedure with it.
662 1.1 rmind */
663 1.1 rmind void
664 1.23 christos npf_conn_setpass(npf_conn_t *con, const npf_match_info_t *mi, npf_rproc_t *rp)
665 1.1 rmind {
666 1.32 rmind KASSERT((atomic_load_relaxed(&con->c_flags) & CONN_ACTIVE) == 0);
667 1.32 rmind KASSERT(atomic_load_relaxed(&con->c_refcnt) > 0);
668 1.1 rmind KASSERT(con->c_rproc == NULL);
669 1.1 rmind
670 1.1 rmind /*
671 1.1 rmind * No need for atomic since the connection is not yet active.
672 1.1 rmind * If rproc is set, the caller transfers its reference to us,
673 1.1 rmind * which will be released on npf_conn_destroy().
674 1.1 rmind */
675 1.14 rmind atomic_or_uint(&con->c_flags, CONN_PASS);
676 1.1 rmind con->c_rproc = rp;
677 1.24 rmind if (rp) {
678 1.24 rmind con->c_rid = mi->mi_rid;
679 1.24 rmind con->c_retfl = mi->mi_retfl;
680 1.24 rmind }
681 1.1 rmind }
682 1.1 rmind
683 1.1 rmind /*
684 1.1 rmind * npf_conn_release: release a reference, which might allow G/C thread
685 1.1 rmind * to destroy this connection.
686 1.1 rmind */
687 1.1 rmind void
688 1.1 rmind npf_conn_release(npf_conn_t *con)
689 1.1 rmind {
690 1.32 rmind const unsigned flags = atomic_load_relaxed(&con->c_flags);
691 1.32 rmind
692 1.32 rmind if ((flags & (CONN_ACTIVE | CONN_EXPIRE)) == 0) {
693 1.1 rmind /* Activate: after this, connection is globally visible. */
694 1.14 rmind atomic_or_uint(&con->c_flags, CONN_ACTIVE);
695 1.1 rmind }
696 1.32 rmind KASSERT(atomic_load_relaxed(&con->c_refcnt) > 0);
697 1.1 rmind atomic_dec_uint(&con->c_refcnt);
698 1.1 rmind }
699 1.1 rmind
700 1.1 rmind /*
701 1.32 rmind * npf_conn_getnat: return the associated NAT entry, if any.
702 1.1 rmind */
703 1.1 rmind npf_nat_t *
704 1.32 rmind npf_conn_getnat(const npf_conn_t *con)
705 1.1 rmind {
706 1.1 rmind return con->c_nat;
707 1.1 rmind }
708 1.1 rmind
709 1.1 rmind /*
710 1.1 rmind * npf_conn_expired: criterion to check if connection is expired.
711 1.1 rmind */
712 1.26 rmind bool
713 1.27 rmind npf_conn_expired(npf_t *npf, const npf_conn_t *con, uint64_t tsnow)
714 1.1 rmind {
715 1.32 rmind const unsigned flags = atomic_load_relaxed(&con->c_flags);
716 1.27 rmind const int etime = npf_state_etime(npf, &con->c_state, con->c_proto);
717 1.22 christos int elapsed;
718 1.1 rmind
719 1.32 rmind if (__predict_false(flags & CONN_EXPIRE)) {
720 1.1 rmind /* Explicitly marked to be expired. */
721 1.1 rmind return true;
722 1.1 rmind }
723 1.22 christos
724 1.22 christos /*
725 1.22 christos * Note: another thread may update 'atime' and it might
726 1.22 christos * become greater than 'now'.
727 1.22 christos */
728 1.32 rmind elapsed = (int64_t)tsnow - atomic_load_relaxed(&con->c_atime);
729 1.22 christos return elapsed > etime;
730 1.1 rmind }
731 1.1 rmind
732 1.1 rmind /*
733 1.26 rmind * npf_conn_remove: unlink the connection and mark as expired.
734 1.1 rmind */
735 1.7 rmind void
736 1.26 rmind npf_conn_remove(npf_conndb_t *cd, npf_conn_t *con)
737 1.1 rmind {
738 1.26 rmind /* Remove both entries of the connection. */
739 1.26 rmind mutex_enter(&con->c_lock);
740 1.32 rmind if ((atomic_load_relaxed(&con->c_flags) & CONN_REMOVED) == 0) {
741 1.27 rmind npf_connkey_t *fw, *bk;
742 1.26 rmind npf_conn_t *ret __diagused;
743 1.1 rmind
744 1.27 rmind fw = npf_conn_getforwkey(con);
745 1.27 rmind ret = npf_conndb_remove(cd, fw);
746 1.26 rmind KASSERT(ret == con);
747 1.27 rmind
748 1.27 rmind bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
749 1.27 rmind ret = npf_conndb_remove(cd, bk);
750 1.26 rmind KASSERT(ret == con);
751 1.1 rmind }
752 1.6 rmind
753 1.26 rmind /* Flag the removal and expiration. */
754 1.26 rmind atomic_or_uint(&con->c_flags, CONN_REMOVED | CONN_EXPIRE);
755 1.26 rmind mutex_exit(&con->c_lock);
756 1.1 rmind }
757 1.1 rmind
758 1.6 rmind /*
759 1.32 rmind * npf_conn_worker: G/C to run from a worker thread or via npfk_gc().
760 1.6 rmind */
761 1.22 christos void
762 1.22 christos npf_conn_worker(npf_t *npf)
763 1.1 rmind {
764 1.34 riastrad npf_conndb_t *conn_db = atomic_load_consume(&npf->conn_db);
765 1.32 rmind npf_conndb_gc(npf, conn_db, false, true);
766 1.1 rmind }
767 1.1 rmind
768 1.1 rmind /*
769 1.10 rmind * npf_conndb_export: construct a list of connections prepared for saving.
770 1.1 rmind * Note: this is expected to be an expensive operation.
771 1.1 rmind */
772 1.1 rmind int
773 1.32 rmind npf_conndb_export(npf_t *npf, nvlist_t *nvl)
774 1.1 rmind {
775 1.26 rmind npf_conn_t *head, *con;
776 1.32 rmind npf_conndb_t *conn_db;
777 1.1 rmind
778 1.1 rmind /*
779 1.1 rmind * Note: acquire conn_lock to prevent from the database
780 1.1 rmind * destruction and G/C thread.
781 1.1 rmind */
782 1.22 christos mutex_enter(&npf->conn_lock);
783 1.32 rmind if (atomic_load_relaxed(&npf->conn_tracking) != CONN_TRACKING_ON) {
784 1.22 christos mutex_exit(&npf->conn_lock);
785 1.1 rmind return 0;
786 1.1 rmind }
787 1.32 rmind conn_db = atomic_load_relaxed(&npf->conn_db);
788 1.32 rmind head = npf_conndb_getlist(conn_db);
789 1.26 rmind con = head;
790 1.1 rmind while (con) {
791 1.32 rmind nvlist_t *con_nvl;
792 1.1 rmind
793 1.32 rmind con_nvl = nvlist_create(0);
794 1.32 rmind if (npf_conn_export(npf, con, con_nvl) == 0) {
795 1.32 rmind nvlist_append_nvlist_array(nvl, "conn-list", con_nvl);
796 1.1 rmind }
797 1.32 rmind nvlist_destroy(con_nvl);
798 1.32 rmind
799 1.32 rmind if ((con = npf_conndb_getnext(conn_db, con)) == head) {
800 1.26 rmind break;
801 1.26 rmind }
802 1.1 rmind }
803 1.22 christos mutex_exit(&npf->conn_lock);
804 1.5 joerg return 0;
805 1.1 rmind }
806 1.1 rmind
807 1.1 rmind /*
808 1.32 rmind * npf_conn_export: serialize a single connection.
809 1.10 rmind */
810 1.32 rmind static int
811 1.32 rmind npf_conn_export(npf_t *npf, npf_conn_t *con, nvlist_t *nvl)
812 1.10 rmind {
813 1.32 rmind nvlist_t *knvl;
814 1.27 rmind npf_connkey_t *fw, *bk;
815 1.32 rmind unsigned flags, alen;
816 1.10 rmind
817 1.32 rmind flags = atomic_load_relaxed(&con->c_flags);
818 1.32 rmind if ((flags & (CONN_ACTIVE|CONN_EXPIRE)) != CONN_ACTIVE) {
819 1.32 rmind return ESRCH;
820 1.10 rmind }
821 1.32 rmind nvlist_add_number(nvl, "flags", flags);
822 1.32 rmind nvlist_add_number(nvl, "proto", con->c_proto);
823 1.10 rmind if (con->c_ifid) {
824 1.30 rmind char ifname[IFNAMSIZ];
825 1.30 rmind npf_ifmap_copyname(npf, con->c_ifid, ifname, sizeof(ifname));
826 1.32 rmind nvlist_add_string(nvl, "ifname", ifname);
827 1.10 rmind }
828 1.32 rmind nvlist_add_binary(nvl, "state", &con->c_state, sizeof(npf_state_t));
829 1.10 rmind
830 1.27 rmind fw = npf_conn_getforwkey(con);
831 1.27 rmind alen = NPF_CONNKEY_ALEN(fw);
832 1.29 christos KASSERT(alen == con->c_alen);
833 1.27 rmind bk = npf_conn_getbackkey(con, alen);
834 1.27 rmind
835 1.32 rmind knvl = npf_connkey_export(npf, fw);
836 1.32 rmind nvlist_move_nvlist(nvl, "forw-key", knvl);
837 1.10 rmind
838 1.32 rmind knvl = npf_connkey_export(npf, bk);
839 1.32 rmind nvlist_move_nvlist(nvl, "back-key", knvl);
840 1.10 rmind
841 1.27 rmind /* Let the address length be based on on first key. */
842 1.32 rmind nvlist_add_number(nvl, "alen", alen);
843 1.27 rmind
844 1.10 rmind if (con->c_nat) {
845 1.32 rmind npf_nat_export(npf, con->c_nat, nvl);
846 1.10 rmind }
847 1.32 rmind return 0;
848 1.10 rmind }
849 1.10 rmind
850 1.10 rmind /*
851 1.6 rmind * npf_conn_import: fully reconstruct a single connection from a
852 1.25 rmind * nvlist and insert into the given database.
853 1.1 rmind */
854 1.1 rmind int
855 1.25 rmind npf_conn_import(npf_t *npf, npf_conndb_t *cd, const nvlist_t *cdict,
856 1.6 rmind npf_ruleset_t *natlist)
857 1.1 rmind {
858 1.1 rmind npf_conn_t *con;
859 1.1 rmind npf_connkey_t *fw, *bk;
860 1.25 rmind const nvlist_t *nat, *conkey;
861 1.32 rmind unsigned flags, alen, idx;
862 1.10 rmind const char *ifname;
863 1.25 rmind const void *state;
864 1.25 rmind size_t len;
865 1.1 rmind
866 1.27 rmind /*
867 1.27 rmind * To determine the length of the connection, which depends
868 1.27 rmind * on the address length in the connection keys.
869 1.27 rmind */
870 1.27 rmind alen = dnvlist_get_number(cdict, "alen", 0);
871 1.27 rmind idx = NPF_CONNCACHE(alen);
872 1.27 rmind
873 1.32 rmind /* Allocate a connection and initialize it (clear first). */
874 1.27 rmind con = pool_cache_get(npf->conn_cache[idx], PR_WAITOK);
875 1.1 rmind memset(con, 0, sizeof(npf_conn_t));
876 1.1 rmind mutex_init(&con->c_lock, MUTEX_DEFAULT, IPL_SOFTNET);
877 1.22 christos npf_stats_inc(npf, NPF_STAT_CONN_CREATE);
878 1.1 rmind
879 1.25 rmind con->c_proto = dnvlist_get_number(cdict, "proto", 0);
880 1.32 rmind flags = dnvlist_get_number(cdict, "flags", 0);
881 1.32 rmind flags &= PFIL_ALL | CONN_ACTIVE | CONN_PASS;
882 1.32 rmind atomic_store_relaxed(&con->c_flags, flags);
883 1.22 christos conn_update_atime(con);
884 1.1 rmind
885 1.25 rmind ifname = dnvlist_get_string(cdict, "ifname", NULL);
886 1.25 rmind if (ifname && (con->c_ifid = npf_ifmap_register(npf, ifname)) == 0) {
887 1.10 rmind goto err;
888 1.10 rmind }
889 1.10 rmind
890 1.25 rmind state = dnvlist_get_binary(cdict, "state", &len, NULL, 0);
891 1.25 rmind if (!state || len != sizeof(npf_state_t)) {
892 1.1 rmind goto err;
893 1.1 rmind }
894 1.25 rmind memcpy(&con->c_state, state, sizeof(npf_state_t));
895 1.1 rmind
896 1.11 rmind /* Reconstruct NAT association, if any. */
897 1.25 rmind if ((nat = dnvlist_get_nvlist(cdict, "nat", NULL)) != NULL &&
898 1.25 rmind (con->c_nat = npf_nat_import(npf, nat, natlist, con)) == NULL) {
899 1.11 rmind goto err;
900 1.11 rmind }
901 1.1 rmind
902 1.1 rmind /*
903 1.1 rmind * Fetch and copy the keys for each direction.
904 1.1 rmind */
905 1.27 rmind fw = npf_conn_getforwkey(con);
906 1.25 rmind conkey = dnvlist_get_nvlist(cdict, "forw-key", NULL);
907 1.32 rmind if (conkey == NULL || !npf_connkey_import(npf, conkey, fw)) {
908 1.1 rmind goto err;
909 1.1 rmind }
910 1.27 rmind bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
911 1.25 rmind conkey = dnvlist_get_nvlist(cdict, "back-key", NULL);
912 1.32 rmind if (conkey == NULL || !npf_connkey_import(npf, conkey, bk)) {
913 1.1 rmind goto err;
914 1.1 rmind }
915 1.27 rmind
916 1.27 rmind /* Guard against the contradicting address lengths. */
917 1.27 rmind if (NPF_CONNKEY_ALEN(fw) != alen || NPF_CONNKEY_ALEN(bk) != alen) {
918 1.27 rmind goto err;
919 1.27 rmind }
920 1.1 rmind
921 1.1 rmind /* Insert the entries and the connection itself. */
922 1.32 rmind if (!npf_conndb_insert(cd, fw, con, NPF_FLOW_FORW)) {
923 1.1 rmind goto err;
924 1.1 rmind }
925 1.32 rmind if (!npf_conndb_insert(cd, bk, con, NPF_FLOW_BACK)) {
926 1.1 rmind npf_conndb_remove(cd, fw);
927 1.1 rmind goto err;
928 1.1 rmind }
929 1.12 rmind
930 1.12 rmind NPF_PRINTF(("NPF: imported conn %p\n", con));
931 1.1 rmind npf_conndb_enqueue(cd, con);
932 1.1 rmind return 0;
933 1.1 rmind err:
934 1.29 christos npf_conn_destroy(npf, con);
935 1.1 rmind return EINVAL;
936 1.1 rmind }
937 1.1 rmind
938 1.32 rmind /*
939 1.32 rmind * npf_conn_find: lookup a connection in the list of connections
940 1.32 rmind */
941 1.20 christos int
942 1.32 rmind npf_conn_find(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
943 1.20 christos {
944 1.32 rmind const nvlist_t *key_nv;
945 1.32 rmind npf_conn_t *con;
946 1.20 christos npf_connkey_t key;
947 1.32 rmind npf_flow_t flow;
948 1.32 rmind int error;
949 1.20 christos
950 1.32 rmind key_nv = dnvlist_get_nvlist(req, "key", NULL);
951 1.32 rmind if (!key_nv || !npf_connkey_import(npf, key_nv, &key)) {
952 1.20 christos return EINVAL;
953 1.25 rmind }
954 1.32 rmind con = npf_conndb_lookup(npf, &key, &flow);
955 1.20 christos if (con == NULL) {
956 1.20 christos return ESRCH;
957 1.20 christos }
958 1.32 rmind if (!npf_conn_check(con, NULL, 0, NPF_FLOW_FORW)) {
959 1.20 christos atomic_dec_uint(&con->c_refcnt);
960 1.20 christos return ESRCH;
961 1.20 christos }
962 1.32 rmind error = npf_conn_export(npf, con, resp);
963 1.32 rmind nvlist_add_number(resp, "flow", flow);
964 1.20 christos atomic_dec_uint(&con->c_refcnt);
965 1.32 rmind return error;
966 1.20 christos }
967 1.20 christos
968 1.1 rmind #if defined(DDB) || defined(_NPF_TESTING)
969 1.1 rmind
970 1.1 rmind void
971 1.27 rmind npf_conn_print(npf_conn_t *con)
972 1.1 rmind {
973 1.27 rmind const npf_connkey_t *fw = npf_conn_getforwkey(con);
974 1.27 rmind const npf_connkey_t *bk = npf_conn_getbackkey(con, NPF_CONNKEY_ALEN(fw));
975 1.32 rmind const unsigned flags = atomic_load_relaxed(&con->c_flags);
976 1.27 rmind const unsigned proto = con->c_proto;
977 1.22 christos struct timespec tspnow;
978 1.1 rmind
979 1.22 christos getnanouptime(&tspnow);
980 1.22 christos printf("%p:\n\tproto %d flags 0x%x tsdiff %ld etime %d\n", con,
981 1.32 rmind proto, flags, (long)(tspnow.tv_sec - con->c_atime),
982 1.27 rmind npf_state_etime(npf_getkernctx(), &con->c_state, proto));
983 1.27 rmind npf_connkey_print(fw);
984 1.27 rmind npf_connkey_print(bk);
985 1.1 rmind npf_state_dump(&con->c_state);
986 1.1 rmind if (con->c_nat) {
987 1.1 rmind npf_nat_dump(con->c_nat);
988 1.1 rmind }
989 1.1 rmind }
990 1.1 rmind
991 1.1 rmind #endif
992