Move CLI examples from wiki to code - VPP-165
[vpp.git] / vnet / vnet / ethernet / arp.c
index c0b06e0..262ccd0 100644 (file)
@@ -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)
@@ -1767,7 +1785,29 @@ ip_arp_add_del_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+
 /* *INDENT-OFF* */
+/*?
+ * Add or delete ip4 ARP cache entries
+ *
+ * @cliexpar
+ * @cliexstart{set ip arp}
+ * Add or delete ip4 ARP cache entries as follows:
+ *  vpp# set ip arp GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ *  vpp# set ip arp delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ * Add or delete ip4 ARP cache entries to a specific fib table:
+ *  vpp# set ip arp fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ *  vpp# set ip arp fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ * Add or delete ip4 static ARP cache entries as follows:
+ *  vpp# set ip arp static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ *  vpp# set ip arp static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ * For testing / debugging purposes, the 'set ip arps command can add or delete multiple entries. Supply the 'count N' parameter:
+ *  vpp# set ip arp count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe
+ * @NOTE: 'set ip arp' options
+ * (e.g. delete, static, 'fib-id <id>', 'count <number>', 'interface ip4_addr mac_addr')
+ * can be added in any order and combination.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
   .path = "set ip arp",
   .short_help =
@@ -1814,7 +1854,23 @@ set_int_proxy_arp_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+
 /* *INDENT-OFF* */
+/*?
+ * Enable proxy-arp on an interface.
+ *
+ * @cliexpar
+ * @cliexstart{set interface proxy-arp}
+ * To enable proxy arp on a range of addresses, use:
+ *  vpp# set ip arp proxy 6.0.0.1 - 6.0.0.11
+ * You must specifically enable proxy arp on individual interfaces:
+ *  vpp# set interface proxy-arp <intfc> [enable|disable]
+ * The vpp stack will answer ARP requests for the indicated address range.
+ * Use with caution. Proxy ARP as a technology is infamous for blackholing traffic.
+ * Multiple proxy-arp ranges may be provisioned.
+ * The underlying implementation has not been performance-tuned; avoid creating an unnecessarily large set of ranges.
+ * @cliexend
+ ?*/
 VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
   .path = "set interface proxy-arp",
   .short_help =
@@ -1825,8 +1881,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 +1919,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 +1940,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 +1955,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 +1970,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 +1981,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 +1995,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 +2010,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 +2022,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 +2034,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 +2045,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 +2123,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* */