diff options
author | Ole Troan <ot@cisco.com> | 2018-12-18 12:33:26 +0100 |
---|---|---|
committer | Damjan Marion <dmarion@me.com> | 2018-12-18 13:02:45 +0000 |
commit | 91bfa6e2666c56f79cc97407c929d188cb34e90f (patch) | |
tree | 47eb4d66b08e0f93ba2218a8a24116daee80e78d /src | |
parent | 9e829a856fdf88b3ea5770048ea20dcd50d1b4eb (diff) |
MAP: Add longest matching prefix (LPM) data structures
In preparation for adding input feature MAP support, as opposed to
going via the FIB, add MAP's own LPM data structures.
Change-Id: Ie363f0961b0ac9dde2a0fb76cb0c58c904876974
Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/map/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/map/lpm.c | 181 | ||||
-rw-r--r-- | src/plugins/map/lpm.h | 38 |
3 files changed, 220 insertions, 0 deletions
diff --git a/src/plugins/map/CMakeLists.txt b/src/plugins/map/CMakeLists.txt index 20d5dfebbe6..2d604e6e4d8 100644 --- a/src/plugins/map/CMakeLists.txt +++ b/src/plugins/map/CMakeLists.txt @@ -20,6 +20,7 @@ add_vpp_plugin(map map_api.c map.c map_dpo.c + lpm.c API_FILES map.api diff --git a/src/plugins/map/lpm.c b/src/plugins/map/lpm.c new file mode 100644 index 00000000000..4abeefca06d --- /dev/null +++ b/src/plugins/map/lpm.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lpm.h" +#include <vnet/ip/ip4_packet.h> +#include <vnet/ip/ip6_packet.h> +#include <arpa/inet.h> +#include <vnet/ip/format.h> + +static uint32_t +masked_address32 (uint32_t addr, uint8_t len) +{ + u32 a = ntohl(addr); + return htonl(len == 32 ? a : a & ~(~0u >> len)); +} +static uint64_t +masked_address64 (uint64_t addr, uint8_t len) +{ + return len == 64 ? addr : addr & ~(~0ull >> len); +} + +static void +lpm_32_add (lpm_t *lpm, void *addr_v, u8 pfxlen, + u32 value) +{ + uword * hash, * result; + u32 key; + ip4_address_t *addr = addr_v; + key = masked_address32(addr->data_u32, pfxlen); + hash = lpm->hash[pfxlen]; + result = hash_get (hash, key); + if (result) /* Entry exists */ + clib_warning("%U/%d already exists in table for domain %d", + format_ip4_address, addr, pfxlen, result[0]); + + /* + * adding a new entry + */ + if (hash == NULL) { + hash = hash_create (32 /* elts */, sizeof (uword)); + hash_set_flags (hash, HASH_FLAG_NO_AUTO_SHRINK); + } + hash = hash_set(hash, key, value); + lpm->hash[pfxlen] = hash; +} + +static void +lpm_32_delete (lpm_t *lpm, void *addr_v, u8 pfxlen) +{ + uword * hash, * result; + u32 key; + ip4_address_t *addr = addr_v; + key = masked_address32(addr->data_u32, pfxlen); + hash = lpm->hash[pfxlen]; + result = hash_get (hash, key); + if (result) + hash_unset(hash, key); + lpm->hash[pfxlen] = hash; +} + +static u32 +lpm_32_lookup (lpm_t *lpm, void *addr_v, u8 pfxlen) +{ + uword * hash, * result; + i32 mask_len; + u32 key; + ip4_address_t *addr = addr_v; + for (mask_len = pfxlen; mask_len >= 0; mask_len--) { + hash = lpm->hash[mask_len]; + if (hash) { + key = masked_address32(addr->data_u32, mask_len); + result = hash_get (hash, key); + if (result != NULL) { + return (result[0]); + } + } + } + return (~0); +} + +static int +lpm_128_lookup_core (lpm_t *lpm, ip6_address_t *addr, u8 pfxlen, u32 *value) +{ + BVT(clib_bihash_kv) kv, v; + int rv; + kv.key[0] = masked_address64(addr->as_u64[0], pfxlen > 64 ? 64 : pfxlen); + kv.key[1] = masked_address64(addr->as_u64[1], pfxlen > 64 ? pfxlen - 64 : 0); + kv.key[2] = pfxlen; + rv = BV(clib_bihash_search_inline_2)(&lpm->bihash, &kv, &v); + if (rv != 0) + return -1; + *value = v.value; + return 0; +} + +static u32 +lpm_128_lookup (lpm_t *lpm, void *addr_v, u8 pfxlen) +{ + ip6_address_t *addr = addr_v; + int i = 0, rv; + u32 value; + clib_bitmap_foreach (i, lpm->prefix_lengths_bitmap, + ({ + rv = lpm_128_lookup_core(lpm, addr, i, &value); + if (rv == 0) + return value; + })); + return ~0; +} + +static void +lpm_128_add (lpm_t *lpm, void *addr_v, u8 pfxlen, u32 value) +{ + BVT(clib_bihash_kv) kv; + ip6_address_t *addr = addr_v; + + kv.key[0] = masked_address64(addr->as_u64[0], pfxlen > 64 ? 64 : pfxlen); + kv.key[1] = masked_address64(addr->as_u64[1], pfxlen > 64 ? pfxlen - 64 : 0); + kv.key[2] = pfxlen; + kv.value = value; + BV(clib_bihash_add_del)(&lpm->bihash, &kv, 1); + lpm->prefix_length_refcount[pfxlen]++; + lpm->prefix_lengths_bitmap = clib_bitmap_set (lpm->prefix_lengths_bitmap, 128 - pfxlen, 1); +} + +static void +lpm_128_delete (lpm_t *lpm, void *addr_v, u8 pfxlen) +{ + ip6_address_t *addr = addr_v; + BVT(clib_bihash_kv) kv; + kv.key[0] = masked_address64(addr->as_u64[0], pfxlen > 64 ? 64 : pfxlen); + kv.key[1] = masked_address64(addr->as_u64[1], pfxlen > 64 ? pfxlen - 64 : 0); + kv.key[2] = pfxlen; + BV(clib_bihash_add_del)(&lpm->bihash, &kv, 0); + + /* refcount accounting */ + ASSERT (lpm->prefix_length_refcount[pfxlen] > 0); + if (--lpm->prefix_length_refcount[pfxlen] == 0) { + lpm->prefix_lengths_bitmap = clib_bitmap_set (lpm->prefix_lengths_bitmap, + 128 - pfxlen, 0); + } +} + +lpm_t * +lpm_table_init (enum lpm_type_e lpm_type) +{ + lpm_t * lpm = clib_mem_alloc(sizeof(*lpm)); + memset(lpm, 0, sizeof(*lpm)); + + switch (lpm_type) { + case LPM_TYPE_KEY32: + lpm->add = lpm_32_add; + lpm->delete = lpm_32_delete; + lpm->lookup = lpm_32_lookup; + break; + case LPM_TYPE_KEY128: + lpm->add = lpm_128_add; + lpm->delete = lpm_128_delete; + lpm->lookup = lpm_128_lookup; + /* Make bihash sizes configurable */ + BV (clib_bihash_init) (&(lpm->bihash), + "LPM 128", 64*1024, 32<<20); + + break; + default: + ASSERT(0); + } + return lpm; +} diff --git a/src/plugins/map/lpm.h b/src/plugins/map/lpm.h new file mode 100644 index 00000000000..5fe373a4abd --- /dev/null +++ b/src/plugins/map/lpm.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vppinfra/types.h> +#include <vppinfra/bihash_24_8.h> + +enum lpm_type_e { + LPM_TYPE_KEY32, + LPM_TYPE_KEY128, +}; + +typedef struct lpm_ { + void (*add) (struct lpm_ *lpm, void *addr_v, u8 pfxlen, u32 value); + void (*delete) (struct lpm_ *lpm, void *addr_v, u8 pfxlen); + u32 (*lookup) (struct lpm_ *lpm, void *addr_v, u8 pfxlen); + + /* IPv4 LPM */ + uword *hash[33]; + + /* IPv6 LPM */ + BVT (clib_bihash) bihash; + uword *prefix_lengths_bitmap; + u32 prefix_length_refcount[129]; +} lpm_t; + +lpm_t *lpm_table_init (enum lpm_type_e lpm_type); |