@@ -370,6 +370,62 @@ destroy_lport_addresses(struct lport_addresses *laddrs)
free(laddrs->ipv6_addrs);
}
+/* Returns a string of the IP address of 'laddrs' that overlaps with 'ip_s'.
+ * If one is not found, returns NULL.
+ *
+ * The caller must not free the returned string. */
+const char *
+find_lport_address(const struct lport_addresses *laddrs, const char *ip_s)
+{
+ bool is_ipv4 = strchr(ip_s, '.') ? true : false;
+
+ if (is_ipv4) {
+ ovs_be32 ip;
+
+ if (!ip_parse(ip_s, &ip)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ip address %s", ip_s);
+ return NULL;
+ }
+
+ for (int i = 0; i < laddrs->n_ipv4_addrs; i++) {
+ const struct ipv4_netaddr *na = &laddrs->ipv4_addrs[i];
+
+ if (!((na->network ^ ip) & na->mask)) {
+ /* There should be only 1 interface that matches the
+ * supplied IP. Otherwise, it's a configuration error,
+ * because subnets of a router's interfaces should NOT
+ * overlap. */
+ return na->addr_s;
+ }
+ }
+ } else {
+ struct in6_addr ip6;
+
+ if (!ipv6_parse(ip_s, &ip6)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ipv6 address %s", ip_s);
+ return NULL;
+ }
+
+ for (int i = 0; i < laddrs->n_ipv6_addrs; i++) {
+ const struct ipv6_netaddr *na = &laddrs->ipv6_addrs[i];
+ struct in6_addr xor_addr = ipv6_addr_bitxor(&na->network, &ip6);
+ struct in6_addr and_addr = ipv6_addr_bitand(&xor_addr, &na->mask);
+
+ if (ipv6_is_zero(&and_addr)) {
+ /* There should be only 1 interface that matches the
+ * supplied IP. Otherwise, it's a configuration error,
+ * because subnets of a router's interfaces should NOT
+ * overlap. */
+ return na->addr_s;
+ }
+ }
+ }
+
+ return NULL;
+}
+
/* Go through 'addresses' and add found IPv4 addresses to 'ipv4_addrs' and
* IPv6 addresses to 'ipv6_addrs'. */
void
@@ -86,6 +86,8 @@ bool extract_lrp_networks__(char *mac, char **networks, size_t n_networks,
bool lport_addresses_is_empty(struct lport_addresses *);
void destroy_lport_addresses(struct lport_addresses *);
+const char *find_lport_address(const struct lport_addresses *laddrs,
+ const char *ip_s);
void split_addresses(const char *addresses, struct svec *ipv4_addrs,
struct svec *ipv6_addrs);
@@ -1965,6 +1965,23 @@ ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op)
}
}
+static const char *find_lrp_member_ip(const struct ovn_port *op,
+ const char *ip_s);
+
+/* Returns true if the given router port 'op' (assumed to be a distributed
+ * gateway port) is the relevant DGP where the NAT rule of the router needs to
+ * be applied. */
+static bool
+is_nat_gateway_port(const struct nbrec_nat *nat, const struct ovn_port *op)
+{
+ 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 false;
+ }
+ return true;
+}
+
enum dynamic_update_type {
NONE, /* No change to the address */
REMOVE, /* Address is no longer dynamic */
@@ -2802,9 +2819,9 @@ join_logical_ports(struct northd_input *input_data,
* port, followed by 'is_chassis_resident("LPORT_NAME")', where the
* LPORT_NAME is the name of the L3 redirect port or the name of the
* logical_port specified in a NAT rule. These strings include the
- * external IP addresses of NAT rules defined on that router which have
- * gateway_port not set or have gateway_port as the router port 'op', and all
- * of the IP addresses used in load balancer VIPs defined on that router.
+ * external IP addresses of NAT rules defined on that router whose
+ * gateway_port is router port 'op', and all of the IP addresses used in
+ * load balancer VIPs defined on that router.
*
* The caller must free each of the n returned strings with free(),
* and must free the returned array when it is no longer needed. */
@@ -2847,7 +2864,7 @@ get_nat_addresses(const struct ovn_port *op, size_t *n, bool routable_only,
/* Not including external IP of NAT rules whose gateway_port is
* not 'op'. */
- if (nat->gateway_port && nat->gateway_port != op->nbrp) {
+ if (!is_nat_gateway_port(nat, op)) {
continue;
}
@@ -3522,8 +3539,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);
@@ -8604,53 +8625,7 @@ build_bfd_table(struct lflow_input *input_data,
static const char *
find_lrp_member_ip(const struct ovn_port *op, const char *ip_s)
{
- bool is_ipv4 = strchr(ip_s, '.') ? true : false;
-
- if (is_ipv4) {
- ovs_be32 ip;
-
- if (!ip_parse(ip_s, &ip)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address %s", ip_s);
- return NULL;
- }
-
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- const struct ipv4_netaddr *na = &op->lrp_networks.ipv4_addrs[i];
-
- if (!((na->network ^ ip) & na->mask)) {
- /* There should be only 1 interface that matches the
- * supplied IP. Otherwise, it's a configuration error,
- * because subnets of a router's interfaces should NOT
- * overlap. */
- return na->addr_s;
- }
- }
- } else {
- struct in6_addr ip6;
-
- if (!ipv6_parse(ip_s, &ip6)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ipv6 address %s", ip_s);
- return NULL;
- }
-
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- const struct ipv6_netaddr *na = &op->lrp_networks.ipv6_addrs[i];
- struct in6_addr xor_addr = ipv6_addr_bitxor(&na->network, &ip6);
- struct in6_addr and_addr = ipv6_addr_bitand(&xor_addr, &na->mask);
-
- if (ipv6_is_zero(&and_addr)) {
- /* There should be only 1 interface that matches the
- * supplied IP. Otherwise, it's a configuration error,
- * because subnets of a router's interfaces should NOT
- * overlap. */
- return na->addr_s;
- }
- }
- }
-
- return NULL;
+ return find_lport_address(&op->lrp_networks, ip_s);
}
static struct ovn_port*
@@ -10385,9 +10360,9 @@ 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
- * the NAT rule. */
- if (nat->gateway_port && nat->gateway_port != op->nbrp) {
+ /* ARP/ND should be sent from distributed gateway port where the NAT rule
+ * will be applied. */
+ if (!is_nat_gateway_port(nat, op)) {
return;
}
@@ -13206,14 +13181,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);
@@ -2135,7 +2135,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>
@@ -2451,11 +2452,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>
@@ -3055,7 +3057,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
@@ -3081,7 +3083,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
@@ -3362,8 +3364,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
@@ -4681,7 +4683,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"
@@ -3380,14 +3380,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
@@ -3405,6 +3397,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">
@@ -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
@@ -756,7 +756,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.
])
@@ -766,50 +765,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], [],
@@ -819,7 +822,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 ---------------------------------------------------------------------
@@ -6564,18 +6564,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;)
@@ -6586,10 +6585,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;)
@@ -6597,10 +6594,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);)
@@ -6608,45 +6603,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
@@ -6655,7 +6649,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
@@ -6663,11 +6657,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
@@ -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>
@@ -4623,6 +4623,17 @@ done:
return ret;
}
+static bool
+ip_in_lrp_networks(const struct nbrec_logical_router_port *lrp,
+ const char *ip_s) {
+ struct lport_addresses lrp_networks;
+ extract_lrp_networks(lrp, &lrp_networks);
+
+ bool retval = find_lport_address(&lrp_networks, ip_s) ? true : false;
+ destroy_lport_addresses(&lrp_networks);
+ return retval;
+}
+
static void
nbctl_pre_lr_nat_add(struct ctl_context *ctx)
{
@@ -4641,6 +4652,8 @@ nbctl_pre_lr_nat_add(struct ctl_context *ctx)
ovsdb_idl_add_column(ctx->idl, &nbrec_nat_col_options);
ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_mac);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_networks);
ovsdb_idl_add_column(ctx->idl,
&nbrec_logical_router_port_col_gateway_chassis);
ovsdb_idl_add_column(ctx->idl,
@@ -4783,6 +4796,8 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
const char *dgw_port_name = shash_find_data(&ctx->options,
"--gateway-port");
const struct nbrec_logical_router_port *dgw_port = NULL;
+ size_t num_l3dgw_ports = 0;
+
if (dgw_port_name) {
error = lrp_by_name_or_uuid(ctx, dgw_port_name,
true, &dgw_port);
@@ -4791,14 +4806,17 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
goto cleanup;
}
- bool nat_lr_port = false;
+ bool is_lr_port = false;
for (size_t i = 0; i < lr->n_ports; i++) {
const struct nbrec_logical_router_port *lrp = lr->ports[i];
+ if (lrp->ha_chassis_group || lrp->n_gateway_chassis) {
+ num_l3dgw_ports++;
+ }
if (lrp == dgw_port) {
- nat_lr_port = true;
+ is_lr_port = true;
}
}
- if (!nat_lr_port) {
+ if (!is_lr_port) {
ctl_error(ctx, "%s is not a router port of logical router: %s.",
dgw_port_name, ctx->argv[1]);
goto cleanup;
@@ -4810,17 +4828,19 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
goto cleanup;
}
} else {
- size_t num_l3dgw_ports = 0;
for (size_t i = 0; i < lr->n_ports; i++) {
const struct nbrec_logical_router_port *lrp = lr->ports[i];
if (lrp->ha_chassis_group || lrp->n_gateway_chassis) {
num_l3dgw_ports++;
+ if (ip_in_lrp_networks(lrp, new_external_ip)) {
+ dgw_port = lrp;
+ }
}
}
- if (num_l3dgw_ports > 1) {
+ if (num_l3dgw_ports > 1 && !dgw_port) {
ctl_error(ctx, "logical router: %s has multiple distributed "
- "gateway ports. NAT rule needs to specify "
- "gateway_port.", ctx->argv[1]);
+ "gateway ports and gateway_port can not be determined "
+ "from external IP of NAT rule.", ctx->argv[1]);
goto cleanup;
}
}
@@ -4841,8 +4861,11 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
continue;
}
- if (!strcmp(nat_type, nat->type) &&
- dgw_port == nat->gateway_port) {
+ if (!strcmp(nat_type, nat->type)
+ && (num_l3dgw_ports <= 1
+ || (nat->gateway_port && nat->gateway_port == dgw_port)
+ || (!nat->gateway_port
+ && ip_in_lrp_networks(dgw_port, old_external_ip)))) {
if (!strcmp(is_snat ? new_logical_ip : new_external_ip,
is_snat ? old_logical_ip : old_external_ip)) {
if (!strcmp(is_snat ? new_external_ip : new_logical_ip,
@@ -4854,20 +4877,18 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
nbrec_nat_set_external_mac(nat, external_mac);
should_return = true;
} else {
- ctl_error(ctx, "%s, %s, %s: a NAT with this "
- "external_ip, logical_ip and "
- "gateway_port already exists",
- new_external_ip, new_logical_ip,
- dgw_port ? dgw_port->name : "");
+ ctl_error(ctx, "%s, %s: a NAT with this "
+ "external_ip and logical_ip already "
+ "exists", new_external_ip,
+ new_logical_ip);
should_return = true;
}
} else {
ctl_error(ctx, "a NAT with this type (%s), %s (%s) "
- "and gateway_port (%s) already exists",
+ "already exists",
nat_type,
is_snat ? "logical_ip" : "external_ip",
- is_snat ? new_logical_ip : new_external_ip,
- dgw_port ? dgw_port->name : "");
+ is_snat ? new_logical_ip : new_external_ip);
should_return = true;
}
}
@@ -4915,7 +4936,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
smap_add(&nat_options, "add_route", "true");
}
- if (dgw_port) {
+ if (dgw_port_name) {
nbrec_nat_update_gateway_port_addvalue(nat, dgw_port);
}
nbrec_nat_set_options(nat, &nat_options);
If multiple distributed gateway ports (DGP) are configured on a logical router, "gateway_port" column needs to be set for NAT entries of the logical router for the rules to be applied, as part of commit 2d942be (northd: Add support for NAT with multiple DGP). This patch updates the behavior by inferring the DGP where the NAT rule needs to be applied based on the "external_ip" column of the NAT rule when "gateway_port" column is not set. Signed-off-by: Abhiram Sangana <sangana.abhiram@nutanix.com> --- lib/ovn-util.c | 56 ++++++++++++++++++++ lib/ovn-util.h | 2 + northd/northd.c | 109 ++++++++++++++++---------------------- northd/ovn-northd.8.xml | 19 ++++--- ovn-nb.xml | 19 ++++--- tests/ovn-nbctl.at | 47 ++++++++-------- tests/ovn-northd.at | 102 ++++++++++++++++++++++------------- utilities/ovn-nbctl.8.xml | 5 +- utilities/ovn-nbctl.c | 57 +++++++++++++------- 9 files changed, 260 insertions(+), 156 deletions(-)