diff --git a/lib/vrf.c b/lib/vrf.c index 3a859895e886..85ec50ce858a 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -35,6 +35,7 @@ #include "lib_errors.h" #include "northbound.h" #include "northbound_cli.h" +#include "jhash.h" /* default VRF name value used when VRF backend is not NETNS */ #define VRF_DEFAULT_NAME_INTERNAL "default" @@ -75,11 +76,90 @@ static struct vrf_master { static int vrf_is_enabled(struct vrf *vrf); +/*Hash Table for table_id based lookup of vrf_id*/ +DEFINE_MTYPE_STATIC(LIB, HASH_ENTRY, "VRF IDs by Table Hash Entry"); + +PREDECL_HASH(vrf_ids_by_table); + +struct vrf_ids_by_table_entry { + struct vrf_ids_by_table_item hash_item; + uint32_t table_id; + vrf_id_t vrf_id; +}; + +static int vrf_ids_by_table_cmp(const struct vrf_ids_by_table_entry *a, + const struct vrf_ids_by_table_entry *b) +{ + return numcmp(a->table_id, b->table_id); +} + +static uint32_t vrf_ids_by_table_hash(const struct vrf_ids_by_table_entry *item) +{ + return jhash_1word(item->table_id, 0xa010bf92); +} + +DECLARE_HASH(vrf_ids_by_table, struct vrf_ids_by_table_entry, hash_item, + vrf_ids_by_table_cmp, vrf_ids_by_table_hash); + +static struct vrf_ids_by_table_head vrf_ids_by_table_head; + +static int vrf_ids_by_table_is_initialized; + +/* Helper functions for vrf_ids_by_table hashmap */ +static struct vrf_ids_by_table_entry *vrf_ids_by_table_entry_new(void) +{ + struct vrf_ids_by_table_entry *new_entry; + new_entry = XCALLOC(MTYPE_HASH_ENTRY, + sizeof(struct vrf_ids_by_table_entry)); + + return new_entry; +} + +static void vrf_ids_by_table_delete(uint32_t table_id) +{ + if (!vrf_ids_by_table_is_initialized) + return; + + struct vrf_ids_by_table_entry lookup = {.table_id = table_id}; + struct vrf_ids_by_table_entry *entry = + vrf_ids_by_table_find(&vrf_ids_by_table_head, &lookup); + + if (entry) { + vrf_ids_by_table_del(&vrf_ids_by_table_head, entry); + XFREE(MTYPE_HASH_ENTRY, entry); + } +} + +static void vrf_ids_by_table_update(uint32_t table_id, vrf_id_t new_vrf_id) +{ + if (new_vrf_id == VRF_UNKNOWN) + return; + + if (!vrf_ids_by_table_is_initialized) { + vrf_ids_by_table_init(&vrf_ids_by_table_head); + vrf_ids_by_table_is_initialized = 1; + } + + struct vrf_ids_by_table_entry lookup = {.table_id = table_id}; + struct vrf_ids_by_table_entry *entry = + vrf_ids_by_table_find(&vrf_ids_by_table_head, &lookup); + + if (!entry) { + entry = vrf_ids_by_table_entry_new(); + entry->table_id = table_id; + vrf_ids_by_table_add(&vrf_ids_by_table_head, entry); + } + + entry->vrf_id = new_vrf_id; +} + /* VRF list existance check by name. */ struct vrf *vrf_lookup_by_name(const char *name) { struct vrf vrf; + strlcpy(vrf.name, name, sizeof(vrf.name)); + return (RB_FIND(vrf_name_head, &vrfs_by_name, &vrf)); } @@ -150,6 +230,7 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) if (!vrf && vrf_id != VRF_UNKNOWN) vrf = vrf_lookup_by_id(vrf_id); + /* Unable to find VRF by either ID or name, so create */ if (vrf == NULL) { vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf)); vrf->vrf_id = VRF_UNKNOWN; @@ -200,6 +281,7 @@ struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name) /*Treat VRF add for existing vrf as update * Update VRF ID and also update in VRF ID table + * and also update in vrf_ids_by_table hashmap */ if (name) vrf = vrf_lookup_by_name(name); @@ -222,6 +304,9 @@ struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name) vrf->vrf_id = new_vrf_id; RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + /* Update hash map */ + vrf_ids_by_table_update(vrf->data.l.table_id, new_vrf_id); + } else { /* @@ -232,6 +317,40 @@ struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name) return vrf; } +/* Update table_id in existing VRF. + * Arg: + * vrf - pointer to vrf struct. + * new_table_id - The table_id to be saved to vrf struct. + * Description: This function first checks if vrf struct has matching table_id + * matching new_table_id arg. If so, it ensures entry is created in + * vrf_ids_by_table hashmap and returns. If not matching, then it removes + * previous entry and adds new entry with new_table_id. + * Finally, it updates the table_id in the vrf struct. + */ +void vrf_update_table_id(struct vrf *vrf, uint32_t new_table_id) +{ + if (!vrf) + return; + + /* If new and old table ids are same */ + if (new_table_id == vrf->data.l.table_id) { + + /* Ensure that entry is added to hash map */ + vrf_ids_by_table_update(new_table_id, vrf->vrf_id); + + return; + } + + /* New and old table ids are different + * so remove any entry with old table_id and vrf_id, and add new entry + */ + vrf_ids_by_table_delete(vrf->data.l.table_id); + vrf_ids_by_table_update(new_table_id, vrf->vrf_id); + + /*Update table_id in vrf struct */ + vrf->data.l.table_id = new_table_id; +} + /* Delete a VRF. This is called when the underlying VRF goes away, a * pre-configured VRF is deleted or when shutting down (vrf_terminate()). */ @@ -263,6 +382,9 @@ void vrf_delete(struct vrf *vrf) if (vrf_master.vrf_delete_hook) (*vrf_master.vrf_delete_hook)(vrf); + /* remove from vrf_ids_by_table hash map*/ + vrf_ids_by_table_delete(vrf->data.l.table_id); + QOBJ_UNREG(vrf); if (vrf->name[0] != '\0') @@ -279,6 +401,48 @@ struct vrf *vrf_lookup_by_id(vrf_id_t vrf_id) return (RB_FIND(vrf_id_head, &vrfs_by_id, &vrf)); } +/* Finds vrf_id of vrf with table_id or ns_id. + * + * table_id + * The table_id associated with vrf of interest + * + * ns_id + * The ns_id associated with vrf of interest + * + * Description: + * This function checks if vrf is backened by netns + * or vrf lite, and looks them up appropriately. + * Hashmap look up implemented for vrf lite, but not netns. + * + * Returns: + * vrf_id if found table by table_id, or VRF_DEFAULT if not found + */ +vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) +{ + struct vrf *vrf; + + /* case vrf with netns : match the netnsid */ + if (vrf_is_backend_netns()) { + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + if (vrf->info == NULL) + continue; + if (ns_id == vrf->vrf_id) + return vrf->vrf_id; + } + /* case vrf with VRF_BACKEND_VRF_LITE : match the table_id */ + } else if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { + + struct vrf_ids_by_table_entry lookup = {.table_id = table_id}; + + struct vrf_ids_by_table_entry *entry = + vrf_ids_by_table_find(&vrf_ids_by_table_head, &lookup); + if (entry != NULL) + return entry->vrf_id; + } + + return VRF_DEFAULT; +} + /* * Enable a VRF - that is, let the VRF be ready to use. * The VRF_ENABLE_HOOK callback will be called to inform @@ -498,6 +662,10 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), vrf_master.vrf_disable_hook = disable; vrf_master.vrf_delete_hook = destroy; + /* Initialize vrf_ids_by_table */ + vrf_ids_by_table_init(&vrf_ids_by_table_head); + vrf_ids_by_table_is_initialized = 1; + /* The default VRF always exists. */ default_vrf = vrf_get(VRF_DEFAULT, VRF_DEFAULT_NAME); if (!default_vrf) { @@ -559,6 +727,10 @@ void vrf_terminate(void) vrf = vrf_lookup_by_id(VRF_DEFAULT); if (vrf) vrf_terminate_single(vrf); + + /* Clean up vrf_ids_by_table hashmap */ + vrf_ids_by_table_fini(&vrf_ids_by_table_head); + vrf_ids_by_table_is_initialized = 0; } int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, diff --git a/lib/vrf.h b/lib/vrf.h index 734176db9046..d063e709245f 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -116,8 +116,10 @@ extern struct vrf_name_head vrfs_by_name; extern struct vrf *vrf_lookup_by_id(vrf_id_t); extern struct vrf *vrf_lookup_by_name(const char *); +extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id); extern struct vrf *vrf_get(vrf_id_t, const char *); extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name); +extern void vrf_update_table_id(struct vrf *vrf, uint32_t new_table_id); extern const char *vrf_id_to_name(vrf_id_t vrf_id); #define VRF_LOGNAME(V) V ? V->name : "Unknown" diff --git a/lib/zclient.c b/lib/zclient.c index f6c5a8af08fb..17d6114dba7a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2150,7 +2150,9 @@ static int zclient_vrf_add(ZAPI_CALLBACK_ARGS) if (!vrf) return 0; - vrf->data.l.table_id = data.l.table_id; + /* set the vrf table_id if created*/ + vrf_update_table_id(vrf, data.l.table_id); + memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ); vrf_enable(vrf); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index c6423dce99cc..27b8d55e2f3f 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -361,33 +361,6 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) return proto; } -/* -Pending: create an efficient table_id (in a tree/hash) based lookup) - */ -vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) -{ - struct vrf *vrf; - struct zebra_vrf *zvrf; - - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - zvrf = vrf->info; - if (zvrf == NULL) - continue; - /* case vrf with netns : match the netnsid */ - if (vrf_is_backend_netns()) { - if (ns_id == zvrf_id(zvrf)) - return zvrf_id(zvrf); - } else { - /* VRF is VRF_BACKEND_VRF_LITE */ - if (zvrf->table_id != table_id) - continue; - return zvrf_id(zvrf); - } - } - - return VRF_DEFAULT; -} - /** * @parse_encap_mpls() - Parses encapsulated mpls attributes * @tb: Pointer to rtattr to look for nested items in. diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index b1af4b20e18c..3a3252b5d9f4 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -103,7 +103,6 @@ extern int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, uint16_t vid); extern int netlink_neigh_read_specific_ip(const struct ipaddr *ip, struct interface *vlan_if); -extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id); struct nl_batch; extern enum netlink_msg_status