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
12 files changed:
self.logger.info(self.vapi.cli("sh ip6 fib 10:222::1"))
rxs = self.send_and_expect(self.pg0, p, self.pg7)
self.logger.info(self.vapi.cli("sh ip6 fib 10:222::1"))
rxs = self.send_and_expect(self.pg0, p, self.pg7)
- self.assertEqual(rxs[0][VXLAN].vni, 446)
- self.assertEqual(rxs[1][VXLAN].vni, 445)
+ self.assertEqual(rxs[0][VXLAN].vni, 445)
+ self.assertEqual(rxs[1][VXLAN].vni, 446)
#
# ping from host in remote to local external subnets
#
# ping from host in remote to local external subnets
rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
- self.assertEqual(rxs[0][Dot1Q].vlan, 100)
- self.assertEqual(rxs[1][Dot1Q].vlan, 101)
+ self.assertEqual(rxs[0][Dot1Q].vlan, 101)
+ self.assertEqual(rxs[1][Dot1Q].vlan, 100)
# two ip4 packets whose port are chosen so they load-balance
p = [(Ether(src=lep1.mac, dst=str(self.router_mac)) /
# two ip4 packets whose port are chosen so they load-balance
p = [(Ether(src=lep1.mac, dst=str(self.router_mac)) /
called through a shared memory interface.
*/
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";
import "vnet/interface_types.api";
import "vnet/fib/fib_types.api";
vl_api_ip_flow_hash_config_t flow_hash_config;
};
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
/** \brief IPv6 interface enable / disable request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
#include <vnet/ip/ip.h>
#include <vnet/fib/fib_table.h>
#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)
{
u8
ip_is_zero (ip46_address_t * ip46_address, u8 is_ip4)
{
+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)
{
u8 *
format_ip_address_family (u8 * s, va_list * args)
{
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);
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,
void ip_feature_enable_disable (ip_address_family_t af,
ip_sub_address_family_t safi,
ip_feature_location_t loc,
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;
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);
hash_v3_mix32 (a, b, c);
hash_v3_finalize32 (a, b, c);
}
b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? protocol : 0;
}
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;
hash_mix64 (a, b, c);
return (u32) c;
& IP6_PACKET_ECN_MASK) >> 20;
}
& 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)
{
static_always_inline void
ip6_set_traffic_class_network_order (ip6_header_t * ip6, ip_dscp_t dscp)
{
_ (IP_PUNT_REDIRECT, ip_punt_redirect) \
_ (SET_IP_FLOW_HASH, set_ip_flow_hash) \
_ (SET_IP_FLOW_HASH_V2, set_ip_flow_hash_v2) \
_ (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) \
_ (IP_CONTAINER_PROXY_ADD_DEL, ip_container_proxy_add_del) \
_ (IP_CONTAINER_PROXY_DUMP, ip_container_proxy_dump) \
_ (IOAM_ENABLE, ioam_enable) \
REPLY_MACRO (VL_API_SET_IP_FLOW_HASH_V2_REPLY);
}
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)
void
vl_mfib_signal_send_one (vl_api_registration_t * reg,
u32 context, const mfib_signal_t * mfs)
#ifndef __IP_FLOW_HASH_H__
#define __IP_FLOW_HASH_H__
#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)
/** Default: 5-tuple + flowlabel without the "reverse" bit */
#define IP_FLOW_HASH_DEFAULT (0x9F)
#undef _
} flow_hash_config_t;
#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__ */
/*
#endif /* __IP_TYPES_H__ */
/*
ho_label = clib_net_to_host_u32(hdr->label_exp_s_ttl);
hash = vnet_mpls_uc_get_label(ho_label);
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))
next_label_is_entropy = 0;
while (MPLS_EOS != vnet_mpls_uc_get_s(ho_label))
super(TestIPLoadBalance, self).tearDown()
def send_and_expect_load_balancing(self, input, pkts, outputs):
super(TestIPLoadBalance, self).tearDown()
def send_and_expect_load_balancing(self, input, pkts, outputs):
+ self.vapi.cli("clear trace")
input.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
input.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
for oo in outputs:
rx = oo._get_capture(1)
self.assertNotEqual(0, len(rx))
for oo in outputs:
rx = oo._get_capture(1)
self.assertNotEqual(0, len(rx))
- for r in rx:
- rxs.append(r)
return rxs
def send_and_expect_one_itf(self, input, pkts, itf):
return rxs
def send_and_expect_one_itf(self, input, pkts, itf):
self.pg_start()
rx = itf.get_capture(len(pkts))
self.pg_start()
rx = itf.get_capture(len(pkts))
+ def total_len(self, rxs):
+ n = 0
+ for rx in rxs:
+ n += len(rx)
+ return n
+
def test_ip_load_balance(self):
""" IP Load-Balancing """
def test_ip_load_balance(self):
""" IP Load-Balancing """
# be guaranteed. But with 64 different packets we do expect some
# balancing. So instead just ensure there is traffic on each link.
#
# be guaranteed. But with 64 different packets we do expect some
# balancing. So instead just ensure there is traffic on each link.
#
- self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
- [self.pg1, self.pg2])
+ rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
+ [self.pg1, self.pg2])
+ n_ip_pg0 = len(rx[0])
self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
[self.pg1, self.pg2])
- self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
- [self.pg1, self.pg2])
+ rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
+ [self.pg1, self.pg2])
+ n_mpls_pg0 = len(rx[0])
+
+ #
+ # change the router ID and expect the distribution changes
+ #
+ self.vapi.set_ip_flow_hash_router_id(router_id=0x11111111)
+
+ rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
+ [self.pg1, self.pg2])
+ self.assertNotEqual(n_ip_pg0, len(rx[0]))
+
+ rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
+ [self.pg1, self.pg2])
+ self.assertNotEqual(n_mpls_pg0, len(rx[0]))
#
# change the flow hash config so it's only IP src,dst
#
# change the flow hash config so it's only IP src,dst
- # bring down pg1 expect LB to adjust to use only those that are pu
+ # bring down pg1 expect LB to adjust to use only those that are up
#
self.pg1.link_down()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg2, self.pg3,
self.pg4])
#
self.pg1.link_down()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg2, self.pg3,
self.pg4])
- self.assertEqual(len(src_pkts), len(rx))
+ self.assertEqual(len(src_pkts), self.total_len(rx))
- # bring down pg2 expect LB to adjust to use only those that are pu
+ # bring down pg2 expect LB to adjust to use only those that are up
#
self.pg2.link_down()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg3, self.pg4])
#
self.pg2.link_down()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg3, self.pg4])
- self.assertEqual(len(src_pkts), len(rx))
+ self.assertEqual(len(src_pkts), self.total_len(rx))
#
# bring the links back up - expect LB over all again
#
# bring the links back up - expect LB over all again
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg1, self.pg2,
self.pg3, self.pg4])
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg1, self.pg2,
self.pg3, self.pg4])
- self.assertEqual(len(src_pkts), len(rx))
+ self.assertEqual(len(src_pkts), self.total_len(rx))
#
# The same link-up/down but this time admin state
#
# The same link-up/down but this time admin state
self.pg2.admin_down()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg3, self.pg4])
self.pg2.admin_down()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg3, self.pg4])
- self.assertEqual(len(src_pkts), len(rx))
+ self.assertEqual(len(src_pkts), self.total_len(rx))
self.pg1.admin_up()
self.pg2.admin_up()
self.pg1.resolve_arp()
self.pg1.admin_up()
self.pg2.admin_up()
self.pg1.resolve_arp()
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg1, self.pg2,
self.pg3, self.pg4])
rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
[self.pg1, self.pg2,
self.pg3, self.pg4])
- self.assertEqual(len(src_pkts), len(rx))
+ self.assertEqual(len(src_pkts), self.total_len(rx))
rx = self.send_and_expect_load_balancing(self.pg0, port_pkts,
[self.pg3, self.pg4])
rx = self.send_and_expect_load_balancing(self.pg0, port_pkts,
[self.pg3, self.pg4])
- self.assertEqual(len(src_pkts), len(rx))
+ self.assertEqual(len(src_pkts), self.total_len(rx))
class TestIPVlan0(VppTestCase):
class TestIPVlan0(VppTestCase):
def send_and_expect_load_balancing(self, input, pkts, outputs):
self.pg_send(input, pkts)
def send_and_expect_load_balancing(self, input, pkts, outputs):
self.pg_send(input, pkts)
for oo in outputs:
rx = oo._get_capture(1)
self.assertNotEqual(0, len(rx))
for oo in outputs:
rx = oo._get_capture(1)
self.assertNotEqual(0, len(rx))
+ rxs.append(rx)
+ return rxs
def send_and_expect_one_itf(self, input, pkts, itf):
self.pg_send(input, pkts)
def send_and_expect_one_itf(self, input, pkts, itf):
self.pg_send(input, pkts)
# be guaranteed. But with 64 different packets we do expect some
# balancing. So instead just ensure there is traffic on each link.
#
# be guaranteed. But with 64 different packets we do expect some
# balancing. So instead just ensure there is traffic on each link.
#
- self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
- [self.pg1, self.pg2])
+ rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
+ [self.pg1, self.pg2])
+ n_ip_pg0 = len(rx[0])
self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
[self.pg1, self.pg2])
self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
[self.pg1, self.pg2])
- self.send_and_expect_load_balancing(self.pg0, port_mpls_neos_pkts,
- [self.pg1, self.pg2])
+ rx = self.send_and_expect_load_balancing(self.pg0, port_mpls_neos_pkts,
+ [self.pg1, self.pg2])
+ n_mpls_pg0 = len(rx[0])
+
+ #
+ # change the router ID and expect the distribution changes
+ #
+ self.vapi.set_ip_flow_hash_router_id(router_id=0x11111111)
+
+ rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
+ [self.pg1, self.pg2])
+ self.assertNotEqual(n_ip_pg0, len(rx[0]))
+
+ rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
+ [self.pg1, self.pg2])
+ self.assertNotEqual(n_mpls_pg0, len(rx[0]))
#
# The packets with Entropy label in should not load-balance,
#
# The packets with Entropy label in should not load-balance,