IP4 Router Alert option handling for IGMP 59/13259/4
authorNeale Ranns <nranns@cisco.com>
Thu, 28 Jun 2018 01:59:03 +0000 (18:59 -0700)
committerDamjan Marion <dmarion@me.com>
Fri, 29 Jun 2018 09:56:16 +0000 (09:56 +0000)
and a new ip4-options node, inserted between ip4-input and ip4-punt,
that checks for IP-router-alert option + IGMP combination and sends
the packet to the ip4-local. This is required because some IGMP
packets are sent to the group address and not the all-routers address.
All IGMP packets are sent with the router alert option.

Change-Id: I01f478d4d98ac9f806e0bcba0f6da6e4e7d26e2a
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/vnet.am
src/vnet/ip/ip4_input.c
src/vnet/ip/ip4_input.h
src/vnet/ip/ip4_options.c [new file with mode: 0644]
src/vnet/ip/ip4_packet.h
test/test_igmp.py

index 78eb481..0a9cb5f 100644 (file)
@@ -354,6 +354,7 @@ libvnet_la_SOURCES +=                               \
  vnet/ip/ip4_forward.c                         \
  vnet/ip/ip4_punt_drop.c                       \
  vnet/ip/ip4_input.c                           \
+ vnet/ip/ip4_options.c                         \
  vnet/ip/ip4_mtrie.c                           \
  vnet/ip/ip4_pg.c                              \
  vnet/ip/ip4_source_and_port_range_check.c     \
index b476f95..1ecd43b 100644 (file)
@@ -324,6 +324,7 @@ VLIB_REGISTER_NODE (ip4_input_node) = {
   .next_nodes = {
     [IP4_INPUT_NEXT_DROP] = "error-drop",
     [IP4_INPUT_NEXT_PUNT] = "error-punt",
+    [IP4_INPUT_NEXT_OPTIONS] = "ip4-options",
     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-mfib-forward-lookup",
     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
@@ -342,6 +343,7 @@ VLIB_REGISTER_NODE (ip4_input_no_checksum_node) = {
   .next_nodes = {
     [IP4_INPUT_NEXT_DROP] = "error-drop",
     [IP4_INPUT_NEXT_PUNT] = "error-punt",
+    [IP4_INPUT_NEXT_OPTIONS] = "ip4-options",
     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-mfib-forward-lookup",
     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
index 889b423..880896e 100644 (file)
@@ -49,6 +49,7 @@ typedef enum
 {
   IP4_INPUT_NEXT_DROP,
   IP4_INPUT_NEXT_PUNT,
+  IP4_INPUT_NEXT_OPTIONS,
   IP4_INPUT_NEXT_LOOKUP,
   IP4_INPUT_NEXT_LOOKUP_MULTICAST,
   IP4_INPUT_NEXT_ICMP_ERROR,
@@ -153,7 +154,7 @@ ip4_input_check_x4 (vlib_main_t * vm,
        }
       else
        next[0] = error0 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p[0]->error = error_node->errors[error0];
     }
   if (PREDICT_FALSE (error1 != IP4_ERROR_NONE))
@@ -167,7 +168,7 @@ ip4_input_check_x4 (vlib_main_t * vm,
        }
       else
        next[1] = error1 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p[1]->error = error_node->errors[error1];
     }
   if (PREDICT_FALSE (error2 != IP4_ERROR_NONE))
@@ -181,7 +182,7 @@ ip4_input_check_x4 (vlib_main_t * vm,
        }
       else
        next[2] = error2 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p[2]->error = error_node->errors[error2];
     }
   if (PREDICT_FALSE (error3 != IP4_ERROR_NONE))
@@ -195,7 +196,7 @@ ip4_input_check_x4 (vlib_main_t * vm,
        }
       else
        next[3] = error3 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p[3]->error = error_node->errors[error3];
     }
 }
@@ -256,7 +257,7 @@ ip4_input_check_x2 (vlib_main_t * vm,
        }
       else
        *next0 = error0 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p0->error = error_node->errors[error0];
     }
   if (PREDICT_FALSE (error1 != IP4_ERROR_NONE))
@@ -270,10 +271,9 @@ ip4_input_check_x2 (vlib_main_t * vm,
        }
       else
        *next1 = error1 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p1->error = error_node->errors[error1];
     }
-
 }
 
 always_inline void
@@ -290,11 +290,6 @@ ip4_input_check_x1 (vlib_main_t * vm,
 
   check_ver_opt_csum (ip0, &error0, verify_checksum);
 
-  /* Punt packets with options or wrong version. */
-  if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
-    error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
-      IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
-
   /* Drop fragmentation offset 1 packets. */
   error0 = ip4_get_fragment_offset (ip0) == 1 ?
     IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
@@ -322,10 +317,9 @@ ip4_input_check_x1 (vlib_main_t * vm,
        }
       else
        *next0 = error0 != IP4_ERROR_OPTIONS ?
-         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
+         IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_OPTIONS;
       p0->error = error_node->errors[error0];
     }
