From d5a65db98d66c66b03b057ac568be05f2456f73c Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Wed, 17 May 2017 17:21:10 +0200 Subject: [PATCH] LISP: L2 ARP handling Change-Id: I1ec328cda73f7eaf7867cd8a2a17852ee0cd23f1 Signed-off-by: Filip Tehlar --- src/vat/api_format.c | 233 ++++++++++++++++++++++++++++++++++++++ src/vnet/lisp-cp/control.c | 232 ++++++++++++++++++++++++++++++++----- src/vnet/lisp-cp/control.h | 15 +++ src/vnet/lisp-cp/gid_dictionary.c | 91 ++++++++++++++- src/vnet/lisp-cp/gid_dictionary.h | 25 +++- src/vnet/lisp-cp/lisp_types.c | 3 + src/vnet/lisp-cp/lisp_types.h | 15 +++ src/vnet/lisp-cp/one.api | 73 ++++++++++++ src/vnet/lisp-cp/one_api.c | 82 ++++++++++++++ src/vnet/lisp-cp/one_cli.c | 100 ++++++++++++++++ 10 files changed, 833 insertions(+), 36 deletions(-) diff --git a/src/vat/api_format.c b/src/vat/api_format.c index f3e6f64c57e..3eff8ef0a20 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -3163,6 +3163,123 @@ end: vam->result_ready = 1; } +static void + vl_api_one_l2_arp_entries_get_reply_t_handler + (vl_api_one_l2_arp_entries_get_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + u32 i, n; + int retval = clib_net_to_host_u32 (mp->retval); + + if (retval) + goto end; + + n = clib_net_to_host_u32 (mp->count); + + for (i = 0; i < n; i++) + print (vam->ofp, "%U -> %U", format_ip4_address, &mp->entries[i].ip4, + format_ethernet_address, mp->entries[i].mac); + +end: + vam->retval = retval; + vam->result_ready = 1; +} + +static void + vl_api_one_l2_arp_entries_get_reply_t_handler_json + (vl_api_one_l2_arp_entries_get_reply_t * mp) +{ + u8 *s = 0; + vat_main_t *vam = &vat_main; + vat_json_node_t *e = 0, root; + u32 i, n; + int retval = clib_net_to_host_u32 (mp->retval); + vl_api_one_l2_arp_entry_t *arp_entry; + + if (retval) + goto end; + + n = clib_net_to_host_u32 (mp->count); + vat_json_init_array (&root); + + for (i = 0; i < n; i++) + { + e = vat_json_array_add (&root); + arp_entry = &mp->entries[i]; + + vat_json_init_object (e); + s = format (0, "%U", format_ethernet_address, arp_entry->mac); + vec_add1 (s, 0); + + vat_json_object_add_string_copy (e, "mac", s); + vec_free (s); + + s = format (0, "%U", format_ip4_address, &arp_entry->ip4); + vec_add1 (s, 0); + vat_json_object_add_string_copy (e, "ip4", s); + vec_free (s); + } + + vat_json_print (vam->ofp, &root); + vat_json_free (&root); + +end: + vam->retval = retval; + vam->result_ready = 1; +} + +static void + vl_api_one_l2_arp_bd_get_reply_t_handler + (vl_api_one_l2_arp_bd_get_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + u32 i, n; + int retval = clib_net_to_host_u32 (mp->retval); + + if (retval) + goto end; + + n = clib_net_to_host_u32 (mp->count); + + for (i = 0; i < n; i++) + { + print (vam->ofp, "%d", clib_net_to_host_u32 (mp->bridge_domains[i])); + } + +end: + vam->retval = retval; + vam->result_ready = 1; +} + +static void + vl_api_one_l2_arp_bd_get_reply_t_handler_json + (vl_api_one_l2_arp_bd_get_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + vat_json_node_t root; + u32 i, n; + int retval = clib_net_to_host_u32 (mp->retval); + + if (retval) + goto end; + + n = clib_net_to_host_u32 (mp->count); + vat_json_init_array (&root); + + for (i = 0; i < n; i++) + { + vat_json_array_add_uint (&root, + clib_net_to_host_u32 (mp->bridge_domains[i])); + } + + vat_json_print (vam->ofp, &root); + vat_json_free (&root); + +end: + vam->retval = retval; + vam->result_ready = 1; +} + static void vl_api_one_adjacencies_get_reply_t_handler (vl_api_one_adjacencies_get_reply_t * mp) @@ -4158,6 +4275,10 @@ static void vl_api_flow_classify_details_t_handler_json #define vl_api_vnet_ip6_nbr_counters_t_print vl_noop_handler #define vl_api_one_adjacencies_get_reply_t_endian vl_noop_handler #define vl_api_one_adjacencies_get_reply_t_print vl_noop_handler +#define vl_api_one_l2_arp_bd_get_reply_t_print vl_noop_handler +#define vl_api_one_l2_arp_entries_get_reply_t_endian vl_noop_handler +#define vl_api_one_l2_arp_entries_get_reply_t_print vl_noop_handler +#define vl_api_one_l2_arp_bd_get_reply_t_endian vl_noop_handler /* * Generate boilerplate reply handlers, which @@ -4267,6 +4388,7 @@ _(one_add_del_map_request_itr_rlocs_reply) \ _(one_eid_table_add_del_map_reply) \ _(one_use_petr_reply) \ _(one_stats_enable_disable_reply) \ +_(one_add_del_l2_arp_entry_reply) \ _(one_stats_flush_reply) \ _(gpe_add_del_fwd_entry_reply) \ _(gpe_enable_disable_reply) \ @@ -4499,6 +4621,9 @@ _(ONE_STATS_FLUSH_REPLY, one_stats_flush_reply) \ _(ONE_STATS_ENABLE_DISABLE_REPLY, one_stats_enable_disable_reply) \ _(SHOW_ONE_STATS_ENABLE_DISABLE_REPLY, \ show_one_stats_enable_disable_reply) \ +_(ONE_ADD_DEL_L2_ARP_ENTRY_REPLY, one_add_del_l2_arp_entry_reply) \ +_(ONE_L2_ARP_BD_GET_REPLY, one_l2_arp_bd_get_reply) \ +_(ONE_L2_ARP_ENTRIES_GET_REPLY, one_l2_arp_entries_get_reply) \ _(GPE_SET_ENCAP_MODE_REPLY, gpe_set_encap_mode_reply) \ _(GPE_GET_ENCAP_MODE_REPLY, gpe_get_encap_mode_reply) \ _(GPE_ADD_DEL_IFACE_REPLY, gpe_add_del_iface_reply) \ @@ -14736,6 +14861,111 @@ api_show_one_rloc_probe_state (vat_main_t * vam) #define api_show_lisp_rloc_probe_state api_show_one_rloc_probe_state +static int +api_one_add_del_l2_arp_entry (vat_main_t * vam) +{ + vl_api_one_add_del_l2_arp_entry_t *mp; + unformat_input_t *input = vam->input; + u8 is_add = 1; + u8 mac_set = 0; + u8 bd_set = 0; + u8 ip_set = 0; + u8 mac[6] = { 0, }; + u32 ip4 = 0, bd = ~0; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_add = 0; + else if (unformat (input, "mac %U", unformat_ethernet_address, mac)) + mac_set = 1; + else if (unformat (input, "ip %U", unformat_ip4_address, &ip4)) + ip_set = 1; + else if (unformat (input, "bd %d", &bd)) + bd_set = 1; + else + { + errmsg ("parse error '%U'", format_unformat_error, input); + return -99; + } + } + + if (!bd_set || !ip_set || (!mac_set && is_add)) + { + errmsg ("Missing BD, IP or MAC!"); + return -99; + } + + M (ONE_ADD_DEL_L2_ARP_ENTRY, mp); + mp->is_add = is_add; + clib_memcpy (mp->mac, mac, 6); + mp->bd = clib_host_to_net_u32 (bd); + mp->ip4 = ip4; + + /* send */ + S (mp); + + /* wait for reply */ + W (ret); + return ret; +} + +static int +api_one_l2_arp_bd_get (vat_main_t * vam) +{ + vl_api_one_l2_arp_bd_get_t *mp; + int ret; + + M (ONE_L2_ARP_BD_GET, mp); + + /* send */ + S (mp); + + /* wait for reply */ + W (ret); + return ret; +} + +static int +api_one_l2_arp_entries_get (vat_main_t * vam) +{ + vl_api_one_l2_arp_entries_get_t *mp; + unformat_input_t *input = vam->input; + u8 bd_set = 0; + u32 bd = ~0; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "bd %d", &bd)) + bd_set = 1; + else + { + errmsg ("parse error '%U'", format_unformat_error, input); + return -99; + } + } + + if (!bd_set) + { + errmsg ("Expected bridge domain!"); + return -99; + } + + M (ONE_L2_ARP_ENTRIES_GET, mp); + mp->bd = clib_host_to_net_u32 (bd); + + /* send */ + S (mp); + + /* wait for reply */ + W (ret); + return ret; +} + static int api_one_stats_enable_disable (vat_main_t * vam) { @@ -19052,6 +19282,9 @@ _(one_locator_set_dump, "[local | remote]") \ _(one_locator_dump, "ls_index | ls_name ") \ _(one_eid_table_dump, "[eid / | ] [vni] " \ "[local] | [remote]") \ +_(one_add_del_l2_arp_entry, "[del] mac bd ip4 ") \ +_(one_l2_arp_bd_get, "") \ +_(one_l2_arp_entries_get, "bd ") \ _(one_stats_enable_disable, "enable|disalbe") \ _(show_one_stats_enable_disable, "") \ _(one_eid_table_vni_dump, "") \ diff --git a/src/vnet/lisp-cp/control.c b/src/vnet/lisp-cp/control.c index 439802c947c..cea92556284 100644 --- a/src/vnet/lisp-cp/control.c +++ b/src/vnet/lisp-cp/control.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -821,6 +823,99 @@ vnet_lisp_add_del_local_mapping (vnet_lisp_add_del_mapping_args_t * a, return vnet_lisp_map_cache_add_del (a, map_index_result); } +static void +add_l2_arp_bd (BVT (clib_bihash_kv) * kvp, void *arg) +{ + u32 **ht = arg; + u32 bd = (u32) kvp->key[0]; + hash_set (ht[0], bd, 0); +} + +u32 * +vnet_lisp_l2_arp_bds_get (void) +{ + lisp_cp_main_t *lcm = vnet_lisp_cp_get_main (); + u32 *bds = 0; + + gid_dict_foreach_l2_arp_entry (&lcm->mapping_index_by_gid, + add_l2_arp_bd, &bds); + return bds; +} + +typedef struct +{ + void *vector; + u32 bd; +} lisp_add_l2_arp_args_t; + +static void +add_l2_arp_entry (BVT (clib_bihash_kv) * kvp, void *arg) +{ + lisp_add_l2_arp_args_t *a = arg; + lisp_api_l2_arp_entry_t **vector = a->vector, e; + + if ((u32) kvp->key[0] == a->bd) + { + mac_copy (e.mac, (void *) &kvp->value); + e.ip4 = (u32) kvp->key[1]; + vec_add1 (vector[0], e); + } +} + +lisp_api_l2_arp_entry_t * +vnet_lisp_l2_arp_entries_get_by_bd (u32 bd) +{ + lisp_api_l2_arp_entry_t *entries = 0; + lisp_cp_main_t *lcm = vnet_lisp_cp_get_main (); + lisp_add_l2_arp_args_t a; + + a.vector = &entries; + a.bd = bd; + + gid_dict_foreach_l2_arp_entry (&lcm->mapping_index_by_gid, + add_l2_arp_entry, &a); + return entries; +} + +int +vnet_lisp_add_del_l2_arp_entry (gid_address_t * key, u8 * mac, u8 is_add) +{ + if (vnet_lisp_enable_disable_status () == 0) + { + clib_warning ("LISP is disabled!"); + return VNET_API_ERROR_LISP_DISABLED; + } + + lisp_cp_main_t *lcm = vnet_lisp_cp_get_main (); + int rc = 0; + + u64 res = gid_dictionary_lookup (&lcm->mapping_index_by_gid, key); + if (is_add) + { + if (res != GID_LOOKUP_MISS_L2) + { + clib_warning ("Entry %U exists in DB!", format_gid_address, key); + return VNET_API_ERROR_ENTRY_ALREADY_EXISTS; + } + u64 val = mac_to_u64 (mac); + gid_dictionary_add_del (&lcm->mapping_index_by_gid, key, val, + 1 /* is_add */ ); + } + else + { + if (res == GID_LOOKUP_MISS_L2) + { + clib_warning ("ONE ARP entry %U not found - cannot delete!", + format_gid_address, key); + return -1; + } + gid_dictionary_add_del (&lcm->mapping_index_by_gid, key, 0, + 0 /* is_add */ ); + } + + return rc; +} + int vnet_lisp_eid_table_map (u32 vni, u32 dp_id, u8 is_l2, u8 is_add) { @@ -830,7 +925,7 @@ vnet_lisp_eid_table_map (u32 vni, u32 dp_id, u8 is_l2, u8 is_add) if (vnet_lisp_enable_disable_status () == 0) { clib_warning ("LISP is disabled!"); - return -1; + return VNET_API_ERROR_LISP_DISABLED; } dp_table_by_vni = is_l2 ? &lcm->bd_id_by_vni : &lcm->table_id_by_vni; @@ -1931,7 +2026,8 @@ vnet_lisp_add_del_mreq_itr_rlocs (vnet_lisp_add_del_mreq_itr_rloc_args_t * a) /* Statistics (not really errors) */ #define foreach_lisp_cp_lookup_error \ _(DROP, "drop") \ -_(MAP_REQUESTS_SENT, "map-request sent") +_(MAP_REQUESTS_SENT, "map-request sent") \ +_(ARP_REPLY_TX, "ARP replies sent") static char *lisp_cp_lookup_error_strings[] = { #define _(sym,string) string, @@ -1950,6 +2046,7 @@ typedef enum typedef enum { LISP_CP_LOOKUP_NEXT_DROP, + LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX, LISP_CP_LOOKUP_N_NEXT, } lisp_cp_lookup_next_t; @@ -2710,10 +2807,8 @@ lisp_get_vni_from_buffer_ip (lisp_cp_main_t * lcm, vlib_buffer_t * b, } always_inline u32 -lisp_get_vni_from_buffer_eth (lisp_cp_main_t * lcm, vlib_buffer_t * b) +lisp_get_bd_from_buffer_eth (vlib_buffer_t * b) { - uword *vnip; - u32 vni = ~0; u32 sw_if_index0; l2input_main_t *l2im = &l2input_main; @@ -2724,12 +2819,21 @@ lisp_get_vni_from_buffer_eth (lisp_cp_main_t * lcm, vlib_buffer_t * b) config = vec_elt_at_index (l2im->configs, sw_if_index0); bd_config = vec_elt_at_index (l2im->bd_configs, config->bd_index); - vnip = hash_get (lcm->vni_by_bd_id, bd_config->bd_id); + return bd_config->bd_id; +} + +always_inline u32 +lisp_get_vni_from_buffer_eth (lisp_cp_main_t * lcm, vlib_buffer_t * b) +{ + uword *vnip; + u32 vni = ~0; + u32 bd = lisp_get_bd_from_buffer_eth (b); + + vnip = hash_get (lcm->vni_by_bd_id, bd); if (vnip) vni = vnip[0]; else - clib_warning ("bridge domain %d is not mapped to any vni!", - config->bd_index); + clib_warning ("bridge domain %d is not mapped to any vni!", bd); return vni; } @@ -2744,6 +2848,9 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b, memset (src, 0, sizeof (*src)); memset (dst, 0, sizeof (*dst)); + gid_address_type (dst) = GID_ADDR_NO_ADDRESS; + gid_address_type (src) = GID_ADDR_NO_ADDRESS; + if (LISP_AFI_IP == type || LISP_AFI_IP6 == type) { ip4_header_t *ip; @@ -2767,19 +2874,35 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b, else if (LISP_AFI_MAC == type) { ethernet_header_t *eh; + ethernet_arp_header_t *ah; eh = vlib_buffer_get_current (b); - gid_address_type (src) = GID_ADDR_MAC; - gid_address_type (dst) = GID_ADDR_MAC; - mac_copy (&gid_address_mac (src), eh->src_address); - mac_copy (&gid_address_mac (dst), eh->dst_address); + if (clib_net_to_host_u16 (eh->type) == ETHERNET_TYPE_ARP) + { + ah = (ethernet_arp_header_t *) (((u8 *) eh) + sizeof (*eh)); + if (clib_net_to_host_u16 (ah->opcode) + != ETHERNET_ARP_OPCODE_request) + return; + + gid_address_type (dst) = GID_ADDR_ARP; + gid_address_arp_bd (dst) = lisp_get_bd_from_buffer_eth (b); + clib_memcpy (&gid_address_arp_ip4 (dst), + &ah->ip4_over_ethernet[1].ip4, 4); + } + else + { + gid_address_type (src) = GID_ADDR_MAC; + gid_address_type (dst) = GID_ADDR_MAC; + mac_copy (&gid_address_mac (src), eh->src_address); + mac_copy (&gid_address_mac (dst), eh->dst_address); - /* get vni */ - vni = lisp_get_vni_from_buffer_eth (lcm, b); + /* get vni */ + vni = lisp_get_vni_from_buffer_eth (lcm, b); - gid_address_vni (dst) = vni; - gid_address_vni (src) = vni; + gid_address_vni (dst) = vni; + gid_address_vni (src) = vni; + } } else if (LISP_AFI_LCAF == type) { @@ -2793,38 +2916,81 @@ lisp_cp_lookup_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame, int overlay) { - u32 *from, *to_next_drop, di, si; + u32 *from, *to_next, di, si; lisp_cp_main_t *lcm = vnet_lisp_cp_get_main (); - u32 pkts_mapped = 0; - uword n_left_from, n_left_to_next_drop; + u32 pkts_mapped = 0, next_index; + uword n_left_from, n_left_to_next; + vnet_main_t *vnm = vnet_get_main (); from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; + next_index = node->cached_next_index; while (n_left_from > 0) { - vlib_get_next_frame (vm, node, LISP_CP_LOOKUP_NEXT_DROP, - to_next_drop, n_left_to_next_drop); + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - while (n_left_from > 0 && n_left_to_next_drop > 0) + while (n_left_from > 0 && n_left_to_next > 0) { - u32 pi0; + u32 pi0, sw_if_index0, next0; + u64 mac0; vlib_buffer_t *b0; gid_address_t src, dst; + ethernet_arp_header_t *arp0; + ethernet_header_t *eth0; + vnet_hw_interface_t *hw_if0; pi0 = from[0]; from += 1; n_left_from -= 1; - to_next_drop[0] = pi0; - to_next_drop += 1; - n_left_to_next_drop -= 1; + to_next[0] = pi0; + to_next += 1; + n_left_to_next -= 1; b0 = vlib_get_buffer (vm, pi0); - b0->error = node->errors[LISP_CP_LOOKUP_ERROR_DROP]; /* src/dst eid pair */ get_src_and_dst_eids_from_buffer (lcm, b0, &src, &dst, overlay); + if (gid_address_type (&dst) == GID_ADDR_ARP) + { + mac0 = gid_dictionary_lookup (&lcm->mapping_index_by_gid, &dst); + if (GID_LOOKUP_MISS_L2 != mac0) + { + /* send ARP reply */ + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0; + + hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0); + + eth0 = vlib_buffer_get_current (b0); + arp0 = (ethernet_arp_header_t *) (((u8 *) eth0) + + sizeof (*eth0)); + arp0->opcode = + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply); + arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0]; + clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, + (u8 *) & mac0, 6); + clib_memcpy (&arp0->ip4_over_ethernet[0].ip4, + &gid_address_arp_ip4 (&dst), 4); + + /* Hardware must be ethernet-like. */ + ASSERT (vec_len (hw_if0->hw_address) == 6); + + clib_memcpy (eth0->dst_address, eth0->src_address, 6); + clib_memcpy (eth0->src_address, hw_if0->hw_address, 6); + + b0->error = node->errors[LISP_CP_LOOKUP_ERROR_ARP_REPLY_TX]; + next0 = LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX; + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, + n_left_to_next, pi0, + next0); + } + continue; + } + /* if we have remote mapping for destination already in map-chache add forwarding tunnel directly. If not send a map-request */ di = gid_dictionary_sd_lookup (&lcm->mapping_index_by_gid, &dst, @@ -2859,6 +3025,7 @@ lisp_cp_lookup_inline (vlib_main_t * vm, pkts_mapped++; } + b0->error = node->errors[LISP_CP_LOOKUP_ERROR_DROP]; if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { lisp_cp_lookup_trace_t *tr = vlib_add_trace (vm, node, b0, @@ -2871,10 +3038,13 @@ lisp_cp_lookup_inline (vlib_main_t * vm, } gid_address_free (&dst); gid_address_free (&src); + next0 = LISP_CP_LOOKUP_NEXT_DROP; + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, + n_left_to_next, pi0, next0); } - vlib_put_next_frame (vm, node, LISP_CP_LOOKUP_NEXT_DROP, - n_left_to_next_drop); + vlib_put_next_frame (vm, node, next_index, n_left_to_next); } vlib_node_increment_counter (vm, node->node_index, LISP_CP_LOOKUP_ERROR_MAP_REQUESTS_SENT, @@ -2926,6 +3096,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_ip4_node) = { .next_nodes = { [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop", + [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output", }, }; /* *INDENT-ON* */ @@ -2945,6 +3116,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_ip6_node) = { .next_nodes = { [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop", + [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output", }, }; /* *INDENT-ON* */ @@ -2964,6 +3136,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_l2_node) = { .next_nodes = { [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop", + [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output", }, }; /* *INDENT-ON* */ @@ -2983,6 +3156,7 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_nsh_node) = { .next_nodes = { [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop", + [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output", }, }; /* *INDENT-ON* */ diff --git a/src/vnet/lisp-cp/control.h b/src/vnet/lisp-cp/control.h index cb98eb09e16..feb8cfa41a3 100644 --- a/src/vnet/lisp-cp/control.h +++ b/src/vnet/lisp-cp/control.h @@ -87,6 +87,12 @@ typedef struct miss_packet_type_t type; } miss_packet_t; +typedef struct +{ + u8 mac[6]; + u32 ip4; +} lisp_api_l2_arp_entry_t; + typedef enum { MR_MODE_DST_ONLY = 0, @@ -112,6 +118,12 @@ typedef enum lisp_flags #undef _ } lisp_flags_e; +typedef struct +{ + ip_address_t addr; + u32 bd; +} lisp_l2_arp_key_t; + typedef struct { u32 flags; @@ -338,6 +350,9 @@ int vnet_lisp_rloc_probe_enable_disable (u8 is_enable); int vnet_lisp_map_register_enable_disable (u8 is_enable); u8 vnet_lisp_map_register_state_get (void); u8 vnet_lisp_rloc_probe_state_get (void); +int vnet_lisp_add_del_l2_arp_entry (gid_address_t * key, u8 * mac, u8 is_add); +u32 *vnet_lisp_l2_arp_bds_get (void); +lisp_api_l2_arp_entry_t *vnet_lisp_l2_arp_entries_get_by_bd (u32 bd); map_records_arg_t *parse_map_reply (vlib_buffer_t * b); diff --git a/src/vnet/lisp-cp/gid_dictionary.c b/src/vnet/lisp-cp/gid_dictionary.c index b01bb0e0684..80d59fafdd7 100644 --- a/src/vnet/lisp-cp/gid_dictionary.c +++ b/src/vnet/lisp-cp/gid_dictionary.c @@ -138,6 +138,15 @@ gid_dict_foreach_subprefix (gid_dictionary_t * db, gid_address_t * eid, arg); } +void +gid_dict_foreach_l2_arp_entry (gid_dictionary_t * db, void (*cb) + (BVT (clib_bihash_kv) * kvp, void *arg), + void *ht) +{ + gid_l2_arp_table_t *tab = &db->arp_table; + BV (clib_bihash_foreach_key_value_pair) (&tab->arp_lookup_table, cb, ht); +} + static void make_mac_sd_key (BVT (clib_bihash_kv) * kv, u32 vni, u8 src_mac[6], u8 dst_mac[6]) @@ -328,7 +337,30 @@ ip_sd_lookup (gid_dictionary_t * db, u32 vni, ip_prefix_t * dst, return GID_LOOKUP_MISS; } -u32 +static void +make_arp_key (BVT (clib_bihash_kv) * kv, u32 bd, ip4_address_t * addr) +{ + kv->key[0] = (u64) bd; + kv->key[1] = (u64) addr->as_u32; + kv->key[2] = (u64) 0; +} + +static u64 +arp_lookup (gid_l2_arp_table_t * db, u32 bd, ip4_address_t * key) +{ + int rv; + BVT (clib_bihash_kv) kv, value; + + make_arp_key (&kv, bd, key); + rv = BV (clib_bihash_search_inline_2) (&db->arp_lookup_table, &kv, &value); + + if (rv == 0) + return value.value; + + return GID_LOOKUP_MISS_L2; +} + +u64 gid_dictionary_lookup (gid_dictionary_t * db, gid_address_t * key) { switch (gid_address_type (key)) @@ -358,6 +390,9 @@ gid_dictionary_lookup (gid_dictionary_t * db, gid_address_t * key) break; } break; + case GID_ADDR_ARP: + return arp_lookup (&db->arp_table, gid_address_arp_bd (key), + &gid_address_arp_ip4 (key)); default: clib_warning ("address type %d not supported!", gid_address_type (key)); break; @@ -825,21 +860,49 @@ add_del_sd (gid_dictionary_t * db, u32 vni, source_dest_t * key, u32 value, return ~0; } +static u32 +add_del_arp (gid_l2_arp_table_t * db, u32 bd, ip4_address_t * key, u64 value, + u8 is_add) +{ + BVT (clib_bihash_kv) kv, result; + u32 old_val = ~0; + + make_arp_key (&kv, bd, key); + if (BV (clib_bihash_search) (&db->arp_lookup_table, &kv, &result) == 0) + old_val = result.value; + + if (is_add) + { + kv.value = value; + BV (clib_bihash_add_del) (&db->arp_lookup_table, &kv, 1 /* is_add */ ); + db->count++; + } + else + { + BV (clib_bihash_add_del) (&db->arp_lookup_table, &kv, 0 /* is_add */ ); + db->count--; + } + return old_val; +} + u32 -gid_dictionary_add_del (gid_dictionary_t * db, gid_address_t * key, u32 value, +gid_dictionary_add_del (gid_dictionary_t * db, gid_address_t * key, u64 value, u8 is_add) { switch (gid_address_type (key)) { case GID_ADDR_IP_PREFIX: return add_del_ip (db, gid_address_vni (key), &gid_address_ippref (key), - 0, value, is_add); + 0, (u32) value, is_add); case GID_ADDR_MAC: return add_del_mac (&db->sd_mac_table, gid_address_vni (key), - gid_address_mac (key), 0, value, is_add); + gid_address_mac (key), 0, (u32) value, is_add); case GID_ADDR_SRC_DST: return add_del_sd (db, gid_address_vni (key), &gid_address_sd (key), - value, is_add); + (u32) value, is_add); + case GID_ADDR_ARP: + return add_del_arp (&db->arp_table, gid_address_arp_bd (key), + &gid_address_arp_ip4 (key), value, is_add); default: clib_warning ("address type %d not supported!", gid_address_type (key)); break; @@ -864,12 +927,30 @@ mac_lookup_init (gid_mac_table_t * db) db->mac_lookup_table_size); } +static void +arp_lookup_init (gid_l2_arp_table_t * db) +{ + if (db->arp_lookup_table_nbuckets == 0) + db->arp_lookup_table_nbuckets = ARP_LOOKUP_DEFAULT_HASH_NUM_BUCKETS; + + db->arp_lookup_table_nbuckets = + 1 << max_log2 (db->arp_lookup_table_nbuckets); + + if (db->arp_lookup_table_size == 0) + db->arp_lookup_table_size = ARP_LOOKUP_DEFAULT_HASH_MEMORY_SIZE; + + BV (clib_bihash_init) (&db->arp_lookup_table, "arp lookup table", + db->arp_lookup_table_nbuckets, + db->arp_lookup_table_size); +} + void gid_dictionary_init (gid_dictionary_t * db) { ip4_lookup_init (&db->dst_ip4_table); ip6_lookup_init (&db->dst_ip6_table); mac_lookup_init (&db->sd_mac_table); + arp_lookup_init (&db->arp_table); } /* diff --git a/src/vnet/lisp-cp/gid_dictionary.h b/src/vnet/lisp-cp/gid_dictionary.h index 825e9856e7a..9612fb133fd 100644 --- a/src/vnet/lisp-cp/gid_dictionary.h +++ b/src/vnet/lisp-cp/gid_dictionary.h @@ -22,6 +22,7 @@ #include #define GID_LOOKUP_MISS ((u32)~0) +#define GID_LOOKUP_MISS_L2 ((u64)~0) /* Default size of the ip4 hash table */ #define IP4_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) @@ -35,6 +36,10 @@ #define MAC_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) #define MAC_LOOKUP_DEFAULT_HASH_MEMORY_SIZE (32<<20) +/* Default size of the ARP hash table */ +#define ARP_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) +#define ARP_LOOKUP_DEFAULT_HASH_MEMORY_SIZE (32<<20) + typedef void (*foreach_subprefix_match_cb_t) (u32, void *); typedef struct @@ -81,6 +86,17 @@ typedef struct gid_mac_table typedef struct { + BVT (clib_bihash) arp_lookup_table; + u32 arp_lookup_table_nbuckets; + uword arp_lookup_table_size; + u64 count; +} gid_l2_arp_table_t; + +typedef struct +{ + /** L2 ARP table */ + gid_l2_arp_table_t arp_table; + /** destination IP LPM ip4 lookup table */ gid_ip4_table_t dst_ip4_table; @@ -99,10 +115,10 @@ typedef struct } gid_dictionary_t; u32 -gid_dictionary_add_del (gid_dictionary_t * db, gid_address_t * key, u32 value, +gid_dictionary_add_del (gid_dictionary_t * db, gid_address_t * key, u64 value, u8 is_add); -u32 gid_dictionary_lookup (gid_dictionary_t * db, gid_address_t * key); +u64 gid_dictionary_lookup (gid_dictionary_t * db, gid_address_t * key); u32 gid_dictionary_sd_lookup (gid_dictionary_t * db, gid_address_t * dst, gid_address_t * src); @@ -112,6 +128,11 @@ void gid_dict_foreach_subprefix (gid_dictionary_t * db, gid_address_t * eid, foreach_subprefix_match_cb_t cb, void *arg); +void +gid_dict_foreach_l2_arp_entry (gid_dictionary_t * db, void (*cb) + (BVT (clib_bihash_kv) * kvp, void *arg), + void *ht); + #endif /* VNET_LISP_GPE_GID_DICTIONARY_H_ */ /* diff --git a/src/vnet/lisp-cp/lisp_types.c b/src/vnet/lisp-cp/lisp_types.c index 31a80081330..85cefae05ab 100644 --- a/src/vnet/lisp-cp/lisp_types.c +++ b/src/vnet/lisp-cp/lisp_types.c @@ -256,6 +256,9 @@ format_gid_address (u8 * s, va_list * args) &gid_address_mac (a)); case GID_ADDR_NSH: return format (s, "%U", format_nsh_address, &gid_address_nsh (a)); + case GID_ADDR_ARP: + return format (s, "[%d, %U]", gid_address_arp_bd (a), + format_ip4_address, &gid_address_arp_ip4 (a)); default: clib_warning ("Can't format gid type %d", type); return 0; diff --git a/src/vnet/lisp-cp/lisp_types.h b/src/vnet/lisp-cp/lisp_types.h index a65a479e533..f5d2a676ff7 100644 --- a/src/vnet/lisp-cp/lisp_types.h +++ b/src/vnet/lisp-cp/lisp_types.h @@ -90,6 +90,7 @@ typedef enum GID_ADDR_MAC, GID_ADDR_SRC_DST, GID_ADDR_NSH, + GID_ADDR_ARP, GID_ADDR_NO_ADDRESS, GID_ADDR_TYPES } gid_address_type_t; @@ -165,12 +166,22 @@ typedef struct u8 si; } nsh_t; +typedef struct +{ + ip4_address_t addr; + u32 bd; +} lcaf_arp_t; + +#define lcaf_arp_ip4(_a) (_a)->addr +#define lcaf_arp_bd(_a) (_a)->bd + typedef struct { /* the union needs to be at the beginning! */ union { source_dest_t sd; + lcaf_arp_t arp; vni_t uni; }; u8 type; @@ -189,6 +200,7 @@ typedef struct _gid_address_t lcaf_t lcaf; u8 mac[6]; source_dest_t sd; + lcaf_arp_t arp; nsh_t nsh; }; u8 type; @@ -257,6 +269,9 @@ void gid_address_ip_set (gid_address_t * dst, void *src, u8 version); #define gid_address_sd_dst(_a) sd_dst(&gid_address_sd(_a)) #define gid_address_sd_src_type(_a) sd_src_type(&gid_address_sd(_a)) #define gid_address_sd_dst_type(_a) sd_dst_type(&gid_address_sd(_a)) +#define gid_address_arp(_a) (_a)->arp +#define gid_address_arp_ip4(_a) lcaf_arp_ip4(&gid_address_arp (_a)) +#define gid_address_arp_bd(_a) lcaf_arp_bd(&gid_address_arp (_a)) /* 'sub'address functions */ #define foreach_gid_address_type_fcns \ diff --git a/src/vnet/lisp-cp/one.api b/src/vnet/lisp-cp/one.api index 2fa1edf6f91..7d07cc47db5 100644 --- a/src/vnet/lisp-cp/one.api +++ b/src/vnet/lisp-cp/one.api @@ -348,6 +348,79 @@ autoreply manual_print manual_endian define one_add_del_remote_mapping vl_api_one_remote_locator_t rlocs[rloc_num]; }; +/** \brief Add/delete L2 ARP entries + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - add if non-zero; delete otherwise + @param bd - bridge domain + @param mac - MAC address + @param ip4 - IPv4 address +*/ +autoreply define one_add_del_l2_arp_entry +{ + u32 client_index; + u32 context; + u8 is_add; + u8 mac[6]; + u32 bd; + u32 ip4; +}; + +/** \brief Request for L2 ARP entries from specified bridge domain + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bd - bridge domain +*/ +define one_l2_arp_entries_get +{ + u32 client_index; + u32 context; + u32 bd; +}; + +typeonly manual_print manual_endian define one_l2_arp_entry +{ + u8 mac[6]; + u32 ip4; +}; + +/** \brief Reply with L2 ARP entries from specified bridge domain + @param context - sender context, to match reply w/ request + @param retval - error code + @param count - number of elements in the list + @param vl_api_one_arp_entry_t - list of entries +*/ +manual_print manual_endian define one_l2_arp_entries_get_reply +{ + u32 context; + i32 retval; + u32 count; + vl_api_one_l2_arp_entry_t entries[count]; +}; + +/** \brief Request for list of bridge domains used by L2 ARP table + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define one_l2_arp_bd_get +{ + u32 client_index; + u32 context; +}; + +/** \brief Reply with list of bridge domains used by L2 ARP table + @param context - sender context, to match reply w/ request + @param count - number of elements in the list + @param bridge_domains - list of BDs +*/ +manual_print manual_endian define one_l2_arp_bd_get_reply +{ + u32 context; + i32 retval; + u32 count; + u32 bridge_domains[count]; +}; + /** \brief add or delete ONE adjacency adjacency @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/vnet/lisp-cp/one_api.c b/src/vnet/lisp-cp/one_api.c index 23549afa68b..724e58df839 100644 --- a/src/vnet/lisp-cp/one_api.c +++ b/src/vnet/lisp-cp/one_api.c @@ -42,6 +42,11 @@ #define vl_api_one_add_del_remote_mapping_t_endian vl_noop_handler #define vl_api_one_add_del_remote_mapping_t_print vl_noop_handler +#define vl_api_one_l2_arp_entry_t_endian vl_noop_handler +#define vl_api_one_l2_arp_entry_t_print vl_noop_handler +#define vl_api_one_add_del_l2_arp_entry vl_noop_handler +#define vl_api_one_l2_arp_bd_get vl_noop_handler + #define vl_typedefs /* define message structures */ #include #undef vl_typedefs @@ -109,6 +114,9 @@ _(SHOW_ONE_STATS_ENABLE_DISABLE, show_one_stats_enable_disable) \ _(ONE_STATS_ENABLE_DISABLE, one_stats_enable_disable) \ _(ONE_STATS_DUMP, one_stats_dump) \ _(ONE_STATS_FLUSH, one_stats_flush) \ +_(ONE_L2_ARP_BD_GET, one_l2_arp_bd_get) \ +_(ONE_L2_ARP_ENTRIES_GET, one_l2_arp_entries_get) \ +_(ONE_ADD_DEL_L2_ARP_ENTRY, one_add_del_l2_arp_entry) \ static locator_t * @@ -1387,6 +1395,80 @@ vl_api_one_stats_dump_t_handler (vl_api_one_stats_dump_t * mp) } } +static void + vl_api_one_add_del_l2_arp_entry_t_handler + (vl_api_one_add_del_l2_arp_entry_t * mp) +{ + vl_api_one_add_del_l2_arp_entry_reply_t *rmp; + int rv = 0; + gid_address_t _arp, *arp = &_arp; + memset (arp, 0, sizeof (*arp)); + + gid_address_type (arp) = GID_ADDR_ARP; + gid_address_arp_bd (arp) = clib_net_to_host_u32 (mp->bd); + + /* vpp keeps ip4 addresses in network byte order */ + clib_memcpy (&gid_address_arp_ip4 (arp), &mp->ip4, 4); + + rv = vnet_lisp_add_del_l2_arp_entry (arp, mp->mac, mp->is_add); + + REPLY_MACRO (VL_API_ONE_ADD_DEL_L2_ARP_ENTRY_REPLY); +} + +static void +vl_api_one_l2_arp_bd_get_t_handler (vl_api_one_l2_arp_bd_get_t * mp) +{ + vl_api_one_l2_arp_bd_get_reply_t *rmp; + int rv = 0; + u32 i = 0; + hash_pair_t *p; + + u32 *bds = vnet_lisp_l2_arp_bds_get (); + u32 size = hash_elts (bds) * sizeof (u32); + + /* *INDENT-OFF* */ + REPLY_MACRO4 (VL_API_ONE_L2_ARP_BD_GET_REPLY, size, + { + rmp->count = clib_host_to_net_u32 (hash_elts (bds)); + hash_foreach_pair (p, bds, + ({ + rmp->bridge_domains[i++] = clib_host_to_net_u32 (p->key); + })); + }); + /* *INDENT-ON* */ + + hash_free (bds); +} + +static void +vl_api_one_l2_arp_entries_get_t_handler (vl_api_one_l2_arp_entries_get_t * mp) +{ + vl_api_one_l2_arp_entries_get_reply_t *rmp; + lisp_api_l2_arp_entry_t *entries = 0, *e; + u32 i = 0; + int rv = 0; + + u32 bd = clib_net_to_host_u32 (mp->bd); + + entries = vnet_lisp_l2_arp_entries_get_by_bd (bd); + u32 size = vec_len (entries) * sizeof (u32); + + /* *INDENT-OFF* */ + REPLY_MACRO4 (VL_API_ONE_L2_ARP_ENTRIES_GET_REPLY, size, + { + rmp->count = clib_host_to_net_u32 (vec_len (entries)); + vec_foreach (e, entries) + { + mac_copy (rmp->entries[i].mac, e->mac); + rmp->entries[i].ip4 = e->ip4; + i++; + } + }); + /* *INDENT-ON* */ + + vec_free (entries); +} + /* * one_api_hookup * Add vpe's API message handlers to the table. diff --git a/src/vnet/lisp-cp/one_cli.c b/src/vnet/lisp-cp/one_cli.c index 109aca2e350..db6a6c833a9 100644 --- a/src/vnet/lisp-cp/one_cli.c +++ b/src/vnet/lisp-cp/one_cli.c @@ -276,6 +276,106 @@ VLIB_CLI_COMMAND (one_eid_table_map_command) = { }; /* *INDENT-ON* */ +static clib_error_t * +lisp_add_del_l2_arp_entry_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = NULL; + int rc = 0; + u8 hw_addr[6], bd = 0; + ip4_address_t ip4; + u32 hw_addr_set = 0, ip_set = 0, is_add = 1; + gid_address_t _arp, *arp = &_arp; + + memset (&ip4, 0, sizeof (ip4)); + memset (hw_addr, 0, sizeof (hw_addr)); + memset (arp, 0, sizeof (*arp)); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "mac %U", unformat_mac_address, hw_addr)) + hw_addr_set = 1; + else if (unformat (line_input, "ip %U", unformat_ip4_address, &ip4)) + ip_set = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "bd %d", &bd)) + ; + else + { + error = clib_error_return (0, "parse error"); + goto done; + } + } + + if (!ip_set || (!hw_addr_set && is_add)) + { + vlib_cli_output (vm, "expected IP and MAC addresses!"); + return 0; + } + + /* build GID address */ + gid_address_arp_ip4 (arp) = ip4; + gid_address_arp_bd (arp) = bd; + gid_address_type (arp) = GID_ADDR_ARP; + rc = vnet_lisp_add_del_l2_arp_entry (arp, hw_addr, is_add); + if (rc) + clib_warning ("Failed to %s l2 arp entry!", is_add ? "add" : "delete"); + +done: + unformat_free (line_input); + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (one_add_del_l2_arp_entry_command) = { + .path = "one l2 arp", + .short_help = "one l2 arp [del] bd mac ip ", + .function = lisp_add_del_l2_arp_entry_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +lisp_show_l2_arp_entries_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u32 *ht = vnet_lisp_l2_arp_bds_get (); + lisp_api_l2_arp_entry_t *entries, *e; + hash_pair_t *p; + + /* *INDENT-OFF* */ + hash_foreach_pair (p, ht, + ({ + entries = vnet_lisp_l2_arp_entries_get_by_bd (p->key); + vlib_cli_output (vm, "Table: %d", p->key); + + vec_foreach (e, entries) + { + vlib_cli_output (vm, "\t%U -> %U", format_ip4_address, &e->ip4, + format_mac_address, e->mac); + } + vec_free (entries); + })); + /* *INDENT-ON* */ + + hash_free (ht); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (one_show_l2_arp_entries_command) = { + .path = "show one l2 arp entries", + .short_help = "Show ONE L2 ARP entries", + .function = lisp_show_l2_arp_entries_command_fn, +}; +/* *INDENT-ON* */ + /** * Handler for add/del remote mapping CLI. * -- 2.16.6