From cbe25aab3be72154f2c706c39eeba6a77f34450f Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 30 Sep 2019 10:53:31 +0000 Subject: ip: Protocol Independent IP Neighbors Type: feature - ip-neighbour: generic neighbour handling; APIs, DBs, event handling, aging - arp: ARP protocol implementation - ip6-nd; IPv6 neighbor discovery implementation; separate ND, MLD, RA - ip6-link; manage link-local addresses - l2-arp-term; events separated from IP neighbours, since they are not the same. vnet retains just enough education to perform ND/ARP packet construction. arp and ip6-nd to be moved to plugins soon. Change-Id: I88dedd0006b299344f4c7024a0aa5baa6b9a8bbe Signed-off-by: Neale Ranns --- src/vnet/ethernet/arp.c | 3062 --------------------------------------- src/vnet/ethernet/arp.h | 123 -- src/vnet/ethernet/arp_packet.c | 111 ++ src/vnet/ethernet/arp_packet.h | 7 +- src/vnet/ethernet/ethernet.h | 17 +- src/vnet/ethernet/interface.c | 89 +- src/vnet/ethernet/mac_address.c | 10 + src/vnet/ethernet/mac_address.h | 1 + 8 files changed, 212 insertions(+), 3208 deletions(-) delete mode 100644 src/vnet/ethernet/arp.c delete mode 100644 src/vnet/ethernet/arp.h create mode 100644 src/vnet/ethernet/arp_packet.c (limited to 'src/vnet/ethernet') diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c deleted file mode 100644 index 6b1069ecb6d..00000000000 --- a/src/vnet/ethernet/arp.c +++ /dev/null @@ -1,3062 +0,0 @@ -/* - * ethernet/arp.c: IP v4 ARP node - * - * Copyright (c) 2010 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** - * @file - * @brief IPv4 ARP. - * - * This file contains code to manage the IPv4 ARP tables (IP Address - * to MAC Address lookup). - */ - - -/** - * @brief Per-interface ARP configuration and state - */ -typedef struct ethernet_arp_interface_t_ -{ - /** - * Hash table of ARP entries. - * Since this hash table is per-interface, the key is only the IPv4 address. - */ - uword *arp_entries; - /** - * Is ARP enabled on this interface - */ - u32 enabled; - /** - * Is Proxy ARP enabled on this interface - */ - u32 proxy_enabled; -} ethernet_arp_interface_t; - -typedef struct -{ - ip4_address_t lo_addr; - ip4_address_t hi_addr; - u32 fib_index; -} ethernet_proxy_arp_t; - -typedef struct -{ - u32 next_index; - uword node_index; - uword type_opaque; - uword data; - /* Used for arp event notification only */ - arp_change_event_cb_t data_callback; - u32 pid; -} pending_resolution_t; - -typedef struct -{ - /* Hash tables mapping name to opcode. */ - uword *opcode_by_name; - - /* lite beer "glean" adjacency handling */ - uword *pending_resolutions_by_address; - pending_resolution_t *pending_resolutions; - - /* Mac address change notification */ - uword *mac_changes_by_address; - pending_resolution_t *mac_changes; - - ethernet_arp_ip4_entry_t *ip4_entry_pool; - - /* ARP attack mitigation */ - u32 arp_delete_rotor; - u32 limit_arp_cache_size; - - /** Per interface state */ - ethernet_arp_interface_t *ethernet_arp_by_sw_if_index; - - /* Proxy arp vector */ - ethernet_proxy_arp_t *proxy_arps; - - uword wc_ip4_arp_publisher_node; - uword wc_ip4_arp_publisher_et; - - /* ARP feature arc index */ - u8 feature_arc_index; -} ethernet_arp_main_t; - -static ethernet_arp_main_t ethernet_arp_main; - -typedef struct -{ - u32 sw_if_index; - ip4_address_t ip4; - mac_address_t mac; - ip_neighbor_flags_t nbr_flags; - u32 flags; -#define ETHERNET_ARP_ARGS_REMOVE (1<<0) -#define ETHERNET_ARP_ARGS_FLUSH (1<<1) -#define ETHERNET_ARP_ARGS_POPULATE (1<<2) -#define ETHERNET_ARP_ARGS_WC_PUB (1<<3) -} vnet_arp_set_ip4_over_ethernet_rpc_args_t; - -static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 }; - -/* Node index for send_garp_na_process */ -u32 send_garp_na_process_node_index; - -static void -set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t - * a); - -static u8 * -format_ethernet_arp_hardware_type (u8 * s, va_list * va) -{ - ethernet_arp_hardware_type_t h = va_arg (*va, ethernet_arp_hardware_type_t); - char *t = 0; - switch (h) - { -#define _(n,f) case n: t = #f; break; - foreach_ethernet_arp_hardware_type; -#undef _ - - default: - return format (s, "unknown 0x%x", h); - } - - return format (s, "%s", t); -} - -static u8 * -format_ethernet_arp_opcode (u8 * s, va_list * va) -{ - ethernet_arp_opcode_t o = va_arg (*va, ethernet_arp_opcode_t); - char *t = 0; - switch (o) - { -#define _(f) case ETHERNET_ARP_OPCODE_##f: t = #f; break; - foreach_ethernet_arp_opcode; -#undef _ - - default: - return format (s, "unknown 0x%x", o); - } - - return format (s, "%s", t); -} - -static uword -unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input, - va_list * args) -{ - int *result = va_arg (*args, int *); - ethernet_arp_main_t *am = ðernet_arp_main; - int x, i; - - /* Numeric opcode. */ - if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x)) - { - if (x >= (1 << 16)) - return 0; - *result = x; - return 1; - } - - /* Named type. */ - if (unformat_user (input, unformat_vlib_number_by_name, - am->opcode_by_name, &i)) - { - *result = i; - return 1; - } - - return 0; -} - -static uword -unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input, - va_list * args) -{ - int *result = va_arg (*args, int *); - if (!unformat_user - (input, unformat_ethernet_arp_opcode_host_byte_order, result)) - return 0; - - *result = clib_host_to_net_u16 ((u16) * result); - return 1; -} - -static u8 * -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); - u32 indent; - u16 l2_type, l3_type; - - if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes) - return format (s, "ARP header truncated"); - - l2_type = clib_net_to_host_u16 (a->l2_type); - l3_type = clib_net_to_host_u16 (a->l3_type); - - indent = format_get_indent (s); - - s = format (s, "%U, type %U/%U, address size %d/%d", - format_ethernet_arp_opcode, clib_net_to_host_u16 (a->opcode), - format_ethernet_arp_hardware_type, l2_type, - format_ethernet_type, l3_type, - a->n_l2_address_bytes, a->n_l3_address_bytes); - - if (l2_type == ETHERNET_ARP_HARDWARE_TYPE_ethernet - && l3_type == ETHERNET_TYPE_IP4) - { - s = format (s, "\n%U%U/%U -> %U/%U", - format_white_space, indent, - format_mac_address_t, &a->ip4_over_ethernet[0].mac, - format_ip4_address, &a->ip4_over_ethernet[0].ip4, - format_mac_address_t, &a->ip4_over_ethernet[1].mac, - format_ip4_address, &a->ip4_over_ethernet[1].ip4); - } - else - { - uword n2 = a->n_l2_address_bytes; - uword n3 = a->n_l3_address_bytes; - s = format (s, "\n%U%U/%U -> %U/%U", - format_white_space, indent, - format_hex_bytes, a->data + 0 * n2 + 0 * n3, n2, - format_hex_bytes, a->data + 1 * n2 + 0 * n3, n3, - format_hex_bytes, a->data + 1 * n2 + 1 * n3, n2, - format_hex_bytes, a->data + 2 * n2 + 1 * n3, n3); - } - - return s; -} - -u8 * -format_ethernet_arp_ip4_entry (u8 * s, va_list * va) -{ - vnet_main_t *vnm = va_arg (*va, vnet_main_t *); - ethernet_arp_ip4_entry_t *e = va_arg (*va, ethernet_arp_ip4_entry_t *); - vnet_sw_interface_t *si; - - if (!e) - return format (s, "%=12s%=16s%=6s%=20s%=24s", "Time", "IP4", - "Flags", "Ethernet", "Interface"); - - si = vnet_get_sw_interface (vnm, e->sw_if_index); - - return format (s, "%=12U%=16U%=6U%=20U%U", - format_vlib_time, vnm->vlib_main, e->time_last_updated, - format_ip4_address, &e->ip4_address, - format_ip_neighbor_flags, e->flags, - format_mac_address_t, &e->mac, - format_vnet_sw_interface_name, vnm, si); -} - -typedef struct -{ - u8 packet_data[64]; -} ethernet_arp_input_trace_t; - -static u8 * -format_ethernet_arp_input_trace (u8 * s, va_list * va) -{ - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); - ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *); - - s = format (s, "%U", - format_ethernet_arp_header, - t->packet_data, sizeof (t->packet_data)); - - return s; -} - -static u8 * -format_arp_term_input_trace (u8 * s, va_list * va) -{ - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); - ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *); - - /* arp-term trace data saved is either arp or ip6/icmp6 packet: - - for arp, the 1st 16-bit field is hw type of value of 0x0001. - - for ip6, the first nibble has value of 6. */ - s = format (s, "%U", t->packet_data[0] == 0 ? - format_ethernet_arp_header : format_ip6_header, - t->packet_data, sizeof (t->packet_data)); - - return s; -} - -static void -arp_nbr_probe (ip_adjacency_t * adj) -{ - vnet_main_t *vnm = vnet_get_main (); - ip4_main_t *im = &ip4_main; - ip_interface_address_t *ia; - ethernet_arp_header_t *h; - vnet_hw_interface_t *hi; - vnet_sw_interface_t *si; - ip4_address_t *src; - vlib_buffer_t *b; - vlib_main_t *vm; - u32 bi = 0; - - vm = vlib_get_main (); - - si = vnet_get_sw_interface (vnm, adj->rewrite_header.sw_if_index); - - if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)) - { - return; - } - - src = - ip4_interface_address_matching_destination (im, - &adj->sub_type.nbr.next_hop. - ip4, - adj->rewrite_header. - sw_if_index, &ia); - if (!src) - { - return; - } - - h = - vlib_packet_template_get_packet (vm, &im->ip4_arp_request_packet_template, - &bi); - if (!h) - return; - - hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index); - - mac_address_from_bytes (&h->ip4_over_ethernet[0].mac, hi->hw_address); - - h->ip4_over_ethernet[0].ip4 = src[0]; - h->ip4_over_ethernet[1].ip4 = adj->sub_type.nbr.next_hop.ip4; - - b = vlib_get_buffer (vm, bi); - vnet_buffer (b)->sw_if_index[VLIB_RX] = - vnet_buffer (b)->sw_if_index[VLIB_TX] = adj->rewrite_header.sw_if_index; - - /* Add encapsulation string for software interface (e.g. ethernet header). */ - vnet_rewrite_one_header (adj[0], h, sizeof (ethernet_header_t)); - vlib_buffer_advance (b, -adj->rewrite_header.data_bytes); - - { - vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index); - u32 *to_next = vlib_frame_vector_args (f); - to_next[0] = bi; - f->n_vectors = 1; - vlib_put_frame_to_node (vm, hi->output_node_index, f); - } -} - -static void -arp_mk_complete (adj_index_t ai, ethernet_arp_ip4_entry_t * e) -{ - adj_nbr_update_rewrite - (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE, - ethernet_build_rewrite (vnet_get_main (), - e->sw_if_index, - adj_get_link_type (ai), &e->mac)); -} - -static void -arp_mk_incomplete (adj_index_t ai) -{ - ip_adjacency_t *adj = adj_get (ai); - - adj_nbr_update_rewrite - (ai, - ADJ_NBR_REWRITE_FLAG_INCOMPLETE, - ethernet_build_rewrite (vnet_get_main (), - adj->rewrite_header.sw_if_index, - VNET_LINK_ARP, - VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST)); -} - -static ethernet_arp_ip4_entry_t * -arp_entry_find (ethernet_arp_interface_t * eai, const ip4_address_t * addr) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e = NULL; - uword *p; - - if (NULL != eai->arp_entries) - { - p = hash_get (eai->arp_entries, addr->as_u32); - if (!p) - return (NULL); - - e = pool_elt_at_index (am->ip4_entry_pool, p[0]); - } - - return (e); -} - -static adj_walk_rc_t -arp_mk_complete_walk (adj_index_t ai, void *ctx) -{ - ethernet_arp_ip4_entry_t *e = ctx; - - arp_mk_complete (ai, e); - - return (ADJ_WALK_RC_CONTINUE); -} - -static adj_walk_rc_t -arp_mk_incomplete_walk (adj_index_t ai, void *ctx) -{ - arp_mk_incomplete (ai); - - return (ADJ_WALK_RC_CONTINUE); -} - -static int -arp_is_enabled (ethernet_arp_main_t * am, u32 sw_if_index) -{ - if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index) - return 0; - - return (am->ethernet_arp_by_sw_if_index[sw_if_index].enabled); -} - -static void -arp_enable (ethernet_arp_main_t * am, u32 sw_if_index) -{ - if (arp_is_enabled (am, sw_if_index)) - return; - - vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index); - - am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 1; - - vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 1, NULL, 0); - vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 0, NULL, - 0); -} - -static int -vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm, - vnet_arp_set_ip4_over_ethernet_rpc_args_t - * args); - -static void -arp_disable (ethernet_arp_main_t * am, u32 sw_if_index) -{ - ethernet_arp_interface_t *eai; - ethernet_arp_ip4_entry_t *e; - u32 i, *to_delete = 0; - hash_pair_t *pair; - - if (!arp_is_enabled (am, sw_if_index)) - return; - - vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 1, NULL, - 0); - vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 0, NULL, 0); - - eai = &am->ethernet_arp_by_sw_if_index[sw_if_index]; - - - /* *INDENT-OFF* */ - hash_foreach_pair (pair, eai->arp_entries, - ({ - e = pool_elt_at_index(am->ip4_entry_pool, - pair->value[0]); - vec_add1 (to_delete, e - am->ip4_entry_pool); - })); - /* *INDENT-ON* */ - - for (i = 0; i < vec_len (to_delete); i++) - { - e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]); - - vnet_arp_set_ip4_over_ethernet_rpc_args_t delme = { - .ip4.as_u32 = e->ip4_address.as_u32, - .sw_if_index = e->sw_if_index, - .flags = ETHERNET_ARP_ARGS_FLUSH, - }; - mac_address_copy (&delme.mac, &e->mac); - - vnet_arp_flush_ip4_over_ethernet_internal (vnet_get_main (), &delme); - } - - vec_free (to_delete); - - eai->enabled = 0; -} - -void -arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_interface_t *arp_int; - ethernet_arp_ip4_entry_t *e; - ip_adjacency_t *adj; - - adj = adj_get (ai); - - arp_enable (am, sw_if_index); - arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index]; - e = arp_entry_find (arp_int, &adj->sub_type.nbr.next_hop.ip4); - - switch (adj->lookup_next_index) - { - 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, - &e->ip4_address, arp_mk_complete_walk, e); - } - else - { - /* - * no matching ARP entry. - * construct the rewrite required to for an ARP packet, and stick - * that in the adj's pipe to smoke. - */ - adj_nbr_update_rewrite - (ai, - ADJ_NBR_REWRITE_FLAG_INCOMPLETE, - ethernet_build_rewrite - (vnm, - sw_if_index, - VNET_LINK_ARP, - VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST)); - - /* - * since the FIB has added this adj for a route, it makes sense it - * may want to forward traffic sometime soon. Let's send a - * speculative ARP. just one. If we were to do periodically that - * wouldn't be bad either, but that's more code than i'm prepared to - * write at this time for relatively little reward. - */ - arp_nbr_probe (adj); - } - break; - case IP_LOOKUP_NEXT_BCAST: - adj_nbr_update_rewrite (ai, - ADJ_NBR_REWRITE_FLAG_COMPLETE, - ethernet_build_rewrite - (vnm, - sw_if_index, - VNET_LINK_IP4, - VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST)); - break; - case IP_LOOKUP_NEXT_MCAST: - { - /* - * Construct a partial rewrite from the known ethernet mcast dest MAC - */ - u8 *rewrite; - u8 offset; - - rewrite = ethernet_build_rewrite (vnm, - sw_if_index, - adj->ia_link, - ethernet_ip4_mcast_dst_addr ()); - offset = vec_len (rewrite) - 2; - - /* - * 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. - * Offset is 2 bytes into the MAC destination address. - */ - adj_mcast_update_rewrite (ai, rewrite, offset); - - break; - } - case IP_LOOKUP_NEXT_DROP: - case IP_LOOKUP_NEXT_PUNT: - case IP_LOOKUP_NEXT_LOCAL: - case IP_LOOKUP_NEXT_REWRITE: - case IP_LOOKUP_NEXT_MCAST_MIDCHAIN: - case IP_LOOKUP_NEXT_MIDCHAIN: - case IP_LOOKUP_NEXT_ICMP_ERROR: - case IP_LOOKUP_N_NEXT: - ASSERT (0); - break; - } -} - -static void -arp_adj_fib_add (ethernet_arp_ip4_entry_t * e, u32 fib_index) -{ - fib_prefix_t pfx = { - .fp_len = 32, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr.ip4 = e->ip4_address, - }; - - e->fib_entry_index = - fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ, - FIB_ENTRY_FLAG_ATTACHED, - DPO_PROTO_IP4, &pfx.fp_addr, - e->sw_if_index, ~0, 1, NULL, - FIB_ROUTE_PATH_FLAG_NONE); - fib_table_lock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ); -} - -static 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 & IP_NEIGHBOR_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, e); - return e; -} - -static int -vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm, - vnet_arp_set_ip4_over_ethernet_rpc_args_t - * args) -{ - ethernet_arp_ip4_entry_t *e = 0; - ethernet_arp_main_t *am = ðernet_arp_main; - vlib_main_t *vm = vlib_get_main (); - int make_new_arp_cache_entry = 1; - uword *p; - pending_resolution_t *pr, *mc; - ethernet_arp_interface_t *arp_int; - u32 sw_if_index = args->sw_if_index; - - arp_enable (am, sw_if_index); - - arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index]; - - if (NULL != arp_int->arp_entries) - { - p = hash_get (arp_int->arp_entries, args->ip4.as_u32); - if (p) - { - e = pool_elt_at_index (am->ip4_entry_pool, p[0]); - - /* Refuse to over-write static arp. */ - if (!(args->nbr_flags & IP_NEIGHBOR_FLAG_STATIC) && - (e->flags & IP_NEIGHBOR_FLAG_STATIC)) - { - /* if MAC address match, still check to send event */ - if (mac_address_equal (&e->mac, &args->mac)) - goto check_customers; - return -2; - } - make_new_arp_cache_entry = 0; - } - } - - if (make_new_arp_cache_entry) - { - if (am->limit_arp_cache_size && - pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size) - { - 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, args->ip4.as_u32, - e - am->ip4_entry_pool); - - e->sw_if_index = sw_if_index; - e->ip4_address = args->ip4; - e->fib_entry_index = FIB_NODE_INDEX_INVALID; - mac_address_copy (&e->mac, &args->mac); - - if (!(args->nbr_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY)) - { - arp_adj_fib_add (e, - ip4_fib_table_get_index_for_sw_if_index - (e->sw_if_index)); - } - else - { - e->flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY; - } - } - else - { - /* - * prevent a DoS attack from the data-plane that - * spams us with no-op updates to the MAC address - */ - if (mac_address_equal (&e->mac, &args->mac)) - { - e->time_last_updated = vlib_time_now (vm); - goto check_customers; - } - - /* Update ethernet address. */ - mac_address_copy (&e->mac, &args->mac); - } - - /* Update time stamp and flags. */ - e->time_last_updated = vlib_time_now (vm); - if (args->nbr_flags & IP_NEIGHBOR_FLAG_STATIC) - { - e->flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC; - e->flags |= IP_NEIGHBOR_FLAG_STATIC; - } - else - { - e->flags &= ~IP_NEIGHBOR_FLAG_STATIC; - e->flags |= IP_NEIGHBOR_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, args->ip4.as_u32); - if (p) - { - u32 next_index; - next_index = p[0]; - - while (next_index != (u32) ~ 0) - { - pr = pool_elt_at_index (am->pending_resolutions, next_index); - vlib_process_signal_event (vm, pr->node_index, - pr->type_opaque, pr->data); - next_index = pr->next_index; - pool_put (am->pending_resolutions, pr); - } - - hash_unset (am->pending_resolutions_by_address, args->ip4.as_u32); - } - - /* Customer(s) requesting ARP event for this address? */ - p = hash_get (am->mac_changes_by_address, args->ip4.as_u32); - if (p) - { - u32 next_index; - next_index = p[0]; - - while (next_index != (u32) ~ 0) - { - int rv = 1; - mc = pool_elt_at_index (am->mac_changes, next_index); - - /* Call the user's data callback, return 1 to suppress dup events */ - if (mc->data_callback) - rv = (mc->data_callback) (mc->data, &args->mac, sw_if_index, 0); - - /* - * Signal the resolver process, as long as the user - * says they want to be notified - */ - if (rv == 0) - vlib_process_signal_event (vm, mc->node_index, - mc->type_opaque, mc->data); - next_index = mc->next_index; - } - } - - return 0; -} - -void -vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm, - void *address_arg, - uword node_index, - uword type_opaque, uword data) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ip4_address_t *address = address_arg; - uword *p; - pending_resolution_t *pr; - - pool_get (am->pending_resolutions, pr); - - pr->next_index = ~0; - pr->node_index = node_index; - pr->type_opaque = type_opaque; - pr->data = data; - pr->data_callback = 0; - - p = hash_get (am->pending_resolutions_by_address, address->as_u32); - if (p) - { - /* Insert new resolution at the head of the list */ - pr->next_index = p[0]; - hash_unset (am->pending_resolutions_by_address, address->as_u32); - } - - hash_set (am->pending_resolutions_by_address, address->as_u32, - pr - am->pending_resolutions); -} - -int -vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm, - arp_change_event_cb_t data_callback, - u32 pid, - void *address_arg, - uword node_index, - uword type_opaque, uword data, int is_add) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ip4_address_t *address = address_arg; - - /* Try to find an existing entry */ - u32 *first = (u32 *) hash_get (am->mac_changes_by_address, address->as_u32); - u32 *p = first; - pending_resolution_t *mc; - while (p && *p != ~0) - { - mc = pool_elt_at_index (am->mac_changes, *p); - if (mc->node_index == node_index && mc->type_opaque == type_opaque - && mc->pid == pid) - break; - p = &mc->next_index; - } - - int found = p && *p != ~0; - if (is_add) - { - if (found) - return VNET_API_ERROR_ENTRY_ALREADY_EXISTS; - - pool_get (am->mac_changes, mc); - /* *INDENT-OFF* */ - *mc = (pending_resolution_t) - { - .next_index = ~0, - .node_index = node_index, - .type_opaque = type_opaque, - .data = data, - .data_callback = data_callback, - .pid = pid, - }; - /* *INDENT-ON* */ - - /* Insert new resolution at the end of the list */ - u32 new_idx = mc - am->mac_changes; - if (p) - p[0] = new_idx; - else - hash_set (am->mac_changes_by_address, address->as_u32, new_idx); - } - else - { - if (!found) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - /* Clients may need to clean up pool entries, too */ - if (data_callback) - /* no new mac addrs */ - (data_callback) (mc->data, NULL, ~0, NULL); - - /* Remove the entry from the list and delete the entry */ - *p = mc->next_index; - pool_put (am->mac_changes, mc); - - /* Remove from hash if we deleted the last entry */ - if (*p == ~0 && p == first) - hash_unset (am->mac_changes_by_address, address->as_u32); - } - return 0; -} - -/* Either we drop the packet or we send a reply to the sender. */ -typedef enum -{ - ARP_REPLY_NEXT_DROP, - ARP_REPLY_NEXT_REPLY_TX, - ARP_REPLY_N_NEXT, -} arp_reply_next_t; - -#define foreach_ethernet_arp_error \ - _ (replies_sent, "ARP replies sent") \ - _ (l2_type_not_ethernet, "L2 type not ethernet") \ - _ (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") \ - _ (opcode_not_request, "ARP opcode not request") \ - _ (proxy_arp_replies_sent, "Proxy ARP replies sent") \ - _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \ - _ (gratuitous_arp, "ARP probe or announcement dropped") \ - _ (interface_no_table, "Interface is not mapped to an IP table") \ - _ (interface_not_ip_enabled, "Interface is not IP enabled") \ - _ (unnumbered_mismatch, "RX interface is unnumbered to different subnet") \ - -typedef enum -{ -#define _(sym,string) ETHERNET_ARP_ERROR_##sym, - foreach_ethernet_arp_error -#undef _ - ETHERNET_ARP_N_ERROR, -} ethernet_arp_reply_error_t; - -static int -arp_unnumbered (vlib_buffer_t * p0, - u32 input_sw_if_index, u32 conn_sw_if_index) -{ - vnet_main_t *vnm = vnet_get_main (); - vnet_interface_main_t *vim = &vnm->interface_main; - vnet_sw_interface_t *si; - - /* verify that the input interface is unnumbered to the connected. - * the connected interface is the interface on which the subnet is - * configured */ - si = &vim->sw_interfaces[input_sw_if_index]; - - if (!(si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED && - (si->unnumbered_sw_if_index == conn_sw_if_index))) - { - /* the input interface is not unnumbered to the interface on which - * the sub-net is configured that covers the ARP request. - * So this is not the case for unnumbered.. */ - return 0; - } - - return !0; -} - -static u32 -arp_learn (vnet_main_t * vnm, - ethernet_arp_main_t * am, u32 sw_if_index, - const ethernet_arp_ip4_over_ethernet_address_t * addr) -{ - vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index, addr, 0); - return (ETHERNET_ARP_ERROR_l3_src_address_learned); -} - -typedef enum arp_input_next_t_ -{ - ARP_INPUT_NEXT_DROP, - ARP_INPUT_NEXT_DISABLED, - ARP_INPUT_N_NEXT, -} arp_input_next_t; - -static uword -arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - u32 n_left_from, next_index, *from, *to_next, n_left_to_next; - ethernet_arp_main_t *am = ðernet_arp_main; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - if (node->flags & VLIB_NODE_FLAG_TRACE) - vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, - /* stride */ 1, - sizeof (ethernet_arp_input_trace_t)); - - while (n_left_from > 0) - { - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - const ethernet_arp_header_t *arp0; - arp_input_next_t next0; - vlib_buffer_t *p0; - u32 pi0, error0; - - pi0 = to_next[0] = from[0]; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - p0 = vlib_get_buffer (vm, pi0); - arp0 = vlib_buffer_get_current (p0); - - error0 = ETHERNET_ARP_ERROR_replies_sent; - next0 = ARP_INPUT_NEXT_DROP; - - error0 = - (arp0->l2_type != - clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ? - ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0); - error0 = - (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); - - if (ETHERNET_ARP_ERROR_replies_sent == error0) - { - next0 = ARP_INPUT_NEXT_DISABLED; - vnet_feature_arc_start (am->feature_arc_index, - vnet_buffer (p0)->sw_if_index[VLIB_RX], - &next0, p0); - } - else - p0->error = node->errors[error0]; - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, pi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - return frame->n_vectors; -} - -typedef enum arp_disabled_next_t_ -{ - ARP_DISABLED_NEXT_DROP, - ARP_DISABLED_N_NEXT, -} arp_disabled_next_t; - -#define foreach_arp_disabled_error \ - _ (DISABLED, "ARP Disabled on this interface") \ - -typedef enum -{ -#define _(sym,string) ARP_DISABLED_ERROR_##sym, - foreach_arp_disabled_error -#undef _ - ARP_DISABLED_N_ERROR, -} arp_disabled_error_t; - -static char *arp_disabled_error_strings[] = { -#define _(sym,string) string, - foreach_arp_disabled_error -#undef _ -}; - -static uword -arp_disabled (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - u32 n_left_from, next_index, *from, *to_next, n_left_to_next; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - if (node->flags & VLIB_NODE_FLAG_TRACE) - vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, - /* stride */ 1, - sizeof (ethernet_arp_input_trace_t)); - - while (n_left_from > 0) - { - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - arp_disabled_next_t next0 = ARP_DISABLED_NEXT_DROP; - vlib_buffer_t *p0; - u32 pi0, error0; - - next0 = ARP_DISABLED_NEXT_DROP; - error0 = ARP_DISABLED_ERROR_DISABLED; - - pi0 = to_next[0] = from[0]; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - p0 = vlib_get_buffer (vm, pi0); - p0->error = node->errors[error0]; - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, pi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - return frame->n_vectors; -} - -static_always_inline u32 -arp_mk_reply (vnet_main_t * vnm, - vlib_buffer_t * p0, - u32 sw_if_index0, - const ip4_address_t * if_addr0, - ethernet_arp_header_t * arp0, ethernet_header_t * eth_rx) -{ - vnet_hw_interface_t *hw_if0; - u8 *rewrite0, rewrite0_len; - ethernet_header_t *eth_tx; - u32 next0; - - /* Send a reply. - An adjacency to the sender is not always present, - so we use the interface to build us a rewrite string - which will contain all the necessary tags. */ - rewrite0 = ethernet_build_rewrite (vnm, sw_if_index0, - VNET_LINK_ARP, eth_rx->src_address); - rewrite0_len = vec_len (rewrite0); - - /* Figure out how much to rewind current data from adjacency. */ - vlib_buffer_advance (p0, -rewrite0_len); - eth_tx = vlib_buffer_get_current (p0); - - vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0; - hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0); - - /* Send reply back through input interface */ - vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0; - next0 = ARP_REPLY_NEXT_REPLY_TX; - - arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply); - - arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0]; - - mac_address_from_bytes (&arp0->ip4_over_ethernet[0].mac, - hw_if0->hw_address); - clib_mem_unaligned (&arp0->ip4_over_ethernet[0].ip4.data_u32, u32) = - if_addr0->data_u32; - - /* Hardware must be ethernet-like. */ - ASSERT (vec_len (hw_if0->hw_address) == 6); - - /* the rx nd tx ethernet headers wil overlap in the case - * when we received a tagged VLAN=0 packet, but we are sending - * back untagged */ - clib_memcpy_fast (eth_tx, rewrite0, vec_len (rewrite0)); - vec_free (rewrite0); - - return (next0); -} - -enum arp_dst_fib_type -{ - ARP_DST_FIB_NONE, - ARP_DST_FIB_ADJ, - ARP_DST_FIB_CONN -}; - -/* - * we're looking for FIB sources that indicate the destination - * is attached. There may be interposed DPO prior to the one - * we are looking for - */ -static enum arp_dst_fib_type -arp_dst_fib_check (const fib_node_index_t fei, fib_entry_flag_t * flags) -{ - const fib_entry_t *entry = fib_entry_get (fei); - const fib_entry_src_t *entry_src; - fib_source_t src; - /* *INDENT-OFF* */ - FOR_EACH_SRC_ADDED(entry, entry_src, src, - ({ - *flags = fib_entry_get_flags_for_source (fei, src); - if (fib_entry_is_sourced (fei, FIB_SOURCE_ADJ)) - return ARP_DST_FIB_ADJ; - else if (FIB_ENTRY_FLAG_CONNECTED & *flags) - return ARP_DST_FIB_CONN; - })) - /* *INDENT-ON* */ - - return ARP_DST_FIB_NONE; -} - -static uword -arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - vnet_main_t *vnm = vnet_get_main (); - u32 n_left_from, next_index, *from, *to_next; - u32 n_replies_sent = 0; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - if (node->flags & VLIB_NODE_FLAG_TRACE) - vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, - /* stride */ 1, - sizeof (ethernet_arp_input_trace_t)); - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - vlib_buffer_t *p0; - ethernet_arp_header_t *arp0; - ethernet_header_t *eth_rx; - const ip4_address_t *if_addr0; - u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0; - u8 dst_is_local0, is_vrrp_reply0; - fib_node_index_t dst_fei, src_fei; - const fib_prefix_t *pfx0; - fib_entry_flag_t src_flags, dst_flags; - - pi0 = from[0]; - to_next[0] = pi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - p0 = vlib_get_buffer (vm, pi0); - arp0 = vlib_buffer_get_current (p0); - /* Fill in ethernet header. */ - eth_rx = ethernet_buffer_get_header (p0); - - next0 = ARP_REPLY_NEXT_DROP; - error0 = ETHERNET_ARP_ERROR_replies_sent; - sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX]; - - /* Check that IP address is local and matches incoming interface. */ - fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - if (~0 == fib_index0) - { - error0 = ETHERNET_ARP_ERROR_interface_no_table; - goto drop; - - } - - { - /* - * we're looking for FIB entries that indicate the source - * is attached. There may be more specific non-attached - * 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. - */ - fib_entry_t *src_fib_entry; - const fib_prefix_t *pfx; - fib_entry_src_t *src; - fib_source_t source; - int attached; - int mask; - - mask = 32; - attached = 0; - - do - { - src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0), - &arp0-> - ip4_over_ethernet[0].ip4, - mask); - src_fib_entry = fib_entry_get (src_fei); - - /* - * It's possible that the source that provides the - * flags we need, or the flags we must not have, - * is not the best source, so check then all. - */ - /* *INDENT-OFF* */ - FOR_EACH_SRC_ADDED(src_fib_entry, src, source, - ({ - src_flags = fib_entry_get_flags_for_source (src_fei, source); - - /* Reject requests/replies with our local interface - address. */ - if (FIB_ENTRY_FLAG_LOCAL & src_flags) - { - error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local; - /* - * When VPP has an interface whose address is also - * applied to a TAP interface on the host, then VPP's - * TAP interface will be unnumbered to the 'real' - * interface and do proxy ARP from the host. - * The curious aspect of this setup is that ARP requests - * from the host will come from the VPP's own address. - * So don't drop immediately here, instead go see if this - * is a proxy ARP case. - */ - goto next_feature; - } - /* A Source must also be local to subnet of matching - * interface address. */ - if ((FIB_ENTRY_FLAG_ATTACHED & src_flags) || - (FIB_ENTRY_FLAG_CONNECTED & src_flags)) - { - attached = 1; - break; - } - /* - * else - * The packet was sent from an address that is not - * connected nor attached i.e. it is not from an - * address that is covered by a link's sub-net, - * nor is it a already learned host resp. - */ - })); - /* *INDENT-ON* */ - - /* - * shorter mask lookup for the next iteration. - */ - pfx = fib_entry_get_prefix (src_fei); - mask = pfx->fp_len - 1; - - /* - * continue until we hit the default route or we find - * the attached we are looking for. The most likely - * outcome is we find the attached with the first source - * on the first lookup. - */ - } - while (!attached && - !fib_entry_is_sourced (src_fei, FIB_SOURCE_DEFAULT_ROUTE)); - - if (!attached) - { - /* - * the matching route is a not attached, i.e. it was - * added as a result of routing, rather than interface/ARP - * configuration. If the matching route is not a host route - * (i.e. a /32) - */ - error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local; - goto drop; - } - } - - dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0), - &arp0->ip4_over_ethernet[1].ip4, - 32); - switch (arp_dst_fib_check (dst_fei, &dst_flags)) - { - case ARP_DST_FIB_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 drop; - case ARP_DST_FIB_CONN: - /* destination is connected, continue to process */ - break; - case ARP_DST_FIB_NONE: - /* destination is not connected, stop here */ - error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local; - goto next_feature; - } - - dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags); - pfx0 = fib_entry_get_prefix (dst_fei); - if_addr0 = &pfx0->fp_addr.ip4; - - is_vrrp_reply0 = - ((arp0->opcode == - clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)) - && - (!memcmp - (arp0->ip4_over_ethernet[0].mac.bytes, vrrp_prefix, - sizeof (vrrp_prefix)))); - - /* Trash ARP packets whose ARP-level source addresses do not - match their L2-frame-level source addresses, unless it's - a reply from a VRRP virtual router */ - if (!ethernet_mac_address_equal - (eth_rx->src_address, - arp0->ip4_over_ethernet[0].mac.bytes) && !is_vrrp_reply0) - { - error0 = ETHERNET_ARP_ERROR_l2_address_mismatch; - goto drop; - } - - /* 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)) - { - 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 next_feature; - } - else if (arp0->opcode == - clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request) && - (dst_is_local0 == 0)) - { - goto next_feature; - } - - /* Honor unnumbered interface, if any */ - conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei); - if (sw_if_index0 != conn_sw_if_index0 || - sw_if_index0 != fib_entry_get_resolving_interface (src_fei)) - { - /* - * The interface the ARP is sent to or was received on is not the - * interface on which the covering prefix is configured. - * Maybe this is a case for unnumbered. - */ - if (!arp_unnumbered (p0, sw_if_index0, conn_sw_if_index0)) - { - error0 = ETHERNET_ARP_ERROR_unnumbered_mismatch; - goto drop; - } - } - if (arp0->ip4_over_ethernet[0].ip4.as_u32 == - arp0->ip4_over_ethernet[1].ip4.as_u32) - { - error0 = ETHERNET_ARP_ERROR_gratuitous_arp; - goto drop; - } - - next0 = arp_mk_reply (vnm, p0, sw_if_index0, - if_addr0, arp0, eth_rx); - - /* We are going to reply to this request, so, in the absence of - errors, learn the sender */ - if (!error0) - error0 = arp_learn (vnm, am, sw_if_index0, - &arp0->ip4_over_ethernet[1]); - - n_replies_sent += 1; - goto enqueue; - - next_feature: - vnet_feature_next (&next0, p0); - goto enqueue; - - drop: - p0->error = node->errors[error0]; - - enqueue: - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, pi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - vlib_error_count (vm, node->node_index, - ETHERNET_ARP_ERROR_replies_sent, n_replies_sent); - - return frame->n_vectors; -} - -static uword -arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - vnet_main_t *vnm = vnet_get_main (); - u32 n_left_from, next_index, *from, *to_next; - u32 n_arp_replies_sent = 0; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - if (node->flags & VLIB_NODE_FLAG_TRACE) - vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors, - /* stride */ 1, - sizeof (ethernet_arp_input_trace_t)); - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - vlib_buffer_t *p0; - ethernet_arp_header_t *arp0; - ethernet_header_t *eth_rx; - ip4_address_t proxy_src; - u32 pi0, error0, next0, sw_if_index0, fib_index0; - u8 is_request0; - ethernet_proxy_arp_t *pa; - - pi0 = from[0]; - to_next[0] = pi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - p0 = vlib_get_buffer (vm, pi0); - arp0 = vlib_buffer_get_current (p0); - /* Fill in ethernet header. */ - eth_rx = ethernet_buffer_get_header (p0); - - is_request0 = arp0->opcode - == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request); - - error0 = ETHERNET_ARP_ERROR_replies_sent; - sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX]; - next0 = ARP_REPLY_NEXT_DROP; - - fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - if (~0 == fib_index0) - { - error0 = ETHERNET_ARP_ERROR_interface_no_table; - } - - if (0 == error0 && is_request0) - { - u32 this_addr = clib_net_to_host_u32 - (arp0->ip4_over_ethernet[1].ip4.as_u32); - - vec_foreach (pa, am->proxy_arps) - { - 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) && - (fib_index0 == pa->fib_index)) - { - proxy_src.as_u32 = - arp0->ip4_over_ethernet[1].ip4.data_u32; - - /* - * change the interface address to the proxied - */ - n_arp_replies_sent++; - - next0 = - arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0, - eth_rx); - } - } - } - else - { - p0->error = node->errors[error0]; - } - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, pi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - vlib_error_count (vm, node->node_index, - ETHERNET_ARP_ERROR_replies_sent, n_arp_replies_sent); - - return frame->n_vectors; -} - -static char *ethernet_arp_error_strings[] = { -#define _(sym,string) string, - foreach_ethernet_arp_error -#undef _ -}; - -/* *INDENT-OFF* */ - -VLIB_REGISTER_NODE (arp_input_node, static) = -{ - .function = arp_input, - .name = "arp-input", - .vector_size = sizeof (u32), - .n_errors = ETHERNET_ARP_N_ERROR, - .error_strings = ethernet_arp_error_strings, - .n_next_nodes = ARP_INPUT_N_NEXT, - .next_nodes = { - [ARP_INPUT_NEXT_DROP] = "error-drop", - [ARP_INPUT_NEXT_DISABLED] = "arp-disabled", - }, - .format_buffer = format_ethernet_arp_header, - .format_trace = format_ethernet_arp_input_trace, -}; - -VLIB_REGISTER_NODE (arp_disabled_node, static) = -{ - .function = arp_disabled, - .name = "arp-disabled", - .vector_size = sizeof (u32), - .n_errors = ARP_DISABLED_N_ERROR, - .error_strings = arp_disabled_error_strings, - .n_next_nodes = ARP_DISABLED_N_NEXT, - .next_nodes = { - [ARP_INPUT_NEXT_DROP] = "error-drop", - }, - .format_buffer = format_ethernet_arp_header, - .format_trace = format_ethernet_arp_input_trace, -}; - -VLIB_REGISTER_NODE (arp_reply_node, static) = -{ - .function = arp_reply, - .name = "arp-reply", - .vector_size = sizeof (u32), - .n_errors = ETHERNET_ARP_N_ERROR, - .error_strings = ethernet_arp_error_strings, - .n_next_nodes = ARP_REPLY_N_NEXT, - .next_nodes = { - [ARP_REPLY_NEXT_DROP] = "error-drop", - [ARP_REPLY_NEXT_REPLY_TX] = "interface-output", - }, - .format_buffer = format_ethernet_arp_header, - .format_trace = format_ethernet_arp_input_trace, -}; - -VLIB_REGISTER_NODE (arp_proxy_node, static) = -{ - .function = arp_proxy, - .name = "arp-proxy", - .vector_size = sizeof (u32), - .n_errors = ETHERNET_ARP_N_ERROR, - .error_strings = ethernet_arp_error_strings, - .n_next_nodes = ARP_REPLY_N_NEXT, - .next_nodes = { - [ARP_REPLY_NEXT_DROP] = "error-drop", - [ARP_REPLY_NEXT_REPLY_TX] = "interface-output", - }, - .format_buffer = format_ethernet_arp_header, - .format_trace = format_ethernet_arp_input_trace, -}; - -/* Built-in ARP rx feature path definition */ -VNET_FEATURE_ARC_INIT (arp_feat, static) = -{ - .arc_name = "arp", - .start_nodes = VNET_FEATURES ("arp-input"), - .last_in_arc = "error-drop", - .arc_index_ptr = ðernet_arp_main.feature_arc_index, -}; - -VNET_FEATURE_INIT (arp_reply_feat_node, static) = -{ - .arc_name = "arp", - .node_name = "arp-reply", - .runs_before = VNET_FEATURES ("arp-disabled"), -}; - -VNET_FEATURE_INIT (arp_proxy_feat_node, static) = -{ - .arc_name = "arp", - .node_name = "arp-proxy", - .runs_after = VNET_FEATURES ("arp-reply"), - .runs_before = VNET_FEATURES ("arp-disabled"), -}; - -VNET_FEATURE_INIT (arp_disabled_feat_node, static) = -{ - .arc_name = "arp", - .node_name = "arp-disabled", - .runs_before = VNET_FEATURES ("error-drop"), -}; - -VNET_FEATURE_INIT (arp_drop_feat_node, static) = -{ - .arc_name = "arp", - .node_name = "error-drop", - .runs_before = 0, /* last feature */ -}; - -/* *INDENT-ON* */ - -static int -ip4_arp_entry_sort (void *a1, void *a2) -{ - ethernet_arp_ip4_entry_t *e1 = a1; - ethernet_arp_ip4_entry_t *e2 = a2; - - int cmp; - vnet_main_t *vnm = vnet_get_main (); - - cmp = vnet_sw_interface_compare (vnm, e1->sw_if_index, e2->sw_if_index); - if (!cmp) - cmp = ip4_address_compare (&e1->ip4_address, &e2->ip4_address); - 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) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *n, *ns = 0; - - /* *INDENT-OFF* */ - pool_foreach (n, am->ip4_entry_pool, ({ - if (sw_if_index != ~0 && n->sw_if_index != sw_if_index) - continue; - vec_add1 (ns, n[0]); - })); - /* *INDENT-ON* */ - - if (ns) - vec_sort_with_function (ns, ip4_arp_entry_sort); - return ns; -} - -static clib_error_t * -show_ip4_arp (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e, *es; - ethernet_proxy_arp_t *pa; - clib_error_t *error = 0; - u32 sw_if_index; - - /* Filter entries by interface if given. */ - sw_if_index = ~0; - (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index); - - es = ip4_neighbor_entries (sw_if_index); - if (es) - { - vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0); - vec_foreach (e, es) - { - vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e); - } - vec_free (es); - } - - if (vec_len (am->proxy_arps)) - { - vlib_cli_output (vm, "Proxy arps enabled for:"); - vec_foreach (pa, am->proxy_arps) - { - vlib_cli_output (vm, "Fib_index %d %U - %U ", - pa->fib_index, - format_ip4_address, &pa->lo_addr, - format_ip4_address, &pa->hi_addr); - } - } - - return error; -} - -/*? - * Display all the IPv4 ARP entries. - * - * @cliexpar - * Example of how to display the IPv4 ARP table: - * @cliexstart{show ip arp} - * Time FIB IP4 Flags Ethernet Interface - * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0 - * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0 - * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0 - * Proxy arps enabled for: - * Fib_index 0 6.0.0.1 - 6.0.0.11 - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_ip4_arp_command, static) = { - .path = "show ip arp", - .function = show_ip4_arp, - .short_help = "show ip arp", -}; -/* *INDENT-ON* */ - -typedef struct -{ - pg_edit_t l2_type, l3_type; - pg_edit_t n_l2_address_bytes, n_l3_address_bytes; - pg_edit_t opcode; - struct - { - pg_edit_t mac; - pg_edit_t ip4; - } ip4_over_ethernet[2]; -} pg_ethernet_arp_header_t; - -static inline void -pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p) -{ - /* Initialize fields that are not bit fields in the IP header. */ -#define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f); - _(l2_type); - _(l3_type); - _(n_l2_address_bytes); - _(n_l3_address_bytes); - _(opcode); - _(ip4_over_ethernet[0].mac); - _(ip4_over_ethernet[0].ip4); - _(ip4_over_ethernet[1].mac); - _(ip4_over_ethernet[1].ip4); -#undef _ -} - -uword -unformat_pg_arp_header (unformat_input_t * input, va_list * args) -{ - pg_stream_t *s = va_arg (*args, pg_stream_t *); - pg_ethernet_arp_header_t *p; - u32 group_index; - - p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t), - &group_index); - pg_ethernet_arp_header_init (p); - - /* Defaults. */ - pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet); - pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4); - pg_edit_set_fixed (&p->n_l2_address_bytes, 6); - pg_edit_set_fixed (&p->n_l3_address_bytes, 4); - - if (!unformat (input, "%U: %U/%U -> %U/%U", - unformat_pg_edit, - unformat_ethernet_arp_opcode_net_byte_order, &p->opcode, - unformat_pg_edit, - unformat_mac_address_t, &p->ip4_over_ethernet[0].mac, - unformat_pg_edit, - unformat_ip4_address, &p->ip4_over_ethernet[0].ip4, - unformat_pg_edit, - unformat_mac_address_t, &p->ip4_over_ethernet[1].mac, - unformat_pg_edit, - unformat_ip4_address, &p->ip4_over_ethernet[1].ip4)) - { - /* Free up any edits we may have added. */ - pg_free_edit_group (s); - return 0; - } - return 1; -} - -clib_error_t * -ip4_set_arp_limit (u32 arp_limit) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - - am->limit_arp_cache_size = arp_limit; - return 0; -} - -/** - * @brief Control Plane hook to remove an ARP entry - */ -int -vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm, - u32 sw_if_index, - const - ethernet_arp_ip4_over_ethernet_address_t * - a) -{ - vnet_arp_set_ip4_over_ethernet_rpc_args_t args = { - .sw_if_index = sw_if_index, - .flags = ETHERNET_ARP_ARGS_REMOVE, - .ip4 = a->ip4, - .mac = a->mac, - }; - - vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback, - (u8 *) & args, sizeof (args)); - return 0; -} - -/** - * @brief publish wildcard arp event - * @param sw_if_index The interface on which the ARP entries are acted - */ -static int -vnet_arp_wc_publish (u32 sw_if_index, - const ethernet_arp_ip4_over_ethernet_address_t * a) -{ - vnet_arp_set_ip4_over_ethernet_rpc_args_t args = { - .flags = ETHERNET_ARP_ARGS_WC_PUB, - .sw_if_index = sw_if_index, - .ip4 = a->ip4, - .mac = a->mac, - }; - - vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback, - (u8 *) & args, sizeof (args)); - return 0; -} - -static void -vnet_arp_wc_publish_internal (vnet_main_t * vnm, - vnet_arp_set_ip4_over_ethernet_rpc_args_t * - args) -{ - vlib_main_t *vm = vlib_get_main (); - ethernet_arp_main_t *am = ðernet_arp_main; - uword ni = am->wc_ip4_arp_publisher_node; - uword et = am->wc_ip4_arp_publisher_et; - - if (ni == (uword) ~ 0) - return; - wc_arp_report_t *r = - vlib_process_signal_event_data (vm, ni, et, 1, sizeof *r); - r->ip.as_u32 = args->ip4.as_u32; - r->sw_if_index = args->sw_if_index; - mac_address_copy (&r->mac, &args->mac); -} - -void -wc_arp_set_publisher_node (uword node_index, uword event_type) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - am->wc_ip4_arp_publisher_node = node_index; - am->wc_ip4_arp_publisher_et = event_type; -} - -static void -arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e); - -static int -vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm, - vnet_arp_set_ip4_over_ethernet_rpc_args_t - * args) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e; - ethernet_arp_interface_t *eai; - - if (vec_len (am->ethernet_arp_by_sw_if_index) <= args->sw_if_index) - return 0; - - eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index]; - - e = arp_entry_find (eai, &args->ip4); - - if (NULL != e) - { - adj_nbr_walk_nh4 (e->sw_if_index, - &e->ip4_address, arp_mk_incomplete_walk, e); - - /* - * The difference between flush and unset, is that an unset - * means delete for static and dynamic entries. A flush - * means delete only for dynamic. Flushing is what the DP - * does in response to interface events. unset is only done - * by the control plane. - */ - if (e->flags & IP_NEIGHBOR_FLAG_STATIC) - { - e->flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC; - } - else if (e->flags & IP_NEIGHBOR_FLAG_DYNAMIC) - { - arp_entry_free (eai, e); - } - } - return (0); -} - -/* - * arp_add_del_interface_address - * - * callback when an interface address is added or deleted - */ -static void -arp_enable_disable_interface (ip4_main_t * im, - uword opaque, u32 sw_if_index, u32 is_enable) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - - if (is_enable) - arp_enable (am, sw_if_index); - else - arp_disable (am, sw_if_index); -} - -/* - * arp_add_del_interface_address - * - * callback when an interface address is added or deleted - */ -static void -arp_add_del_interface_address (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, u32 is_del) -{ - /* - * Flush the ARP cache of all entries covered by the address - * that is being removed. - */ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e; - - if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index) - return; - - if (is_del) - { - ethernet_arp_interface_t *eai; - u32 i, *to_delete = 0; - hash_pair_t *pair; - - eai = &am->ethernet_arp_by_sw_if_index[sw_if_index]; - - /* *INDENT-OFF* */ - hash_foreach_pair (pair, eai->arp_entries, - ({ - e = pool_elt_at_index(am->ip4_entry_pool, - pair->value[0]); - if (ip4_destination_matches_route (im, &e->ip4_address, - address, address_length)) - { - vec_add1 (to_delete, e - am->ip4_entry_pool); - } - })); - /* *INDENT-ON* */ - - for (i = 0; i < vec_len (to_delete); i++) - { - e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]); - - vnet_arp_set_ip4_over_ethernet_rpc_args_t delme = { - .ip4.as_u32 = e->ip4_address.as_u32, - .sw_if_index = e->sw_if_index, - .flags = ETHERNET_ARP_ARGS_FLUSH, - }; - mac_address_copy (&delme.mac, &e->mac); - - vnet_arp_flush_ip4_over_ethernet_internal (vnet_get_main (), - &delme); - } - - vec_free (to_delete); - } -} - -static void -arp_table_bind (ip4_main_t * im, - uword opaque, - u32 sw_if_index, u32 new_fib_index, u32 old_fib_index) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_interface_t *eai; - ethernet_arp_ip4_entry_t *e; - hash_pair_t *pair; - - /* - * the IP table that the interface is bound to has changed. - * reinstall all the adj fibs. - */ - - if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index) - return; - - eai = &am->ethernet_arp_by_sw_if_index[sw_if_index]; - - /* *INDENT-OFF* */ - hash_foreach_pair (pair, eai->arp_entries, - ({ - e = pool_elt_at_index(am->ip4_entry_pool, - pair->value[0]); - /* - * remove the adj-fib from the old table and add to the new - */ - arp_adj_fib_remove(e, old_fib_index); - arp_adj_fib_add(e, new_fib_index); - })); - /* *INDENT-ON* */ - -} - -static clib_error_t * -ethernet_arp_init (vlib_main_t * vm) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ip4_main_t *im = &ip4_main; - pg_node_t *pn; - - ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index); - - pn = pg_get_node (arp_input_node.index); - pn->unformat_edit = unformat_pg_arp_header; - - am->opcode_by_name = hash_create_string (0, sizeof (uword)); -#define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o); - foreach_ethernet_arp_opcode; -#undef _ - - /* $$$ configurable */ - am->limit_arp_cache_size = 50000; - - am->pending_resolutions_by_address = hash_create (0, sizeof (uword)); - am->mac_changes_by_address = hash_create (0, sizeof (uword)); - am->wc_ip4_arp_publisher_node = (uword) ~ 0; - - /* don't trace ARP error packets */ - { - vlib_node_runtime_t *rt = - vlib_node_get_runtime (vm, arp_input_node.index); - -#define _(a,b) \ - vnet_pcap_drop_trace_filter_add_del \ - (rt->errors[ETHERNET_ARP_ERROR_##a], \ - 1 /* is_add */); - foreach_ethernet_arp_error -#undef _ - } - - ip4_add_del_interface_address_callback_t cb; - cb.function = arp_add_del_interface_address; - cb.function_opaque = 0; - vec_add1 (im->add_del_interface_address_callbacks, cb); - - ip4_enable_disable_interface_callback_t cbe; - cbe.function = arp_enable_disable_interface; - cbe.function_opaque = 0; - vec_add1 (im->enable_disable_interface_callbacks, cbe); - - ip4_table_bind_callback_t cbt; - cbt.function = arp_table_bind; - cbt.function_opaque = 0; - vec_add1 (im->table_bind_callbacks, cbt); - - return 0; -} -/* *INDENT-OFF* */ -VLIB_INIT_FUNCTION (ethernet_arp_init) = -{ - .runs_after = VLIB_INITS("ethernet_init"), -}; -/* *INDENT-ON* */ - -static void -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)); - hash_unset (eai->arp_entries, e->ip4_address.as_u32); - pool_put (am->ip4_entry_pool, e); -} - -static inline int -vnet_arp_unset_ip4_over_ethernet_internal (vnet_main_t * vnm, - vnet_arp_set_ip4_over_ethernet_rpc_args_t - * args) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e; - ethernet_arp_interface_t *eai; - - if (vec_len (am->ethernet_arp_by_sw_if_index) <= args->sw_if_index) - return 0; - - eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index]; - - e = arp_entry_find (eai, &args->ip4); - - if (NULL != e) - { - adj_nbr_walk_nh4 (e->sw_if_index, - &e->ip4_address, arp_mk_incomplete_walk, e); - arp_entry_free (eai, e); - } - - return 0; -} - - -static int -vnet_arp_populate_ip4_over_ethernet_internal (vnet_main_t * vnm, - vnet_arp_set_ip4_over_ethernet_rpc_args_t - * args) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e; - ethernet_arp_interface_t *eai; - - arp_enable (am, args->sw_if_index); - eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index]; - - e = arp_entry_find (eai, &args->ip4); - - if (NULL != e) - { - adj_nbr_walk_nh4 (e->sw_if_index, - &e->ip4_address, arp_mk_complete_walk, e); - } - return (0); -} - -static void -set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t - * a) -{ - vnet_main_t *vm = vnet_get_main (); - ASSERT (vlib_get_thread_index () == 0); - - if (a->flags & ETHERNET_ARP_ARGS_REMOVE) - vnet_arp_unset_ip4_over_ethernet_internal (vm, a); - else if (a->flags & ETHERNET_ARP_ARGS_FLUSH) - vnet_arp_flush_ip4_over_ethernet_internal (vm, a); - else if (a->flags & ETHERNET_ARP_ARGS_POPULATE) - vnet_arp_populate_ip4_over_ethernet_internal (vm, a); - else if (a->flags & ETHERNET_ARP_ARGS_WC_PUB) - vnet_arp_wc_publish_internal (vm, a); - else - vnet_arp_set_ip4_over_ethernet_internal (vm, a); -} - -/** - * @brief Invoked when the interface's admin state changes - */ -static clib_error_t * -ethernet_arp_sw_interface_up_down (vnet_main_t * vnm, - u32 sw_if_index, u32 flags) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_ip4_entry_t *e; - u32 i, *to_update = 0; - - /* *INDENT-OFF* */ - pool_foreach (e, am->ip4_entry_pool, - ({ - if (e->sw_if_index == sw_if_index) - vec_add1 (to_update, - e - am->ip4_entry_pool); - })); - /* *INDENT-ON* */ - - for (i = 0; i < vec_len (to_update); i++) - { - e = pool_elt_at_index (am->ip4_entry_pool, to_update[i]); - - vnet_arp_set_ip4_over_ethernet_rpc_args_t update_me = { - .ip4.as_u32 = e->ip4_address.as_u32, - .sw_if_index = e->sw_if_index, - }; - mac_address_copy (&update_me.mac, &e->mac); - - if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) - { - update_me.flags = ETHERNET_ARP_ARGS_POPULATE; - vnet_arp_populate_ip4_over_ethernet_internal (vnm, &update_me); - } - else - { - update_me.flags = ETHERNET_ARP_ARGS_FLUSH; - vnet_arp_flush_ip4_over_ethernet_internal (vnm, &update_me); - } - } - vec_free (to_update); - - return 0; -} - -VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_arp_sw_interface_up_down); - -static void -increment_ip4_and_mac_address (ethernet_arp_ip4_over_ethernet_address_t * a) -{ - u8 old; - int i; - - for (i = 3; i >= 0; i--) - { - old = a->ip4.as_u8[i]; - a->ip4.as_u8[i] += 1; - if (old < a->ip4.as_u8[i]) - break; - } - - for (i = 5; i >= 0; i--) - { - old = a->mac.bytes[i]; - a->mac.bytes[i] += 1; - if (old < a->mac.bytes[i]) - break; - } -} - -int -vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm, - u32 sw_if_index, - const ethernet_arp_ip4_over_ethernet_address_t - * a, ip_neighbor_flags_t flags) -{ - vnet_arp_set_ip4_over_ethernet_rpc_args_t args = { - .sw_if_index = sw_if_index, - .nbr_flags = flags, - .flags = 0, - .ip4.as_u32 = a->ip4.as_u32, - .mac = a->mac, - }; - - vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback, - (u8 *) & args, sizeof (args)); - 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_enable_disable (vnet_main_t * vnm, u32 sw_if_index, u8 enable) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_interface_t *eai; - - vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index); - - eai = &am->ethernet_arp_by_sw_if_index[sw_if_index]; - - if (enable) - { - if (!eai->proxy_enabled) - { - vnet_feature_enable_disable ("arp", "arp-proxy", - sw_if_index, 1, NULL, 0); - } - eai->proxy_enabled = 1; - } - else - { - if (eai->proxy_enabled) - { - vnet_feature_enable_disable ("arp", "arp-proxy", - sw_if_index, 0, NULL, 0); - } - eai->proxy_enabled = 0; - } - - return (0); -} - -int -vnet_proxy_arp_add_del (ip4_address_t * lo_addr, - ip4_address_t * hi_addr, u32 fib_index, int is_del) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_proxy_arp_t *pa; - u32 found_at_index = ~0; - - vec_foreach (pa, am->proxy_arps) - { - 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; - } - } - - if (found_at_index != ~0) - { - /* Delete, otherwise it's already in the table */ - if (is_del) - vec_delete (am->proxy_arps, 1, found_at_index); - return 0; - } - /* delete, no such entry */ - if (is_del) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - /* add, not in table */ - vec_add2 (am->proxy_arps, pa, 1); - 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; -} - -void -proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - ethernet_arp_interface_t *eai; - - vec_foreach (eai, am->ethernet_arp_by_sw_if_index) - { - if (eai->proxy_enabled) - cb (eai - am->ethernet_arp_by_sw_if_index, data); - } -} - -static clib_error_t * -ip_arp_add_del_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - u32 sw_if_index; - ethernet_arp_ip4_over_ethernet_address_t lo_addr, hi_addr, addr; - int addr_valid = 0; - int is_del = 0; - int count = 1; - u32 fib_index = 0; - u32 fib_id; - int is_proxy = 0; - ip_neighbor_flags_t flags; - - flags = IP_NEIGHBOR_FLAG_NONE; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */ - if (unformat (input, "%U %U %U", - unformat_vnet_sw_interface, vnm, &sw_if_index, - unformat_ip4_address, &addr.ip4, - unformat_mac_address_t, &addr.mac)) - addr_valid = 1; - - else if (unformat (input, "delete") || unformat (input, "del")) - is_del = 1; - - else if (unformat (input, "static")) - flags |= IP_NEIGHBOR_FLAG_STATIC; - - else if (unformat (input, "no-fib-entry")) - flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY; - - else if (unformat (input, "count %d", &count)) - ; - - else if (unformat (input, "fib-id %d", &fib_id)) - { - fib_index = fib_table_find (FIB_PROTOCOL_IP4, fib_id); - - if (~0 == fib_index) - return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id); - } - - else if (unformat (input, "proxy %U - %U", - unformat_ip4_address, &lo_addr.ip4, - unformat_ip4_address, &hi_addr.ip4)) - is_proxy = 1; - else - break; - } - - if (is_proxy) - { - (void) vnet_proxy_arp_add_del (&lo_addr.ip4, &hi_addr.ip4, - fib_index, is_del); - return 0; - } - - if (addr_valid) - { - int i; - - for (i = 0; i < count; i++) - { - if (is_del == 0) - { - uword event_type, *event_data = 0; - - /* Park the debug CLI until the arp entry is installed */ - vnet_register_ip4_arp_resolution_event - (vnm, &addr.ip4, vlib_current_process (vm), - 1 /* type */ , 0 /* data */ ); - - vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index, &addr, flags); - - vlib_process_wait_for_event (vm); - event_type = vlib_process_get_events (vm, &event_data); - vec_reset_length (event_data); - if (event_type != 1) - clib_warning ("event type %d unexpected", event_type); - } - else - vnet_arp_unset_ip4_over_ethernet (vnm, sw_if_index, &addr); - - increment_ip4_and_mac_address (&addr); - } - } - else - { - return clib_error_return (0, "unknown input `%U'", - format_unformat_error, input); - } - - return 0; -} - -/* *INDENT-OFF* */ -/*? - * Add or delete IPv4 ARP cache entries. - * - * @note 'set ip arp' options (e.g. delete, static, 'fib-id ', - * 'count ', 'interface ip4_addr mac_addr') can be added in - * any order and combination. - * - * @cliexpar - * @parblock - * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in - * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format. - * @cliexcmd{set ip arp GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe} - * @cliexcmd{set ip arp delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be} - * - * To add or delete an IPv4 ARP cache entry to or from a specific fib - * table: - * @cliexcmd{set ip arp fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe} - * @cliexcmd{set ip arp fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe} - * - * Add or delete IPv4 static ARP cache entries as follows: - * @cliexcmd{set ip arp static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe} - * @cliexcmd{set ip arp static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe} - * - * For testing / debugging purposes, the 'set ip arp' command can add or - * delete multiple entries. Supply the 'count N' parameter: - * @cliexcmd{set ip arp count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe} - * @endparblock - ?*/ -VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = { - .path = "set ip arp", - .short_help = - "set ip arp [del] [static] [no-fib-entry] [count ] [fib-id ] [proxy - ]", - .function = ip_arp_add_del_command_fn, -}; -/* *INDENT-ON* */ - -static clib_error_t * -set_int_proxy_arp_command_fn (vlib_main_t * vm, - unformat_input_t * - input, vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - u32 sw_if_index; - int enable = 0; - - sw_if_index = ~0; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "%U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - ; - else if (unformat (input, "enable") || unformat (input, "on")) - enable = 1; - else if (unformat (input, "disable") || unformat (input, "off")) - enable = 0; - else - break; - } - - if (~0 == sw_if_index) - return clib_error_return (0, "unknown input '%U'", - format_unformat_error, input); - - vnet_proxy_arp_enable_disable (vnm, sw_if_index, enable); - - return 0; -} - -/* *INDENT-OFF* */ -/*? - * Enable proxy-arp on an interface. The vpp stack will answer ARP - * requests for the indicated address range. Multiple proxy-arp - * ranges may be provisioned. - * - * @note Proxy ARP as a technology is infamous for blackholing traffic. - * Also, the underlying implementation has not been performance-tuned. - * Avoid creating an unnecessarily large set of ranges. - * - * @cliexpar - * To enable proxy arp on a range of addresses, use: - * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11} - * Append 'del' to delete a range of proxy ARP addresses: - * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del} - * You must then specifically enable proxy arp on individual interfaces: - * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable} - * To disable proxy arp on an individual interface: - * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable} - ?*/ -VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = { - .path = "set interface proxy-arp", - .short_help = - "set interface proxy-arp [enable|disable]", - .function = set_int_proxy_arp_command_fn, -}; -/* *INDENT-ON* */ - - -/* - * ARP/ND Termination in a L2 Bridge Domain based on IP4/IP6 to MAC - * hash tables mac_by_ip4 and mac_by_ip6 for each BD. - */ -typedef enum -{ - ARP_TERM_NEXT_L2_OUTPUT, - ARP_TERM_NEXT_DROP, - ARP_TERM_N_NEXT, -} arp_term_next_t; - -u32 arp_term_next_node_index[32]; - -static uword -arp_term_l2bd (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - l2input_main_t *l2im = &l2input_main; - u32 n_left_from, next_index, *from, *to_next; - u32 n_replies_sent = 0; - u16 last_bd_index = ~0; - l2_bridge_domain_t *last_bd_config = 0; - l2_input_config_t *cfg0; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - vlib_buffer_t *p0; - ethernet_header_t *eth0; - ethernet_arp_header_t *arp0; - ip6_header_t *iph0; - u8 *l3h0; - u32 pi0, error0, next0, sw_if_index0; - u16 ethertype0; - u16 bd_index0; - u32 ip0; - u8 *macp0; - - pi0 = from[0]; - to_next[0] = pi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - p0 = vlib_get_buffer (vm, pi0); - // Terminate only local (SHG == 0) ARP - if (vnet_buffer (p0)->l2.shg != 0) - goto next_l2_feature; - - eth0 = vlib_buffer_get_current (p0); - l3h0 = (u8 *) eth0 + vnet_buffer (p0)->l2.l2_len; - ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2)); - arp0 = (ethernet_arp_header_t *) l3h0; - - if (ethertype0 != ETHERNET_TYPE_ARP) - goto check_ip6_nd; - - if ((arp0->opcode != - clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request)) && - (arp0->opcode != - clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))) - goto check_ip6_nd; - - /* Must be ARP request/reply packet here */ - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && - (p0->flags & VLIB_BUFFER_IS_TRACED))) - { - u8 *t0 = vlib_add_trace (vm, node, p0, - sizeof (ethernet_arp_input_trace_t)); - clib_memcpy_fast (t0, l3h0, - sizeof (ethernet_arp_input_trace_t)); - } - - error0 = 0; - error0 = - (arp0->l2_type != - clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) - ? ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0); - error0 = - (arp0->l3_type != - clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ? - ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0); - - sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX]; - - if (error0) - goto drop; - - /* Trash ARP packets whose ARP-level source addresses do not - match, or if requester address is mcast */ - if (PREDICT_FALSE - (!ethernet_mac_address_equal (eth0->src_address, - arp0->ip4_over_ethernet[0]. - mac.bytes)) - || ethernet_address_cast (arp0->ip4_over_ethernet[0].mac.bytes)) - { - /* VRRP virtual MAC may be different to SMAC in ARP reply */ - if (!ethernet_mac_address_equal - (arp0->ip4_over_ethernet[0].mac.bytes, vrrp_prefix)) - { - error0 = ETHERNET_ARP_ERROR_l2_address_mismatch; - goto drop; - } - } - if (PREDICT_FALSE - (ip4_address_is_multicast (&arp0->ip4_over_ethernet[0].ip4))) - { - error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local; - goto drop; - } - - /* Check if anyone want ARP request events for L2 BDs */ - { - ethernet_arp_main_t *am = ðernet_arp_main; - if (am->wc_ip4_arp_publisher_node != (uword) ~ 0) - vnet_arp_wc_publish (sw_if_index0, &arp0->ip4_over_ethernet[0]); - } - - /* lookup BD mac_by_ip4 hash table for MAC entry */ - ip0 = arp0->ip4_over_ethernet[1].ip4.as_u32; - bd_index0 = vnet_buffer (p0)->l2.bd_index; - if (PREDICT_FALSE ((bd_index0 != last_bd_index) - || (last_bd_index == (u16) ~ 0))) - { - last_bd_index = bd_index0; - last_bd_config = vec_elt_at_index (l2im->bd_configs, bd_index0); - } - macp0 = (u8 *) hash_get (last_bd_config->mac_by_ip4, ip0); - - if (PREDICT_FALSE (!macp0)) - goto next_l2_feature; /* MAC not found */ - if (PREDICT_FALSE (arp0->ip4_over_ethernet[0].ip4.as_u32 == - arp0->ip4_over_ethernet[1].ip4.as_u32)) - goto next_l2_feature; /* GARP */ - - /* MAC found, send ARP reply - - Convert ARP request packet to ARP reply */ - arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply); - arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0]; - arp0->ip4_over_ethernet[0].ip4.as_u32 = ip0; - mac_address_from_bytes (&arp0->ip4_over_ethernet[0].mac, macp0); - clib_memcpy_fast (eth0->dst_address, eth0->src_address, 6); - clib_memcpy_fast (eth0->src_address, macp0, 6); - n_replies_sent += 1; - - output_response: - /* For BVI, need to use l2-fwd node to send ARP reply as - l2-output node cannot output packet to BVI properly */ - cfg0 = vec_elt_at_index (l2im->configs, sw_if_index0); - if (PREDICT_FALSE (cfg0->bvi)) - { - vnet_buffer (p0)->l2.feature_bitmap |= L2INPUT_FEAT_FWD; - vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0; - goto next_l2_feature; - } - - /* Send ARP/ND reply back out input interface through l2-output */ - vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0; - next0 = ARP_TERM_NEXT_L2_OUTPUT; - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, pi0, - next0); - continue; - - check_ip6_nd: - /* IP6 ND event notification or solicitation handling to generate - local response instead of flooding */ - iph0 = (ip6_header_t *) l3h0; - if (PREDICT_FALSE (ethertype0 == ETHERNET_TYPE_IP6 && - iph0->protocol == IP_PROTOCOL_ICMP6 && - !ip6_address_is_unspecified - (&iph0->src_address))) - { - sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX]; - if (vnet_ip6_nd_term - (vm, node, p0, eth0, iph0, sw_if_index0, - vnet_buffer (p0)->l2.bd_index)) - goto output_response; - } - - next_l2_feature: - { - next0 = vnet_l2_feature_next (p0, arp_term_next_node_index, - L2INPUT_FEAT_ARP_TERM); - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - pi0, next0); - continue; - } - - drop: - 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)) - { - error0 = ETHERNET_ARP_ERROR_gratuitous_arp; - } - next0 = ARP_TERM_NEXT_DROP; - p0->error = node->errors[error0]; - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, pi0, - next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - vlib_error_count (vm, node->node_index, - ETHERNET_ARP_ERROR_replies_sent, n_replies_sent); - return frame->n_vectors; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (arp_term_l2bd_node, static) = { - .function = arp_term_l2bd, - .name = "arp-term-l2bd", - .vector_size = sizeof (u32), - .n_errors = ETHERNET_ARP_N_ERROR, - .error_strings = ethernet_arp_error_strings, - .n_next_nodes = ARP_TERM_N_NEXT, - .next_nodes = { - [ARP_TERM_NEXT_L2_OUTPUT] = "l2-output", - [ARP_TERM_NEXT_DROP] = "error-drop", - }, - .format_buffer = format_ethernet_arp_header, - .format_trace = format_arp_term_input_trace, -}; -/* *INDENT-ON* */ - -clib_error_t * -arp_term_init (vlib_main_t * vm) -{ - // Initialize the feature next-node indexes - feat_bitmap_init_next_nodes (vm, - arp_term_l2bd_node.index, - L2INPUT_N_FEAT, - l2input_get_feat_names (), - arp_term_next_node_index); - return 0; -} - -VLIB_INIT_FUNCTION (arp_term_init); - -void -change_arp_mac (u32 sw_if_index, ethernet_arp_ip4_entry_t * e) -{ - if (e->sw_if_index == sw_if_index) - { - adj_nbr_walk_nh4 (e->sw_if_index, - &e->ip4_address, arp_mk_complete_walk, e); - } -} - -void -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, - ({ - 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, u32 sw_if_index) -{ - ip4_main_t *i4m = &ip4_main; - 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", - format_ip4_address, ip4_addr, sw_if_index); - - /* Form GARP packet for output - Gratuitous ARP is an ARP request packet - where the interface IP/MAC pair is used for both source and request - MAC/IP pairs in the request */ - u32 bi = 0; - ethernet_arp_header_t *h = vlib_packet_template_get_packet - (vm, &i4m->ip4_arp_request_packet_template, &bi); - - if (!h) - return; - - mac_address_from_bytes (&h->ip4_over_ethernet[0].mac, hi->hw_address); - mac_address_from_bytes (&h->ip4_over_ethernet[1].mac, hi->hw_address); - h->ip4_over_ethernet[0].ip4 = ip4_addr[0]; - h->ip4_over_ethernet[1].ip4 = ip4_addr[0]; - - /* Setup MAC header with ARP Etype and broadcast DMAC */ - vlib_buffer_t *b = vlib_get_buffer (vm, bi); - 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); - clib_memcpy_fast (e->dst_address, rewrite, rewrite_len); - vec_free (rewrite); - - /* Send GARP packet out the specified interface */ - vnet_buffer (b)->sw_if_index[VLIB_RX] = - vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index; - vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index); - u32 *to_next = vlib_frame_vector_args (f); - to_next[0] = bi; - f->n_vectors = 1; - vlib_put_frame_to_node (vm, hi->output_node_index, f); - } -} - -/* - * Remove any arp entries associated with the specified interface - */ -static clib_error_t * -vnet_arp_delete_sw_interface (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) -{ - ethernet_arp_main_t *am = ðernet_arp_main; - - if (!is_add && sw_if_index != ~0) - { - ethernet_arp_ip4_entry_t *e; - /* *INDENT-OFF* */ - pool_foreach (e, am->ip4_entry_pool, ({ - if (e->sw_if_index != sw_if_index) - continue; - vnet_arp_set_ip4_over_ethernet_rpc_args_t args = { - .sw_if_index = sw_if_index, - .ip4 = e->ip4_address, - }; - vnet_arp_unset_ip4_over_ethernet_internal (vnm, &args); - })); - /* *INDENT-ON* */ - arp_disable (am, sw_if_index); - } - else if (is_add) - { - vnet_feature_enable_disable ("arp", "arp-disabled", - sw_if_index, 1, NULL, 0); - } - - return (NULL); -} - -VNET_SW_INTERFACE_ADD_DEL_FUNCTION (vnet_arp_delete_sw_interface); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vnet/ethernet/arp.h b/src/vnet/ethernet/arp.h deleted file mode 100644 index de92c4f419c..00000000000 --- a/src/vnet/ethernet/arp.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ - -#ifndef __ARP_H__ -#define __ARP_H__ - -#include -#include -#include -#include - -typedef struct -{ - u32 sw_if_index; - ip4_address_t ip4_address; - - mac_address_t mac; - - ip_neighbor_flags_t flags; - - f64 time_last_updated; - - /** - * The index of the adj-fib entry created - */ - fib_node_index_t fib_entry_index; -} ethernet_arp_ip4_entry_t; - -extern u8 *format_ethernet_arp_ip4_entry (u8 * s, va_list * va); - -ethernet_arp_ip4_entry_t *ip4_neighbors_pool (void); -ethernet_arp_ip4_entry_t *ip4_neighbor_entries (u32 sw_if_index); - -extern int vnet_proxy_arp_add_del (ip4_address_t * lo_addr, - ip4_address_t * hi_addr, - u32 fib_index, int is_del); - -extern int vnet_proxy_arp_enable_disable (vnet_main_t * vnm, - u32 sw_if_index, u8 enable); - -extern int vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm, - u32 sw_if_index, - const - ethernet_arp_ip4_over_ethernet_address_t - * a, ip_neighbor_flags_t flags); - -extern int vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm, - u32 sw_if_index, - const - ethernet_arp_ip4_over_ethernet_address_t - * a); - -void vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm, - void *address_arg, - uword node_index, - uword type_opaque, uword data); - -typedef int (*arp_change_event_cb_t) (u32 pool_index, - const mac_address_t * mac, - u32 sw_if_index, - const ip4_address_t * address); - -int vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm, - arp_change_event_cb_t data_callback, - u32 pid, - void *address_arg, - uword node_index, - uword type_opaque, - uword data, int is_add); - -void wc_arp_set_publisher_node (uword inode_index, uword event_type); - -void ethernet_arp_change_mac (u32 sw_if_index); -void ethernet_ndp_change_mac (u32 sw_if_index); - -void arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai); - -typedef struct -{ - u32 sw_if_index; - ip4_address_t ip; - mac_address_t mac; -} wc_arp_report_t; - -/** - * call back function when walking the DB of proxy ARPs - * @return 0 to stop the walk !0 to continue - */ -typedef walk_rc_t (proxy_arp_walk_t) (const ip4_address_t * lo_addr, - const ip4_address_t * hi_addr, - u32 fib_index, void *dat); - -extern void proxy_arp_walk (proxy_arp_walk_t cb, void *data); - -/** - * call back function when walking the DB of proxy ARP interface - * @return 0 to stop the walk !0 to continue - */ -typedef walk_rc_t (proxy_arp_intf_walk_t) (u32 sw_if_index, void *data); - -extern void proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data); - -#endif - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vnet/ethernet/arp_packet.c b/src/vnet/ethernet/arp_packet.c new file mode 100644 index 00000000000..c72dab99ad5 --- /dev/null +++ b/src/vnet/ethernet/arp_packet.c @@ -0,0 +1,111 @@ +/* + * ethernet/arp.c: IP v4 ARP node + * + * Copyright (c) 2010 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 +#include +#include + +u8 * +format_ethernet_arp_opcode (u8 * s, va_list * va) +{ + ethernet_arp_opcode_t o = va_arg (*va, ethernet_arp_opcode_t); + char *t = 0; + switch (o) + { +#define _(f) case ETHERNET_ARP_OPCODE_##f: t = #f; break; + foreach_ethernet_arp_opcode; +#undef _ + + default: + return format (s, "unknown 0x%x", o); + } + + return format (s, "%s", t); +} + +u8 * +format_ethernet_arp_hardware_type (u8 * s, va_list * va) +{ + ethernet_arp_hardware_type_t h = va_arg (*va, ethernet_arp_hardware_type_t); + char *t = 0; + switch (h) + { +#define _(n,f) case n: t = #f; break; + foreach_ethernet_arp_hardware_type; +#undef _ + + default: + return format (s, "unknown 0x%x", h); + } + + return format (s, "%s", t); +} + +u8 * +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); + u32 indent; + u16 l2_type, l3_type; + + if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes) + return format (s, "ARP header truncated"); + + l2_type = clib_net_to_host_u16 (a->l2_type); + l3_type = clib_net_to_host_u16 (a->l3_type); + + indent = format_get_indent (s); + + s = format (s, "%U, type %U/%U, address size %d/%d", + format_ethernet_arp_opcode, clib_net_to_host_u16 (a->opcode), + format_ethernet_arp_hardware_type, l2_type, + format_ethernet_type, l3_type, + a->n_l2_address_bytes, a->n_l3_address_bytes); + + if (l2_type == ETHERNET_ARP_HARDWARE_TYPE_ethernet + && l3_type == ETHERNET_TYPE_IP4) + { + s = format (s, "\n%U%U/%U -> %U/%U", + format_white_space, indent, + format_mac_address_t, &a->ip4_over_ethernet[0].mac, + format_ip4_address, &a->ip4_over_ethernet[0].ip4, + format_mac_address_t, &a->ip4_over_ethernet[1].mac, + format_ip4_address, &a->ip4_over_ethernet[1].ip4); + } + else + { + uword n2 = a->n_l2_address_bytes; + uword n3 = a->n_l3_address_bytes; + s = format (s, "\n%U%U/%U -> %U/%U", + format_white_space, indent, + format_hex_bytes, a->data + 0 * n2 + 0 * n3, n2, + format_hex_bytes, a->data + 1 * n2 + 0 * n3, n3, + format_hex_bytes, a->data + 1 * n2 + 1 * n3, n2, + format_hex_bytes, a->data + 2 * n2 + 1 * n3, n3); + } + + return s; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/ethernet/arp_packet.h b/src/vnet/ethernet/arp_packet.h index b4e021cce19..a35adf17c85 100644 --- a/src/vnet/ethernet/arp_packet.h +++ b/src/vnet/ethernet/arp_packet.h @@ -18,6 +18,7 @@ #ifndef included_ethernet_arp_packet_h #define included_ethernet_arp_packet_h +#include #include #define foreach_ethernet_arp_hardware_type \ @@ -145,9 +146,9 @@ typedef struct }; } ethernet_arp_header_t; -void send_ip4_garp (vlib_main_t * vm, u32 sw_if_index); -void send_ip4_garp_w_addr (vlib_main_t * vm, - const ip4_address_t * ip4_addr, u32 sw_if_index); +extern u8 *format_ethernet_arp_header (u8 * s, va_list * va); +extern u8 *format_ethernet_arp_opcode (u8 * s, va_list * va); +extern u8 *format_ethernet_arp_hardware_type (u8 * s, va_list * va); #endif /* included_ethernet_arp_packet_h */ diff --git a/src/vnet/ethernet/ethernet.h b/src/vnet/ethernet/ethernet.h index cde6fb90038..2e5209c4423 100644 --- a/src/vnet/ethernet/ethernet.h +++ b/src/vnet/ethernet/ethernet.h @@ -257,7 +257,18 @@ typedef struct u32 input_next_mpls; } next_by_ethertype_t; +struct ethernet_main_t_; + +typedef void (ethernet_address_change_function_t) + (struct ethernet_main_t_ * im, u32 sw_if_index, uword opaque); + typedef struct +{ + ethernet_address_change_function_t *function; + uword function_opaque; +} ethernet_address_change_ctx_t; + +typedef struct ethernet_main_t_ { vlib_main_t *vlib_main; @@ -300,6 +311,10 @@ typedef struct /* Allocated loopback instances */ uword *bm_loopback_instances; + + /** Functions to call when interface hw address changes. */ + ethernet_address_change_ctx_t *address_change_callbacks; + } ethernet_main_t; extern ethernet_main_t ethernet_main; @@ -552,8 +567,6 @@ void ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai); u8 *ethernet_build_rewrite (vnet_main_t * vnm, u32 sw_if_index, vnet_link_t link_type, const void *dst_address); -const u8 *ethernet_ip4_mcast_dst_addr (void); -const u8 *ethernet_ip6_mcast_dst_addr (void); void ethernet_input_init (vlib_main_t * vm, ethernet_main_t * em); extern vlib_node_registration_t ethernet_input_node; diff --git a/src/vnet/ethernet/interface.c b/src/vnet/ethernet/interface.c index d79669206a9..39e5cfb3cf8 100644 --- a/src/vnet/ethernet/interface.c +++ b/src/vnet/ethernet/interface.c @@ -41,10 +41,12 @@ #include #include #include -#include +//#include #include #include #include +#include +#include /** * @file @@ -53,7 +55,7 @@ * This file contains code to manage loopback interfaces. */ -const u8 * +static const u8 * ethernet_ip4_mcast_dst_addr (void) { const static u8 ethernet_mcast_dst_mac[] = { @@ -63,7 +65,7 @@ ethernet_ip4_mcast_dst_addr (void) return (ethernet_mcast_dst_mac); } -const u8 * +static const u8 * ethernet_ip6_mcast_dst_addr (void) { const static u8 ethernet_mcast_dst_mac[] = { @@ -195,27 +197,74 @@ ethernet_build_rewrite (vnet_main_t * vnm, void ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai) { - ip_adjacency_t *adj; - - adj = adj_get (ai); - vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); + if ((si->type == VNET_SW_INTERFACE_TYPE_P2P) || (si->type == VNET_SW_INTERFACE_TYPE_PIPE)) { default_update_adjacency (vnm, sw_if_index, ai); } - else if (FIB_PROTOCOL_IP4 == adj->ia_nh_proto) - { - arp_update_adjacency (vnm, sw_if_index, ai); - } - else if (FIB_PROTOCOL_IP6 == adj->ia_nh_proto) - { - ip6_ethernet_update_adjacency (vnm, sw_if_index, ai); - } else { - ASSERT (0); + ip_adjacency_t *adj; + + adj = adj_get (ai); + + switch (adj->lookup_next_index) + { + case IP_LOOKUP_NEXT_GLEAN: + adj_glean_update_rewrite (ai); + break; + case IP_LOOKUP_NEXT_ARP: + ip_neighbor_update (vnm, ai); + break; + case IP_LOOKUP_NEXT_BCAST: + adj_nbr_update_rewrite (ai, + ADJ_NBR_REWRITE_FLAG_COMPLETE, + ethernet_build_rewrite + (vnm, + adj->rewrite_header.sw_if_index, + adj->ia_link, + VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST)); + break; + case IP_LOOKUP_NEXT_MCAST: + { + /* + * Construct a partial rewrite from the known ethernet mcast dest MAC + */ + u8 *rewrite; + u8 offset; + + rewrite = ethernet_build_rewrite + (vnm, + sw_if_index, + adj->ia_link, + (adj->ia_nh_proto == FIB_PROTOCOL_IP6 ? + ethernet_ip6_mcast_dst_addr () : + ethernet_ip4_mcast_dst_addr ())); + + /* + * 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 destintation address. + */ + offset = vec_len (rewrite) - 2; + adj_mcast_update_rewrite (ai, rewrite, offset); + + break; + } + case IP_LOOKUP_NEXT_DROP: + case IP_LOOKUP_NEXT_PUNT: + case IP_LOOKUP_NEXT_LOCAL: + case IP_LOOKUP_NEXT_REWRITE: + case IP_LOOKUP_NEXT_MCAST_MIDCHAIN: + case IP_LOOKUP_NEXT_MIDCHAIN: + case IP_LOOKUP_NEXT_ICMP_ERROR: + case IP_LOOKUP_N_NEXT: + ASSERT (0); + break; + } } } @@ -234,8 +283,12 @@ ethernet_mac_change (vnet_hw_interface_t * hi, clib_memcpy (hi->hw_address, mac_address, vec_len (hi->hw_address)); clib_memcpy (ei->address, (u8 *) mac_address, sizeof (ei->address)); - ethernet_arp_change_mac (hi->sw_if_index); - ethernet_ndp_change_mac (hi->sw_if_index); + + { + ethernet_address_change_ctx_t *cb; + vec_foreach (cb, em->address_change_callbacks) + cb->function (em, hi->sw_if_index, cb->function_opaque); + } return (NULL); } diff --git a/src/vnet/ethernet/mac_address.c b/src/vnet/ethernet/mac_address.c index 6f40e50efa2..b7981299700 100644 --- a/src/vnet/ethernet/mac_address.c +++ b/src/vnet/ethernet/mac_address.c @@ -61,6 +61,16 @@ unformat_mac_address_t (unformat_input_t * input, va_list * args) return (0); } +void +mac_address_increment (mac_address_t * mac) +{ + u64 a; + + a = mac_address_as_u64 (mac); + a++; + mac_address_from_u64 (mac, a); +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ethernet/mac_address.h b/src/vnet/ethernet/mac_address.h index 01fb76e002b..aa773c5c449 100644 --- a/src/vnet/ethernet/mac_address.h +++ b/src/vnet/ethernet/mac_address.h @@ -149,6 +149,7 @@ mac_address_set_zero (mac_address_t * mac) mac->u.last_2 = 0; } +extern void mac_address_increment (mac_address_t * mac); extern uword unformat_mac_address_t (unformat_input_t * input, va_list * args); extern u8 *format_mac_address_t (u8 * s, va_list * args); -- cgit 1.2.3-korg