IGMP improvements

- Enable/Disable an interface for IGMP
- improve logging
- refactor common code
- no orphaned timers
- IGMP state changes in main thread only
- Large groups split over multiple state-change reports
- SSM range configuration API.
- more tests

Change-Id: If5674f1044e7e97274a711f47807c9ba689d7b9a
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/ip/igmp_packet.h b/src/vnet/ip/igmp_packet.h
index a8e9db6..cd4a40d 100644
--- a/src/vnet/ip/igmp_packet.h
+++ b/src/vnet/ip/igmp_packet.h
@@ -63,32 +63,46 @@
 #define _(n,f) IGMP_TYPE_##f = n,
   foreach_igmp_type
 #undef _
-} igmp_type_t;
+} __attribute__ ((packed)) igmp_type_t;
 
 typedef struct
 {
-  igmp_type_t type:8;
+  igmp_type_t type;
 
   u8 code;
 
   u16 checksum;
 } igmp_header_t;
 
-typedef struct
+/**
+ * Calculate the maximum response time allowed from the header.
+ *  - RFC 3367 Section 4.1.1
+ */
+always_inline f64
+igmp_header_get_max_resp_time (const igmp_header_t * header)
 {
-  /* membership_query, version <= 2 reports. */
-  igmp_header_t header;
+  f64 qqi;
 
-  /* Multicast destination address. */
-  ip4_address_t dst;
-} igmp_message_t;
+  if (header->code < 128)
+    qqi = header->code;
+  else
+    {
+      u8 mant = header->code << 4;
+      u8 exp = (header->code & 0x7) << 1;
+
+      qqi = ((mant | 0x10) << (exp + 3));
+    }
+
+  /* Querier's Query Interval (QQI), is represented in units of seconds */
+  return (qqi / 10);
+}
 
 typedef struct
 {
   /* type 0x11 (IGMPv3) */
   igmp_header_t header;
 
-  ip4_address_t dst;
+  ip4_address_t group_address;
 
   /* Reserved, Suppress Router-Side Processing flag and
      Querier's Robustness Variable RRRRSQQQ. */
@@ -101,11 +115,25 @@
   ip4_address_t src_addresses[0];
 } igmp_membership_query_v3_t;
 
+always_inline u32
+igmp_membership_query_v3_length (const igmp_membership_query_v3_t * q)
+{
+  return (sizeof (*q) +
+	  (sizeof (ip4_address_t) *
+	   clib_net_to_host_u16 (q->n_src_addresses)));
+}
+
+always_inline int
+igmp_membership_query_v3_is_geeral (const igmp_membership_query_v3_t * q)
+{
+  return (0 == q->group_address.as_u32);
+}
+
 #define foreach_igmp_membership_group_v3_type	\
-  _ (1, mode_is_filter_include)			\
-  _ (2, mode_is_filter_exclude)			\
-  _ (3, change_to_filter_include)		\
-  _ (4, change_to_filter_exclude)		\
+  _ (1, mode_is_include)			\
+  _ (2, mode_is_exclude)			\
+  _ (3, change_to_include)                      \
+  _ (4, change_to_exclude)                      \
   _ (5, allow_new_sources)			\
   _ (6, block_old_sources)
 
@@ -114,11 +142,11 @@
 #define _(n,f) IGMP_MEMBERSHIP_GROUP_##f = n,
   foreach_igmp_membership_group_v3_type
 #undef _
-} igmp_membership_group_v3_type_t;
+} __attribute__ ((packed)) igmp_membership_group_v3_type_t;
 
 typedef struct
 {
-  igmp_membership_group_v3_type_t type:8;
+  igmp_membership_group_v3_type_t type;
 
   /* Number of 32 bit words of aux data after source addresses. */
   u8 n_aux_u32s;
@@ -126,12 +154,20 @@
   /* Number of source addresses that follow. */
   u16 n_src_addresses;
 
-  /* Destination multicast address. */
-  ip4_address_t dst_address;
+  /* Destination multicast group address. */
+  ip4_address_t group_address;
 
   ip4_address_t src_addresses[0];
 } igmp_membership_group_v3_t;
 
+always_inline u32
+igmp_membership_group_v3_length (const igmp_membership_group_v3_t * g)
+{
+  return (sizeof (*g) +
+	  (sizeof (ip4_address_t) *
+	   clib_net_to_host_u16 (g->n_src_addresses)));
+}
+
 always_inline igmp_membership_group_v3_t *
 igmp_membership_group_v3_next (igmp_membership_group_v3_t * g)
 {
@@ -153,6 +189,24 @@
   igmp_membership_group_v3_t groups[0];
 } igmp_membership_report_v3_t;
 
+always_inline u32
+igmp_membership_report_v3_length (const igmp_membership_report_v3_t * r)
+{
+  const igmp_membership_group_v3_t *g;
+  u32 len, ii, glen;
+
+  len = sizeof (igmp_membership_report_v3_t);
+  g = r->groups;
+
+  for (ii = 0; ii < clib_net_to_host_u16 (r->n_groups); ii++)
+    {
+      glen = igmp_membership_group_v3_length (g);
+      g = (const igmp_membership_group_v3_t *) (((u8 *) g) + glen);
+      len += glen;
+    }
+  return (len);
+}
+
 /* IP6 flavor of IGMP is called MLD which is embedded in ICMP6. */
 typedef struct
 {