if (trk->ct_flags & CNAT_TRK_ACTIVE)
vec_add1 (ct->ct_active_paths, *trk);
+ flow_hash_config_t fhc = IP_FLOW_HASH_DEFAULT;
+ if (ct->fhc != 0)
+ fhc = ct->fhc;
lbi = load_balance_create (vec_len (ct->ct_active_paths),
- fib_proto_to_dpo (fproto), IP_FLOW_HASH_DEFAULT);
+ fib_proto_to_dpo (fproto), fhc);
ep_idx = 0;
vec_foreach (trk, ct->ct_active_paths)
u32
cnat_translation_update (cnat_endpoint_t *vip, ip_protocol_t proto,
cnat_endpoint_tuple_t *paths, u8 flags,
- cnat_lb_type_t lb_type)
+ cnat_lb_type_t lb_type, flow_hash_config_t fhc)
{
cnat_endpoint_tuple_t *path;
const cnat_client_t *cc;
ct->ct_cci = cci;
ct->index = ct - cnat_translation_pool;
ct->lb_type = lb_type;
+ ct->fhc = fhc;
cnat_add_translation_to_db (cci, vip, proto, ct->index);
cnat_client_translation_added (cci);
format_ip_protocol, ct->ct_proto);
s = format (s, "lb:%U ", format_cnat_lb_type, ct->lb_type);
+ if ((ct->fhc == 0) || (ct->fhc == IP_FLOW_HASH_DEFAULT))
+ s = format (s, "fhc:0x%x(default)", IP_FLOW_HASH_DEFAULT);
+ else
+ s = format (s, "fhc:0x%x", ct->fhc);
+
vec_foreach (ck, ct->ct_paths)
s = format (s, "\n%U", format_cnat_ep_trk, ck, 2);
}
}
+ flow_hash_config_t fhc = 0;
if (INDEX_INVALID == del_index)
- cnat_translation_update (&vip, proto, paths, flags, lb_type);
+ cnat_translation_update (&vip, proto, paths, flags, lb_type, fhc);
else
cnat_translation_delete (del_index);
class Translation(VppObject):
- def __init__(self, test, iproto, vip, paths):
+ def __init__(self, test, iproto, vip, paths, fhc):
self._test = test
self.vip = vip
self.iproto = iproto
self.paths = paths
+ self.fhc = fhc
self.id = None
def __str__(self):
"ip_proto": self._vl4_proto(),
"n_paths": len(self.paths),
"paths": self._encoded_paths(),
+ "flow_hash_config": self.fhc,
}
)
self._test.registry.register(self, self._test.logger)
i.admin_down()
super(TestCNatTranslation, self).tearDown()
+ def cnat_fhc_translation(self):
+ """CNat Translation"""
+ self.logger.info(self.vapi.cli("sh cnat client"))
+ self.logger.info(self.vapi.cli("sh cnat translation"))
+
+ for nbr, translation in enumerate(self.mbtranslations):
+ vip = translation.vip
+
+ #
+ # Flows to the VIP with same ips and different source ports are loadbalanced identically
+ # in both cases of flow hash 0x03 (src ip and dst ip) and 0x08 (dst port)
+ #
+ ctx = CnatTestContext(self, translation.iproto, vip.is_v6)
+ for src_pgi, sport in product(range(N_REMOTE_HOSTS), [1234, 1233]):
+ # from client to vip
+ ctx.cnat_send(self.pg0, src_pgi, sport, self.pg1, vip.ip, vip.port)
+ dport1 = ctx.rxs[0][ctx.L4PROTO].dport
+ ctx._test.assertIn(
+ dport1,
+ [translation.paths[0][DST].port, translation.paths[1][DST].port],
+ )
+ ctx.cnat_expect(self.pg0, src_pgi, sport, self.pg1, nbr, dport1)
+
+ ctx.cnat_send(
+ self.pg0, src_pgi, sport + 122, self.pg1, vip.ip, vip.port
+ )
+ dport2 = ctx.rxs[0][ctx.L4PROTO].dport
+ ctx._test.assertIn(
+ dport2,
+ [translation.paths[0][DST].port, translation.paths[1][DST].port],
+ )
+ ctx.cnat_expect(self.pg0, src_pgi, sport + 122, self.pg1, nbr, dport2)
+
+ ctx._test.assertEqual(dport1, dport2)
+
def cnat_translation(self):
"""CNat Translation"""
self.logger.info(self.vapi.cli("sh cnat client"))
ctx.cnat_expect(self.pg0, 0, 1234, self.pg2, 0, vip.port)
ctx.cnat_send_icmp_return_error().cnat_expect_icmp_error_return()
+ def _make_multi_backend_translations(self):
+ self.translations = []
+ self.mbtranslations = []
+ self.mbtranslations.append(
+ Translation(
+ self,
+ TCP,
+ Endpoint(ip="30.0.0.5", port=5555, is_v6=False),
+ [
+ (
+ Endpoint(is_v6=False),
+ Endpoint(pg=self.pg1, pgi=0, port=4001, is_v6=False),
+ ),
+ (
+ Endpoint(is_v6=False),
+ Endpoint(pg=self.pg1, pgi=0, port=4005, is_v6=False),
+ ),
+ ],
+ 0x03, # hash only on dst ip and src ip
+ ).add_vpp_config()
+ )
+ self.mbtranslations.append(
+ Translation(
+ self,
+ TCP,
+ Endpoint(ip="30.0.0.6", port=5555, is_v6=False),
+ [
+ (
+ Endpoint(is_v6=False),
+ Endpoint(pg=self.pg1, pgi=1, port=4006, is_v6=False),
+ ),
+ (
+ Endpoint(is_v6=False),
+ Endpoint(pg=self.pg1, pgi=1, port=4007, is_v6=False),
+ ),
+ ],
+ 0x08, # hash only on dst port
+ ).add_vpp_config()
+ )
+
def _make_translations_v4(self):
self.translations = []
self.translations.append(
Endpoint(pg=self.pg1, pgi=0, port=4001, is_v6=False),
)
],
+ 0x9F,
).add_vpp_config()
)
self.translations.append(
Endpoint(pg=self.pg1, pgi=1, port=4002, is_v6=False),
)
],
+ 0x9F,
).add_vpp_config()
)
self.translations.append(
Endpoint(pg=self.pg1, pgi=2, port=4003, is_v6=False),
)
],
+ 0x9F,
).add_vpp_config()
)
Endpoint(pg=self.pg1, pgi=0, port=4001, is_v6=True),
)
],
+ 0x9F,
).add_vpp_config()
)
self.translations.append(
Endpoint(pg=self.pg1, pgi=1, port=4002, is_v6=True),
)
],
+ 0x9F,
).add_vpp_config()
)
self.translations.append(
Endpoint(pg=self.pg1, pgi=2, port=4003, is_v6=True),
)
],
+ 0x9F,
).add_vpp_config()
)
self._make_translations_v4()
self.cnat_translation()
+ def test_cnat_fhc(self):
+ # """ CNat Translation flow hash config """
+ self._make_multi_backend_translations()
+ self.cnat_fhc_translation()
+
class TestCNatSourceNAT(CnatCommonTestCase):
"""CNat Source NAT"""
(Endpoint(pg=self.pg1, is_v6=is_v6), Endpoint(pg=self.pg3, is_v6=is_v6)),
]
ep = Endpoint(pg=self.pg0, is_v6=is_v6)
- t = Translation(self, TCP, ep, paths).add_vpp_config()
+ t = Translation(self, TCP, ep, paths, 0x9F).add_vpp_config()
# Add an address on every interface
# and check it is reflected in the cnat config
for pg in self.pg_interfaces: