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/l2/l2.api | 40 +++- src/vnet/l2/l2_api.c | 167 +++++++++++++++- src/vnet/l2/l2_arp_term.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++ src/vnet/l2/l2_arp_term.h | 54 ++++++ src/vnet/l2/l2_bd.h | 2 +- 5 files changed, 741 insertions(+), 3 deletions(-) create mode 100644 src/vnet/l2/l2_arp_term.c create mode 100644 src/vnet/l2/l2_arp_term.h (limited to 'src/vnet/l2') diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index b0031ba16cc..06663595cf7 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -18,6 +18,7 @@ option version = "2.2.2"; import "vnet/ip/ip_types.api"; import "vnet/ethernet/ethernet_types.api"; +import "vnet/interface_types.api"; /** \brief Reply to l2_xconnect_dump @param context - sender context which was passed in the request @@ -154,7 +155,7 @@ autoreply define want_l2_macs_events u32 learn_limit; u8 scan_delay; u8 max_macs_in_event; - u8 enable_disable; + bool enable_disable; u32 pid; }; @@ -598,6 +599,43 @@ autoreply define bvi_delete u32 sw_if_index; }; +/** \brief Register for IP4 ARP resolution event on receing ARP reply or + MAC/IP info from ARP requests in L2 BDs + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param enable - 1 => register for events, 0 => cancel registration + @param pid - sender's pid +*/ +autoreply define want_l2_arp_term_events +{ + u32 client_index; + u32 context; + bool enable; + u32 pid; +}; + +/** \brief Tell client about an IP4 ARP resolution event or + MAC/IP info from ARP requests in L2 BDs + @param client_index - opaque cookie to identify the sender + @param pid - client pid registered to receive notification + @param ip - IP address of new ARP term entry + @param sw_if_index - interface of new ARP term entry + @param mac - MAC address of new ARP term entry +*/ +define l2_arp_term_event +{ + u32 client_index; + u32 pid; + vl_api_address_t ip; + vl_api_interface_index_t sw_if_index; + vl_api_mac_address_t mac; +}; + +service { + rpc want_l2_arp_term_events returns want_l2_arp_term_events_reply + events l2_arp_term_event; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index e8b103ac2c7..fa852742f9f 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -77,7 +78,8 @@ _(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \ _(BRIDGE_DOMAIN_SET_MAC_AGE, bridge_domain_set_mac_age) \ _(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) \ _(BVI_CREATE, bvi_create) \ -_(BVI_DELETE, bvi_delete) +_(BVI_DELETE, bvi_delete) \ +_(WANT_L2_ARP_TERM_EVENTS, want_l2_arp_term_events) static void send_l2_xconnect_details (vl_api_registration_t * reg, u32 context, @@ -1045,6 +1047,169 @@ vl_api_bvi_delete_t_handler (vl_api_bvi_delete_t * mp) REPLY_MACRO (VL_API_BVI_DELETE_REPLY); } +static bool +l2_arp_term_publish_event_is_equal (const l2_arp_term_publish_event_t * e1, + const l2_arp_term_publish_event_t * e2) +{ + if (e1 == NULL || e2 == NULL) + return false; + return (ip46_address_is_equal (&e1->ip, &e2->ip) && + (e1->sw_if_index == e2->sw_if_index) && + (mac_address_equal (&e1->mac, &e2->mac))); +} + +static uword +l2_arp_term_process (vlib_main_t * vm, vlib_node_runtime_t * rt, + vlib_frame_t * f) +{ + /* These cross the longjmp boundary (vlib_process_wait_for_event) + * and need to be volatile - to prevent them from being optimized into + * a register - which could change during suspension */ + volatile f64 last = vlib_time_now (vm); + volatile l2_arp_term_publish_event_t last_event = { }; + + l2_arp_term_main_t *l2am = &l2_arp_term_main; + + while (1) + { + uword event_type = L2_ARP_TERM_EVENT_PUBLISH; + vpe_client_registration_t *reg; + f64 now; + + vlib_process_wait_for_event (vm); + + vlib_process_get_event_data (vm, &event_type); + now = vlib_time_now (vm); + + if (event_type == L2_ARP_TERM_EVENT_PUBLISH) + { + l2_arp_term_publish_event_t *event; + + vec_foreach (event, l2am->publish_events) + { + /* dampen duplicate events - cast away volatile */ + if (l2_arp_term_publish_event_is_equal + (event, (l2_arp_term_publish_event_t *) & last_event) && + (now - last) < 10.0) + { + continue; + } + last_event = *event; + last = now; + + /* *INDENT-OFF* */ + pool_foreach(reg, vpe_api_main.l2_arp_term_events_registrations, + ({ + vl_api_registration_t *vl_reg; + vl_reg = vl_api_client_index_to_registration (reg->client_index); + ASSERT (vl_reg != NULL); + + if (reg && vl_api_can_send_msg (vl_reg)) + { + vl_api_l2_arp_term_event_t * vevent; + vevent = vl_msg_api_alloc (sizeof *vevent); + clib_memset (vevent, 0, sizeof *vevent); + vevent->_vl_msg_id = htons (VL_API_L2_ARP_TERM_EVENT); + vevent->client_index = reg->client_index; + vevent->pid = reg->client_pid; + ip_address_encode(&event->ip, + event->type, + &vevent->ip); + vevent->sw_if_index = htonl(event->sw_if_index); + mac_address_encode(&event->mac, vevent->mac); + vl_api_send_msg (vl_reg, (u8 *) vevent); + } + })); + /* *INDENT-ON* */ + } + vec_reset_length (l2am->publish_events); + } + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (l2_arp_term_process_node) = { + .function = l2_arp_term_process, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "l2-arp-term-publisher", +}; +/* *INDENT-ON* */ + +static void +vl_api_want_l2_arp_term_events_t_handler (vl_api_want_l2_arp_term_events_t * + mp) +{ + vl_api_want_l2_arp_term_events_reply_t *rmp; + vpe_api_main_t *am = &vpe_api_main; + vpe_client_registration_t *rp; + int rv = 0; + uword *p; + + p = hash_get (am->l2_arp_term_events_registration_hash, mp->client_index); + + if (p) + { + if (mp->enable) + { + clib_warning ("pid %d: already enabled...", mp->pid); + rv = VNET_API_ERROR_INVALID_REGISTRATION; + goto reply; + } + else + { + rp = pool_elt_at_index (am->l2_arp_term_events_registrations, p[0]); + pool_put (am->l2_arp_term_events_registrations, rp); + hash_unset (am->l2_arp_term_events_registration_hash, + mp->client_index); + if (pool_elts (am->l2_arp_term_events_registrations) == 0) + l2_arp_term_set_publisher_node (false); + goto reply; + } + } + if (mp->enable == 0) + { + clib_warning ("pid %d: already disabled...", mp->pid); + rv = VNET_API_ERROR_INVALID_REGISTRATION; + goto reply; + } + pool_get (am->l2_arp_term_events_registrations, rp); + rp->client_index = mp->client_index; + rp->client_pid = mp->pid; + hash_set (am->l2_arp_term_events_registration_hash, rp->client_index, + rp - am->l2_arp_term_events_registrations); + l2_arp_term_set_publisher_node (true); + +reply: + REPLY_MACRO (VL_API_WANT_L2_ARP_TERM_EVENTS_REPLY); +} + +static clib_error_t * +want_l2_arp_term_events_reaper (u32 client_index) +{ + vpe_client_registration_t *rp; + vpe_api_main_t *am; + uword *p; + + am = &vpe_api_main; + + /* remove from the registration hash */ + p = hash_get (am->l2_arp_term_events_registration_hash, client_index); + + if (p) + { + rp = pool_elt_at_index (am->l2_arp_term_events_registrations, p[0]); + pool_put (am->l2_arp_term_events_registrations, rp); + hash_unset (am->l2_arp_term_events_registration_hash, client_index); + if (pool_elts (am->l2_arp_term_events_registrations) == 0) + l2_arp_term_set_publisher_node (false); + } + return (NULL); +} + +VL_MSG_API_REAPER_FUNCTION (want_l2_arp_term_events_reaper); + /* * l2_api_hookup * Add vpe's API message handlers to the table. diff --git a/src/vnet/l2/l2_arp_term.c b/src/vnet/l2/l2_arp_term.c new file mode 100644 index 00000000000..982fd9f05ea --- /dev/null +++ b/src/vnet/l2/l2_arp_term.c @@ -0,0 +1,481 @@ +/* + * l2/l2_arp_term.c: IP v4 ARP L2 BD termination + * + * 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 + +static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 }; + +l2_arp_term_main_t l2_arp_term_main; + +/* + * 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]; + +typedef struct +{ + u8 packet_data[64]; +} ethernet_arp_input_trace_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 char *ethernet_arp_error_strings[] = { +#define _(sym,string) string, + foreach_ethernet_arp_error +#undef _ +}; + +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; +} + +void +l2_arp_term_set_publisher_node (bool on) +{ + l2_arp_term_main_t *l2am = &l2_arp_term_main; + + l2am->publish = on; +} + +static int +l2_arp_term_publish (l2_arp_term_publish_event_t * ctx) +{ + l2_arp_term_main_t *l2am = &l2_arp_term_main; + + vec_add1 (l2am->publish_events, *ctx); + + vlib_process_signal_event (vlib_get_main (), + l2_arp_term_process_node.index, + L2_ARP_TERM_EVENT_PUBLISH, 0); + + return 0; +} + +static inline void +l2_arp_term_publish_v4_dp (u32 sw_if_index, + const ethernet_arp_ip4_over_ethernet_address_t * a) +{ + l2_arp_term_main_t *l2am = &l2_arp_term_main; + + if (!l2am->publish) + return; + + l2_arp_term_publish_event_t args = { + .sw_if_index = sw_if_index, + .type = IP46_TYPE_IP4, + .ip.ip4 = a->ip4, + .mac = a->mac, + }; + + vl_api_rpc_call_main_thread (l2_arp_term_publish, (u8 *) & args, + sizeof (args)); +} + +static inline void +l2_arp_term_publish_v6_dp (u32 sw_if_index, + const ip6_address_t * addr, + const mac_address_t * mac) +{ + l2_arp_term_main_t *l2am = &l2_arp_term_main; + + if (!l2am->publish) + return; + + l2_arp_term_publish_event_t args = { + .sw_if_index = sw_if_index, + .type = IP46_TYPE_IP6, + .ip.ip6 = *addr, + .mac = *mac, + }; + + vl_api_rpc_call_main_thread (l2_arp_term_publish, (u8 *) & args, + sizeof (args)); +} + +static inline int +vnet_ip6_nd_term (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_buffer_t * p0, + ethernet_header_t * eth, + ip6_header_t * ip, u32 sw_if_index, u16 bd_index) +{ + icmp6_neighbor_solicitation_or_advertisement_header_t *ndh; + mac_address_t mac; + + mac_address_from_bytes (&mac, eth->src_address); + ndh = ip6_next_header (ip); + if (ndh->icmp.type != ICMP6_neighbor_solicitation && + ndh->icmp.type != ICMP6_neighbor_advertisement) + return 0; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (p0->flags & VLIB_BUFFER_IS_TRACED))) + { + u8 *t0 = vlib_add_trace (vm, node, p0, + sizeof (icmp6_input_trace_t)); + clib_memcpy (t0, ip, sizeof (icmp6_input_trace_t)); + } + + /* Check if anyone want ND events for L2 BDs */ + if (PREDICT_FALSE (!ip6_address_is_link_local_unicast (&ip->src_address))) + { + l2_arp_term_publish_v6_dp (sw_if_index, &ip->src_address, &mac); + } + + /* Check if MAC entry exsist for solicited target IP */ + if (ndh->icmp.type == ICMP6_neighbor_solicitation) + { + icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *opt; + l2_bridge_domain_t *bd_config; + u8 *macp; + + opt = (void *) (ndh + 1); + if ((opt->header.type != + ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address) || + (opt->header.n_data_u64s != 1)) + return 0; /* source link layer address option not present */ + + bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); + macp = + (u8 *) hash_get_mem (bd_config->mac_by_ip6, &ndh->target_address); + if (macp) + { /* found ip-mac entry, generate eighbor advertisement response */ + int bogus_length; + vlib_node_runtime_t *error_node = + vlib_node_get_runtime (vm, ip6_icmp_input_node.index); + ip->dst_address = ip->src_address; + ip->src_address = ndh->target_address; + ip->hop_limit = 255; + opt->header.type = + ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address; + clib_memcpy (opt->ethernet_address, macp, 6); + ndh->icmp.type = ICMP6_neighbor_advertisement; + ndh->advertisement_flags = clib_host_to_net_u32 + (ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED | + ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE); + ndh->icmp.checksum = 0; + ndh->icmp.checksum = + ip6_tcp_udp_icmp_compute_checksum (vm, p0, ip, &bogus_length); + clib_memcpy (eth->dst_address, eth->src_address, 6); + clib_memcpy (eth->src_address, macp, 6); + vlib_error_count (vm, error_node->node_index, + ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX, 1); + return 1; + } + } + + return 0; + +} + +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 */ + l2_arp_term_publish_v4_dp (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); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2/l2_arp_term.h b/src/vnet/l2/l2_arp_term.h new file mode 100644 index 00000000000..5426d03af69 --- /dev/null +++ b/src/vnet/l2/l2_arp_term.h @@ -0,0 +1,54 @@ +/* + * l2/l2_arp_term.c: IP v4 ARP L2 BD termination + * + * 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 + +typedef struct l2_arp_term_publish_ctx_t_ +{ + u32 sw_if_index; + ip46_type_t type; + ip46_address_t ip; + mac_address_t mac; +} l2_arp_term_publish_event_t; + +enum +{ + L2_ARP_TERM_EVENT_PUBLISH, +}; + +typedef struct l2_arp_term_main_t_ +{ + bool publish; + + l2_arp_term_publish_event_t *publish_events; + +} l2_arp_term_main_t; + +extern l2_arp_term_main_t l2_arp_term_main; +extern vlib_node_registration_t l2_arp_term_process_node; + +extern void l2_arp_term_set_publisher_node (bool on); + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2/l2_bd.h b/src/vnet/l2/l2_bd.h index 360880839ec..35c0e0bf0a1 100644 --- a/src/vnet/l2/l2_bd.h +++ b/src/vnet/l2/l2_bd.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include typedef enum l2_bd_port_type_t_ -- cgit 1.2.3-korg