ip: Router ID included in flow hash

Type: feature

A device/router needs to have a unique ID which is included in the flow
has so that flows are not polarised through the network, i.e. each deice
in the network chooses the same nth link for the same flow.

Signed-off-by: Neale Ranns <neale@graphiant.com>
Change-Id: I963e03674adbb085902b4084fdc4886b88f5734c
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index f201ffb..3072e3e 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -20,7 +20,7 @@
     called through a shared memory interface.
 */
 
-option version = "3.0.2";
+option version = "3.0.3";
 
 import "vnet/interface_types.api";
 import "vnet/fib/fib_types.api";
@@ -277,6 +277,20 @@
   vl_api_ip_flow_hash_config_t flow_hash_config;
 };
 
+/** \brief Set the ip flow hash router ID
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param router_id - The ID of the router. Mixed into the hash.
+                       Used to prevent polarisation across a network,
+                       since each router is assumed to have a different ID
+*/
+autoreply define set_ip_flow_hash_router_id
+{
+  u32 client_index;
+  u32 context;
+  u32 router_id;
+};
+
 /** \brief IPv6 interface enable / disable request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/ip/ip.c b/src/vnet/ip/ip.c
index f247533..5d0c770 100644
--- a/src/vnet/ip/ip.c
+++ b/src/vnet/ip/ip.c
@@ -16,6 +16,8 @@
 #include <vnet/ip/ip.h>
 #include <vnet/fib/fib_table.h>
 
+u32 ip_flow_hash_router_id;
+
 u8
 ip_is_zero (ip46_address_t * ip46_address, u8 is_ip4)
 {
@@ -203,6 +205,12 @@
   return 0;
 }
 
+void
+ip_flow_hash_router_id_set (u32 router_id)
+{
+  ip_flow_hash_router_id = router_id;
+}
+
 u8 *
 format_ip_address_family (u8 * s, va_list * args)
 {
diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h
index 1789fa1..6d822d2 100644
--- a/src/vnet/ip/ip.h
+++ b/src/vnet/ip/ip.h
@@ -278,8 +278,6 @@
 void ip_copy (ip46_address_t * dst, ip46_address_t * src, u8 is_ip4);
 void ip_set (ip46_address_t * dst, void *src, u8 is_ip4);
 
-int ip_flow_hash_set (ip_address_family_t af, u32 table_id,
-		      flow_hash_config_t flow_hash_config);
 void ip_feature_enable_disable (ip_address_family_t af,
 				ip_sub_address_family_t safi,
 				ip_feature_location_t loc,
diff --git a/src/vnet/ip/ip4_inlines.h b/src/vnet/ip/ip4_inlines.h
index bdb82af..3075fbf 100644
--- a/src/vnet/ip/ip4_inlines.h
+++ b/src/vnet/ip/ip4_inlines.h
@@ -89,6 +89,7 @@
   b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? ip->protocol : 0;
   c = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ?
     (t1 << 16) | t2 : (t2 << 16) | t1;
+  a ^= ip_flow_hash_router_id;
 
   hash_v3_mix32 (a, b, c);
   hash_v3_finalize32 (a, b, c);
diff --git a/src/vnet/ip/ip6_inlines.h b/src/vnet/ip/ip6_inlines.h
index 8376377..2a4bb70 100644
--- a/src/vnet/ip/ip6_inlines.h
+++ b/src/vnet/ip/ip6_inlines.h
@@ -106,10 +106,13 @@
     }
 
   b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? protocol : 0;
-  c = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ?
-    ((t1 << 16) | t2) : ((t2 << 16) | t1);
-  t1 = ip->ip_version_traffic_class_and_flow_label & IP6_PACKET_FL_MASK;
-  c ^= (flow_hash_config & IP_FLOW_HASH_FL) ? (t1 << 32) : 0;
+  c = ((flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ? ((t1 << 16) | t2) :
+							   ((t2 << 16) | t1));
+  t1 = ((u64) ip_flow_hash_router_id << 32);
+  t1 |=
+    ((flow_hash_config & IP_FLOW_HASH_FL) ? ip6_flow_label_network_order (ip) :
+					    0);
+  c ^= t1;
 
   hash_mix64 (a, b, c);
   return (u32) c;
diff --git a/src/vnet/ip/ip6_packet.h b/src/vnet/ip/ip6_packet.h
index 03aac1b..1be2cea 100644
--- a/src/vnet/ip/ip6_packet.h
+++ b/src/vnet/ip/ip6_packet.h
@@ -343,6 +343,13 @@
 	  & IP6_PACKET_ECN_MASK) >> 20;
 }
 
+static_always_inline u32
+ip6_flow_label_network_order (const ip6_header_t *ip6)
+{
+  return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label) &
+	  IP6_PACKET_FL_MASK);
+}
+
 static_always_inline void
 ip6_set_traffic_class_network_order (ip6_header_t * ip6, ip_dscp_t dscp)
 {
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index 37656f3..3bf404b 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -88,6 +88,7 @@
   _ (IP_PUNT_REDIRECT, ip_punt_redirect)                                      \
   _ (SET_IP_FLOW_HASH, set_ip_flow_hash)                                      \
   _ (SET_IP_FLOW_HASH_V2, set_ip_flow_hash_v2)                                \
+  _ (SET_IP_FLOW_HASH_ROUTER_ID, set_ip_flow_hash_router_id)                  \
   _ (IP_CONTAINER_PROXY_ADD_DEL, ip_container_proxy_add_del)                  \
   _ (IP_CONTAINER_PROXY_DUMP, ip_container_proxy_dump)                        \
   _ (IOAM_ENABLE, ioam_enable)                                                \
@@ -1046,6 +1047,18 @@
   REPLY_MACRO (VL_API_SET_IP_FLOW_HASH_V2_REPLY);
 }
 
+static void
+vl_api_set_ip_flow_hash_router_id_t_handler (
+  vl_api_set_ip_flow_hash_router_id_t *mp)
+{
+  vl_api_set_ip_flow_hash_router_id_reply_t *rmp;
+  int rv = 0;
+
+  ip_flow_hash_router_id_set (ntohl (mp->router_id));
+
+  REPLY_MACRO (VL_API_SET_IP_FLOW_HASH_ROUTER_ID_REPLY);
+}
+
 void
 vl_mfib_signal_send_one (vl_api_registration_t * reg,
 			 u32 context, const mfib_signal_t * mfs)
diff --git a/src/vnet/ip/ip_flow_hash.h b/src/vnet/ip/ip_flow_hash.h
index 82e0efb..bd37ef7 100644
--- a/src/vnet/ip/ip_flow_hash.h
+++ b/src/vnet/ip/ip_flow_hash.h
@@ -16,6 +16,8 @@
 #ifndef __IP_FLOW_HASH_H__
 #define __IP_FLOW_HASH_H__
 
+#include <vnet/ip/ip_types.h>
+
 /** Default: 5-tuple + flowlabel without the "reverse" bit */
 #define IP_FLOW_HASH_DEFAULT (0x9F)
 
@@ -48,6 +50,13 @@
 #undef _
 } flow_hash_config_t;
 
+/* Router ID mixed into the flow hash to prevent network polarisation */
+extern u32 ip_flow_hash_router_id;
+
+int ip_flow_hash_set (ip_address_family_t af, u32 table_id,
+		      flow_hash_config_t flow_hash_config);
+void ip_flow_hash_router_id_set (u32 router_id);
+
 #endif /* __IP_TYPES_H__ */
 
 /*
diff --git a/src/vnet/mpls/mpls_lookup.h b/src/vnet/mpls/mpls_lookup.h
index 5b88be1..81c67ce 100644
--- a/src/vnet/mpls/mpls_lookup.h
+++ b/src/vnet/mpls/mpls_lookup.h
@@ -53,6 +53,7 @@
 
     ho_label = clib_net_to_host_u32(hdr->label_exp_s_ttl);
     hash = vnet_mpls_uc_get_label(ho_label);
+    hash ^= ip_flow_hash_router_id;
     next_label_is_entropy = 0;
 
     while (MPLS_EOS != vnet_mpls_uc_get_s(ho_label))