@@ -11566,10 +11566,17 @@ lrouter_dnat_and_snat_is_stateless(const struct nbrec_nat *nat)
!strcmp(nat->type, "dnat_and_snat");
}
+#define NAT_PRIORITY_MATCH_OFFSET 300
+
static inline uint16_t
-lrouter_nat_get_priority(const struct ovn_datapath *od, bool is_dnat,
+lrouter_nat_get_priority(const struct ovn_datapath *od,
+ const struct nbrec_nat *nat, bool is_dnat,
uint16_t prefix_len)
{
+ if (nat->match[0]) {
+ return NAT_PRIORITY_MATCH_OFFSET + nat->priority;
+ }
+
if (is_dnat) {
return 100;
}
@@ -11631,7 +11638,7 @@ lrouter_nat_add_ext_ip_match(const struct ovn_datapath *od,
*
*/
uint16_t priority =
- lrouter_nat_get_priority(od, is_src, cidr_bits) + 2;
+ lrouter_nat_get_priority(od, nat, is_src, cidr_bits) + 2;
ds_clone(&match_exempt, match);
ds_put_format(&match_exempt, " && ip%s.%s == $%s",
@@ -14600,6 +14607,7 @@ build_lrouter_in_dnat_flow(struct lflow_table *lflows,
const char *nat_action = lrouter_use_common_zone(od)
? "ct_dnat_in_czone"
: "ct_dnat";
+ uint16_t priority = lrouter_nat_get_priority(od, nat, true, cidr_bits);
ds_put_format(match, "ip && ip%c.dst == %s", is_v6 ? '6' : '4',
nat->external_ip);
@@ -14646,8 +14654,11 @@ build_lrouter_in_dnat_flow(struct lflow_table *lflows,
ds_put_format(actions, ");");
}
- ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT,
- lrouter_nat_get_priority(od, true, cidr_bits),
+ if (nat->match[0]) {
+ ds_put_format(match, " && (%s)", nat->match);
+ }
+
+ ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
ds_cstr(match), ds_cstr(actions),
&nat->header_, lflow_ref);
}
@@ -14772,6 +14783,10 @@ build_lrouter_out_snat_match(struct lflow_table *lflows,
is_v6, is_reverse, cidr_bits,
lflow_ref);
}
+
+ if (nat->match[0]) {
+ ds_put_format(match, " && (%s)", nat->match);
+ }
}
static void
@@ -14790,7 +14805,7 @@ build_lrouter_out_snat_stateless_flow(struct lflow_table *lflows,
ds_clear(actions);
- uint16_t priority = lrouter_nat_get_priority(od, false, cidr_bits);
+ uint16_t priority = lrouter_nat_get_priority(od, nat, false, cidr_bits);
build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat,
cidr_bits, is_v6, l3dgw_port, lflow_ref,
false);
@@ -14825,7 +14840,7 @@ build_lrouter_out_snat_in_czone_flow(struct lflow_table *lflows,
ds_clear(actions);
- uint16_t priority = lrouter_nat_get_priority(od, false, cidr_bits);
+ uint16_t priority = lrouter_nat_get_priority(od, nat, false, cidr_bits);
struct ds zone_actions = DS_EMPTY_INITIALIZER;
build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat,
@@ -14884,7 +14899,7 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows,
ds_clear(actions);
- uint16_t priority = lrouter_nat_get_priority(od, false, cidr_bits);
+ uint16_t priority = lrouter_nat_get_priority(od, nat, false, cidr_bits);
build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat,
cidr_bits, is_v6, l3dgw_port, lflow_ref,
@@ -3794,6 +3794,23 @@ next;
<code>exempted_ext_ips</code>.
</p>
+ <p>
+ For each configuration in the OVN Northbound database, that asks
+ to change the destination IP address of a packet from <var>A</var>
+ to <var>B</var>, match <var>M</var> and priority <var>P</var>,
+ a logical flow that matches <code>ip && ip4.dst ==
+ <var>A</var></code> or <code>ip && ip6.dst == <var>A</var>
+ && (<var>M</var>)</code> with an action
+ <code>flags.loopback = 1; ct_dnat(<var>B</var>);</code>.
+ The priority of the flow is calculated based as
+ <code>300 + <var>P</var></code>. If the Gateway router is
+ configured to force SNAT any DNATed packet, the above action will
+ be replaced by <code>flags.force_snat_for_dnat = 1;
+ flags.loopback = 1; ct_dnat(<var>B</var>);</code>. If the NAT rule
+ is of type dnat_and_snat and has <code>stateless=true</code> in the
+ options, then the action would be <code>ip4/6.dst=
+ (<var>B</var>)</code>.
+ </p>
</li>
<li>
@@ -5162,6 +5179,20 @@ nd_ns {
options, then the action would be <code>ip4/6.src=
(<var>B</var>)</code>.
</p>
+
+ <p>
+ For each configuration in the OVN Northbound database, that asks
+ to change the source IP address of a packet from an IP address of
+ <var>A</var> or to change the source IP address of a packet that
+ belongs to network <var>A</var> to <var>B</var>, match <var>M</var>
+ and priority <var>P</var>, a flow matches <code>ip &&
+ ip4.src == <var>A</var> && (!ct.trk || !ct.rpl) &&
+ (<var>M</var>)</code> with an action <code>ct_snat(<var>B</var>);
+ </code>. The priority of the flow is calculated based as
+ <code>300 + <var>P</var></code>. If the NAT rule is of type
+ dnat_and_snat and has <code>stateless=true</code> in the options,
+ then the action would be <code>ip4/6.src=(<var>B</var>)</code>.
+ </p>
</li>
<li>
@@ -1024,22 +1024,46 @@ ovn-nbctl --wait=sb lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11
check_flow_match_sets 2 2 2 0 0 0 0
ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1
+echo
+echo "IPv4: stateful with match"
+ovn-nbctl --wait=sb --match="udp" lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11
+check_flow_match_sets 2 2 2 0 0 0 0
+ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1
+
echo
echo "IPv4: stateless"
ovn-nbctl --wait=sb --stateless lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11
check_flow_match_sets 2 0 0 1 1 0 0
ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1
+echo
+echo "IPv4: stateless with match"
+ovn-nbctl --wait=sb --match="udp" --stateless lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11
+check_flow_match_sets 2 0 0 1 1 0 0
+ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1
+
echo
echo "IPv6: stateful"
ovn-nbctl --wait=sb lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
check_flow_match_sets 2 2 2 0 0 0 0
ovn-nbctl lr-nat-del R1 dnat_and_snat fd01::1
+echo
+echo "IPv6: stateful with match"
+ovn-nbctl --wait=sb --match="udp" lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
+check_flow_match_sets 2 2 2 0 0 0 0
+ovn-nbctl lr-nat-del R1 dnat_and_snat fd01::1
+
echo
echo "IPv6: stateless"
ovn-nbctl --wait=sb --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
check_flow_match_sets 2 0 0 0 0 1 1
+ovn-nbctl lr-nat-del R1 dnat_and_snat fd01::1
+
+echo
+echo "IPv6: stateless with match"
+ovn-nbctl --wait=sb --match="udp" --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
+check_flow_match_sets 2 0 0 0 0 1 1
AT_CLEANUP
])
@@ -12612,3 +12636,58 @@ check_engine_stats northd recompute nocompute
check_engine_stats lflow recompute nocompute
AT_CLEANUP
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([NAT with match])
+ovn_start
+
+check ovn-sbctl chassis-add hv1 geneve 127.0.0.1
+
+check ovn-nbctl lr-add lr -- \
+ lrp-add lr lr-ls 02:ac:10:01:00:01 172.16.1.1/24
+
+check ovn-nbctl ls-add ls -- \
+ lsp-add ls ls-lr -- \
+ lsp-set-type ls-lr router -- \
+ lsp-set-addresses ls-lr router -- \
+ lsp-set-options ls-lr router-port=lr-ls
+
+check ovn-nbctl lr-nat-add lr snat 172.16.1.1 10.0.0.0/24
+check ovn-nbctl --match="udp" --priority=10 lr-nat-add lr snat 172.16.1.2 10.0.0.0/24
+
+check ovn-nbctl lr-nat-add lr dnat 10.0.0.100 10.0.0.10
+check ovn-nbctl --match="udp" --priority=20 lr-nat-add lr dnat 10.0.0.100 10.0.0.20
+
+check ovn-nbctl --wait=sb lrp-set-gateway-chassis lr-ls hv1
+
+AT_CHECK([ovn-sbctl dump-flows lr | grep lr_out_snat | ovn_strip_lflows], [0], [dnl
+ table=??(lr_out_snat ), priority=0 , match=(1), action=(next;)
+ table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;)
+ table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr-ls" && is_chassis_resident("cr-lr-ls") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
+ table=??(lr_out_snat ), priority=310 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr-ls" && is_chassis_resident("cr-lr-ls") && (udp) && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
+])
+
+AT_CHECK([ovn-sbctl dump-flows lr | grep lr_in_dnat | ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;)
+ table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.100 && inport == "lr-ls" && is_chassis_resident("cr-lr-ls")), action=(ct_dnat(10.0.0.10);)
+ table=??(lr_in_dnat ), priority=320 , match=(ip && ip4.dst == 10.0.0.100 && inport == "lr-ls" && is_chassis_resident("cr-lr-ls") && (udp)), action=(ct_dnat(10.0.0.20);)
+])
+
+check ovn-nbctl lrp-del-gateway-chassis lr-ls hv1
+check ovn-nbctl --wait=sb set logical_router lr options:chassis=hv1
+
+AT_CHECK([ovn-sbctl dump-flows lr | grep lr_out_snat | ovn_strip_lflows], [0], [dnl
+ table=??(lr_out_snat ), priority=0 , match=(1), action=(next;)
+ table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;)
+ table=??(lr_out_snat ), priority=25 , match=(ip && ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
+ table=??(lr_out_snat ), priority=310 , match=(ip && ip4.src == 10.0.0.0/24 && (udp) && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
+])
+
+AT_CHECK([ovn-sbctl dump-flows lr | grep lr_in_dnat | ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;)
+ table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.100), action=(flags.loopback = 1; ct_dnat(10.0.0.10);)
+ table=??(lr_in_dnat ), priority=320 , match=(ip && ip4.dst == 10.0.0.100 && (udp)), action=(flags.loopback = 1; ct_dnat(10.0.0.20);)
+])
+
+AT_CLEANUP
+])
@@ -12735,3 +12735,275 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
/.*terminating with signal 15.*/d"])
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([NAT arbitrary match - IPv4])
+AT_KEYWORDS([ovnlb])
+
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext])
+
+check ovs-ofctl add-flow br-ext action=normal
+# Set external-ids in br-int needed for ovn-controller
+check ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \
+ -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+check ovn-nbctl lr-add lr
+check ovn-nbctl ls-add internal
+check ovn-nbctl ls-add public
+
+check ovn-nbctl lrp-add lr lr-pub 00:00:01:01:02:03 192.168.100.1/24
+check ovn-nbctl lsp-add public pub-lr -- set Logical_Switch_Port pub-lr \
+ type=router options:router-port=lr-pub addresses=\"00:00:01:01:02:03\"
+
+check ovn-nbctl lrp-add lr lr-internal 00:00:01:01:02:04 192.168.200.1/24
+check ovn-nbctl lsp-add internal internal-lr -- set Logical_Switch_Port internal-lr \
+ type=router options:router-port=lr-internal addresses=\"00:00:01:01:02:04\"
+
+check ovn-nbctl lsp-add internal vm0 \
+ -- lsp-set-addresses vm0 "f0:00:0f:01:02:03 192.168.200.10"
+check ovn-nbctl lsp-add internal vm2 \
+ -- lsp-set-addresses vm2 "f0:00:0f:01:02:04 192.168.200.20"
+check ovn-nbctl lsp-add internal vm3 \
+ -- lsp-set-addresses vm3 "f0:00:0f:01:02:05 192.168.200.30"
+
+ovn-nbctl lsp-add public ln_port \
+ -- lsp-set-addresses ln_port unknown \
+ -- lsp-set-type ln_port localnet \
+ -- lsp-set-options ln_port network_name=phynet
+
+check ovn-nbctl --wait=hv sync
+
+ADD_NAMESPACES(vm0)
+ADD_VETH(vm0, vm0, br-int, "192.168.200.10/24", "f0:00:0f:01:02:03", "192.168.200.1")
+
+ADD_NAMESPACES(vm1)
+ADD_VETH(vm1, vm1, br-ext, "192.168.100.10/24", "f0:00:00:01:02:03", "192.168.100.1")
+
+ADD_NAMESPACES(vm2)
+ADD_VETH(vm2, vm2, br-int, "192.168.200.20/24", "f0:00:0f:01:02:04", "192.168.200.1")
+
+ADD_NAMESPACES(vm3)
+ADD_VETH(vm3, vm3, br-int, "192.168.200.30/24", "f0:00:0f:01:02:05", "192.168.200.1")
+
+NETNS_DAEMONIZE([vm0], [nc -l -k 192.168.200.10 4242], [server0.pid])
+NETNS_DAEMONIZE([vm1], [nc -l -k 192.168.100.10 4242], [server1.pid])
+NETNS_DAEMONIZE([vm2], [nc -l -k 192.168.200.20 4242], [server2.pid])
+NETNS_DAEMONIZE([vm3], [nc -l -k 192.168.200.30 4242], [server3.pid])
+
+check_snat() {
+ check ovn-nbctl lr-nat-del lr
+ check ovn-nbctl lr-nat-add lr snat 192.168.100.1 192.168.200.0/24
+ check ovn-nbctl --match="tcp && tcp.src == 2001" lr-nat-add lr snat 192.168.100.11 192.168.200.0/24
+ check ovn-nbctl --match="tcp && tcp.src == 2002" lr-nat-add lr snat 192.168.100.12 192.168.200.0/24
+ check ovn-nbctl --wait=hv sync
+
+ check ovs-appctl dpctl/flush-conntrack
+
+ NS_CHECK_EXEC([vm0], [nc -z 192.168.100.10 4242 -p 2000])
+ NS_CHECK_EXEC([vm0], [nc -z 192.168.100.10 4242 -p 2001])
+ NS_CHECK_EXEC([vm0], [nc -z 192.168.100.10 4242 -p 2002])
+
+ snat_id=$(ovn-appctl -t ovn-controller ct-zone-list | grep lr_snat | cut -d ' ' -f2)
+ AT_CHECK_UNQUOTED([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.100.10) | grep "zone=$snat_id"], [0], [dnl
+tcp,orig=(src=192.168.200.10,dst=192.168.100.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.100.10,dst=192.168.100.1,sport=<cleared>,dport=<cleared>),zone=$snat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.200.10,dst=192.168.100.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.100.10,dst=192.168.100.11,sport=<cleared>,dport=<cleared>),zone=$snat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.200.10,dst=192.168.100.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.100.10,dst=192.168.100.12,sport=<cleared>,dport=<cleared>),zone=$snat_id,protoinfo=(state=<cleared>)
+])
+}
+
+check_dnat() {
+ check ovn-nbctl lr-nat-del lr
+ check ovn-nbctl lr-nat-add lr dnat 192.168.100.100 192.168.200.10
+ check ovn-nbctl --match="tcp && tcp.src == 2001" lr-nat-add lr dnat 192.168.100.100 192.168.200.20
+ check ovn-nbctl --match="tcp && tcp.src == 2002" lr-nat-add lr dnat 192.168.100.100 192.168.200.30
+ check ovn-nbctl --wait=hv sync
+
+ check ovs-appctl dpctl/flush-conntrack
+
+ NS_CHECK_EXEC([vm1], [nc -z 192.168.100.100 4242 -p 2000])
+ NS_CHECK_EXEC([vm1], [nc -z 192.168.100.100 4242 -p 2001])
+ NS_CHECK_EXEC([vm1], [nc -z 192.168.100.100 4242 -p 2002])
+
+ dnat_id=$(ovn-appctl -t ovn-controller ct-zone-list | grep lr_dnat | cut -d ' ' -f2)
+ AT_CHECK_UNQUOTED([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.100.100) | grep "zone=$dnat_id"], [0], [dnl
+tcp,orig=(src=192.168.100.10,dst=192.168.100.100,sport=<cleared>,dport=<cleared>),reply=(src=192.168.200.10,dst=192.168.100.10,sport=<cleared>,dport=<cleared>),zone=$dnat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.100.10,dst=192.168.100.100,sport=<cleared>,dport=<cleared>),reply=(src=192.168.200.20,dst=192.168.100.10,sport=<cleared>,dport=<cleared>),zone=$dnat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.100.10,dst=192.168.100.100,sport=<cleared>,dport=<cleared>),reply=(src=192.168.200.30,dst=192.168.100.10,sport=<cleared>,dport=<cleared>),zone=$dnat_id,protoinfo=(state=<cleared>)
+])
+}
+
+check ovn-nbctl lrp-set-gateway-chassis lr-pub hv1
+check_snat
+check_dnat
+check ovn-nbctl lrp-del-gateway-chassis lr-pub hv1
+
+check ovn-nbctl set logical_router lr options:chassis=hv1
+check_snat
+check_dnat
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([NAT arbitrary match - IPv6])
+AT_KEYWORDS([ovnlb])
+
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext])
+
+check ovs-ofctl add-flow br-ext action=normal
+# Set external-ids in br-int needed for ovn-controller
+check ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \
+ -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+check ovn-nbctl lr-add lr
+check ovn-nbctl ls-add internal
+check ovn-nbctl ls-add public
+
+check ovn-nbctl lrp-add lr lr-pub 00:00:01:01:02:03 1000::1/64
+check ovn-nbctl lsp-add public pub-lr -- set Logical_Switch_Port pub-lr \
+ type=router options:router-port=lr-pub addresses=\"00:00:01:01:02:03\"
+
+check ovn-nbctl lrp-add lr lr-internal 00:00:01:01:02:04 2000::1/64
+check ovn-nbctl lsp-add internal internal-lr -- set Logical_Switch_Port internal-lr \
+ type=router options:router-port=lr-internal addresses=\"00:00:01:01:02:04\"
+
+check ovn-nbctl lsp-add internal vm0 \
+ -- lsp-set-addresses vm0 "f0:00:0f:01:02:03 2000::10"
+check ovn-nbctl lsp-add internal vm2 \
+ -- lsp-set-addresses vm2 "f0:00:0f:01:02:04 2000::20"
+check ovn-nbctl lsp-add internal vm3 \
+ -- lsp-set-addresses vm3 "f0:00:0f:01:02:05 2000::30"
+
+ovn-nbctl lsp-add public ln_port \
+ -- lsp-set-addresses ln_port unknown \
+ -- lsp-set-type ln_port localnet \
+ -- lsp-set-options ln_port network_name=phynet
+
+check ovn-nbctl --wait=hv sync
+
+ADD_NAMESPACES(vm0)
+ADD_VETH(vm0, vm0, br-int, "2000::10/64", "f0:00:0f:01:02:03", "2000::1", "nodad")
+
+ADD_NAMESPACES(vm1)
+ADD_VETH(vm1, vm1, br-ext, "1000::10/64", "f0:00:00:01:02:03", "1000::1", "nodad")
+
+ADD_NAMESPACES(vm2)
+ADD_VETH(vm2, vm2, br-int, "2000::20/64", "f0:00:0f:01:02:04", "2000::1", "nodad")
+
+ADD_NAMESPACES(vm3)
+ADD_VETH(vm3, vm3, br-int, "2000::30/64", "f0:00:0f:01:02:05", "2000::1", "nodad")
+
+NETNS_DAEMONIZE([vm0], [nc -lk 2000::10 4242 > /dev/null], [server0.pid])
+NETNS_DAEMONIZE([vm1], [nc -lk 1000::10 4242 > /dev/null], [server1.pid])
+NETNS_DAEMONIZE([vm2], [nc -lk 2000::20 4242 > /dev/null], [server2.pid])
+NETNS_DAEMONIZE([vm3], [nc -lk 2000::30 4242 > /dev/null], [server3.pid])
+
+check_snat() {
+ check ovn-nbctl lr-nat-del lr
+ check ovn-nbctl lr-nat-add lr snat 1000::1 2000::/64
+ check ovn-nbctl --match="tcp && tcp.src == 2001" lr-nat-add lr snat 1000::11 2000::/64
+ check ovn-nbctl --match="tcp && tcp.src == 2002" lr-nat-add lr snat 1000::12 2000::/64
+ check ovn-nbctl --wait=hv sync
+
+ check ovs-appctl dpctl/flush-conntrack
+
+ NS_CHECK_EXEC([vm0], [nc -z 1000::10 4242 -p 2000])
+ NS_CHECK_EXEC([vm0], [nc -z 1000::10 4242 -p 2001])
+ NS_CHECK_EXEC([vm0], [nc -z 1000::10 4242 -p 2002])
+
+ snat_id=$(ovn-appctl -t ovn-controller ct-zone-list | grep lr_snat | cut -d ' ' -f2)
+ AT_CHECK_UNQUOTED([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(1000::10) | grep "zone=$snat_id"], [0], [dnl
+tcp,orig=(src=2000::10,dst=1000::10,sport=<cleared>,dport=<cleared>),reply=(src=1000::10,dst=1000::1,sport=<cleared>,dport=<cleared>),zone=$snat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=2000::10,dst=1000::10,sport=<cleared>,dport=<cleared>),reply=(src=1000::10,dst=1000::11,sport=<cleared>,dport=<cleared>),zone=$snat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=2000::10,dst=1000::10,sport=<cleared>,dport=<cleared>),reply=(src=1000::10,dst=1000::12,sport=<cleared>,dport=<cleared>),zone=$snat_id,protoinfo=(state=<cleared>)
+])
+}
+
+check_dnat() {
+ check ovn-nbctl lr-nat-del lr
+ check ovn-nbctl lr-nat-add lr dnat 1000::100 2000::10
+ check ovn-nbctl --match="tcp && tcp.src == 2001" lr-nat-add lr dnat 1000::100 2000::20
+ check ovn-nbctl --match="tcp && tcp.src == 2002" lr-nat-add lr dnat 1000::100 2000::30
+ check ovn-nbctl --wait=hv sync
+
+ check ovs-appctl dpctl/flush-conntrack
+
+ NS_CHECK_EXEC([vm1], [nc -z 1000::100 4242 -p 2000])
+ NS_CHECK_EXEC([vm1], [nc -z 1000::100 4242 -p 2001])
+ NS_CHECK_EXEC([vm1], [nc -z 1000::100 4242 -p 2002])
+
+ dnat_id=$(ovn-appctl -t ovn-controller ct-zone-list | grep lr_dnat | cut -d ' ' -f2)
+ AT_CHECK_UNQUOTED([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(1000::100) | grep "zone=$dnat_id"], [0], [dnl
+tcp,orig=(src=1000::10,dst=1000::100,sport=<cleared>,dport=<cleared>),reply=(src=2000::10,dst=1000::10,sport=<cleared>,dport=<cleared>),zone=$dnat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=1000::10,dst=1000::100,sport=<cleared>,dport=<cleared>),reply=(src=2000::20,dst=1000::10,sport=<cleared>,dport=<cleared>),zone=$dnat_id,protoinfo=(state=<cleared>)
+tcp,orig=(src=1000::10,dst=1000::100,sport=<cleared>,dport=<cleared>),reply=(src=2000::30,dst=1000::10,sport=<cleared>,dport=<cleared>),zone=$dnat_id,protoinfo=(state=<cleared>)
+])
+}
+
+check ovn-nbctl lrp-set-gateway-chassis lr-pub hv1
+check_snat
+check_dnat
+check ovn-nbctl lrp-del-gateway-chassis lr-pub hv1
+
+check ovn-nbctl set logical_router lr options:chassis=hv1
+check_snat
+check_dnat
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
Use the newly added NAT match and priority column in logical flows. This allows to differentiate between various scenarios and more fine-grained control over the resulting translation. The flows with the extra match have higher priority than regular flows as the flows without match are subset of the flows with match, the priority is calculated as 300 + priority column. Reported-at: https://issues.redhat.com/browse/FDP-433 Signed-off-by: Ales Musil <amusil@redhat.com> --- v2: Rebase on top of current main. Fix the common zone issue noticed by Mark. --- northd/northd.c | 29 +++-- northd/ovn-northd.8.xml | 31 +++++ tests/ovn-northd.at | 79 ++++++++++++ tests/system-ovn.at | 272 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 404 insertions(+), 7 deletions(-)