continue;
}
@@ -3454,8 +3459,12 @@ ovn_port_update_sbrec(struct northd_input *input_data,
struct ds nat_addr = DS_EMPTY_INITIALIZER;
ds_put_format(&nat_addr, "%s", nat_addresses);
if (l3dgw_ports) {
+ const struct ovn_port *l3dgw_port = (
+ is_l3dgw_port(op->peer)
+ ? op->peer
+ : op->peer->od->l3dgw_ports[0]);
ds_put_format(&nat_addr, " is_chassis_resident(%s)",
- op->peer->od->l3dgw_ports[0]->cr_port->json_key);
+ l3dgw_port->cr_port->json_key);
}
nats[0] = xstrdup(ds_cstr(&nat_addr));
ds_destroy(&nat_addr);
@@ -10502,9 +10511,11 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
const struct nbrec_nat *nat = nat_entry->nb;
struct ds match = DS_EMPTY_INITIALIZER;
- /* ARP/ND should be sent from distributed gateway port specified in
+ /* ARP/ND should be sent from distributed gateway port relevant to
* the NAT rule. */
- if (nat->gateway_port && nat->gateway_port != op->nbrp) {
+ if (op->od->n_l3dgw_ports > 1 &&
+ ((!nat->gateway_port && !find_lrp_member_ip(op, nat->external_ip)) ||
+ (nat->gateway_port && nat->gateway_port != op->nbrp))) {
return;
}
@@ -13287,14 +13298,24 @@ lrouter_check_nat_entry(struct ovn_datapath *od, const struct nbrec_nat *nat,
/* Validate gateway_port of NAT rule. */
*nat_l3dgw_port = NULL;
if (nat->gateway_port == NULL) {
- if (od->n_l3dgw_ports > 1) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "NAT configured on logical router: %s with"
- "multiple distributed gateway ports needs to specify"
- "valid gateway_port.", od->nbr->name);
- return -EINVAL;
- } else if (od->n_l3dgw_ports) {
+ if (od->n_l3dgw_ports == 1) {
*nat_l3dgw_port = od->l3dgw_ports[0];
+ } else if (od->n_l3dgw_ports > 1) {
+ /* Find the DGP reachable for the NAT external IP. */
+ for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
+ if (find_lrp_member_ip(od->l3dgw_ports[i], nat->external_ip)) {
+ *nat_l3dgw_port = od->l3dgw_ports[i];
+ break;
+ }
+ }
+ if (*nat_l3dgw_port == NULL) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "Unable to determine gateway_port for NAT "
+ "with external_ip: %s configured on logical "
+ "router: %s with multiple distributed gateway "
+ "ports", nat->external_ip, od->nbr->name);
+ return -EINVAL;
+ }
}
} else {
*nat_l3dgw_port = ovn_port_find(ports, nat->gateway_port->name);
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 0fe350d0e..948c929e7 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -2137,7 +2137,8 @@ output;
router that specifies an external Ethernet address <var>E</var>,
a priority-50 flow that matches <code>inport == <var>GW</var>
&& eth.dst == <var>E</var></code>, where <var>GW</var>
- is the logical router gateway port, with action
+ is the logical router distributed gateway port corresponding to the
+ NAT rule (specified or inferred), with action
<code>xreg0[0..47]=E; next;</code>.
</p>
@@ -2453,11 +2454,12 @@ icmp6_error {
<li>
<p>
For each NAT entry of a distributed logical router (with
- distributed gateway router port) of type <code>snat</code>,
+ distributed gateway router port(s)) of type <code>snat</code>,
a priority-120 flow with the match <code>inport == <var>P</var>
&& ip4.src == <var>A</var></code> advances the packet to
the next pipeline, where <var>P</var> is the distributed logical
- router port and <var>A</var> is the <code>external_ip</code> set
+ router port corresponding to the NAT entry (specified or inferred)
+ and <var>A</var> is the <code>external_ip</code> set
in the NAT entry. If <var>A</var> is an IPv6 address, then
<code>ip6.src</code> is used for the match.
</p>
@@ -3057,7 +3059,7 @@ icmp6 {
ip6.dst == <var>B</var> && inport == <var>GW</var>
&& flags.loopback == 0</code>
where <var>GW</var> is the distributed gateway port
- specified in the NAT rule, with an
+ corresponding to the NAT rule (specified or inferred), with an
action <code>ct_snat_in_czone;</code> to unSNAT in the common
zone. If the NAT rule is of type dnat_and_snat and has
<code>stateless=true</code> in the options, then the action
@@ -3083,7 +3085,7 @@ icmp6 {
&& flags.loopback == 0 &&
flags.use_snat_zone == 1</code>
where <var>GW</var> is the distributed gateway port
- specified in the NAT rule, with an
+ corresponding to the NAT rule (specified or inferred), with an
action <code>ct_snat;</code> to unSNAT in the snat zone. If the
NAT rule is of type dnat_and_snat and has
<code>stateless=true</code> in the options, then the action
@@ -3364,8 +3366,8 @@ icmp6 {
to change the destination IP address of a packet from <var>A</var>
to
<var>B</var>, a priority-100 flow matches <code>ip &&
ip4.dst == <var>B</var> && inport == <var>GW</var></code>,
- where <var>GW</var> is the logical router gateway port configured
- for the NAT rule, with an action
+ where <var>GW</var> is the logical router gateway port corresponding
+ to the NAT rule (specified or inferred), with an action
<code>ct_dnat(<var>B</var>);</code>. The match will include
<code>ip6.dst == <var>B</var></code> in the IPv6 case.
If the NAT rule is of type dnat_and_snat and has
@@ -4683,7 +4685,8 @@ nd_ns {
outport == <var>GW</var> &&
is_chassis_resident(<var>P</var>)</code>, where <var>E</var> is the
external IP address specified in the NAT rule, <var>GW</var>
- is the distributed gateway port specified in the NAT rule.
+ is the distributed gateway port corresponding to the NAT rule
+ (specified or inferred).
For dnat_and_snat NAT rule, <var>P</var> is the logical port
specified in the NAT rule.
If <ref column="logical_port"
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 9010240a8..288d00f00 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -3366,14 +3366,6 @@
table where the NAT rule needs to be applied.
</p>
- <p>
- This column needs to be set when multiple distributed gateway ports
- are configured on a <ref table="Logical_Router"/> for the NAT rule to
- be applied. If logical router has a single distributed gateway port,
- NAT rule is applied at the distributed gateway port even if this
- column is not set.
- </p>
-
<p>
When multiple distributed gateway ports are configured on a
<ref table="Logical_Router"/>, applying a NAT rule at each of the
@@ -3391,6 +3383,17 @@
<ref column="networks" table="Logical_Router_Port"/>
<code>50.0.0.10/24</code>.
</p>
+
+ <p>
+ When a logical router has multiple distributed gateway ports and this
+ column is not set for a NAT rule, then the rule will be applied at the
+ distributed gateway port which is in the same network as the
+ <ref column="external_ip"/> of the NAT rule, if such a router port
+ exists. If logical router has a single distributed gateway port and
+ this column is not set for a NAT rule, the rule will be applied at the
+ distributed gateway port even if the router port is not in the same
+ network as the <ref column="external_ip"/> of the NAT rule.
+ </p>
</column>
<column name="options" key="stateless">
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
index f9b9defd0..8b8801af0 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -521,28 +521,28 @@ snat 30.0.0.1
192.1
snat fd01::1
fd11::/64
])
AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.0/24, : a NAT with this external_ip,
logical_ip and gateway_port already exists
+[ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and
logical_ip already exists
])
AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.10/24], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.0/24, : a NAT with this external_ip,
logical_ip and gateway_port already exists
+[ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and
logical_ip already exists
])
AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24])
AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.0/24], [1], [],
-[ovn-nbctl: a NAT with this type (snat), logical_ip (192.168.1.0/24) and
gateway_port () already exists
+[ovn-nbctl: a NAT with this type (snat), logical_ip (192.168.1.0/24) already
exists
])
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.2, : a NAT with this external_ip, logical_ip
and gateway_port already exists
+[ovn-nbctl: 30.0.0.1, 192.168.1.2: a NAT with this external_ip and logical_ip
already exists
])
AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2])
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.3], [1], [],
-[ovn-nbctl: a NAT with this type (dnat), external_ip (30.0.0.1) and
gateway_port () already exists
+[ovn-nbctl: a NAT with this type (dnat), external_ip (30.0.0.1) already exists
])
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2], [1],
[],
-[ovn-nbctl: 30.0.0.1, 192.168.1.2, : a NAT with this external_ip, logical_ip
and gateway_port already exists
+[ovn-nbctl: 30.0.0.1, 192.168.1.2: a NAT with this external_ip and logical_ip
already exists
])
AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.1
192.168.1.2])
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.3], [1],
[],
-[ovn-nbctl: a NAT with this type (dnat_and_snat), external_ip (30.0.0.1) and
gateway_port () already exists
+[ovn-nbctl: a NAT with this type (dnat_and_snat), external_ip (30.0.0.1)
already exists
])
AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2
192.168.1.3 lp0 00:00:00:04:05:06])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
@@ -754,7 +754,6 @@ AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp00 chassis1])
AT_CHECK([ovn-nbctl lr-add lr1])
AT_CHECK([ovn-nbctl lrp-add lr1 lrp10 00:00:00:01:02:05 172.64.2.10/24])
-AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 snat 172.64.0.10 20.0.0.10])
AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 dnat 172.64.1.10
20.0.0.10], [1], [],
[ovn-nbctl: lrp01 is not a distributed gateway router port.
])
@@ -764,50 +763,54 @@ AT_CHECK([ovn-nbctl --gateway-port=lrp10 lr-nat-add lr0
dnat_and_snat 172.64.2.1
AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp01 chassis2])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 172.64.1.10 20.0.0.10], [1], [],
-[ovn-nbctl: logical router: lr0 has multiple distributed gateway ports. NAT
rule needs to specify gateway_port.
+AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 snat 172.64.0.10
20.0.0.10])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 172.64.1.10 20.0.0.10])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.10 20.0.0.10], [1], [],
+[ovn-nbctl: logical router: lr0 has multiple distributed gateway ports and
gateway_port can not be determined from external IP of NAT rule.
])
AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 dnat 30.0.0.10
20.0.0.10])
-AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 dnat 30.0.0.10
20.0.0.10])
-AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 dnat 30.0.0.10
20.0.0.20], [1], [],
-[ovn-nbctl: a NAT with this type (dnat), external_ip (30.0.0.10) and
gateway_port (lrp01) already exists
+AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 snat 172.64.1.10
20.0.0.10], [1], [],
+[ovn-nbctl: 172.64.1.10, 20.0.0.10: a NAT with this external_ip and logical_ip
already exists
])
+AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 dnat 30.0.0.10
20.0.0.11], [1], [],
+[ovn-nbctl: a NAT with this type (dnat), external_ip (30.0.0.10) already exists
+])
+AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 dnat 30.0.0.10
20.0.0.10])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
TYPE GATEWAY_PORT EXTERNAL_IP EXTERNAL_PORT
LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
dnat lrp00 30.0.0.10
20.0.0.10
dnat lrp01 30.0.0.10
20.0.0.10
+snat 172.64.1.10
20.0.0.10
snat lrp00 172.64.0.10
20.0.0.10
])
AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat 30.0.0.10])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
TYPE GATEWAY_PORT EXTERNAL_IP EXTERNAL_PORT
LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
+snat 172.64.1.10
20.0.0.10
snat lrp00 172.64.0.10
20.0.0.10
])
AT_CHECK([ovn-nbctl --gateway-port=lrp00 lr-nat-add lr0 snat 30.0.0.10 20.0.0.20])
-AT_CHECK([ovn-nbctl --gateway-port=lrp01 lr-nat-add lr0 snat 30.0.0.10
20.0.0.20])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
TYPE GATEWAY_PORT EXTERNAL_IP EXTERNAL_PORT
LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
+snat 172.64.1.10
20.0.0.10
snat lrp00 172.64.0.10
20.0.0.10
snat lrp00 30.0.0.10
20.0.0.20
-snat lrp01 30.0.0.10
20.0.0.20
])
AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat lrp11], [1], [],
[ovn-nbctl: lrp11: port name not found
])
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat lrp00])
+AT_CHECK([ovn-nbctl lr-nat-del lr0 snat lrp00])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
TYPE GATEWAY_PORT EXTERNAL_IP EXTERNAL_PORT
LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-snat lrp00 172.64.0.10
20.0.0.10
-snat lrp00 30.0.0.10
20.0.0.20
-snat lrp01 30.0.0.10
20.0.0.20
+snat 172.64.1.10
20.0.0.10
])
-AT_CHECK([ovn-nbctl lr-nat-del lr0 snat lrp00])
+AT_CHECK([ovn-nbctl lr-nat-del lr0 snat lrp01])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
TYPE GATEWAY_PORT EXTERNAL_IP EXTERNAL_PORT
LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-snat lrp01 30.0.0.10
20.0.0.20
+snat 172.64.1.10
20.0.0.10
])
AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 20.0.0 lrp01], [1], [],
@@ -817,7 +820,7 @@ AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 20.0.0.10 lrp01],
[1], [],
[ovn-nbctl: no matching NAT with the type (snat), logical_ip (20.0.0.10) and
gateway_port (lrp01)
])
AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 20.0.0.10 lrp01])
-AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 20.0.0.20 lrp01])
+AT_CHECK([ovn-nbctl lr-nat-del lr0 snat])
])
dnl ---------------------------------------------------------------------
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 69ad85533..4b49540a9 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -6536,18 +6536,17 @@ check ovn-nbctl lrp-set-gateway-chassis DR-S1 gw1
check ovn-nbctl lrp-set-gateway-chassis DR-S2 gw2
check ovn-nbctl lrp-set-gateway-chassis DR-S3 gw3
-# Configure SNAT
-check ovn-nbctl --gateway-port=DR-S1 lr-nat-add DR snat 172.16.1.10
20.0.0.10
+# Configure SNAT with and without setting "gateway_port" column
+check ovn-nbctl lr-nat-add DR snat 172.16.1.10
20.0.0.10
check ovn-nbctl --gateway-port=DR-S2 lr-nat-add DR snat 10.0.0.10
20.0.0.10
-check ovn-nbctl --gateway-port=DR-S3 lr-nat-add DR snat 192.168.0.10
20.0.0.10
+check ovn-nbctl lr-nat-add DR snat 192.168.0.10
20.0.0.10
check ovn-nbctl --wait=sb sync
ovn-sbctl dump-flows DR > lrflows
AT_CAPTURE_FILE([lrflows])
-check_lr_in_arp_nat_flows() {
- AT_CHECK([grep lr_in_ip_input lrflows | grep arp | grep -e 172.16.1.10 -e
10.0.0.10 -e 192.168.0.10 | sed 's/table=../table=??/' | sort], [0], [dnl
+AT_CHECK([grep lr_in_ip_input lrflows | grep arp | grep -e 172.16.1.10 -e
10.0.0.10 -e 192.168.0.10 | sed 's/table=../table=??/' | sort], [0], [dnl
table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
10.0.0.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */
arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
172.16.1.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply
*/ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
192.168.0.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply
*/ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
@@ -6558,10 +6557,8 @@ check_lr_in_arp_nat_flows() {
table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S2" && arp.op == 1 && arp.tpa
== 10.0.0.10 && is_chassis_resident("cr-DR-S2")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op =
2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S3" && arp.op == 1 && arp.tpa
== 192.168.0.10 && is_chassis_resident("cr-DR-S3")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]];
arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
])
-}
-check_lr_in_unsnat_flows() {
- AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed
's/table=../table=??/' | sort], [0], [dnl
+AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed
's/table=../table=??/' | sort], [0], [dnl
table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.10 && inport == "DR-S2"
&& flags.loopback == 0 && is_chassis_resident("cr-DR-S2")), action=(ct_snat_in_czone;)
table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.10 && inport == "DR-S2"
&& flags.loopback == 1 && flags.use_snat_zone == 1 && is_chassis_resident("cr-DR-S2")), action=(ct_snat;)
table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.16.1.10 && inport ==
"DR-S1" && flags.loopback == 0 && is_chassis_resident("cr-DR-S1")), action=(ct_snat_in_czone;)
@@ -6569,10 +6566,8 @@ check_lr_in_unsnat_flows() {
table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 192.168.0.10 && inport ==
"DR-S3" && flags.loopback == 0 && is_chassis_resident("cr-DR-S3")), action=(ct_snat_in_czone;)
table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 192.168.0.10 && inport == "DR-S3"
&& flags.loopback == 1 && flags.use_snat_zone == 1 && is_chassis_resident("cr-DR-S3")), action=(ct_snat;)
])
-}
-check_lr_out_snat_flows() {
- AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | sed
's/table=../table=??/' | sort], [0], [dnl
+AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | sed 's/table=../table=??/'
| sort], [0], [dnl
table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_snat_in_czone(172.16.1.10);)
table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_snat_in_czone(10.0.0.10);)
table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_snat_in_czone(192.168.0.10);)
@@ -6580,45 +6575,44 @@ check_lr_out_snat_flows() {
table=??(lr_out_snat ), priority=162 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S2" && is_chassis_resident("cr-DR-S2") && reg9[[4]] == 1), action=(reg9[[4]] = 0;
ct_snat(10.0.0.10);)
table=??(lr_out_snat ), priority=162 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S3" && is_chassis_resident("cr-DR-S3") && reg9[[4]] == 1), action=(reg9[[4]] = 0;
ct_snat(192.168.0.10);)
])
-}
-
-check_lr_in_unsnat_flows
-check_lr_out_snat_flows
-check_lr_in_arp_nat_flows
check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
AT_CHECK([ovn-sbctl dump-flows DR | grep -e lr_in_unsnat -e lr_out_snat |
grep ct_snat | wc -l], [0], [0
])
-# Configure DNAT
-check ovn-nbctl --gateway-port=DR-S1 lr-nat-add DR dnat 172.16.1.10
20.0.0.10
+# Configure DNAT - 2 gateway_ports configured for same external IP
+check ovn-nbctl lr-nat-add DR dnat 172.16.1.10
20.0.0.10
check ovn-nbctl --gateway-port=DR-S2 lr-nat-add DR dnat 10.0.0.10
20.0.0.10
-check ovn-nbctl --gateway-port=DR-S3 lr-nat-add DR dnat 192.168.0.10
20.0.0.10
+check ovn-nbctl --gateway-port=DR-S3 lr-nat-add DR dnat 172.16.1.10
20.0.0.10
check ovn-nbctl --wait=sb sync
ovn-sbctl dump-flows DR > lrflows
AT_CAPTURE_FILE([lrflows])
-check_lr_in_dnat_flows() {
- AT_CHECK([grep lr_in_dnat lrflows | grep ct_dnat | sed
's/table=../table=??/' | sort], [0], [dnl
+AT_CHECK([grep lr_in_ip_input lrflows | grep arp | grep -e 172.16.1.10 -e
10.0.0.10 | sed 's/table=../table=??/' | sort], [0], [dnl
+ table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
10.0.0.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */
arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
+ table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
172.16.1.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply
*/ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
+ table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
172.16.1.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply
*/ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
+ table=??(lr_in_ip_input ), priority=91 , match=(inport == "DR-S1" && arp.op ==
1 && arp.tpa == 172.16.1.10), action=(drop;)
+ table=??(lr_in_ip_input ), priority=91 , match=(inport == "DR-S2" && arp.op ==
1 && arp.tpa == 10.0.0.10), action=(drop;)
+ table=??(lr_in_ip_input ), priority=91 , match=(inport == "DR-S3" && arp.op ==
1 && arp.tpa == 172.16.1.10), action=(drop;)
+ table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S1" && arp.op == 1 && arp.tpa ==
172.16.1.10 && is_chassis_resident("cr-DR-S1")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op =
2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
+ table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S2" && arp.op == 1 && arp.tpa ==
10.0.0.10 && is_chassis_resident("cr-DR-S2")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2;
/* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
+ table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S3" && arp.op == 1 && arp.tpa ==
172.16.1.10 && is_chassis_resident("cr-DR-S3")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op =
2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
+])
+
+AT_CHECK([grep lr_in_dnat lrflows | grep ct_dnat | sed 's/table=../table=??/'
| sort], [0], [dnl
table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.10 && inport ==
"DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_dnat_in_czone(20.0.0.10);)
table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.16.1.10 && inport ==
"DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_dnat_in_czone(20.0.0.10);)
- table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 192.168.0.10 && inport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_dnat_in_czone(20.0.0.10);)
+ table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.16.1.10 && inport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_dnat_in_czone(20.0.0.10);)
])
-}
-check_lr_out_undnat_flows() {
- AT_CHECK([grep lr_out_undnat lrflows | grep ct_dnat | sed
's/table=../table=??/' | sort], [0], [dnl
+AT_CHECK([grep lr_out_undnat lrflows | grep ct_dnat | sed
's/table=../table=??/' | sort], [0], [dnl
table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_dnat_in_czone;)
table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_dnat_in_czone;)
table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_dnat_in_czone;)
])
-}
-
-check_lr_in_dnat_flows
-check_lr_out_undnat_flows
-check_lr_in_arp_nat_flows
check ovn-nbctl --wait=sb lr-nat-del DR dnat
@@ -6627,7 +6621,7 @@ AT_CHECK([ovn-sbctl dump-flows DR | grep -e lr_in_dnat -e lr_out_undnat | grep c
# Configure DNAT_AND_SNAT
check ovn-nbctl --gateway-port=DR-S1 lr-nat-add DR dnat_and_snat 172.16.1.10
20.0.0.10
-check ovn-nbctl --gateway-port=DR-S2 lr-nat-add DR dnat_and_snat 10.0.0.10
20.0.0.10
+check ovn-nbctl lr-nat-add DR dnat_and_snat 10.0.0.10
20.0.0.10
check ovn-nbctl --gateway-port=DR-S3 lr-nat-add DR dnat_and_snat
192.168.0.10 20.0.0.10
check ovn-nbctl --wait=sb sync
@@ -6635,11 +6629,47 @@ check ovn-nbctl --wait=sb sync
ovn-sbctl dump-flows DR > lrflows
AT_CAPTURE_FILE([lrflows])
-check_lr_in_unsnat_flows
-check_lr_out_snat_flows
-check_lr_in_dnat_flows
-check_lr_out_undnat_flows
-check_lr_in_arp_nat_flows
+AT_CHECK([grep lr_in_ip_input lrflows | grep arp | grep -e 172.16.1.10 -e
10.0.0.10 -e 192.168.0.10 | sed 's/table=../table=??/' | sort], [0], [dnl
+ table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
10.0.0.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */
arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
+ table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
172.16.1.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply
*/ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
+ table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa ==
192.168.0.10), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply
*/ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport;
flags.loopback = 1; output;)
+ table=??(lr_in_ip_input ), priority=91 , match=(inport == "DR-S1" && arp.op ==
1 && arp.tpa == 172.16.1.10), action=(drop;)
+ table=??(lr_in_ip_input ), priority=91 , match=(inport == "DR-S2" && arp.op ==
1 && arp.tpa == 10.0.0.10), action=(drop;)
+ table=??(lr_in_ip_input ), priority=91 , match=(inport == "DR-S3" && arp.op ==
1 && arp.tpa == 192.168.0.10), action=(drop;)
+ table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S1" && arp.op == 1 && arp.tpa ==
172.16.1.10 && is_chassis_resident("cr-DR-S1")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op =
2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
+ table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S2" && arp.op == 1 && arp.tpa ==
10.0.0.10 && is_chassis_resident("cr-DR-S2")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2;
/* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
+ table=??(lr_in_ip_input ), priority=92 , match=(inport == "DR-S3" && arp.op == 1 && arp.tpa ==
192.168.0.10 && is_chassis_resident("cr-DR-S3")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op =
2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1;
output;)
+])
+
+AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed
's/table=../table=??/' | sort], [0], [dnl
+ table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.10 && inport == "DR-S2"
&& flags.loopback == 0 && is_chassis_resident("cr-DR-S2")), action=(ct_snat_in_czone;)
+ table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.10 && inport == "DR-S2"
&& flags.loopback == 1 && flags.use_snat_zone == 1 && is_chassis_resident("cr-DR-S2")), action=(ct_snat;)
+ table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.16.1.10 && inport ==
"DR-S1" && flags.loopback == 0 && is_chassis_resident("cr-DR-S1")), action=(ct_snat_in_czone;)
+ table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.16.1.10 && inport == "DR-S1"
&& flags.loopback == 1 && flags.use_snat_zone == 1 && is_chassis_resident("cr-DR-S1")), action=(ct_snat;)
+ table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 192.168.0.10 && inport ==
"DR-S3" && flags.loopback == 0 && is_chassis_resident("cr-DR-S3")), action=(ct_snat_in_czone;)
+ table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 192.168.0.10 && inport == "DR-S3"
&& flags.loopback == 1 && flags.use_snat_zone == 1 && is_chassis_resident("cr-DR-S3")), action=(ct_snat;)
+])
+
+AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | sed 's/table=../table=??/'
| sort], [0], [dnl
+ table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_snat_in_czone(172.16.1.10);)
+ table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_snat_in_czone(10.0.0.10);)
+ table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_snat_in_czone(192.168.0.10);)
+ table=??(lr_out_snat ), priority=162 , match=(ip && ip4.src == 20.0.0.10 && outport == "DR-S1"
&& is_chassis_resident("cr-DR-S1") && reg9[[4]] == 1), action=(reg9[[4]] = 0; ct_snat(172.16.1.10);)
+ table=??(lr_out_snat ), priority=162 , match=(ip && ip4.src == 20.0.0.10 && outport == "DR-S2"
&& is_chassis_resident("cr-DR-S2") && reg9[[4]] == 1), action=(reg9[[4]] = 0; ct_snat(10.0.0.10);)
+ table=??(lr_out_snat ), priority=162 , match=(ip && ip4.src == 20.0.0.10 && outport == "DR-S3"
&& is_chassis_resident("cr-DR-S3") && reg9[[4]] == 1), action=(reg9[[4]] = 0; ct_snat(192.168.0.10);)
+])
+
+AT_CHECK([grep lr_in_dnat lrflows | grep ct_dnat | sed 's/table=../table=??/'
| sort], [0], [dnl
+ table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 10.0.0.10 && inport ==
"DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_dnat_in_czone(20.0.0.10);)
+ table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.16.1.10 && inport ==
"DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_dnat_in_czone(20.0.0.10);)
+ table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 192.168.0.10 && inport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_dnat_in_czone(20.0.0.10);)
+])
+
+AT_CHECK([grep lr_out_undnat lrflows | grep ct_dnat | sed
's/table=../table=??/' | sort], [0], [dnl
+ table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_dnat_in_czone;)
+ table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_dnat_in_czone;)
+ table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.10 && outport ==
"DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_dnat_in_czone;)
+])
check ovn-nbctl --wait=sb lr-nat-del DR dnat_and_snat
diff --git a/tests/ovn.at b/tests/ovn.at
index 799a6aabd..fefad934b 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -29869,7 +29869,7 @@ ovn-nbctl --wait=hv sync
AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [1])
# Enable dnat_and_snat on lr, and now hv2 should see flows for lsp1.
-AT_CHECK([ovn-nbctl --wait=hv --gateway-port=lrp_lr_ls1 lr-nat-add lr
dnat_and_snat 192.168.0.1 10.0.1.3 lsp1 f0:00:00:00:00:03])
+AT_CHECK([ovn-nbctl --wait=hv --gateway-port=lrp_lr_ls1 lr-nat-add lr
dnat_and_snat 192.168.0.1 10.0.1.3 lsp1 f0:00:00:00:00:03])
AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=26 | grep 10.0.1.2], [0],
[ignore])
OVN_CLEANUP([hv1],[hv2])
diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
index 3e9176fa0..040d05227 100644
--- a/utilities/ovn-nbctl.8.xml
+++ b/utilities/ovn-nbctl.8.xml
@@ -1174,8 +1174,9 @@
applied. <var>GATEWAY_PORT</var> should reference a
<ref table="Logical_Router_Port"/> row that is a distributed gateway
port of <var>router</var>. When <var>router</var> has multiple
- distributed gateway ports, it is an error to not specify the
- <var>GATEWAY_PORT</var>.
+ distributed gateway ports and the gateway port for this NAT can't
+ be inferred from the <var>external_ip</var>, it is an error to not
+ specify the <var>GATEWAY_PORT</var>.
</p>
<p>
diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
index e747f6933..68b158e9c 100644
--- a/utilities/ovn-nbctl.c
+++ b/utilities/ovn-nbctl.c
@@ -4569,6 +4569,54 @@ done:
return ret;
}
+static bool
+ip_in_lrp_networks(const struct nbrec_logical_router_port *lrp,