xref: /netbsd-src/sys/net/npf/lpm.c (revision a80fb120747194c8ba5a29ebbda163311f00f77c)
18f6d079fSchristos /*-
28f6d079fSchristos  * Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu>
38f6d079fSchristos  * All rights reserved.
48f6d079fSchristos  *
58f6d079fSchristos  * Redistribution and use in source and binary forms, with or without
68f6d079fSchristos  * modification, are permitted provided that the following conditions
78f6d079fSchristos  * are met:
88f6d079fSchristos  * 1. Redistributions of source code must retain the above copyright
98f6d079fSchristos  *    notice, this list of conditions and the following disclaimer.
108f6d079fSchristos  * 2. Redistributions in binary form must reproduce the above copyright
118f6d079fSchristos  *    notice, this list of conditions and the following disclaimer in the
128f6d079fSchristos  *    documentation and/or other materials provided with the distribution.
138f6d079fSchristos  *
148f6d079fSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
158f6d079fSchristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
168f6d079fSchristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
178f6d079fSchristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
188f6d079fSchristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
198f6d079fSchristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
208f6d079fSchristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
218f6d079fSchristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
228f6d079fSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
238f6d079fSchristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
248f6d079fSchristos  * SUCH DAMAGE.
258f6d079fSchristos  */
268f6d079fSchristos 
278f6d079fSchristos /*
2839013e66Srmind  * Longest Prefix Match (LPM) library supporting IPv4 and IPv6.
2939013e66Srmind  *
3039013e66Srmind  * Algorithm:
3139013e66Srmind  *
3239013e66Srmind  * Each prefix gets its own hash map and all added prefixes are saved
3339013e66Srmind  * in a bitmap.  On a lookup, we perform a linear scan of hash maps,
3439013e66Srmind  * iterating through the added prefixes only.  Usually, there are only
3539013e66Srmind  * a few unique prefixes used and such simple algorithm is very efficient.
3639013e66Srmind  * With many IPv6 prefixes, the linear scan might become a bottleneck.
378f6d079fSchristos  */
388f6d079fSchristos 
398f6d079fSchristos #if defined(_KERNEL)
408f6d079fSchristos #include <sys/cdefs.h>
41*a80fb120Schristos __KERNEL_RCSID(0, "$NetBSD: lpm.c,v 1.6 2019/06/12 14:36:32 christos Exp $");
428f6d079fSchristos 
438f6d079fSchristos #include <sys/param.h>
448f6d079fSchristos #include <sys/types.h>
458f6d079fSchristos #include <sys/malloc.h>
468f6d079fSchristos #include <sys/kmem.h>
478f6d079fSchristos #else
488f6d079fSchristos #include <sys/socket.h>
498f6d079fSchristos #include <arpa/inet.h>
508f6d079fSchristos 
518f6d079fSchristos #include <stdio.h>
528f6d079fSchristos #include <stdlib.h>
538f6d079fSchristos #include <stdbool.h>
548f6d079fSchristos #include <stddef.h>
558f6d079fSchristos #include <string.h>
568f6d079fSchristos #include <strings.h>
578f6d079fSchristos #include <errno.h>
588f6d079fSchristos #include <assert.h>
598f6d079fSchristos #define kmem_alloc(a, b) malloc(a)
608f6d079fSchristos #define kmem_free(a, b) free(a)
618f6d079fSchristos #define kmem_zalloc(a, b) calloc(a, 1)
628f6d079fSchristos #endif
638f6d079fSchristos 
648f6d079fSchristos #include "lpm.h"
658f6d079fSchristos 
668f6d079fSchristos #define	LPM_MAX_PREFIX		(128)
678f6d079fSchristos #define	LPM_MAX_WORDS		(LPM_MAX_PREFIX >> 5)
688f6d079fSchristos #define	LPM_TO_WORDS(x)		((x) >> 2)
698f6d079fSchristos #define	LPM_HASH_STEP		(8)
7039013e66Srmind #define	LPM_LEN_IDX(len)	((len) >> 4)
718f6d079fSchristos 
728f6d079fSchristos #ifdef DEBUG
738f6d079fSchristos #define	ASSERT			assert
748f6d079fSchristos #else
7539013e66Srmind #define	ASSERT(x)
768f6d079fSchristos #endif
778f6d079fSchristos 
788f6d079fSchristos typedef struct lpm_ent {
798f6d079fSchristos 	struct lpm_ent *next;
808f6d079fSchristos 	void *		val;
818f6d079fSchristos 	unsigned	len;
828f6d079fSchristos 	uint8_t		key[];
838f6d079fSchristos } lpm_ent_t;
848f6d079fSchristos 
858f6d079fSchristos typedef struct {
8639013e66Srmind 	unsigned	hashsize;
8739013e66Srmind 	unsigned	nitems;
888f6d079fSchristos 	lpm_ent_t **	bucket;
898f6d079fSchristos } lpm_hmap_t;
908f6d079fSchristos 
918f6d079fSchristos struct lpm {
928f6d079fSchristos 	uint32_t	bitmask[LPM_MAX_WORDS];
93*a80fb120Schristos 	int		flags;
9439013e66Srmind 	void *		defvals[2];
958f6d079fSchristos 	lpm_hmap_t	prefix[LPM_MAX_PREFIX + 1];
968f6d079fSchristos };
978f6d079fSchristos 
9839013e66Srmind static const uint32_t zero_address[LPM_MAX_WORDS];
9939013e66Srmind 
1008f6d079fSchristos lpm_t *
lpm_create(int flags)101*a80fb120Schristos lpm_create(int flags)
1028f6d079fSchristos {
103*a80fb120Schristos 	lpm_t *lpm = kmem_zalloc(sizeof(*lpm), KM_SLEEP);
104*a80fb120Schristos 	lpm->flags = flags;
105*a80fb120Schristos 	return lpm;
1068f6d079fSchristos }
1078f6d079fSchristos 
1088f6d079fSchristos void
lpm_clear(lpm_t * lpm,lpm_dtor_t dtor,void * arg)1098f6d079fSchristos lpm_clear(lpm_t *lpm, lpm_dtor_t dtor, void *arg)
1108f6d079fSchristos {
1118f6d079fSchristos 	for (unsigned n = 0; n <= LPM_MAX_PREFIX; n++) {
1128f6d079fSchristos 		lpm_hmap_t *hmap = &lpm->prefix[n];
1138f6d079fSchristos 
1148f6d079fSchristos 		if (!hmap->hashsize) {
1158f6d079fSchristos 			KASSERT(!hmap->bucket);
1168f6d079fSchristos 			continue;
1178f6d079fSchristos 		}
1188f6d079fSchristos 		for (unsigned i = 0; i < hmap->hashsize; i++) {
1198f6d079fSchristos 			lpm_ent_t *entry = hmap->bucket[i];
1208f6d079fSchristos 
1218f6d079fSchristos 			while (entry) {
1228f6d079fSchristos 				lpm_ent_t *next = entry->next;
1238f6d079fSchristos 
1248f6d079fSchristos 				if (dtor) {
1258f6d079fSchristos 					dtor(arg, entry->key,
1268f6d079fSchristos 					    entry->len, entry->val);
1278f6d079fSchristos 				}
1288f6d079fSchristos 				kmem_free(entry,
1298f6d079fSchristos 				    offsetof(lpm_ent_t, key[entry->len]));
1308f6d079fSchristos 				entry = next;
1318f6d079fSchristos 			}
1328f6d079fSchristos 		}
13350c16119Srmind 		kmem_free(hmap->bucket, hmap->hashsize * sizeof(lpm_ent_t *));
1348f6d079fSchristos 		hmap->bucket = NULL;
1358f6d079fSchristos 		hmap->hashsize = 0;
1368f6d079fSchristos 		hmap->nitems = 0;
1378f6d079fSchristos 	}
13839013e66Srmind 	if (dtor) {
13939013e66Srmind 		dtor(arg, zero_address, 4, lpm->defvals[0]);
14039013e66Srmind 		dtor(arg, zero_address, 16, lpm->defvals[1]);
14139013e66Srmind 	}
1428f6d079fSchristos 	memset(lpm->bitmask, 0, sizeof(lpm->bitmask));
14339013e66Srmind 	memset(lpm->defvals, 0, sizeof(lpm->defvals));
1448f6d079fSchristos }
1458f6d079fSchristos 
1468f6d079fSchristos void
lpm_destroy(lpm_t * lpm)1478f6d079fSchristos lpm_destroy(lpm_t *lpm)
1488f6d079fSchristos {
1498f6d079fSchristos 	lpm_clear(lpm, NULL, NULL);
1508f6d079fSchristos 	kmem_free(lpm, sizeof(*lpm));
1518f6d079fSchristos }
1528f6d079fSchristos 
1538f6d079fSchristos /*
1548f6d079fSchristos  * fnv1a_hash: Fowler-Noll-Vo hash function (FNV-1a variant).
1558f6d079fSchristos  */
1568f6d079fSchristos static uint32_t
fnv1a_hash(const void * buf,size_t len)1578f6d079fSchristos fnv1a_hash(const void *buf, size_t len)
1588f6d079fSchristos {
1598f6d079fSchristos 	uint32_t hash = 2166136261UL;
1608f6d079fSchristos 	const uint8_t *p = buf;
1618f6d079fSchristos 
1628f6d079fSchristos 	while (len--) {
1638f6d079fSchristos 		hash ^= *p++;
1648f6d079fSchristos 		hash *= 16777619U;
1658f6d079fSchristos 	}
1668f6d079fSchristos 	return hash;
1678f6d079fSchristos }
1688f6d079fSchristos 
1698f6d079fSchristos static bool
hashmap_rehash(lpm_hmap_t * hmap,unsigned size,int flags)170*a80fb120Schristos hashmap_rehash(lpm_hmap_t *hmap, unsigned size, int flags)
1718f6d079fSchristos {
1728f6d079fSchristos 	lpm_ent_t **bucket;
17339013e66Srmind 	unsigned hashsize;
1748f6d079fSchristos 
1758f6d079fSchristos 	for (hashsize = 1; hashsize < size; hashsize <<= 1) {
1768f6d079fSchristos 		continue;
1778f6d079fSchristos 	}
178*a80fb120Schristos 	bucket = kmem_zalloc(hashsize * sizeof(lpm_ent_t *), flags);
179*a80fb120Schristos 	if (bucket == NULL)
180*a80fb120Schristos 		return false;
1818f6d079fSchristos 	for (unsigned n = 0; n < hmap->hashsize; n++) {
1828f6d079fSchristos 		lpm_ent_t *list = hmap->bucket[n];
1838f6d079fSchristos 
1848f6d079fSchristos 		while (list) {
1858f6d079fSchristos 			lpm_ent_t *entry = list;
1868f6d079fSchristos 			uint32_t hash = fnv1a_hash(entry->key, entry->len);
18739013e66Srmind 			const unsigned i = hash & (hashsize - 1);
1888f6d079fSchristos 
1898f6d079fSchristos 			list = entry->next;
1908f6d079fSchristos 			entry->next = bucket[i];
1918f6d079fSchristos 			bucket[i] = entry;
1928f6d079fSchristos 		}
1938f6d079fSchristos 	}
1948f6d079fSchristos 	if (hmap->bucket)
19550c16119Srmind 		kmem_free(hmap->bucket, hmap->hashsize * sizeof(lpm_ent_t *));
1968f6d079fSchristos 	hmap->bucket = bucket;
1978f6d079fSchristos 	hmap->hashsize = hashsize;
1988f6d079fSchristos 	return true;
1998f6d079fSchristos }
2008f6d079fSchristos 
2018f6d079fSchristos static lpm_ent_t *
hashmap_insert(lpm_hmap_t * hmap,const void * key,size_t len,int flags)202*a80fb120Schristos hashmap_insert(lpm_hmap_t *hmap, const void *key, size_t len, int flags)
2038f6d079fSchristos {
20439013e66Srmind 	const unsigned target = hmap->nitems + LPM_HASH_STEP;
2058f6d079fSchristos 	const size_t entlen = offsetof(lpm_ent_t, key[len]);
2068f6d079fSchristos 	uint32_t hash, i;
2078f6d079fSchristos 	lpm_ent_t *entry;
2088f6d079fSchristos 
209*a80fb120Schristos 	if (hmap->hashsize < target && !hashmap_rehash(hmap, target, flags)) {
2108f6d079fSchristos 		return NULL;
2118f6d079fSchristos 	}
2128f6d079fSchristos 
2138f6d079fSchristos 	hash = fnv1a_hash(key, len);
2148f6d079fSchristos 	i = hash & (hmap->hashsize - 1);
2158f6d079fSchristos 	entry = hmap->bucket[i];
2168f6d079fSchristos 	while (entry) {
2178f6d079fSchristos 		if (entry->len == len && memcmp(entry->key, key, len) == 0) {
2188f6d079fSchristos 			return entry;
2198f6d079fSchristos 		}
2208f6d079fSchristos 		entry = entry->next;
2218f6d079fSchristos 	}
2228f6d079fSchristos 
223*a80fb120Schristos 	if ((entry = kmem_alloc(entlen, flags)) != NULL) {
2248f6d079fSchristos 		memcpy(entry->key, key, len);
2258f6d079fSchristos 		entry->next = hmap->bucket[i];
2268f6d079fSchristos 		entry->len = len;
2278f6d079fSchristos 
2288f6d079fSchristos 		hmap->bucket[i] = entry;
2298f6d079fSchristos 		hmap->nitems++;
23039013e66Srmind 	}
2318f6d079fSchristos 	return entry;
2328f6d079fSchristos }
2338f6d079fSchristos 
2348f6d079fSchristos static lpm_ent_t *
hashmap_lookup(lpm_hmap_t * hmap,const void * key,size_t len)2358f6d079fSchristos hashmap_lookup(lpm_hmap_t *hmap, const void *key, size_t len)
2368f6d079fSchristos {
2378f6d079fSchristos 	const uint32_t hash = fnv1a_hash(key, len);
23839013e66Srmind 	const unsigned i = hash & (hmap->hashsize - 1);
23939013e66Srmind 	lpm_ent_t *entry;
24039013e66Srmind 
24139013e66Srmind 	if (hmap->hashsize == 0) {
24239013e66Srmind 		return NULL;
24339013e66Srmind 	}
24439013e66Srmind 	entry = hmap->bucket[i];
2458f6d079fSchristos 
2468f6d079fSchristos 	while (entry) {
2478f6d079fSchristos 		if (entry->len == len && memcmp(entry->key, key, len) == 0) {
2488f6d079fSchristos 			return entry;
2498f6d079fSchristos 		}
2508f6d079fSchristos 		entry = entry->next;
2518f6d079fSchristos 	}
2528f6d079fSchristos 	return NULL;
2538f6d079fSchristos }
2548f6d079fSchristos 
2558f6d079fSchristos static int
hashmap_remove(lpm_hmap_t * hmap,const void * key,size_t len)2568f6d079fSchristos hashmap_remove(lpm_hmap_t *hmap, const void *key, size_t len)
2578f6d079fSchristos {
2588f6d079fSchristos 	const uint32_t hash = fnv1a_hash(key, len);
25939013e66Srmind 	const unsigned i = hash & (hmap->hashsize - 1);
26039013e66Srmind 	lpm_ent_t *prev = NULL, *entry;
26139013e66Srmind 
26239013e66Srmind 	if (hmap->hashsize == 0) {
26339013e66Srmind 		return -1;
26439013e66Srmind 	}
26539013e66Srmind 	entry = hmap->bucket[i];
2668f6d079fSchristos 
2678f6d079fSchristos 	while (entry) {
2688f6d079fSchristos 		if (entry->len == len && memcmp(entry->key, key, len) == 0) {
2698f6d079fSchristos 			if (prev) {
2708f6d079fSchristos 				prev->next = entry->next;
2718f6d079fSchristos 			} else {
2728f6d079fSchristos 				hmap->bucket[i] = entry->next;
2738f6d079fSchristos 			}
274e8a8032dSrmind 			kmem_free(entry, offsetof(lpm_ent_t, key[len]));
2758f6d079fSchristos 			return 0;
2768f6d079fSchristos 		}
2778f6d079fSchristos 		prev = entry;
2788f6d079fSchristos 		entry = entry->next;
2798f6d079fSchristos 	}
2808f6d079fSchristos 	return -1;
2818f6d079fSchristos }
2828f6d079fSchristos 
2838f6d079fSchristos /*
2848f6d079fSchristos  * compute_prefix: given the address and prefix length, compute and
2858f6d079fSchristos  * return the address prefix.
2868f6d079fSchristos  */
2878f6d079fSchristos static inline void
compute_prefix(const unsigned nwords,const uint32_t * addr,unsigned preflen,uint32_t * prefix)2888f6d079fSchristos compute_prefix(const unsigned nwords, const uint32_t *addr,
2898f6d079fSchristos     unsigned preflen, uint32_t *prefix)
2908f6d079fSchristos {
2918f6d079fSchristos 	uint32_t addr2[4];
2928f6d079fSchristos 
2938f6d079fSchristos 	if ((uintptr_t)addr & 3) {
2948f6d079fSchristos 		/* Unaligned address: just copy for now. */
2958f6d079fSchristos 		memcpy(addr2, addr, nwords * 4);
2968f6d079fSchristos 		addr = addr2;
2978f6d079fSchristos 	}
2988f6d079fSchristos 	for (unsigned i = 0; i < nwords; i++) {
2998f6d079fSchristos 		if (preflen == 0) {
3008f6d079fSchristos 			prefix[i] = 0;
3018f6d079fSchristos 			continue;
3028f6d079fSchristos 		}
3038f6d079fSchristos 		if (preflen < 32) {
3048f6d079fSchristos 			uint32_t mask = htonl(0xffffffff << (32 - preflen));
3058f6d079fSchristos 			prefix[i] = addr[i] & mask;
3068f6d079fSchristos 			preflen = 0;
3078f6d079fSchristos 		} else {
3088f6d079fSchristos 			prefix[i] = addr[i];
3098f6d079fSchristos 			preflen -= 32;
3108f6d079fSchristos 		}
3118f6d079fSchristos 	}
3128f6d079fSchristos }
3138f6d079fSchristos 
3148f6d079fSchristos /*
3158f6d079fSchristos  * lpm_insert: insert the CIDR into the LPM table.
3168f6d079fSchristos  *
3178f6d079fSchristos  * => Returns zero on success and -1 on failure.
3188f6d079fSchristos  */
3198f6d079fSchristos int
lpm_insert(lpm_t * lpm,const void * addr,size_t len,unsigned preflen,void * val)3208f6d079fSchristos lpm_insert(lpm_t *lpm, const void *addr,
3218f6d079fSchristos     size_t len, unsigned preflen, void *val)
3228f6d079fSchristos {
3238f6d079fSchristos 	const unsigned nwords = LPM_TO_WORDS(len);
3248f6d079fSchristos 	uint32_t prefix[LPM_MAX_WORDS];
3258f6d079fSchristos 	lpm_ent_t *entry;
32639013e66Srmind 	KASSERT(len == 4 || len == 16);
3278f6d079fSchristos 
3288f6d079fSchristos 	if (preflen == 0) {
32939013e66Srmind 		/* 0-length prefix is a special case. */
33039013e66Srmind 		lpm->defvals[LPM_LEN_IDX(len)] = val;
3318f6d079fSchristos 		return 0;
3328f6d079fSchristos 	}
3338f6d079fSchristos 	compute_prefix(nwords, addr, preflen, prefix);
334*a80fb120Schristos 	entry = hashmap_insert(&lpm->prefix[preflen], prefix, len, lpm->flags);
3358f6d079fSchristos 	if (entry) {
3368f6d079fSchristos 		const unsigned n = --preflen >> 5;
3378f6d079fSchristos 		lpm->bitmask[n] |= 0x80000000U >> (preflen & 31);
3388f6d079fSchristos 		entry->val = val;
3398f6d079fSchristos 		return 0;
3408f6d079fSchristos 	}
3418f6d079fSchristos 	return -1;
3428f6d079fSchristos }
3438f6d079fSchristos 
3448f6d079fSchristos /*
3458f6d079fSchristos  * lpm_remove: remove the specified prefix.
3468f6d079fSchristos  */
3478f6d079fSchristos int
lpm_remove(lpm_t * lpm,const void * addr,size_t len,unsigned preflen)3488f6d079fSchristos lpm_remove(lpm_t *lpm, const void *addr, size_t len, unsigned preflen)
3498f6d079fSchristos {
3508f6d079fSchristos 	const unsigned nwords = LPM_TO_WORDS(len);
3518f6d079fSchristos 	uint32_t prefix[LPM_MAX_WORDS];
35239013e66Srmind 	KASSERT(len == 4 || len == 16);
3538f6d079fSchristos 
3548f6d079fSchristos 	if (preflen == 0) {
35539013e66Srmind 		lpm->defvals[LPM_LEN_IDX(len)] = NULL;
3568f6d079fSchristos 		return 0;
3578f6d079fSchristos 	}
3588f6d079fSchristos 	compute_prefix(nwords, addr, preflen, prefix);
3598f6d079fSchristos 	return hashmap_remove(&lpm->prefix[preflen], prefix, len);
3608f6d079fSchristos }
3618f6d079fSchristos 
3628f6d079fSchristos /*
3638f6d079fSchristos  * lpm_lookup: find the longest matching prefix given the IP address.
3648f6d079fSchristos  *
3658f6d079fSchristos  * => Returns the associated value on success or NULL on failure.
3668f6d079fSchristos  */
3678f6d079fSchristos void *
lpm_lookup(lpm_t * lpm,const void * addr,size_t len)3688f6d079fSchristos lpm_lookup(lpm_t *lpm, const void *addr, size_t len)
3698f6d079fSchristos {
3708f6d079fSchristos 	const unsigned nwords = LPM_TO_WORDS(len);
3718f6d079fSchristos 	unsigned i, n = nwords;
3728f6d079fSchristos 	uint32_t prefix[LPM_MAX_WORDS];
3738f6d079fSchristos 
3748f6d079fSchristos 	while (n--) {
3758f6d079fSchristos 		uint32_t bitmask = lpm->bitmask[n];
3768f6d079fSchristos 
3778f6d079fSchristos 		while ((i = ffs(bitmask)) != 0) {
3788f6d079fSchristos 			const unsigned preflen = (32 * n) + (32 - --i);
3798f6d079fSchristos 			lpm_hmap_t *hmap = &lpm->prefix[preflen];
3808f6d079fSchristos 			lpm_ent_t *entry;
3818f6d079fSchristos 
3828f6d079fSchristos 			compute_prefix(nwords, addr, preflen, prefix);
3838f6d079fSchristos 			entry = hashmap_lookup(hmap, prefix, len);
3848f6d079fSchristos 			if (entry) {
3858f6d079fSchristos 				return entry->val;
3868f6d079fSchristos 			}
3878f6d079fSchristos 			bitmask &= ~(1U << i);
3888f6d079fSchristos 		}
3898f6d079fSchristos 	}
39039013e66Srmind 	return lpm->defvals[LPM_LEN_IDX(len)];
39139013e66Srmind }
39239013e66Srmind 
39339013e66Srmind /*
39439013e66Srmind  * lpm_lookup_prefix: return the value associated with a prefix
39539013e66Srmind  *
39639013e66Srmind  * => Returns the associated value on success or NULL on failure.
39739013e66Srmind  */
39839013e66Srmind void *
lpm_lookup_prefix(lpm_t * lpm,const void * addr,size_t len,unsigned preflen)39939013e66Srmind lpm_lookup_prefix(lpm_t *lpm, const void *addr, size_t len, unsigned preflen)
40039013e66Srmind {
40139013e66Srmind 	const unsigned nwords = LPM_TO_WORDS(len);
40239013e66Srmind 	uint32_t prefix[LPM_MAX_WORDS];
40339013e66Srmind 	lpm_ent_t *entry;
40439013e66Srmind 	KASSERT(len == 4 || len == 16);
40539013e66Srmind 
40639013e66Srmind 	if (preflen == 0) {
40739013e66Srmind 		return lpm->defvals[LPM_LEN_IDX(len)];
40839013e66Srmind 	}
40939013e66Srmind 	compute_prefix(nwords, addr, preflen, prefix);
41039013e66Srmind 	entry = hashmap_lookup(&lpm->prefix[preflen], prefix, len);
41139013e66Srmind 	if (entry) {
41239013e66Srmind 		return entry->val;
41339013e66Srmind 	}
41439013e66Srmind 	return NULL;
4158f6d079fSchristos }
4168f6d079fSchristos 
4178f6d079fSchristos #if !defined(_KERNEL)
4188f6d079fSchristos /*
4198f6d079fSchristos  * lpm_strtobin: convert CIDR string to the binary IP address and mask.
4208f6d079fSchristos  *
4218f6d079fSchristos  * => The address will be in the network byte order.
4228f6d079fSchristos  * => Returns 0 on success or -1 on failure.
4238f6d079fSchristos  */
4248f6d079fSchristos int
lpm_strtobin(const char * cidr,void * addr,size_t * len,unsigned * preflen)4258f6d079fSchristos lpm_strtobin(const char *cidr, void *addr, size_t *len, unsigned *preflen)
4268f6d079fSchristos {
4278f6d079fSchristos 	char *p, buf[INET6_ADDRSTRLEN];
4288f6d079fSchristos 
4298f6d079fSchristos 	strncpy(buf, cidr, sizeof(buf));
4308f6d079fSchristos 	buf[sizeof(buf) - 1] = '\0';
4318f6d079fSchristos 
4328f6d079fSchristos 	if ((p = strchr(buf, '/')) != NULL) {
4338f6d079fSchristos 		const ptrdiff_t off = p - buf;
4348f6d079fSchristos 		*preflen = atoi(&buf[off + 1]);
4358f6d079fSchristos 		buf[off] = '\0';
4368f6d079fSchristos 	} else {
4378f6d079fSchristos 		*preflen = LPM_MAX_PREFIX;
4388f6d079fSchristos 	}
4398f6d079fSchristos 
4408f6d079fSchristos 	if (inet_pton(AF_INET6, buf, addr) == 1) {
4418f6d079fSchristos 		*len = 16;
4428f6d079fSchristos 		return 0;
4438f6d079fSchristos 	}
4448f6d079fSchristos 	if (inet_pton(AF_INET, buf, addr) == 1) {
4458f6d079fSchristos 		if (*preflen == LPM_MAX_PREFIX) {
4468f6d079fSchristos 			*preflen = 32;
4478f6d079fSchristos 		}
4488f6d079fSchristos 		*len = 4;
4498f6d079fSchristos 		return 0;
4508f6d079fSchristos 	}
4518f6d079fSchristos 	return -1;
4528f6d079fSchristos }
4538f6d079fSchristos #endif
454