xref: /netbsd-src/sys/net/npf/npf_conndb.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /*	$NetBSD: npf_conndb.c,v 1.2 2014/07/23 01:25:34 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010-2014 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 connection storage.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: npf_conndb.c,v 1.2 2014/07/23 01:25:34 rmind Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/types.h>
41 
42 #include <sys/atomic.h>
43 #include <sys/cprng.h>
44 #include <sys/hash.h>
45 #include <sys/kmem.h>
46 
47 #define __NPF_CONN_PRIVATE
48 #include "npf_conn.h"
49 #include "npf_impl.h"
50 
51 #define	CONNDB_HASH_BUCKETS	1024	/* XXX tune + make tunable */
52 #define	CONNDB_HASH_MASK	(CONNDB_HASH_BUCKETS - 1)
53 
54 typedef struct {
55 	rb_tree_t		hb_tree;
56 	krwlock_t		hb_lock;
57 	u_int			hb_count;
58 } npf_hashbucket_t;
59 
60 struct npf_conndb {
61 	npf_conn_t *		cd_recent;
62 	npf_conn_t *		cd_list;
63 	npf_conn_t *		cd_tail;
64 	uint32_t		cd_seed;
65 	npf_hashbucket_t	cd_hashtbl[];
66 };
67 
68 /*
69  * Connection hash table and RB-tree helper routines.
70  * Note: (node1 < node2) shall return negative.
71  */
72 
73 static signed int
74 conndb_rbtree_cmp_nodes(void *ctx, const void *n1, const void *n2)
75 {
76 	const npf_connkey_t * const ck1 = n1;
77 	const npf_connkey_t * const ck2 = n2;
78 	const u_int keylen = MIN(NPF_CONN_KEYLEN(ck1), NPF_CONN_KEYLEN(ck2));
79 
80 	KASSERT((keylen >> 2) <= NPF_CONN_NKEYWORDS);
81 	return memcmp(ck1->ck_key, ck2->ck_key, keylen);
82 }
83 
84 static signed int
85 conndb_rbtree_cmp_key(void *ctx, const void *n1, const void *key)
86 {
87 	const npf_connkey_t * const ck1 = n1;
88 	const npf_connkey_t * const ck2 = key;
89 	return conndb_rbtree_cmp_nodes(ctx, ck1, ck2);
90 }
91 
92 static const rb_tree_ops_t conndb_rbtree_ops = {
93 	.rbto_compare_nodes	= conndb_rbtree_cmp_nodes,
94 	.rbto_compare_key	= conndb_rbtree_cmp_key,
95 	.rbto_node_offset	= offsetof(npf_connkey_t, ck_rbnode),
96 	.rbto_context		= NULL
97 };
98 
99 static npf_hashbucket_t *
100 conndb_hash_bucket(npf_conndb_t *cd, const npf_connkey_t *key)
101 {
102 	const u_int keylen = NPF_CONN_KEYLEN(key);
103 	uint32_t hash = murmurhash2(key->ck_key, keylen, cd->cd_seed);
104 	return &cd->cd_hashtbl[hash & CONNDB_HASH_MASK];
105 }
106 
107 npf_conndb_t *
108 npf_conndb_create(void)
109 {
110 	size_t len = offsetof(npf_conndb_t, cd_hashtbl[CONNDB_HASH_BUCKETS]);
111 	npf_conndb_t *cd;
112 
113 	cd = kmem_zalloc(len, KM_SLEEP);
114 	for (u_int i = 0; i < CONNDB_HASH_BUCKETS; i++) {
115 		npf_hashbucket_t *hb = &cd->cd_hashtbl[i];
116 
117 		rb_tree_init(&hb->hb_tree, &conndb_rbtree_ops);
118 		rw_init(&hb->hb_lock);
119 		hb->hb_count = 0;
120 	}
121 	cd->cd_seed = cprng_fast32();
122 	return cd;
123 }
124 
125 void
126 npf_conndb_destroy(npf_conndb_t *cd)
127 {
128 	size_t len = offsetof(npf_conndb_t, cd_hashtbl[CONNDB_HASH_BUCKETS]);
129 
130 	KASSERT(cd->cd_recent == NULL);
131 	KASSERT(cd->cd_list == NULL);
132 	KASSERT(cd->cd_tail == NULL);
133 
134 	for (u_int i = 0; i < CONNDB_HASH_BUCKETS; i++) {
135 		npf_hashbucket_t *hb = &cd->cd_hashtbl[i];
136 
137 		KASSERT(hb->hb_count == 0);
138 		KASSERT(!rb_tree_iterate(&hb->hb_tree, NULL, RB_DIR_LEFT));
139 		rw_destroy(&hb->hb_lock);
140 	}
141 	kmem_free(cd, len);
142 }
143 
144 /*
145  * npf_conndb_lookup: find a connection given the key.
146  */
147 npf_conn_t *
148 npf_conndb_lookup(npf_conndb_t *cd, const npf_connkey_t *key, bool *forw)
149 {
150 	npf_connkey_t *foundkey;
151 	npf_hashbucket_t *hb;
152 	npf_conn_t *con;
153 
154 	/* Get a hash bucket from the cached key data. */
155 	hb = conndb_hash_bucket(cd, key);
156 	if (hb->hb_count == 0) {
157 		return NULL;
158 	}
159 
160 	/* Lookup the tree given the key and get the actual connection. */
161 	rw_enter(&hb->hb_lock, RW_READER);
162 	foundkey = rb_tree_find_node(&hb->hb_tree, key);
163 	if (foundkey == NULL) {
164 		rw_exit(&hb->hb_lock);
165 		return NULL;
166 	}
167 	con = foundkey->ck_backptr;
168 	*forw = (foundkey == &con->c_forw_entry);
169 
170 	/* Acquire the reference and return the connection. */
171 	atomic_inc_uint(&con->c_refcnt);
172 	rw_exit(&hb->hb_lock);
173 	return con;
174 }
175 
176 /*
177  * npf_conndb_insert: insert the key representing the connection.
178  */
179 bool
180 npf_conndb_insert(npf_conndb_t *cd, npf_connkey_t *key, npf_conn_t *con)
181 {
182 	npf_hashbucket_t *hb = conndb_hash_bucket(cd, key);
183 	bool ok;
184 
185 	rw_enter(&hb->hb_lock, RW_WRITER);
186 	ok = rb_tree_insert_node(&hb->hb_tree, key) == key;
187 	hb->hb_count += (u_int)ok;
188 	rw_exit(&hb->hb_lock);
189 	return ok;
190 }
191 
192 /*
193  * npf_conndb_remove: find and delete the key and return the connection
194  * it represents.
195  */
196 npf_conn_t *
197 npf_conndb_remove(npf_conndb_t *cd, const npf_connkey_t *key)
198 {
199 	npf_hashbucket_t *hb = conndb_hash_bucket(cd, key);
200 	npf_connkey_t *foundkey;
201 	npf_conn_t *con;
202 
203 	rw_enter(&hb->hb_lock, RW_WRITER);
204 	if ((foundkey = rb_tree_find_node(&hb->hb_tree, key)) != NULL) {
205 		rb_tree_remove_node(&hb->hb_tree, foundkey);
206 		con = foundkey->ck_backptr;
207 		hb->hb_count--;
208 	} else {
209 		con = NULL;
210 	}
211 	rw_exit(&hb->hb_lock);
212 	return con;
213 }
214 
215 /*
216  * npf_conndb_enqueue: atomically insert the connection into the
217  * singly-linked list of "recent" connections.
218  */
219 void
220 npf_conndb_enqueue(npf_conndb_t *cd, npf_conn_t *con)
221 {
222 	npf_conn_t *head;
223 
224 	do {
225 		head = cd->cd_recent;
226 		con->c_next = head;
227 	} while (atomic_cas_ptr(&cd->cd_recent, head, con) != head);
228 }
229 
230 /*
231  * npf_conndb_dequeue: remove the connection from a singly-linked list
232  * given the previous element; no concurrent writers are allowed here.
233  */
234 void
235 npf_conndb_dequeue(npf_conndb_t *cd, npf_conn_t *con, npf_conn_t *prev)
236 {
237 	if (prev == NULL) {
238 		KASSERT(cd->cd_list == con);
239 		cd->cd_list = con->c_next;
240 	} else {
241 		prev->c_next = con->c_next;
242 	}
243 }
244 
245 /*
246  * npf_conndb_getlist: atomically take the "recent" connections and add
247  * them to the singly-linked list of the connections.
248  */
249 npf_conn_t *
250 npf_conndb_getlist(npf_conndb_t *cd)
251 {
252 	npf_conn_t *con, *prev;
253 
254 	con = atomic_swap_ptr(&cd->cd_recent, NULL);
255 	if ((prev = cd->cd_tail) == NULL) {
256 		KASSERT(cd->cd_list == NULL);
257 		cd->cd_list = con;
258 	} else {
259 		KASSERT(prev->c_next == NULL);
260 		prev->c_next = con;
261 	}
262 	return cd->cd_list;
263 }
264 
265 /*
266  * npf_conndb_settail: assign a new tail of the singly-linked list.
267  */
268 void
269 npf_conndb_settail(npf_conndb_t *cd, npf_conn_t *con)
270 {
271 	KASSERT(con || cd->cd_list == NULL);
272 	KASSERT(!con || con->c_next == NULL);
273 	cd->cd_tail = con;
274 }
275