Use thread local storage for thread index
[vpp.git] / src / vnet / ip / ip6_forward.c
index 866a44e..c2fc4f8 100644 (file)
 #include <vnet/ethernet/ethernet.h>    /* for ethernet_header_t */
 #include <vnet/srp/srp.h>      /* for srp_hw_interface_class */
 #include <vppinfra/cache.h>
-#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_urpf_list.h>    /* for FIB uRPF check */
 #include <vnet/fib/ip6_fib.h>
+#include <vnet/mfib/ip6_mfib.h>
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/classify_dpo.h>
 
 #include <vppinfra/bihash_template.c>
 
+/* Flag used by IOAM code. Classifier sets it pop-hop-by-hop checks it */
+#define OI_DECAP   0x80000000
+
 /**
  * @file
  * @brief IPv6 Forwarding.
@@ -70,7 +74,7 @@ ip6_lookup_inline (vlib_main_t * vm,
   vlib_combined_counter_main_t *cm = &load_balance_main.lbm_to_counters;
   u32 n_left_from, n_left_to_next, *from, *to_next;
   ip_lookup_next_t next;
-  u32 cpu_index = os_get_cpu_number ();
+  u32 thread_index = vlib_get_thread_index ();
 
   from = vlib_frame_vector_args (frame);
   n_left_from = frame->n_vectors;
@@ -181,9 +185,9 @@ ip6_lookup_inline (vlib_main_t * vm,
          vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
 
          vlib_increment_combined_counter
-           (cm, cpu_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
+           (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
          vlib_increment_combined_counter
-           (cm, cpu_index, lbi1, 1, vlib_buffer_length_in_chain (vm, p1));
+           (cm, thread_index, lbi1, 1, vlib_buffer_length_in_chain (vm, p1));
 
          from += 2;
          to_next += 2;
@@ -287,7 +291,7 @@ ip6_lookup_inline (vlib_main_t * vm,
          vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
 
          vlib_increment_combined_counter
-           (cm, cpu_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
+           (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
 
          from += 1;
          to_next += 1;
@@ -414,16 +418,14 @@ ip6_sw_interface_enable_disable (u32 sw_if_index, u32 is_enable)
   vnet_feature_enable_disable ("ip6-unicast", "ip6-lookup", sw_if_index,
                               is_enable, 0, 0);
 
-  vnet_feature_enable_disable ("ip6-multicast", "ip6-lookup", sw_if_index,
-                              is_enable, 0, 0);
+  vnet_feature_enable_disable ("ip6-multicast", "ip6-mfib-forward-lookup",
+                              sw_if_index, is_enable, 0, 0);
 
 }
 
 /* get first interface address */
 ip6_address_t *
-ip6_interface_first_address (ip6_main_t * im,
-                            u32 sw_if_index,
-                            ip_interface_address_t ** result_ia)
+ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index)
 {
   ip_lookup_main_t *lm = &im->lookup_main;
   ip_interface_address_t *ia = 0;
@@ -438,8 +440,6 @@ ip6_interface_first_address (ip6_main_t * im,
     break;
   }));
   /* *INDENT-ON* */
-  if (result_ia)
-    *result_ia = result ? ia : 0;
   return result;
 }
 
