X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Fethernet%2Farp.c;h=37e3e88c2f232806e6fb42de4e3b52507ae185a6;hb=59ae61ee7587502c0446655ecbe3daa296498f56;hp=120a276cc0fdbcc5c9c1b80dff50fb449cfb422f;hpb=7e9743aef924093c9c25bdf445637434c190d31a;p=vpp.git diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c index 120a276cc0f..37e3e88c2f2 100644 --- a/src/vnet/ethernet/arp.c +++ b/src/vnet/ethernet/arp.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -52,8 +52,8 @@ typedef struct ethernet_arp_interface_t_ typedef struct { - u32 lo_addr; - u32 hi_addr; + ip4_address_t lo_addr; + ip4_address_t hi_addr; u32 fib_index; } ethernet_proxy_arp_t; @@ -203,7 +203,7 @@ format_ethernet_arp_header (u8 * s, va_list * va) { ethernet_arp_header_t *a = va_arg (*va, ethernet_arp_header_t *); u32 max_header_bytes = va_arg (*va, u32); - uword indent; + u32 indent; u16 l2_type, l3_type; if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes) @@ -269,7 +269,7 @@ format_ethernet_arp_ip4_entry (u8 * s, va_list * va) flags = format (flags, "N"); s = format (s, "%=12U%=16U%=6s%=20U%U", - format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated, + format_vlib_time, vnm->vlib_main, e->time_last_updated, format_ip4_address, &e->ip4_address, flags ? (char *) flags : "", format_ethernet_address, e->ethernet_address, @@ -455,8 +455,10 @@ arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai) switch (adj->lookup_next_index) { - case IP_LOOKUP_NEXT_ARP: case IP_LOOKUP_NEXT_GLEAN: + adj_glean_update_rewrite (ai); + break; + case IP_LOOKUP_NEXT_ARP: if (NULL != e) { adj_nbr_walk_nh4 (sw_if_index, @@ -506,10 +508,9 @@ arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai) * Complete the remaining fields of the adj's rewrite to direct the * complete of the rewrite at switch time by copying in the IP * dst address's bytes. - * Ofset is 2 bytes into the MAC desintation address. And we copy 23 bits - * from the address. + * Ofset is 2 bytes into the MAC desintation address. */ - adj_mcast_update_rewrite (ai, rewrite, offset, 0x007fffff); + adj_mcast_update_rewrite (ai, rewrite, offset); break; } @@ -544,6 +545,62 @@ arp_adj_fib_add (ethernet_arp_ip4_entry_t * e, u32 fib_index) fib_table_lock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ); } +void +arp_adj_fib_remove (ethernet_arp_ip4_entry_t * e, u32 fib_index) +{ + if (FIB_NODE_INDEX_INVALID != e->fib_entry_index) + { + fib_prefix_t pfx = { + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr.ip4 = e->ip4_address, + }; + u32 fib_index; + + fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index); + + fib_table_entry_path_remove (fib_index, &pfx, + FIB_SOURCE_ADJ, + DPO_PROTO_IP4, + &pfx.fp_addr, + e->sw_if_index, ~0, 1, + FIB_ROUTE_PATH_FLAG_NONE); + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ); + } +} + +static ethernet_arp_ip4_entry_t * +force_reuse_arp_entry (void) +{ + ethernet_arp_ip4_entry_t *e; + ethernet_arp_main_t *am = ðernet_arp_main; + u32 count = 0; + u32 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor); + if (index == ~0) /* Try again from elt 0 */ + index = pool_next_index (am->ip4_entry_pool, index); + + /* Find a non-static random entry to free up for reuse */ + do + { + if ((count++ == 100) || (index == ~0)) + return NULL; /* give up after 100 entries */ + e = pool_elt_at_index (am->ip4_entry_pool, index); + am->arp_delete_rotor = index; + index = pool_next_index (am->ip4_entry_pool, index); + } + while (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC); + + /* Remove ARP entry from its interface and update fib */ + hash_unset + (am->ethernet_arp_by_sw_if_index[e->sw_if_index].arp_entries, + e->ip4_address.as_u32); + arp_adj_fib_remove + (e, ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index)); + adj_nbr_walk_nh4 (e->sw_if_index, + &e->ip4_address, arp_mk_incomplete_walk, NULL); + return e; +} + static int vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm, vnet_arp_set_ip4_over_ethernet_rpc_args_t @@ -581,12 +638,18 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm, if (make_new_arp_cache_entry) { - pool_get (am->ip4_entry_pool, e); - - if (NULL == arp_int->arp_entries) + if (am->limit_arp_cache_size && + pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size) { - arp_int->arp_entries = hash_create (0, sizeof (u32)); + e = force_reuse_arp_entry (); + if (NULL == e) + return -2; } + else + pool_get (am->ip4_entry_pool, e); + + if (NULL == arp_int->arp_entries) + arp_int->arp_entries = hash_create (0, sizeof (u32)); hash_set (arp_int->arp_entries, a->ip4.as_u32, e - am->ip4_entry_pool); @@ -615,21 +678,31 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm, */ if (0 == memcmp (e->ethernet_address, a->ethernet, sizeof (e->ethernet_address))) - return -1; + { + e->time_last_updated = vlib_time_now (vm); + goto check_customers; + } /* Update time stamp and ethernet address. */ clib_memcpy (e->ethernet_address, a->ethernet, sizeof (e->ethernet_address)); } - e->cpu_time_last_updated = clib_cpu_time_now (); + e->time_last_updated = vlib_time_now (vm); if (is_static) - e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC; + { + e->flags &= ~ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC; + e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC; + } else - e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC; + { + e->flags &= ~ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC; + e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC; + } adj_nbr_walk_nh4 (sw_if_index, &e->ip4_address, arp_mk_complete_walk, e); +check_customers: /* Customer(s) waiting for this address to be resolved? */ p = hash_get (am->pending_resolutions_by_address, a->ip4.as_u32); if (p) @@ -791,6 +864,7 @@ typedef enum _ (l3_type_not_ip4, "L3 type not IP4") \ _ (l3_src_address_not_local, "IP4 source address not local to subnet") \ _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \ + _ (l3_dst_address_unset, "IP4 destination address is unset") \ _ (l3_src_address_is_local, "IP4 source address matches local interface") \ _ (l3_src_address_learned, "ARP request IP4 source address learned") \ _ (replies_received, "ARP replies received") \ @@ -809,38 +883,6 @@ typedef enum ETHERNET_ARP_N_ERROR, } ethernet_arp_input_error_t; - -static void -unset_random_arp_entry (void) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e; - vnet_main_t *vnm = vnet_get_main (); - ethernet_arp_ip4_over_ethernet_address_t delme; - u32 index; - - index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor); - am->arp_delete_rotor = index; - - /* Try again from elt 0, could happen if an intfc goes down */ - if (index == ~0) - { - index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor); - am->arp_delete_rotor = index; - } - - /* Nothing left in the pool */ - if (index == ~0) - return; - - e = pool_elt_at_index (am->ip4_entry_pool, index); - - clib_memcpy (&delme.ethernet, e->ethernet_address, 6); - delme.ip4.as_u32 = e->ip4_address.as_u32; - - vnet_arp_unset_ip4_over_ethernet (vnm, e->sw_if_index, &delme); -} - static int arp_unnumbered (vlib_buffer_t * p0, u32 input_sw_if_index, u32 conn_sw_if_index) @@ -870,10 +912,6 @@ static u32 arp_learn (vnet_main_t * vnm, ethernet_arp_main_t * am, u32 sw_if_index, void *addr) { - if (am->limit_arp_cache_size && - pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size) - unset_random_arp_entry (); - vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index, addr, 0, 0); return (ETHERNET_ARP_ERROR_l3_src_address_learned); } @@ -943,6 +981,9 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) (arp0->l3_type != clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ? ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0); + error0 = + (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ? + ETHERNET_ARP_ERROR_l3_dst_address_unset : error0); sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX]; @@ -976,7 +1017,7 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) /* * we're looking for FIB entries that indicate the source * is attached. There may be more specific non-attached - * routes tht match the source, but these do not influence + * routes that match the source, but these do not influence * whether we respond to an ARP request, i.e. they do not * influence whether we are the correct way for the sender * to reach us, they only affect how we reach the sender. @@ -1073,7 +1114,23 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) } } - if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags)) + if (fib_entry_is_sourced (dst_fei, FIB_SOURCE_ADJ)) + { + /* + * We matched an adj-fib on ths source subnet (a /32 previously + * added as a result of ARP). If this request is a gratuitous + * ARP, then learn from it. + * The check for matching an adj-fib, is to prevent hosts + * from spamming us with gratuitous ARPS that might otherwise + * blow our ARP cache + */ + if (arp0->ip4_over_ethernet[0].ip4.as_u32 == + arp0->ip4_over_ethernet[1].ip4.as_u32) + error0 = arp_learn (vnm, am, sw_if_index0, + &arp0->ip4_over_ethernet[0]); + goto drop2; + } + else if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags)) { error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local; goto drop1; @@ -1115,11 +1172,23 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) /* Learn or update sender's mapping only for replies to addresses * that are local to the subnet */ if (arp0->opcode == - clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) && - dst_is_local0) + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)) + { + if (dst_is_local0) + error0 = arp_learn (vnm, am, sw_if_index0, + &arp0->ip4_over_ethernet[0]); + else + /* a reply for a non-local destination could be a GARP. + * GARPs for hosts we know were handled above, so this one + * we drop */ + error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local; + + goto drop1; + } + else if (arp0->opcode == + clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request) && + (dst_is_local0 == 0)) { - error0 = arp_learn (vnm, am, sw_if_index0, - &arp0->ip4_over_ethernet[0]); goto drop1; } @@ -1184,9 +1253,8 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) continue; drop1: - if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 || - (arp0->ip4_over_ethernet[0].ip4.as_u32 == - arp0->ip4_over_ethernet[1].ip4.as_u32)) + if (arp0->ip4_over_ethernet[0].ip4.as_u32 == + arp0->ip4_over_ethernet[1].ip4.as_u32) { error0 = ETHERNET_ARP_ERROR_gratuitous_arp; goto drop2; @@ -1209,8 +1277,8 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) vec_foreach (pa, am->proxy_arps) { - u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr); - u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr); + u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32); + u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr.as_u32); /* an ARP request hit in the proxy-arp table? */ if ((this_addr >= lo_addr && this_addr <= hi_addr) && @@ -1291,6 +1359,13 @@ ip4_arp_entry_sort (void *a1, void *a2) return cmp; } +ethernet_arp_ip4_entry_t * +ip4_neighbors_pool (void) +{ + ethernet_arp_main_t *am = ðernet_arp_main; + return am->ip4_entry_pool; +} + ethernet_arp_ip4_entry_t * ip4_neighbor_entries (u32 sw_if_index) { @@ -1617,30 +1692,6 @@ arp_add_del_interface_address (ip4_main_t * im, } } -void -arp_adj_fib_remove (ethernet_arp_ip4_entry_t * e, u32 fib_index) -{ - if (FIB_NODE_INDEX_INVALID != e->fib_entry_index) - { - fib_prefix_t pfx = { - .fp_len = 32, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr.ip4 = e->ip4_address, - }; - u32 fib_index; - - fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index); - - fib_table_entry_path_remove (fib_index, &pfx, - FIB_SOURCE_ADJ, - DPO_PROTO_IP4, - &pfx.fp_addr, - e->sw_if_index, ~0, 1, - FIB_ROUTE_PATH_FLAG_NONE); - fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ); - } -} - static void arp_table_bind (ip4_main_t * im, uword opaque, @@ -1737,9 +1788,8 @@ arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e) { ethernet_arp_main_t *am = ðernet_arp_main; - arp_adj_fib_remove (e, - ip4_fib_table_get_index_for_sw_if_index - (e->sw_if_index)); + arp_adj_fib_remove + (e, ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index)); hash_unset (eai->arp_entries, e->ip4_address.as_u32); pool_put (am->ip4_entry_pool, e); } @@ -1762,10 +1812,9 @@ vnet_arp_unset_ip4_over_ethernet_internal (vnet_main_t * vnm, if (NULL != e) { - arp_entry_free (eai, e); - adj_nbr_walk_nh4 (e->sw_if_index, &e->ip4_address, arp_mk_incomplete_walk, NULL); + arp_entry_free (eai, e); } return 0; @@ -1801,7 +1850,7 @@ vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm, */ if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC) { - e->flags &= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC; + e->flags &= ~ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC; } else if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC) { @@ -1939,6 +1988,19 @@ vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm, return 0; } +void +proxy_arp_walk (proxy_arp_walk_t cb, void *data) +{ + ethernet_arp_main_t *am = ðernet_arp_main; + ethernet_proxy_arp_t *pa; + + vec_foreach (pa, am->proxy_arps) + { + if (!cb (&pa->lo_addr, &pa->hi_addr, pa->fib_index, data)) + break; + } +} + int vnet_proxy_arp_add_del (ip4_address_t * lo_addr, ip4_address_t * hi_addr, u32 fib_index, int is_del) @@ -1949,8 +2011,8 @@ vnet_proxy_arp_add_del (ip4_address_t * lo_addr, vec_foreach (pa, am->proxy_arps) { - if (pa->lo_addr == lo_addr->as_u32 - && pa->hi_addr == hi_addr->as_u32 && pa->fib_index == fib_index) + if (pa->lo_addr.as_u32 == lo_addr->as_u32 && + pa->hi_addr.as_u32 == hi_addr->as_u32 && pa->fib_index == fib_index) { found_at_index = pa - am->proxy_arps; break; @@ -1970,8 +2032,8 @@ vnet_proxy_arp_add_del (ip4_address_t * lo_addr, /* add, not in table */ vec_add2 (am->proxy_arps, pa, 1); - pa->lo_addr = lo_addr->as_u32; - pa->hi_addr = hi_addr->as_u32; + pa->lo_addr.as_u32 = lo_addr->as_u32; + pa->hi_addr.as_u32 = hi_addr->as_u32; pa->fib_index = fib_index; return 0; } @@ -2473,6 +2535,7 @@ ethernet_arp_change_mac (u32 sw_if_index) { ethernet_arp_main_t *am = ðernet_arp_main; ethernet_arp_ip4_entry_t *e; + adj_index_t ai; /* *INDENT-OFF* */ pool_foreach (e, am->ip4_entry_pool, @@ -2480,15 +2543,31 @@ ethernet_arp_change_mac (u32 sw_if_index) change_arp_mac (sw_if_index, e); })); /* *INDENT-ON* */ + + ai = adj_glean_get (FIB_PROTOCOL_IP4, sw_if_index); + + if (ADJ_INDEX_INVALID != ai) + adj_glean_update_rewrite (ai); } void -send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi) +send_ip4_garp (vlib_main_t * vm, u32 sw_if_index) { ip4_main_t *i4m = &ip4_main; - u32 sw_if_index = hi->sw_if_index; ip4_address_t *ip4_addr = ip4_interface_first_address (i4m, sw_if_index, 0); + send_ip4_garp_w_addr (vm, ip4_addr, sw_if_index); +} + +void +send_ip4_garp_w_addr (vlib_main_t * vm, + const ip4_address_t * ip4_addr, u32 sw_if_index) +{ + ip4_main_t *i4m = &ip4_main; + vnet_main_t *vnm = vnet_get_main (); + u8 *rewrite, rewrite_len; + vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index); + if (ip4_addr) { clib_warning ("Sending GARP for IP4 address %U on sw_if_idex %d", @@ -2509,11 +2588,14 @@ send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi) /* Setup MAC header with ARP Etype and broadcast DMAC */ vlib_buffer_t *b = vlib_get_buffer (vm, bi); - vlib_buffer_advance (b, -sizeof (ethernet_header_t)); + rewrite = + ethernet_build_rewrite (vnm, sw_if_index, VNET_LINK_ARP, + VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST); + rewrite_len = vec_len (rewrite); + vlib_buffer_advance (b, -rewrite_len); ethernet_header_t *e = vlib_buffer_get_current (b); - e->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP); - clib_memcpy (e->src_address, hi->hw_address, sizeof (e->src_address)); - memset (e->dst_address, 0xff, sizeof (e->dst_address)); + clib_memcpy (e->dst_address, rewrite, rewrite_len); + vec_free (rewrite); /* Send GARP packet out the specified interface */ vnet_buffer (b)->sw_if_index[VLIB_RX] =