aboutsummaryrefslogtreecommitdiffstats
path: root/vnet
diff options
context:
space:
mode:
authorJohn Lo <loj@cisco.com>2016-08-27 01:11:57 -0400
committerDave Barach <openvpp@barachs.net>2016-08-31 21:05:06 +0000
commit1edfba9a6394128ee5fad2b413e9e0a05972ef48 (patch)
treee1fbed7fe39b487bba72ead48020b3f25d1cbef6 /vnet
parent7214cf11e9b9edbc015994cb7f9f5828e02d3791 (diff)
VPP-358: Add IPv6 ND Event Notification and Termination
Add IPv6 equivalent of IPv4 ARP event notification which covers address resolution for L3 and MAC/IP binding in L2 BD and ARP termination in BD. For IPv6, ICMP6 neighbor solicitation and advertisement packets are utilized instead of ARP request and response packets for IPv4. Change-Id: I0088fa173e4480de297c8053ea2fcd0821322815 Signed-off-by: John Lo <loj@cisco.com>
Diffstat (limited to 'vnet')
-rw-r--r--vnet/vnet/ethernet/arp.c93
-rw-r--r--vnet/vnet/ip/ip6.h20
-rw-r--r--vnet/vnet/ip/ip6_neighbor.c251
-rw-r--r--vnet/vnet/l2/l2_bd.c71
-rw-r--r--vnet/vnet/l2/l2_bd.h2
-rw-r--r--vnet/vnet/l2/l2_input.c6
6 files changed, 381 insertions, 62 deletions
diff --git a/vnet/vnet/ethernet/arp.c b/vnet/vnet/ethernet/arp.c
index c0b06e0f7b1..d0ed1dcb3c1 100644
--- a/vnet/vnet/ethernet/arp.c
+++ b/vnet/vnet/ethernet/arp.c
@@ -16,6 +16,7 @@
*/
#include <vnet/ip/ip.h>
+#include <vnet/ip/ip6.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ethernet/arp_packet.h>
#include <vnet/l2/l2_input.h>
@@ -267,6 +268,23 @@ format_ethernet_arp_input_trace (u8 * s, va_list * va)
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;
+}
+
clib_error_t *
ethernet_arp_sw_interface_up_down (vnet_main_t * vnm,
u32 sw_if_index, u32 flags)
@@ -1825,8 +1843,8 @@ VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
/*
- * ARP Termination in a L2 Bridge Domain based on an
- * IP4 to MAC hash table mac_by_ip4 for each BD.
+ * 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
{
@@ -1863,6 +1881,7 @@ arp_term_l2bd (vlib_main_t * vm,
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;
@@ -1883,6 +1902,13 @@ arp_term_l2bd (vlib_main_t * vm,
ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2));
arp0 = (ethernet_arp_header_t *) l3h0;
+ if (PREDICT_FALSE ((ethertype0 != ETHERNET_TYPE_ARP) ||
+ (arp0->opcode !=
+ clib_host_to_net_u16
+ (ETHERNET_ARP_OPCODE_request))))
+ goto check_ip6_nd;
+
+ /* Must be ARP request packet here */
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
(p0->flags & VLIB_BUFFER_IS_TRACED)))
{
@@ -1891,12 +1917,6 @@ arp_term_l2bd (vlib_main_t * vm,
clib_memcpy (t0, l3h0, sizeof (ethernet_arp_input_trace_t));
}
- if (PREDICT_FALSE ((ethertype0 != ETHERNET_TYPE_ARP) ||
- (arp0->opcode !=
- clib_host_to_net_u16
- (ETHERNET_ARP_OPCODE_request))))
- goto next_l2_feature;
-
error0 = ETHERNET_ARP_ERROR_replies_sent;
error0 =
(arp0->l2_type !=
@@ -1912,8 +1932,8 @@ arp_term_l2bd (vlib_main_t * vm,
if (error0)
goto drop;
- // Trash ARP packets whose ARP-level source addresses do not
- // match their L2-frame-level source addresses */
+ /* Trash ARP packets whose ARP-level source addresses do not
+ match their L2-frame-level source addresses */
if (PREDICT_FALSE
(memcmp
(eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
@@ -1923,7 +1943,7 @@ arp_term_l2bd (vlib_main_t * vm,
goto drop;
}
- // Check if anyone want ARP request events for L2 BDs
+ /* Check if anyone want ARP request events for L2 BDs */
{
pending_resolution_t *mc;
ethernet_arp_main_t *am = &ethernet_arp_main;
@@ -1937,13 +1957,13 @@ arp_term_l2bd (vlib_main_t * vm,
int rv = 1;
mc = pool_elt_at_index (am->mac_changes, next_index);
fp = mc->data_callback;
- // Call the callback, return 1 to suppress dup events */
+ /* Call the callback, return 1 to suppress dup events */
if (fp)
rv = (*fp) (mc->data,
arp0->ip4_over_ethernet[0].ethernet,
sw_if_index0,
arp0->ip4_over_ethernet[0].ip4.as_u32);
- // Signal the resolver process
+ /* Signal the resolver process */
if (rv == 0)
vlib_process_signal_event (vm, mc->node_index,
mc->type_opaque, mc->data);
@@ -1952,7 +1972,7 @@ arp_term_l2bd (vlib_main_t * vm,
}
}
- // lookup BD mac_by_ip4 hash table for MAC entry
+ /* 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)
@@ -1964,10 +1984,10 @@ arp_term_l2bd (vlib_main_t * vm,
macp0 = (u8 *) hash_get (last_bd_config->mac_by_ip4, ip0);
if (PREDICT_FALSE (!macp0))
- goto next_l2_feature; // MAC not found
+ goto next_l2_feature; /* MAC not found */
- // MAC found, send ARP reply -
- // Convert ARP request packet to ARP reply
+ /* 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;
@@ -1976,8 +1996,9 @@ arp_term_l2bd (vlib_main_t * vm,
clib_memcpy (eth0->src_address, macp0, 6);
n_replies_sent += 1;
- // For BVI, need to use l2-fwd node to send ARP reply as
- // l2-output node cannot output packet to BVI properly
+ 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))
{
@@ -1986,19 +2007,37 @@ arp_term_l2bd (vlib_main_t * vm,
goto next_l2_feature;
}
- // Send ARP reply back out input interface through l2-output
+ /* 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;
- // Note that output to VXLAN tunnel will fail due to SHG which
- // is probably desireable since ARP termination is not intended
- // for ARP requests from other hosts. If output to VXLAN tunnel is
- // required, however, can just clear the SHG in packet as follows:
- // vnet_buffer(p0)->l2.shg = 0;
-
+ /* Note that output to VXLAN tunnel will fail due to SHG which
+ is probably desireable since ARP termination is not intended
+ for ARP requests from other hosts. If output to VXLAN tunnel is
+ required, however, can just clear the SHG in packet as follows:
+ vnet_buffer(p0)->l2.shg = 0; */
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_link_local_unicast
+ (&iph0->src_address)
+ &&
+ !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,
+ vnet_buffer (p0)->l2.shg))
+ goto output_response;
+ }
+
next_l2_feature:
{
u32 feature_bitmap0 =
@@ -2046,7 +2085,7 @@ VLIB_REGISTER_NODE (arp_term_l2bd_node, static) = {
[ARP_TERM_NEXT_DROP] = "error-drop",
},
.format_buffer = format_ethernet_arp_header,
- .format_trace = format_ethernet_arp_input_trace,
+ .format_trace = format_arp_term_input_trace,
};
/* *INDENT-ON* */
diff --git a/vnet/vnet/ip/ip6.h b/vnet/vnet/ip/ip6.h
index b43e2dac791..f5f3de84676 100644
--- a/vnet/vnet/ip/ip6.h
+++ b/vnet/vnet/ip/ip6.h
@@ -41,6 +41,8 @@
#define included_ip_ip6_h
#include <vlib/mc.h>
+#include <vlib/buffer.h>
+#include <vnet/ethernet/packet.h>
#include <vnet/ip/ip6_packet.h>
#include <vnet/ip/ip6_hop_by_hop_packet.h>
#include <vnet/ip/lookup.h>
@@ -533,6 +535,24 @@ void vnet_register_ip6_neighbor_resolution_event(vnet_main_t * vnm,
uword type_opaque,
uword data);
+int vnet_add_del_ip6_nd_change_event (vnet_main_t * vnm,
+ void * data_callback,
+ u32 pid,
+ void * address_arg,
+ uword node_index,
+ uword type_opaque,
+ uword data,
+ int is_add);
+
+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,
+ u8 shg);
+
int vnet_set_ip6_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
u32 table_index);
extern vlib_node_registration_t ip6_lookup_node;
diff --git a/vnet/vnet/ip/ip6_neighbor.c b/vnet/vnet/ip/ip6_neighbor.c
index 1dd09c11052..a35f58a3039 100644
--- a/vnet/vnet/ip/ip6_neighbor.c
+++ b/vnet/vnet/ip/ip6_neighbor.c
@@ -169,6 +169,9 @@ typedef struct {
uword node_index;
uword type_opaque;
uword data;
+ /* Used for nd event notification only */
+ void * data_callback;
+ u32 pid;
} pending_resolution_t;
@@ -180,6 +183,10 @@ typedef struct {
mhash_t pending_resolutions_by_address;
pending_resolution_t * pending_resolutions;
+ /* Mac address change notification */
+ mhash_t mac_changes_by_address;
+ pending_resolution_t * mac_changes;
+
u32 * neighbor_input_next_index_by_hw_if_index;
ip6_neighbor_t * neighbor_pool;
@@ -197,6 +204,7 @@ typedef struct {
} ip6_neighbor_main_t;
static ip6_neighbor_main_t ip6_neighbor_main;
+static ip6_address_t ip6a_zero; /* ip6 address 0 */
static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va)
{
@@ -341,7 +349,7 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
u32 next_index;
u32 adj_index;
ip_adjacency_t *existing_adj;
- pending_resolution_t * pr;
+ pending_resolution_t * pr, * mc;
#if DPDK > 0
if (os_get_cpu_number())
@@ -442,24 +450,51 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
/* Customer(s) waiting for this address to be resolved? */
p = mhash_get (&nm->pending_resolutions_by_address, a);
- if (p == 0)
- goto out;
-
- next_index = p[0];
+ if (p)
+ {
+ next_index = p[0];
- while (next_index != (u32)~0)
+ while (next_index != (u32)~0)
+ {
+ pr = pool_elt_at_index (nm->pending_resolutions, next_index);
+ vlib_process_signal_event (vm, pr->node_index,
+ pr->type_opaque,
+ pr->data);
+ next_index = pr->next_index;
+ pool_put (nm->pending_resolutions, pr);
+ }
+
+ mhash_unset (&nm->pending_resolutions_by_address, a, 0);
+ }
+
+ /* Customer(s) requesting ND event for this address? */
+ p = mhash_get (&nm->mac_changes_by_address, a);
+ if (p)
{
- pr = pool_elt_at_index (nm->pending_resolutions, next_index);
- vlib_process_signal_event (vm, pr->node_index,
- pr->type_opaque,
- pr->data);
- next_index = pr->next_index;
- pool_put (nm->pending_resolutions, pr);
+ next_index = p[0];
+
+ while (next_index != (u32)~0)
+ {
+ int (*fp)(u32, u8 *, u32, ip6_address_t *);
+ int rv = 1;
+ mc = pool_elt_at_index (nm->mac_changes, next_index);
+ fp = mc->data_callback;
+
+ /* Call the user's data callback, return 1 to suppress dup events */
+ if (fp)
+ rv = (*fp)(mc->data, link_layer_address, sw_if_index, &ip6a_zero);
+ /*
+ * 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;
+ }
}
- mhash_unset (&nm->pending_resolutions_by_address, a, 0);
-
-out:
vlib_worker_thread_barrier_release(vm);
return 0;
}
@@ -3327,6 +3362,10 @@ static clib_error_t * ip6_neighbor_init (vlib_main_t * vm)
/* value size */ sizeof (uword),
/* key size */ sizeof (ip6_address_t));
+ mhash_init (&nm->mac_changes_by_address,
+ /* value size */ sizeof (uword),
+ /* key size */ sizeof (ip6_address_t));
+
/* default, configurable */
nm->limit_neighbor_cache_size = 50000;
@@ -3374,3 +3413,185 @@ void vnet_register_ip6_neighbor_resolution_event (vnet_main_t * vnm,
pr - nm->pending_resolutions, 0 /* old value */);
}
+int vnet_add_del_ip6_nd_change_event (vnet_main_t * vnm,
+ void * data_callback,
+ u32 pid,
+ void * address_arg,
+ uword node_index,
+ uword type_opaque,
+ uword data,
+ int is_add)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ ip6_address_t * address = address_arg;
+ uword * p;
+ pending_resolution_t * mc;
+ void (*fp)(u32, u8 *) = data_callback;
+
+ if (is_add)
+ {
+ pool_get (nm->mac_changes, mc);
+
+ mc->next_index = ~0;
+ mc->node_index = node_index;
+ mc->type_opaque = type_opaque;
+ mc->data = data;
+ mc->data_callback = data_callback;
+ mc->pid = pid;
+
+ p = mhash_get (&nm->mac_changes_by_address, address);
+ if (p)
+ {
+ /* Insert new resolution at the head of the list */
+ mc->next_index = p[0];
+ mhash_unset (&nm->mac_changes_by_address, address, 0);
+ }
+
+ mhash_set (&nm->mac_changes_by_address, address,
+ mc - nm->mac_changes, 0);
+ return 0;
+ }
+ else
+ {
+ u32 index;
+ pending_resolution_t * mc_last = 0;
+
+ p = mhash_get (&nm->mac_changes_by_address, address);
+ if (p == 0)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ index = p[0];
+
+ while (index != (u32)~0)
+ {
+ mc = pool_elt_at_index (nm->mac_changes, index);
+ if (mc->node_index == node_index &&
+ mc->type_opaque == type_opaque &&
+ mc->pid == pid)
+ {
+ /* Clients may need to clean up pool entries, too */
+ if (fp)
+ (*fp)(mc->data, 0 /* no new mac addrs */);
+ if (index == p[0])
+ {
+ mhash_unset (&nm->mac_changes_by_address, address, 0);
+ if (mc->next_index != ~0)
+ mhash_set (&nm->mac_changes_by_address, address,
+ mc->next_index, 0);
+ pool_put (nm->mac_changes, mc);
+ return 0;
+ }
+ else
+ {
+ ASSERT(mc_last);
+ mc_last->next_index = mc->next_index;
+ pool_put (nm->mac_changes, mc);
+ return 0;
+ }
+ }
+ mc_last = mc;
+ index = mc->next_index;
+ }
+
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+ }
+}
+
+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,
+ u8 shg)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ icmp6_neighbor_solicitation_or_advertisement_header_t * ndh;
+ pending_resolution_t * mc;
+ uword *p;
+
+ 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 */
+ p = mhash_get (&nm->mac_changes_by_address, &ip6a_zero);
+ if (p && shg == 0)
+ { /* Only SHG 0 interface which is more likely local */
+ u32 next_index = p[0];
+ while (next_index != (u32)~0)
+ {
+ int (*fp)(u32, u8 *, u32, ip6_address_t *);
+ int rv = 1;
+ mc = pool_elt_at_index (nm->mac_changes, next_index);
+ fp = mc->data_callback;
+ /* Call the callback, return 1 to suppress dup events */
+ if (fp) rv = (*fp)(mc->data,
+ eth->src_address,
+ sw_if_index,
+ &ip->src_address);
+ /* Signal the resolver process */
+ if (rv == 0)
+ vlib_process_signal_event (vm, mc->node_index,
+ mc->type_opaque,
+ mc->data);
+ next_index = mc->next_index;
+ }
+ }
+
+ /* 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;
+
+}
+
+
diff --git a/vnet/vnet/l2/l2_bd.c b/vnet/vnet/l2/l2_bd.c
index a872453a98b..490a08f2d03 100644
--- a/vnet/vnet/l2/l2_bd.c
+++ b/vnet/vnet/l2/l2_bd.c
@@ -47,6 +47,8 @@ bd_validate (l2_bridge_domain_t * bd_config)
bd_config->bvi_sw_if_index = ~0;
bd_config->members = 0;
bd_config->mac_by_ip4 = 0;
+ bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t),
+ sizeof (uword));
}
}
@@ -512,11 +514,42 @@ bd_add_del_ip_mac (u32 bd_index,
ASSERT (sizeof (uword) == sizeof (u64)); /* make sure uword is 8 bytes */
- mac16[3] = 0; // Clear last 2 unsed bytes of the 8-byte MAC address
+ mac16[3] = 0; /* Clear last 2 unsed bytes of the 8-byte MAC address */
if (is_ip6)
{
- /* not yet implemented */
- return 1;
+ ip6_address_t *ip6_addr_key;
+ hash_pair_t *hp;
+ old_mac = (u64 *) hash_get_mem (bd_cfg->mac_by_ip6, ip_addr);
+ if (is_add)
+ {
+ if (old_mac == 0)
+ { /* new entry - allocate and craete ip6 address key */
+ ip6_addr_key = clib_mem_alloc (sizeof (ip6_address_t));
+ clib_memcpy (ip6_addr_key, ip_addr, sizeof (ip6_address_t));
+ }
+ else if (*old_mac == new_mac)
+ { /* same mac entry already exist for ip6 address */
+ return 0;
+ }
+ else
+ { /* updat mac for ip6 address */
+ hp = hash_get_pair (bd_cfg->mac_by_ip6, ip_addr);
+ ip6_addr_key = (ip6_address_t *) hp->key;
+ }
+ hash_set_mem (bd_cfg->mac_by_ip6, ip6_addr_key, new_mac);
+ }
+ else
+ {
+ if (old_mac && (*old_mac == new_mac))
+ {
+ hp = hash_get_pair (bd_cfg->mac_by_ip6, ip_addr);
+ ip6_addr_key = (ip6_address_t *) hp->key;
+ hash_unset_mem (bd_cfg->mac_by_ip6, ip_addr);
+ clib_mem_free (ip6_addr_key);
+ }
+ else
+ return 1;
+ }
}
else
{
@@ -524,26 +557,19 @@ bd_add_del_ip_mac (u32 bd_index,
old_mac = (u64 *) hash_get (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
if (is_add)
{
- /* mac entry already exist? */
if (old_mac && (*old_mac == new_mac))
- return 0;
+ return 0; /* mac entry already exist */
hash_set (bd_cfg->mac_by_ip4, ip4_addr.as_u32, new_mac);
}
else
{
- /* Mac entry match? */
if (old_mac && (*old_mac == new_mac))
- {
- /* clear entry */
- hash_unset (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
- }
+ hash_unset (bd_cfg->mac_by_ip4, ip4_addr.as_u32);
else
- {
- return 1;
- }
+ return 1;
}
- return 0;
}
+ return 0;
}
/**
@@ -610,8 +636,9 @@ bd_arp_entry (vlib_main_t * vm,
{
error = clib_error_return (0, "MAC %s for IP %U and MAC %U failed",
is_add ? "add" : "del",
- format_ip4_address, ip_addr,
- format_ethernet_address, mac_addr);
+ is_ip6 ?
+ format_ip4_address : format_ip6_address,
+ ip_addr, format_ethernet_address, mac_addr);
}
done:
@@ -780,17 +807,25 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
(bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM))
{
u32 ip4_addr;
+ ip6_address_t *ip6_addr;
u64 mac_addr;
vlib_cli_output (vm,
- "\n IP4 to MAC table for ARP Termination");
+ "\n IP4/IP6 to MAC table for ARP Termination");
/* *INDENT-OFF* */
hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4,
({
- vlib_cli_output (vm, "%=20U => %=20U",
+ vlib_cli_output (vm, "%=40U => %=20U",
format_ip4_address, &ip4_addr,
format_ethernet_address, &mac_addr);
}));
+
+ hash_foreach_mem (ip6_addr, mac_addr, bd_config->mac_by_ip6,
+ ({
+ vlib_cli_output (vm, "%=40U => %=20U",
+ format_ip6_address, ip6_addr,
+ format_ethernet_address, &mac_addr);
+ }));
/* *INDENT-ON* */
}
}
diff --git a/vnet/vnet/l2/l2_bd.h b/vnet/vnet/l2/l2_bd.h
index 82453eea153..2d7853ebead 100644
--- a/vnet/vnet/l2/l2_bd.h
+++ b/vnet/vnet/l2/l2_bd.h
@@ -70,7 +70,7 @@ typedef struct
/* Vector of members in the replication group */
l2_flood_member_t *members;
- /* hash ip4/ip6 -> mac for arp termination */
+ /* hash ip4/ip6 -> mac for arp/nd termination */
uword *mac_by_ip4;
uword *mac_by_ip6;
diff --git a/vnet/vnet/l2/l2_input.c b/vnet/vnet/l2/l2_input.c
index 9607031fcda..f337b78d3e6 100644
--- a/vnet/vnet/l2/l2_input.c
+++ b/vnet/vnet/l2/l2_input.c
@@ -197,10 +197,14 @@ classify_and_dispatch (vlib_main_t * vm,
{
u32 *dsthi = (u32 *) & h0->dst_address[0];
u32 *dstlo = (u32 *) & h0->dst_address[2];
+ protocol = ((ip6_header_t *) l3h0)->protocol;
/* Disable bridge forwarding (flooding will execute instead if not xconnect) */
feat_mask &= ~(L2INPUT_FEAT_FWD | L2INPUT_FEAT_UU_FLOOD);
- if (ethertype != ETHERNET_TYPE_ARP) /* Disable ARP-term for non-ARP packet */
+
+ /* Disable ARP-term for non-ARP and non-ICMP6 packet */
+ if (ethertype != ETHERNET_TYPE_ARP &&
+ (ethertype != ETHERNET_TYPE_IP6 || protocol != IP_PROTOCOL_ICMP6))
feat_mask &= ~(L2INPUT_FEAT_ARP_TERM);
/* dest mac is multicast or broadcast */