@@ -457,6 +457,8 @@ ip6_add_del_interface_address (vlib_main_t * vm,
   ip6_address_fib_t ip6_af, *addr_fib = 0;
 
   vec_validate (im->fib_index_by_sw_if_index, sw_if_index);
+  vec_validate (im->mfib_index_by_sw_if_index, sw_if_index);
+
   ip6_addr_fib_init (&ip6_af, address,
                     vec_elt (im->fib_index_by_sw_if_index, sw_if_index));
   vec_add1 (addr_fib, ip6_af);
@@ -583,6 +585,13 @@ VNET_FEATURE_INIT (ip6_vpath, static) =
 {
   .arc_name = "ip6-unicast",
   .node_name = "vpath-input-ip6",
+  .runs_before = VNET_FEATURES ("ip6-vxlan-bypass"),
+};
+
+VNET_FEATURE_INIT (ip6_vxlan_bypass, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "ip6-vxlan-bypass",
   .runs_before = VNET_FEATURES ("ip6-lookup"),
 };
 
@@ -611,12 +620,12 @@ VNET_FEATURE_ARC_INIT (ip6_multicast, static) =
 VNET_FEATURE_INIT (ip6_vpath_mc, static) = {
   .arc_name = "ip6-multicast",
   .node_name = "vpath-input-ip6",
-  .runs_before = VNET_FEATURES ("ip6-lookup"),
+  .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
 };
 
 VNET_FEATURE_INIT (ip6_mc_lookup, static) = {
   .arc_name = "ip6-multicast",
-  .node_name = "ip6-lookup",
+  .node_name = "ip6-mfib-forward-lookup",
   .runs_before = VNET_FEATURES ("ip6-drop"),
 };
 
@@ -694,7 +703,7 @@ ip6_load_balance (vlib_main_t * vm,
   vlib_combined_counter_main_t *cm = &load_balance_main.lbm_via_counters;
   u32 n_left_from, n_left_to_next, *from, *to_next;
   ip_lookup_next_t next;
-  u32 cpu_index = os_get_cpu_number ();
+  u32 thread_index = vlib_get_thread_index ();
   ip6_main_t *im = &ip6_main;
 
   from = vlib_frame_vector_args (frame);
@@ -757,8 +766,7 @@ ip6_load_balance (vlib_main_t * vm,
           * We don't want to use the same hash value at each level in the recursion
           * graph as that would lead to polarisation
           */
-         hc0 = vnet_buffer (p0)->ip.flow_hash = 0;
-         hc1 = vnet_buffer (p1)->ip.flow_hash = 0;
+         hc0 = hc1 = 0;
 
          if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
            {
@@ -770,7 +778,7 @@ ip6_load_balance (vlib_main_t * vm,
              else
                {
                  hc0 = vnet_buffer (p0)->ip.flow_hash =
-                   ip6_compute_flow_hash (ip0, hc0);
+                   ip6_compute_flow_hash (ip0, lb0->lb_hash_config);
                }
            }
          if (PREDICT_FALSE (lb1->lb_n_buckets > 1))
@@ -783,7 +791,7 @@ ip6_load_balance (vlib_main_t * vm,
              else
                {
                  hc1 = vnet_buffer (p1)->ip.flow_hash =
-                   ip6_compute_flow_hash (ip1, hc1);
+                   ip6_compute_flow_hash (ip1, lb1->lb_hash_config);
                }
            }
 
@@ -816,9 +824,9 @@ ip6_load_balance (vlib_main_t * vm,
          vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
 
          vlib_increment_combined_counter
-           (cm, cpu_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
+           (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
          vlib_increment_combined_counter
-           (cm, cpu_index, lbi1, 1, vlib_buffer_length_in_chain (vm, p1));
+           (cm, thread_index, lbi1, 1, vlib_buffer_length_in_chain (vm, p1));
 
          vlib_validate_buffer_enqueue_x2 (vm, node, next,
                                           to_next, n_left_to_next,
@@ -848,7 +856,7 @@ ip6_load_balance (vlib_main_t * vm,
 
          lb0 = load_balance_get (lbi0);
 
-         hc0 = vnet_buffer (p0)->ip.flow_hash = 0;
+         hc0 = 0;
          if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
            {
              if (PREDICT_TRUE (vnet_buffer (p0)->ip.flow_hash))
@@ -859,7 +867,7 @@ ip6_load_balance (vlib_main_t * vm,
              else
                {
                  hc0 = vnet_buffer (p0)->ip.flow_hash =
-                   ip6_compute_flow_hash (ip0, hc0);
+                   ip6_compute_flow_hash (ip0, lb0->lb_hash_config);
                }
            }
          dpo0 =
@@ -878,7 +886,7 @@ ip6_load_balance (vlib_main_t * vm,
            }
 
          vlib_increment_combined_counter
-           (cm, cpu_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
+           (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
 
          vlib_validate_buffer_enqueue_x1 (vm, node, next,
                                           to_next, n_left_to_next,
@@ -916,7 +924,7 @@ typedef struct
 }
 ip6_forward_next_trace_t;
 
-static u8 *
+u8 *
 format_ip6_forward_next_trace (u8 * s, va_list * args)
 {
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
@@ -953,7 +961,6 @@ format_ip6_rewrite_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   ip6_forward_next_trace_t *t = va_arg (*args, ip6_forward_next_trace_t *);
-  vnet_main_t *vnm = vnet_get_main ();
   uword indent = format_get_indent (s);
 
   s = format (s, "tx_sw_if_index %d adj-idx %d : %U flow hash: 0x%08x",
@@ -962,7 +969,7 @@ format_ip6_rewrite_trace (u8 * s, va_list * args)
   s = format (s, "\n%U%U",
              format_white_space, indent,
              format_ip_adjacency_packet_data,
-             vnm, t->adj_index, t->packet_data, sizeof (t->packet_data));
+             t->adj_index, t->packet_data, sizeof (t->packet_data));
   return s;
 }
 
@@ -1122,22 +1129,6 @@ VLIB_REGISTER_NODE (ip6_punt_node, static) =
 
 VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_node, ip6_punt);
 
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (ip6_multicast_node, static) =
-{
-  .function = ip6_drop,
-  .name = "ip6-multicast",
-  .vector_size = sizeof (u32),
-  .format_trace = format_ip6_forward_next_trace,
-  .n_next_nodes = 1,
-  .next_nodes =
-  {
-    [0] = "error-drop",
-  },
-};
-
-/* *INDENT-ON* */
-
 /* Compute TCP/UDP/ICMP6 checksum in software. */
 u16
 ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
@@ -1169,7 +1160,8 @@ ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
                                                uword));
     }
 
-  /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets) */
+  /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets)
+   * or UDP-Ping packets */
   if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
     {
       u32 skip_bytes;
@@ -1177,7 +1169,8 @@ ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
        (ip6_hop_by_hop_ext_t *) data_this_buffer;
 
       /* validate really icmp6 next */
-      ASSERT (ext_hdr->next_hdr == IP_PROTOCOL_ICMP6);
+      ASSERT ((ext_hdr->next_hdr == IP_PROTOCOL_ICMP6)
+             || (ext_hdr->next_hdr == IP_PROTOCOL_UDP));
 
       skip_bytes = 8 * (1 + ext_hdr->n_data_u64s);
       data_this_buffer = (void *) ((u8 *) data_this_buffer + skip_bytes);
@@ -1243,78 +1236,23 @@ ip6_tcp_udp_icmp_validate_checksum (vlib_main_t * vm, vlib_buffer_t * p0)
   return p0->flags;
 }
 
-/* ip6_locate_header
- *
- * This function is to search for the header specified by the find_hdr number.
- *   1. If the find_hdr < 0 then it finds and returns the protocol number and
- *   offset stored in *offset of the transport or ESP header in the chain if
- *   found.
- *   2. If a header with find_hdr > 0 protocol number is found then the
- *      offset is stored in *offset and protocol number of the header is
- *      returned.
- *   3. If find_hdr header is not found or packet is malformed or
- *      it is a non-first fragment -1 is returned.
+/**
+ * @brief returns number of links on which src is reachable.
  */
 always_inline int
-ip6_locate_header (vlib_buffer_t * p0,
-                  ip6_header_t * ip0, int find_hdr, u32 * offset)
-{
-  u8 next_proto = ip0->protocol;
-  u8 *next_header;
-  u8 done = 0;
-  u32 cur_offset;
-  u8 *temp_nxthdr = 0;
-  u32 exthdr_len = 0;
-
-  next_header = ip6_next_header (ip0);
-  cur_offset = sizeof (ip6_header_t);
-  while (1)
-    {
-      done = (next_proto == find_hdr);
-      if (PREDICT_FALSE
-         (next_header >=
-          (u8 *) vlib_buffer_get_current (p0) + p0->current_length))
-       {
-         //A malicious packet could set an extension header with a too big size
-         return (-1);
-       }
-      if (done)
-       break;
-      if ((!ip6_ext_hdr (next_proto)) || next_proto == IP_PROTOCOL_IP6_NONXT)
-       {
-         if (find_hdr < 0)
-           break;
-         return -1;
-       }
-      if (next_proto == IP_PROTOCOL_IPV6_FRAGMENTATION)
-       {
-         ip6_frag_hdr_t *frag_hdr = (ip6_frag_hdr_t *) next_header;
-         u16 frag_off = ip6_frag_hdr_offset (frag_hdr);
-         /* Non first fragment return -1 */
-         if (frag_off)
-           return (-1);
-         exthdr_len = sizeof (ip6_frag_hdr_t);
-         temp_nxthdr = next_header + exthdr_len;
-       }
-      else if (next_proto == IP_PROTOCOL_IPSEC_AH)
-       {
-         exthdr_len =
-           ip6_ext_authhdr_len (((ip6_ext_header_t *) next_header));
-         temp_nxthdr = next_header + exthdr_len;
-       }
-      else
-       {
-         exthdr_len =
-           ip6_ext_header_len (((ip6_ext_header_t *) next_header));
-         temp_nxthdr = next_header + exthdr_len;
-       }
-      next_proto = ((ip6_ext_header_t *) next_header)->next_hdr;
-      next_header = temp_nxthdr;
-      cur_offset += exthdr_len;
-    }
+ip6_urpf_loose_check (ip6_main_t * im, vlib_buffer_t * b, ip6_header_t * i)
+{
+  const load_balance_t *lb0;
+  index_t lbi;
+
+  lbi = ip6_fib_table_fwding_lookup_with_if_index (im,
+                                                  vnet_buffer
+                                                  (b)->sw_if_index[VLIB_RX],
+                                                  &i->src_address);
 
-  *offset = cur_offset;
-  return (next_proto);
+  lb0 = load_balance_get (lbi);
+
+  return (fib_urpf_check_size (lb0->lb_urpf));
 }
 
 static uword
@@ -1363,6 +1301,9 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          ip0 = vlib_buffer_get_current (p0);
          ip1 = vlib_buffer_get_current (p1);
 
+         vnet_buffer (p0)->ip.start_of_ip_header = p0->current_data;
+         vnet_buffer (p1)->ip.start_of_ip_header = p1->current_data;
+
          type0 = lm->builtin_protocol_by_ip_protocol[ip0->protocol];
          type1 = lm->builtin_protocol_by_ip_protocol[ip1->protocol];
 
@@ -1377,23 +1318,6 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          len_diff0 = 0;
          len_diff1 = 0;
 
-         /* Skip HBH local processing */
-         if (PREDICT_FALSE
-             (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
-           {
-             ip6_hop_by_hop_ext_t *ext_hdr =
-               (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
-             next0 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
-             type0 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
-           }
-         if (PREDICT_FALSE
-             (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
-           {
-             ip6_hop_by_hop_ext_t *ext_hdr =
-               (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
-             next1 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
-             type1 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
-           }
          if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p0, ip0,
                                                                  IP_PROTOCOL_UDP,
                                                                  &udp_offset0)))
@@ -1464,16 +1388,14 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
              type0 != IP_BUILTIN_PROTOCOL_ICMP &&
              !ip6_address_is_link_local_unicast (&ip0->src_address))
            {
-             u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
-             error0 = (ADJ_INDEX_INVALID == src_adj_index0
+             error0 = (!ip6_urpf_loose_check (im, p0, ip0)
                        ? IP6_ERROR_SRC_LOOKUP_MISS : error0);
            }
          if (error1 == IP6_ERROR_UNKNOWN_PROTOCOL &&
              type1 != IP_BUILTIN_PROTOCOL_ICMP &&
              !ip6_address_is_link_local_unicast (&ip1->src_address))
            {
-             u32 src_adj_index1 = ip6_src_lookup_for_packet (im, p1, ip1);
-             error1 = (ADJ_INDEX_INVALID == src_adj_index1
+             error1 = (!ip6_urpf_loose_check (im, p1, ip1)
                        ? IP6_ERROR_SRC_LOOKUP_MISS : error1);
            }
 
@@ -1510,6 +1432,8 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
 
          ip0 = vlib_buffer_get_current (p0);
 
+         vnet_buffer (p0)->ip.start_of_ip_header = p0->current_data;
+
          type0 = lm->builtin_protocol_by_ip_protocol[ip0->protocol];
          next0 = lm->local_next_by_ip_protocol[ip0->protocol];
 
@@ -1518,15 +1442,6 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          good_l4_checksum0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
          len_diff0 = 0;
 
-         /* Skip HBH local processing */
-         if (PREDICT_FALSE
-             (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
-           {
-             ip6_hop_by_hop_ext_t *ext_hdr =
-               (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
-             next0 = lm->local_next_by_ip_protocol[ext_hdr->next_hdr];
-             type0 = lm->builtin_protocol_by_ip_protocol[ext_hdr->next_hdr];
-           }
          if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p0, ip0,
                                                                  IP_PROTOCOL_UDP,
                                                                  &udp_offset0)))
@@ -1569,8 +1484,7 @@ ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
              type0 != IP_BUILTIN_PROTOCOL_ICMP &&
              !ip6_address_is_link_local_unicast (&ip0->src_address))
            {
-             u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
-             error0 = (ADJ_INDEX_INVALID == src_adj_index0
+             error0 = (!ip6_urpf_loose_check (im, p0, ip0)
                        ? IP6_ERROR_SRC_LOOKUP_MISS : error0);
            }
 
@@ -1972,7 +1886,8 @@ typedef enum
 always_inline uword
 ip6_rewrite_inline (vlib_main_t * vm,
                    vlib_node_runtime_t * node,
-                   vlib_frame_t * frame, int is_midchain)
+                   vlib_frame_t * frame,
+                   int do_counters, int is_midchain, int is_mcast)
 {
   ip_lookup_main_t *lm = &ip6_main.lookup_main;
   u32 *from = vlib_frame_vector_args (frame);
@@ -1982,7 +1897,7 @@ ip6_rewrite_inline (vlib_main_t * vm,
 
   n_left_from = frame->n_vectors;
   next_index = node->cached_next_index;
-  u32 cpu_index = os_get_cpu_number ();
+  u32 thread_index = vlib_get_thread_index ();
 
   while (n_left_from > 0)
     {
@@ -2028,9 +1943,6 @@ ip6_rewrite_inline (vlib_main_t * vm,
          adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
          adj_index1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
 
-         /* We should never rewrite a pkt using the MISS adjacency */
-         ASSERT (adj_index0 && adj_index1);
-
          ip0 = vlib_buffer_get_current (p0);
          ip1 = vlib_buffer_get_current (p1);
 
@@ -2103,14 +2015,17 @@ ip6_rewrite_inline (vlib_main_t * vm,
          vnet_buffer (p0)->ip.save_rewrite_length = rw_len0;
          vnet_buffer (p1)->ip.save_rewrite_length = rw_len1;
 
-         vlib_increment_combined_counter (&adjacency_counters,
-                                          cpu_index, adj_index0,
-                                          /* packet increment */ 0,
-                                          /* byte increment */ rw_len0);
-         vlib_increment_combined_counter (&adjacency_counters,
-                                          cpu_index, adj_index1,
-                                          /* packet increment */ 0,
-                                          /* byte increment */ rw_len1);
+         if (do_counters)
+           {
+             vlib_increment_combined_counter
+               (&adjacency_counters,
+                thread_index, adj_index0, 1,
+                vlib_buffer_length_in_chain (vm, p0) + rw_len0);
+             vlib_increment_combined_counter
+               (&adjacency_counters,
+                thread_index, adj_index1, 1,
+                vlib_buffer_length_in_chain (vm, p1) + rw_len1);
+           }
 
          /* Check MTU of outgoing interface. */
          error0 =
@@ -2135,8 +2050,10 @@ ip6_rewrite_inline (vlib_main_t * vm,
              vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
              next0 = adj0[0].rewrite_header.next_index;
 
-             vnet_feature_arc_start (lm->output_feature_arc_index,
-                                     tx_sw_if_index0, &next0, p0);
+             if (PREDICT_FALSE
+                 (adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
+               vnet_feature_arc_start (lm->output_feature_arc_index,
+                                       tx_sw_if_index0, &next0, p0);
            }
          if (PREDICT_TRUE (error1 == IP6_ERROR_NONE))
            {
@@ -2147,8 +2064,10 @@ ip6_rewrite_inline (vlib_main_t * vm,
              vnet_buffer (p1)->sw_if_index[VLIB_TX] = tx_sw_if_index1;
              next1 = adj1[0].rewrite_header.next_index;
 
-             vnet_feature_arc_start (lm->output_feature_arc_index,
-                                     tx_sw_if_index1, &next1, p1);
+             if (PREDICT_FALSE
+                 (adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
+               vnet_feature_arc_start (lm->output_feature_arc_index,
+                                       tx_sw_if_index1, &next1, p1);
            }
 
          /* Guess we are only writing on simple Ethernet header. */
@@ -2160,6 +2079,14 @@ ip6_rewrite_inline (vlib_main_t * vm,
              adj0->sub_type.midchain.fixup_func (vm, adj0, p0);
              adj1->sub_type.midchain.fixup_func (vm, adj1, p1);
            }
+         if (is_mcast)
+           {
+             /*
+              * copy bytes from the IP address into the MAC rewrite
+              */
+             vnet_fixup_one_header (adj0[0], &ip0->dst_address, ip0);
+             vnet_fixup_one_header (adj1[0], &ip1->dst_address, ip1);
+           }
 
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
                                           to_next, n_left_to_next,
@@ -2181,9 +2108,6 @@ ip6_rewrite_inline (vlib_main_t * vm,
 
          adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
 
-         /* We should never rewrite a pkt using the MISS adjacency */
-         ASSERT (adj_index0);
-
          adj0 = ip_get_adjacency (lm, adj_index0);
 
          ip0 = vlib_buffer_get_current (p0);
@@ -2228,10 +2152,13 @@ ip6_rewrite_inline (vlib_main_t * vm,
          rw_len0 = adj0[0].rewrite_header.data_bytes;
          vnet_buffer (p0)->ip.save_rewrite_length = rw_len0;
 
-         vlib_increment_combined_counter (&adjacency_counters,
-                                          cpu_index, adj_index0,
-                                          /* packet increment */ 0,
-                                          /* byte increment */ rw_len0);
+         if (do_counters)
+           {
+             vlib_increment_combined_counter
+               (&adjacency_counters,
+                thread_index, adj_index0, 1,
+                vlib_buffer_length_in_chain (vm, p0) + rw_len0);
+           }
 
          /* Check MTU of outgoing interface. */
          error0 =
@@ -2252,14 +2179,20 @@ ip6_rewrite_inline (vlib_main_t * vm,
              vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
              next0 = adj0[0].rewrite_header.next_index;
 
-             vnet_feature_arc_start (lm->output_feature_arc_index,
-                                     tx_sw_if_index0, &next0, p0);
+             if (PREDICT_FALSE
+                 (adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
+               vnet_feature_arc_start (lm->output_feature_arc_index,
+                                       tx_sw_if_index0, &next0, p0);
            }
 
          if (is_midchain)
            {
              adj0->sub_type.midchain.fixup_func (vm, adj0, p0);
            }
+         if (is_mcast)
+           {
+             vnet_fixup_one_header (adj0[0], &ip0->dst_address, ip0);
+           }
 
          p0->error = error_node->errors[error0];
 
@@ -2287,16 +2220,30 @@ static uword
 ip6_rewrite (vlib_main_t * vm,
             vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return ip6_rewrite_inline (vm, node, frame,
-                            /* midchain */ 0);
+  if (adj_are_counters_enabled ())
+    return ip6_rewrite_inline (vm, node, frame, 1, 0, 0);
+  else
+    return ip6_rewrite_inline (vm, node, frame, 0, 0, 0);
+}
+
+static uword
+ip6_rewrite_mcast (vlib_main_t * vm,
+                  vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  if (adj_are_counters_enabled ())
+    return ip6_rewrite_inline (vm, node, frame, 1, 0, 1);
+  else
+    return ip6_rewrite_inline (vm, node, frame, 0, 0, 1);
 }
 
 static uword
 ip6_midchain (vlib_main_t * vm,
              vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return ip6_rewrite_inline (vm, node, frame,
-                            /* midchain */ 1);
+  if (adj_are_counters_enabled ())
+    return ip6_rewrite_inline (vm, node, frame, 1, 1, 0);
+  else
+    return ip6_rewrite_inline (vm, node, frame, 0, 1, 0);
 }
 
 /* *INDENT-OFF* */
@@ -2330,10 +2277,22 @@ VLIB_REGISTER_NODE (ip6_rewrite_node) =
 
 VLIB_NODE_FUNCTION_MULTIARCH (ip6_rewrite_node, ip6_rewrite);
 
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip6_rewrite_mcast_node) =
+{
+  .function = ip6_rewrite_mcast,
+  .name = "ip6-rewrite-mcast",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ip6_rewrite_trace,
+  .sibling_of = "ip6-rewrite",
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_rewrite_mcast_node, ip6_rewrite_mcast);
+
 /*
  * Hop-by-Hop handling
  */
-
 ip6_hop_by_hop_main_t ip6_hop_by_hop_main;
 
 #define foreach_ip6_hop_by_hop_error \
@@ -2341,13 +2300,15 @@ _(PROCESSED, "pkts with ip6 hop-by-hop options") \
 _(FORMAT, "incorrectly formatted hop-by-hop options") \
 _(UNKNOWN_OPTION, "unknown ip6 hop-by-hop options")
 
+/* *INDENT-OFF* */
 typedef enum
 {
 #define _(sym,str) IP6_HOP_BY_HOP_ERROR_##sym,
   foreach_ip6_hop_by_hop_error
 #undef _
-    IP6_HOP_BY_HOP_N_ERROR,
+  IP6_HOP_BY_HOP_N_ERROR,
 } ip6_hop_by_hop_error_t;
+/* *INDENT-ON* */
 
 /*
  * Primary h-b-h handler trace support
@@ -2368,6 +2329,50 @@ static char *ip6_hop_by_hop_error_strings[] = {
 #undef _
 };
 
+u8 *
+format_ip6_hop_by_hop_ext_hdr (u8 * s, va_list * args)
+{
+  ip6_hop_by_hop_header_t *hbh0 = va_arg (*args, ip6_hop_by_hop_header_t *);
+  int total_len = va_arg (*args, int);
+  ip6_hop_by_hop_option_t *opt0, *limit0;
+  ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
+  u8 type0;
+
+  s = format (s, "IP6_HOP_BY_HOP: next protocol %d len %d total %d",
+             hbh0->protocol, (hbh0->length + 1) << 3, total_len);
+
+  opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
+  limit0 = (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 + total_len);
+
+  while (opt0 < limit0)
+    {
+      type0 = opt0->type;
+      switch (type0)
+       {
+       case 0:         /* Pad, just stop */
+         opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0 + 1);
+         break;
+
+       default:
+         if (hm->trace[type0])
+           {
+             s = (*hm->trace[type0]) (s, opt0);
+           }
+         else
+           {
+             s =
+               format (s, "\n    unrecognized option %d length %d", type0,
+                       opt0->length);
+           }
+         opt0 =
+           (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
+                                        sizeof (ip6_hop_by_hop_option_t));
+         break;
+       }
+    }
+  return s;
+}
+
 static u8 *
 format_ip6_hop_by_hop_trace (u8 * s, va_list * args)
 {
@@ -2873,6 +2878,7 @@ ip6_lookup_init (vlib_main_t * vm)
 
   /* Create FIB with index 0 and table id of 0. */
   fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0);
+  mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0);
 
   {
     pg_node_t *pn;
@@ -2923,6 +2929,7 @@ add_del_ip6_interface_table (vlib_main_t * vm,
                             vlib_cli_command_t * cmd)
 {
   vnet_main_t *vnm = vnet_get_main ();
+  ip_interface_address_t *ia;
   clib_error_t *error = 0;
   u32 sw_if_index, table_id;
 
@@ -2944,12 +2951,40 @@ add_del_ip6_interface_table (vlib_main_t * vm,
       goto done;
     }
 
+  /*
+   * If the interface already has in IP address, then a change int
+   * VRF is not allowed. The IP address applied must first be removed.
+   * We do not do that automatically here, since VPP has no knowledge
+   * of whether thoses subnets are valid in the destination VRF.
+   */
+  /* *INDENT-OFF* */
+  foreach_ip_interface_address (&ip6_main.lookup_main,
+                                ia, sw_if_index,
+                                1 /* honor unnumbered */,
+  ({
+      ip4_address_t * a;
+
+      a = ip_interface_address_get_address (&ip6_main.lookup_main, ia);
+      error = clib_error_return (0, "interface %U has address %U",
+                                 format_vnet_sw_if_index_name, vnm,
+                                 sw_if_index,
+                                 format_ip6_address, a);
+      goto done;
+  }));
+  /* *INDENT-ON* */
+
   {
     u32 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
                                                       table_id);
 
     vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index);
     ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index;
+
+    fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
+                                                   table_id);
+
+    vec_validate (ip6_main.mfib_index_by_sw_if_index, sw_if_index);
+    ip6_main.mfib_index_by_sw_if_index[sw_if_index] = fib_index;
   }
 
 
@@ -2965,13 +3000,12 @@ done:
  * an IP Address is assigned to an interface in the table (which adds a route
  * automatically).
  *
- * @note IP addresses added after setting the interface IP table end up in
- * the indicated FIB table. If the IP address is added prior to adding the
- * interface to the FIB table, it will NOT be part of the FIB table. Predictable
- * but potentially counter-intuitive results occur if you provision interface
- * addresses in multiple FIBs. Upon RX, packets will be processed in the last
- * IP table ID provisioned. It might be marginally useful to evade source RPF
- * drops to put an interface address into multiple FIBs.
+ * @note IP addresses added after setting the interface IP table are added to
+ * the indicated FIB table. If an IP address is added prior to changing the
+ * table then this is an error. The control plane must remove these addresses
+ * first and then change the table. VPP will not automatically move the
+ * addresses from the old to the new table as it does not know the validity
+ * of such a change.
  *
  * @cliexpar
  * Example of how to add an interface to an IPv6 FIB table (where 2 is the table-id):
@@ -3259,7 +3293,7 @@ vnet_set_ip6_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
   vec_validate (lm->classify_table_index_by_sw_if_index, sw_if_index);
   lm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
 
-  if_addr = ip6_interface_first_address (ipm, sw_if_index, NULL);
+  if_addr = ip6_interface_first_address (ipm, sw_if_index);
 
   if (NULL != if_addr)
     {