Annotation of src/sys/net/npf/npf_tableset.c, Revision 1.28
1.1 rmind 1: /*-
1.24 christos 2: * Copyright (c) 2009-2016 The NetBSD Foundation, Inc.
1.1 rmind 3: * All rights reserved.
4: *
5: * This material is based upon work partially supported by The
6: * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: *
17: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27: * POSSIBILITY OF SUCH DAMAGE.
28: */
29:
30: /*
1.4 rmind 31: * NPF tableset module.
1.1 rmind 32: *
1.15 rmind 33: * Notes
34: *
35: * The tableset is an array of tables. After the creation, the array
36: * is immutable. The caller is responsible to synchronise the access
37: * to the tableset. The table can either be a hash or a tree. Its
38: * entries are protected by a read-write lock.
1.1 rmind 39: */
40:
1.25 christos 41: #ifdef _KERNEL
1.1 rmind 42: #include <sys/cdefs.h>
1.28 ! rmind 43: __KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.27 2017/03/10 02:21:37 christos Exp $");
1.1 rmind 44:
45: #include <sys/param.h>
1.10 rmind 46: #include <sys/types.h>
1.1 rmind 47:
48: #include <sys/atomic.h>
49: #include <sys/hash.h>
1.21 rmind 50: #include <sys/cdbr.h>
1.1 rmind 51: #include <sys/kmem.h>
1.21 rmind 52: #include <sys/malloc.h>
1.1 rmind 53: #include <sys/pool.h>
54: #include <sys/queue.h>
1.28 ! rmind 55: #include <sys/mutex.h>
1.1 rmind 56: #include <sys/systm.h>
57: #include <sys/types.h>
58:
1.25 christos 59: #include "lpm.h"
60: #endif
61:
1.1 rmind 62: #include "npf_impl.h"
63:
1.15 rmind 64: typedef struct npf_tblent {
1.24 christos 65: LIST_ENTRY(npf_tblent) te_listent;
66: uint16_t te_preflen;
67: uint16_t te_alen;
1.13 rmind 68: npf_addr_t te_addr;
1.15 rmind 69: } npf_tblent_t;
1.1 rmind 70:
71: LIST_HEAD(npf_hashl, npf_tblent);
72:
73: struct npf_table {
1.19 rmind 74: /*
1.21 rmind 75: * The storage type can be: a) hash b) tree c) cdb.
1.19 rmind 76: * There are separate trees for IPv4 and IPv6.
77: */
1.21 rmind 78: union {
79: struct {
80: struct npf_hashl *t_hashl;
81: u_long t_hashmask;
82: };
83: struct {
1.24 christos 84: lpm_t * t_lpm;
85: LIST_HEAD(, npf_tblent) t_list;
1.21 rmind 86: };
87: struct {
88: void * t_blob;
89: size_t t_bsize;
90: struct cdbr * t_cdb;
91: };
92: } /* C11 */;
1.19 rmind 93:
94: /*
95: * Table ID, type and lock. The ID may change during the
96: * config reload, it is protected by the npf_config_lock.
97: */
98: int t_type;
99: u_int t_id;
1.28 ! rmind 100: kmutex_t t_lock;
1.19 rmind 101:
102: /* The number of items, reference count and table name. */
103: u_int t_nitems;
104: u_int t_refcnt;
105: char t_name[NPF_TABLE_MAXNAMELEN];
106: };
107:
108: struct npf_tableset {
109: u_int ts_nitems;
110: npf_table_t * ts_map[];
1.1 rmind 111: };
112:
1.19 rmind 113: #define NPF_TABLESET_SIZE(n) \
114: (offsetof(npf_tableset_t, ts_map[n]) * sizeof(npf_table_t *))
115:
1.13 rmind 116: #define NPF_ADDRLEN2TREE(alen) ((alen) >> 4)
117:
118: static pool_cache_t tblent_cache __read_mostly;
1.1 rmind 119:
120: /*
121: * npf_table_sysinit: initialise tableset structures.
122: */
1.4 rmind 123: void
1.1 rmind 124: npf_tableset_sysinit(void)
125: {
126: tblent_cache = pool_cache_init(sizeof(npf_tblent_t), coherency_unit,
1.14 rmind 127: 0, 0, "npftblpl", NULL, IPL_NONE, NULL, NULL, NULL);
1.1 rmind 128: }
129:
130: void
131: npf_tableset_sysfini(void)
132: {
133: pool_cache_destroy(tblent_cache);
134: }
135:
136: npf_tableset_t *
1.19 rmind 137: npf_tableset_create(u_int nitems)
1.1 rmind 138: {
1.19 rmind 139: npf_tableset_t *ts = kmem_zalloc(NPF_TABLESET_SIZE(nitems), KM_SLEEP);
140: ts->ts_nitems = nitems;
141: return ts;
1.1 rmind 142: }
143:
144: void
1.19 rmind 145: npf_tableset_destroy(npf_tableset_t *ts)
1.1 rmind 146: {
147: /*
1.19 rmind 148: * Destroy all tables (no references should be held, since the
149: * ruleset should be destroyed before).
1.1 rmind 150: */
1.19 rmind 151: for (u_int tid = 0; tid < ts->ts_nitems; tid++) {
152: npf_table_t *t = ts->ts_map[tid];
153:
1.17 rmind 154: if (t && atomic_dec_uint_nv(&t->t_refcnt) == 0) {
1.1 rmind 155: npf_table_destroy(t);
156: }
157: }
1.19 rmind 158: kmem_free(ts, NPF_TABLESET_SIZE(ts->ts_nitems));
1.1 rmind 159: }
160:
161: /*
162: * npf_tableset_insert: insert the table into the specified tableset.
163: *
1.13 rmind 164: * => Returns 0 on success. Fails and returns error if ID is already used.
1.1 rmind 165: */
166: int
1.19 rmind 167: npf_tableset_insert(npf_tableset_t *ts, npf_table_t *t)
1.1 rmind 168: {
169: const u_int tid = t->t_id;
170: int error;
171:
1.19 rmind 172: KASSERT((u_int)tid < ts->ts_nitems);
1.1 rmind 173:
1.19 rmind 174: if (ts->ts_map[tid] == NULL) {
1.17 rmind 175: atomic_inc_uint(&t->t_refcnt);
1.19 rmind 176: ts->ts_map[tid] = t;
1.1 rmind 177: error = 0;
178: } else {
179: error = EEXIST;
180: }
181: return error;
182: }
183:
1.26 rmind 184: npf_table_t *
185: npf_tableset_swap(npf_tableset_t *ts, npf_table_t *newt)
186: {
187: const u_int tid = newt->t_id;
188: npf_table_t *oldt = ts->ts_map[tid];
189:
190: KASSERT(tid < ts->ts_nitems);
191: KASSERT(oldt->t_id == newt->t_id);
192:
193: newt->t_refcnt = oldt->t_refcnt;
194: oldt->t_refcnt = 0;
195:
196: return atomic_swap_ptr(&ts->ts_map[tid], newt);
197: }
198:
1.1 rmind 199: /*
1.19 rmind 200: * npf_tableset_getbyname: look for a table in the set given the name.
201: */
202: npf_table_t *
203: npf_tableset_getbyname(npf_tableset_t *ts, const char *name)
204: {
205: npf_table_t *t;
206:
207: for (u_int tid = 0; tid < ts->ts_nitems; tid++) {
208: if ((t = ts->ts_map[tid]) == NULL)
209: continue;
210: if (strcmp(name, t->t_name) == 0)
211: return t;
212: }
213: return NULL;
214: }
215:
216: npf_table_t *
217: npf_tableset_getbyid(npf_tableset_t *ts, u_int tid)
218: {
219: if (__predict_true(tid < ts->ts_nitems)) {
220: return ts->ts_map[tid];
221: }
222: return NULL;
223: }
224:
225: /*
1.15 rmind 226: * npf_tableset_reload: iterate all tables and if the new table is of the
227: * same type and has no items, then we preserve the old one and its entries.
228: *
229: * => The caller is responsible for providing synchronisation.
230: */
231: void
1.25 christos 232: npf_tableset_reload(npf_t *npf, npf_tableset_t *nts, npf_tableset_t *ots)
1.15 rmind 233: {
1.19 rmind 234: for (u_int tid = 0; tid < nts->ts_nitems; tid++) {
235: npf_table_t *t, *ot;
236:
237: if ((t = nts->ts_map[tid]) == NULL) {
238: continue;
239: }
1.15 rmind 240:
1.19 rmind 241: /* If our table has entries, just load it. */
242: if (t->t_nitems) {
1.15 rmind 243: continue;
244: }
1.19 rmind 245:
246: /* Look for a currently existing table with such name. */
247: ot = npf_tableset_getbyname(ots, t->t_name);
248: if (ot == NULL) {
249: /* Not found: we have a new table. */
250: continue;
251: }
252:
253: /* Found. Did the type change? */
254: if (t->t_type != ot->t_type) {
255: /* Yes, load the new. */
1.15 rmind 256: continue;
257: }
1.17 rmind 258:
259: /*
1.19 rmind 260: * Preserve the current table. Acquire a reference since
261: * we are keeping it in the old table set. Update its ID.
1.17 rmind 262: */
263: atomic_inc_uint(&ot->t_refcnt);
1.19 rmind 264: nts->ts_map[tid] = ot;
265:
1.25 christos 266: KASSERT(npf_config_locked_p(npf));
1.19 rmind 267: ot->t_id = tid;
1.17 rmind 268:
1.21 rmind 269: /* Destroy the new table (we hold the only reference). */
1.17 rmind 270: t->t_refcnt--;
1.15 rmind 271: npf_table_destroy(t);
272: }
273: }
274:
1.22 rmind 275: int
1.28 ! rmind 276: npf_tableset_export(npf_t *npf, const npf_tableset_t *ts, nvlist_t *npf_dict)
1.20 rmind 277: {
278: const npf_table_t *t;
279:
1.25 christos 280: KASSERT(npf_config_locked_p(npf));
1.20 rmind 281:
282: for (u_int tid = 0; tid < ts->ts_nitems; tid++) {
1.28 ! rmind 283: nvlist_t *table;
! 284:
1.20 rmind 285: if ((t = ts->ts_map[tid]) == NULL) {
286: continue;
287: }
1.28 ! rmind 288: table = nvlist_create(0);
! 289: nvlist_add_string(table, "name", t->t_name);
! 290: nvlist_add_number(table, "type", t->t_type);
! 291: nvlist_add_number(table, "id", tid);
1.20 rmind 292:
1.28 ! rmind 293: nvlist_append_nvlist_array(npf_dict, "tables", table);
! 294: nvlist_destroy(table);
1.20 rmind 295: }
1.22 rmind 296: return 0;
1.20 rmind 297: }
298:
1.15 rmind 299: /*
1.13 rmind 300: * Few helper routines.
1.1 rmind 301: */
302:
1.13 rmind 303: static npf_tblent_t *
304: table_hash_lookup(const npf_table_t *t, const npf_addr_t *addr,
305: const int alen, struct npf_hashl **rhtbl)
1.1 rmind 306: {
1.13 rmind 307: const uint32_t hidx = hash32_buf(addr, alen, HASH32_BUF_INIT);
308: struct npf_hashl *htbl = &t->t_hashl[hidx & t->t_hashmask];
309: npf_tblent_t *ent;
1.1 rmind 310:
1.13 rmind 311: /*
312: * Lookup the hash table and check for duplicates.
313: * Note: mask is ignored for the hash storage.
314: */
1.24 christos 315: LIST_FOREACH(ent, htbl, te_listent) {
1.13 rmind 316: if (ent->te_alen != alen) {
317: continue;
318: }
319: if (memcmp(&ent->te_addr, addr, alen) == 0) {
320: break;
321: }
322: }
323: *rhtbl = htbl;
324: return ent;
1.1 rmind 325: }
326:
1.13 rmind 327: static void
1.24 christos 328: table_hash_flush(npf_table_t *t)
1.18 rmind 329: {
330: for (unsigned n = 0; n <= t->t_hashmask; n++) {
331: npf_tblent_t *ent;
332:
333: while ((ent = LIST_FIRST(&t->t_hashl[n])) != NULL) {
1.24 christos 334: LIST_REMOVE(ent, te_listent);
1.18 rmind 335: pool_cache_put(tblent_cache, ent);
336: }
337: }
338: }
339:
340: static void
1.24 christos 341: table_tree_flush(npf_table_t *t)
1.1 rmind 342: {
1.13 rmind 343: npf_tblent_t *ent;
1.1 rmind 344:
1.24 christos 345: while ((ent = LIST_FIRST(&t->t_list)) != NULL) {
346: LIST_REMOVE(ent, te_listent);
1.13 rmind 347: pool_cache_put(tblent_cache, ent);
348: }
1.24 christos 349: lpm_clear(t->t_lpm, NULL, NULL);
1.1 rmind 350: }
351:
352: /*
353: * npf_table_create: create table with a specified ID.
354: */
355: npf_table_t *
1.21 rmind 356: npf_table_create(const char *name, u_int tid, int type,
1.28 ! rmind 357: const void *blob, size_t size)
1.1 rmind 358: {
359: npf_table_t *t;
360:
1.25 christos 361: t = kmem_zalloc(sizeof(npf_table_t), KM_SLEEP);
1.19 rmind 362: strlcpy(t->t_name, name, NPF_TABLE_MAXNAMELEN);
1.1 rmind 363:
364: switch (type) {
1.9 rmind 365: case NPF_TABLE_TREE:
1.25 christos 366: if ((t->t_lpm = lpm_create()) == NULL) {
1.24 christos 367: goto out;
1.25 christos 368: }
1.24 christos 369: LIST_INIT(&t->t_list);
1.1 rmind 370: break;
371: case NPF_TABLE_HASH:
1.28 ! rmind 372: size = MIN(MAX(size, 1024 * 1024), 8); // XXX
1.26 rmind 373: t->t_hashl = hashinit(size, HASH_LIST, true, &t->t_hashmask);
1.25 christos 374: if (t->t_hashl == NULL) {
1.24 christos 375: goto out;
1.25 christos 376: }
1.1 rmind 377: break;
1.21 rmind 378: case NPF_TABLE_CDB:
1.28 ! rmind 379: t->t_blob = kmem_alloc(size, KM_SLEEP);
! 380: if (t->t_blob == NULL) {
! 381: goto out;
! 382: }
! 383: memcpy(t->t_blob, blob, size);
1.21 rmind 384: t->t_bsize = size;
1.28 ! rmind 385:
! 386: t->t_cdb = cdbr_open_mem(t->t_blob, size,
! 387: CDBR_DEFAULT, NULL, NULL);
1.21 rmind 388: if (t->t_cdb == NULL) {
1.28 ! rmind 389: kmem_free(t->t_blob, t->t_bsize);
1.24 christos 390: goto out;
1.21 rmind 391: }
392: t->t_nitems = cdbr_entries(t->t_cdb);
393: break;
1.1 rmind 394: default:
395: KASSERT(false);
396: }
1.28 ! rmind 397: mutex_init(&t->t_lock, MUTEX_DEFAULT, IPL_NET);
1.1 rmind 398: t->t_type = type;
399: t->t_id = tid;
400: return t;
1.24 christos 401: out:
1.25 christos 402: kmem_free(t, sizeof(npf_table_t));
1.24 christos 403: return NULL;
1.1 rmind 404: }
405:
406: /*
407: * npf_table_destroy: free all table entries and table itself.
408: */
409: void
410: npf_table_destroy(npf_table_t *t)
411: {
1.17 rmind 412: KASSERT(t->t_refcnt == 0);
1.1 rmind 413:
414: switch (t->t_type) {
1.15 rmind 415: case NPF_TABLE_HASH:
1.24 christos 416: table_hash_flush(t);
1.1 rmind 417: hashdone(t->t_hashl, HASH_LIST, t->t_hashmask);
418: break;
1.15 rmind 419: case NPF_TABLE_TREE:
1.24 christos 420: table_tree_flush(t);
421: lpm_destroy(t->t_lpm);
1.1 rmind 422: break;
1.21 rmind 423: case NPF_TABLE_CDB:
424: cdbr_close(t->t_cdb);
1.28 ! rmind 425: kmem_free(t->t_blob, t->t_bsize);
1.21 rmind 426: break;
1.1 rmind 427: default:
428: KASSERT(false);
429: }
1.28 ! rmind 430: mutex_destroy(&t->t_lock);
1.25 christos 431: kmem_free(t, sizeof(npf_table_t));
1.1 rmind 432: }
433:
1.26 rmind 434: u_int
435: npf_table_getid(npf_table_t *t)
436: {
437: return t->t_id;
438: }
439:
1.1 rmind 440: /*
1.19 rmind 441: * npf_table_check: validate the name, ID and type.
1.13 rmind 442: */
1.1 rmind 443: int
1.28 ! rmind 444: npf_table_check(npf_tableset_t *ts, const char *name, uint64_t tid, uint64_t type)
1.1 rmind 445: {
1.28 ! rmind 446: if (tid >= ts->ts_nitems) {
1.1 rmind 447: return EINVAL;
448: }
1.19 rmind 449: if (ts->ts_map[tid] != NULL) {
1.1 rmind 450: return EEXIST;
451: }
1.21 rmind 452: switch (type) {
453: case NPF_TABLE_TREE:
454: case NPF_TABLE_HASH:
455: case NPF_TABLE_CDB:
456: break;
457: default:
1.1 rmind 458: return EINVAL;
459: }
1.19 rmind 460: if (strlen(name) >= NPF_TABLE_MAXNAMELEN) {
461: return ENAMETOOLONG;
462: }
463: if (npf_tableset_getbyname(ts, name)) {
1.20 rmind 464: return EEXIST;
1.19 rmind 465: }
1.1 rmind 466: return 0;
467: }
468:
1.13 rmind 469: static int
1.15 rmind 470: table_cidr_check(const u_int aidx, const npf_addr_t *addr,
1.13 rmind 471: const npf_netmask_t mask)
472: {
1.19 rmind 473: if (aidx > 1) {
1.13 rmind 474: return EINVAL;
475: }
1.19 rmind 476: if (mask > NPF_MAX_NETMASK && mask != NPF_NO_NETMASK) {
1.13 rmind 477: return EINVAL;
478: }
479:
480: /*
481: * For IPv4 (aidx = 0) - 32 and for IPv6 (aidx = 1) - 128.
482: * If it is a host - shall use NPF_NO_NETMASK.
483: */
1.23 christos 484: if (mask > (aidx ? 128 : 32) && mask != NPF_NO_NETMASK) {
1.13 rmind 485: return EINVAL;
486: }
487: return 0;
488: }
489:
1.1 rmind 490: /*
1.13 rmind 491: * npf_table_insert: add an IP CIDR entry into the table.
1.1 rmind 492: */
493: int
1.19 rmind 494: npf_table_insert(npf_table_t *t, const int alen,
1.6 zoltan 495: const npf_addr_t *addr, const npf_netmask_t mask)
1.1 rmind 496: {
1.13 rmind 497: const u_int aidx = NPF_ADDRLEN2TREE(alen);
498: npf_tblent_t *ent;
499: int error;
1.1 rmind 500:
1.15 rmind 501: error = table_cidr_check(aidx, addr, mask);
1.13 rmind 502: if (error) {
503: return error;
1.8 rmind 504: }
1.12 rmind 505: ent = pool_cache_get(tblent_cache, PR_WAITOK);
1.13 rmind 506: memcpy(&ent->te_addr, addr, alen);
507: ent->te_alen = alen;
1.1 rmind 508:
1.13 rmind 509: /*
510: * Insert the entry. Return an error on duplicate.
511: */
1.28 ! rmind 512: mutex_enter(&t->t_lock);
1.1 rmind 513: switch (t->t_type) {
1.13 rmind 514: case NPF_TABLE_HASH: {
515: struct npf_hashl *htbl;
516:
517: /*
518: * Hash tables by the concept support only IPs.
519: */
520: if (mask != NPF_NO_NETMASK) {
521: error = EINVAL;
522: break;
1.1 rmind 523: }
1.13 rmind 524: if (!table_hash_lookup(t, addr, alen, &htbl)) {
1.24 christos 525: LIST_INSERT_HEAD(htbl, ent, te_listent);
1.15 rmind 526: t->t_nitems++;
1.1 rmind 527: } else {
528: error = EEXIST;
529: }
530: break;
1.13 rmind 531: }
532: case NPF_TABLE_TREE: {
1.24 christos 533: const unsigned preflen =
534: (mask == NPF_NO_NETMASK) ? (alen * 8) : mask;
535: if (lpm_lookup(t->t_lpm, addr, alen) == NULL &&
536: lpm_insert(t->t_lpm, addr, alen, preflen, ent) == 0) {
537: LIST_INSERT_HEAD(&t->t_list, ent, te_listent);
538: ent->te_preflen = preflen;
1.15 rmind 539: t->t_nitems++;
540: error = 0;
1.13 rmind 541: } else {
1.15 rmind 542: error = EEXIST;
1.1 rmind 543: }
544: break;
1.13 rmind 545: }
1.21 rmind 546: case NPF_TABLE_CDB:
547: error = EINVAL;
548: break;
1.1 rmind 549: default:
550: KASSERT(false);
551: }
1.28 ! rmind 552: mutex_exit(&t->t_lock);
1.1 rmind 553:
1.8 rmind 554: if (error) {
1.12 rmind 555: pool_cache_put(tblent_cache, ent);
1.1 rmind 556: }
557: return error;
558: }
559:
560: /*
1.13 rmind 561: * npf_table_remove: remove the IP CIDR entry from the table.
1.1 rmind 562: */
563: int
1.19 rmind 564: npf_table_remove(npf_table_t *t, const int alen,
1.6 zoltan 565: const npf_addr_t *addr, const npf_netmask_t mask)
1.1 rmind 566: {
1.13 rmind 567: const u_int aidx = NPF_ADDRLEN2TREE(alen);
1.21 rmind 568: npf_tblent_t *ent = NULL;
569: int error = ENOENT;
1.1 rmind 570:
1.15 rmind 571: error = table_cidr_check(aidx, addr, mask);
1.13 rmind 572: if (error) {
573: return error;
1.8 rmind 574: }
1.15 rmind 575:
1.28 ! rmind 576: mutex_enter(&t->t_lock);
1.13 rmind 577: switch (t->t_type) {
578: case NPF_TABLE_HASH: {
579: struct npf_hashl *htbl;
1.8 rmind 580:
1.13 rmind 581: ent = table_hash_lookup(t, addr, alen, &htbl);
1.12 rmind 582: if (__predict_true(ent != NULL)) {
1.24 christos 583: LIST_REMOVE(ent, te_listent);
1.15 rmind 584: t->t_nitems--;
1.1 rmind 585: }
586: break;
1.13 rmind 587: }
588: case NPF_TABLE_TREE: {
1.24 christos 589: ent = lpm_lookup(t->t_lpm, addr, alen);
1.12 rmind 590: if (__predict_true(ent != NULL)) {
1.24 christos 591: LIST_REMOVE(ent, te_listent);
592: lpm_remove(t->t_lpm, &ent->te_addr,
593: ent->te_alen, ent->te_preflen);
1.15 rmind 594: t->t_nitems--;
1.1 rmind 595: }
596: break;
1.13 rmind 597: }
1.21 rmind 598: case NPF_TABLE_CDB:
599: error = EINVAL;
600: break;
1.1 rmind 601: default:
602: KASSERT(false);
1.13 rmind 603: ent = NULL;
1.1 rmind 604: }
1.28 ! rmind 605: mutex_exit(&t->t_lock);
1.1 rmind 606:
1.21 rmind 607: if (ent) {
608: pool_cache_put(tblent_cache, ent);
1.1 rmind 609: }
1.21 rmind 610: return error;
1.1 rmind 611: }
612:
613: /*
1.13 rmind 614: * npf_table_lookup: find the table according to ID, lookup and match
615: * the contents with the specified IP address.
1.1 rmind 616: */
617: int
1.19 rmind 618: npf_table_lookup(npf_table_t *t, const int alen, const npf_addr_t *addr)
1.1 rmind 619: {
1.13 rmind 620: const u_int aidx = NPF_ADDRLEN2TREE(alen);
1.21 rmind 621: struct npf_hashl *htbl;
622: const void *data;
623: size_t dlen;
624: bool found;
1.1 rmind 625:
1.13 rmind 626: if (__predict_false(aidx > 1)) {
627: return EINVAL;
628: }
629:
1.1 rmind 630: switch (t->t_type) {
1.21 rmind 631: case NPF_TABLE_HASH:
1.28 ! rmind 632: mutex_enter(&t->t_lock);
1.21 rmind 633: found = table_hash_lookup(t, addr, alen, &htbl) != NULL;
1.28 ! rmind 634: mutex_exit(&t->t_lock);
1.1 rmind 635: break;
1.21 rmind 636: case NPF_TABLE_TREE:
1.28 ! rmind 637: mutex_enter(&t->t_lock);
1.24 christos 638: found = lpm_lookup(t->t_lpm, addr, alen) != NULL;
1.28 ! rmind 639: mutex_exit(&t->t_lock);
1.21 rmind 640: break;
641: case NPF_TABLE_CDB:
642: if (cdbr_find(t->t_cdb, addr, alen, &data, &dlen) == 0) {
1.25 christos 643: found = dlen == (u_int)alen &&
644: memcmp(addr, data, dlen) == 0;
1.21 rmind 645: } else {
646: found = false;
647: }
1.1 rmind 648: break;
649: default:
650: KASSERT(false);
1.21 rmind 651: found = false;
1.1 rmind 652: }
653:
1.21 rmind 654: return found ? 0 : ENOENT;
1.1 rmind 655: }
1.15 rmind 656:
657: static int
1.21 rmind 658: table_ent_copyout(const npf_addr_t *addr, const int alen, npf_netmask_t mask,
1.15 rmind 659: void *ubuf, size_t len, size_t *off)
660: {
661: void *ubufp = (uint8_t *)ubuf + *off;
662: npf_ioctl_ent_t uent;
663:
664: if ((*off += sizeof(npf_ioctl_ent_t)) > len) {
665: return ENOMEM;
666: }
1.21 rmind 667: uent.alen = alen;
668: memcpy(&uent.addr, addr, sizeof(npf_addr_t));
1.15 rmind 669: uent.mask = mask;
670:
671: return copyout(&uent, ubufp, sizeof(npf_ioctl_ent_t));
672: }
673:
674: static int
1.21 rmind 675: table_hash_list(const npf_table_t *t, void *ubuf, size_t len)
676: {
677: size_t off = 0;
678: int error = 0;
679:
680: for (unsigned n = 0; n <= t->t_hashmask; n++) {
681: npf_tblent_t *ent;
682:
1.24 christos 683: LIST_FOREACH(ent, &t->t_hashl[n], te_listent) {
1.21 rmind 684: error = table_ent_copyout(&ent->te_addr,
685: ent->te_alen, 0, ubuf, len, &off);
686: if (error)
687: break;
688: }
689: }
690: return error;
691: }
692:
693: static int
1.24 christos 694: table_tree_list(const npf_table_t *t, void *ubuf, size_t len)
1.15 rmind 695: {
1.24 christos 696: npf_tblent_t *ent;
697: size_t off = 0;
1.15 rmind 698: int error = 0;
699:
1.24 christos 700: LIST_FOREACH(ent, &t->t_list, te_listent) {
701: error = table_ent_copyout(&ent->te_addr,
702: ent->te_alen, 0, ubuf, len, &off);
1.21 rmind 703: if (error)
704: break;
705: }
706: return error;
707: }
708:
709: static int
710: table_cdb_list(npf_table_t *t, void *ubuf, size_t len)
711: {
712: size_t off = 0, dlen;
713: const void *data;
714: int error = 0;
715:
716: for (size_t i = 0; i < t->t_nitems; i++) {
717: if (cdbr_get(t->t_cdb, i, &data, &dlen) != 0) {
718: return EINVAL;
719: }
720: error = table_ent_copyout(data, dlen, 0, ubuf, len, &off);
1.15 rmind 721: if (error)
722: break;
723: }
724: return error;
725: }
726:
727: /*
728: * npf_table_list: copy a list of all table entries into a userspace buffer.
729: */
730: int
1.19 rmind 731: npf_table_list(npf_table_t *t, void *ubuf, size_t len)
1.15 rmind 732: {
733: int error = 0;
734:
1.28 ! rmind 735: mutex_enter(&t->t_lock);
1.15 rmind 736: switch (t->t_type) {
737: case NPF_TABLE_HASH:
1.21 rmind 738: error = table_hash_list(t, ubuf, len);
1.15 rmind 739: break;
740: case NPF_TABLE_TREE:
1.24 christos 741: error = table_tree_list(t, ubuf, len);
1.16 rmind 742: break;
1.21 rmind 743: case NPF_TABLE_CDB:
744: error = table_cdb_list(t, ubuf, len);
745: break;
1.15 rmind 746: default:
747: KASSERT(false);
748: }
1.28 ! rmind 749: mutex_exit(&t->t_lock);
1.15 rmind 750:
751: return error;
752: }
1.18 rmind 753:
754: /*
755: * npf_table_flush: remove all table entries.
756: */
757: int
1.19 rmind 758: npf_table_flush(npf_table_t *t)
1.18 rmind 759: {
1.21 rmind 760: int error = 0;
761:
1.28 ! rmind 762: mutex_enter(&t->t_lock);
1.18 rmind 763: switch (t->t_type) {
764: case NPF_TABLE_HASH:
1.24 christos 765: table_hash_flush(t);
1.18 rmind 766: t->t_nitems = 0;
767: break;
768: case NPF_TABLE_TREE:
1.24 christos 769: table_tree_flush(t);
1.18 rmind 770: t->t_nitems = 0;
771: break;
1.21 rmind 772: case NPF_TABLE_CDB:
773: error = EINVAL;
774: break;
1.18 rmind 775: default:
776: KASSERT(false);
777: }
1.28 ! rmind 778: mutex_exit(&t->t_lock);
1.21 rmind 779: return error;
1.18 rmind 780: }
CVSweb <webmaster@jp.NetBSD.org>