-
 }
 
 /*
diff --git a/src/vnet/ip/ip4_options.c b/src/vnet/ip/ip4_options.c
new file mode 100644 (file)
index 0000000..c008b9b
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+/**
+ * @brief Handle IPv4 header options in the data-path
+ */
+
+#include <vnet/ip/ip.h>
+
+typedef enum ip4_options_next_t_
+{
+  IP4_OPTIONS_NEXT_PUNT,
+  IP4_OPTIONS_NEXT_LOCAL,
+  IP4_OPTIONS_N_NEXT,
+} ip4_options_next_t;
+
+typedef struct ip4_options_trace_t_
+{
+  u8 option[4];
+} ip4_options_trace_t;
+
+VLIB_NODE_FN (ip4_options_node) (vlib_main_t * vm,
+                                vlib_node_runtime_t * node,
+                                vlib_frame_t * frame)
+{
+  uword n_left_from, n_left_to_next, next_index;
+  u32 *from, *to_next;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = 0;
+
+  while (n_left_from > 0)
+    {
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      /*
+       * IP options packets, when properly used, are very low rate,
+       * so this code is not dual-looped for extra performance.
+       */
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         ip4_options_next_t next;
+         ip4_header_t *ip4;
+         vlib_buffer_t *b;
+         u8 *options;
+         u32 bi;
+
+         bi = from[0];
+         from += 1;
+         n_left_from -= 1;
+         to_next[0] = bi;
+         to_next += 1;
+         n_left_to_next -= 1;
+
+         b = vlib_get_buffer (vm, bi);
+         ip4 = vlib_buffer_get_current (b);
+         next = IP4_OPTIONS_NEXT_PUNT;
+
+         options = (u8 *) (ip4 + 1);
+
+         /*
+          * mask out the copy flag to leave the option type
+          */
+         switch (options[0] & 0x7f)
+           {
+           case IP4_ROUTER_ALERT_OPTION:
+             /*
+              * if it's an IGMP packet, pass up the local stack
+              */
+             if (IP_PROTOCOL_IGMP == ip4->protocol)
+               {
+                 next = IP4_OPTIONS_NEXT_LOCAL;
+               }
+             break;
+           default:
+             break;
+           }
+
+         if (b->flags & VLIB_BUFFER_IS_TRACED)
+           {
+             ip4_options_trace_t *t =
+               vlib_add_trace (vm, node, b, sizeof (*t));
+
+             clib_memcpy (t->option, options, 4);
+           }
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi, next);
+
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  return frame->n_vectors;
+}
+
+u8 *
+format_ip4_options_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 *);
+  ip4_options_trace_t *t = va_arg (*args, ip4_options_trace_t *);
+  u32 indent = format_get_indent (s);
+
+  s = format (s, "%Uoption:[0x%x,0x%x,0x%x,0x%x]",
+             format_white_space, indent,
+             t->option[0], t->option[1], t->option[2], t->option[3]);
+  return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_options_node) = {
+  .name = "ip4-options",
+  .vector_size = sizeof (u32),
+
+  .n_next_nodes = IP4_OPTIONS_N_NEXT,
+  .next_nodes = {
+    [IP4_OPTIONS_NEXT_PUNT] = "ip4-punt",
+    [IP4_OPTIONS_NEXT_LOCAL] = "ip4-local",
+  },
+  .format_buffer = format_ip4_header,
+  .format_trace = format_ip4_options_trace,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 2f0c75e..c41a802 100644 (file)
@@ -193,6 +193,8 @@ typedef union
 #define IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS \
   ((4 << 4) | (sizeof (ip4_header_t) / sizeof (u32)))
 
+#define IP4_ROUTER_ALERT_OPTION 20
+
 always_inline int
 ip4_get_fragment_offset (ip4_header_t * i)
 {
index e741e6b..22b6d2f 100644 (file)
@@ -51,7 +51,11 @@ class TestIgmp(VppTestCase):
 
         # hos sends join IGMP 'join'
         p_join = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
-                  IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
+                  IP(src=self.pg0.remote_ip4, dst='224.0.0.22',
+                     tos=0xc0, ttl=1,
+                     options=IPOption(copy_flag=1, optclass=0,
+                                      option="router_alert",
+                                      length=2, value=0)) /
                   IGMPv3() /
                   IGMPv3mr(numgrp=1) /
                   IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))