@@ -444,7 +444,7 @@
<h1>NAT Commands</h1>
<dl>
- <dt>[<code>--may-exist</code>] <code>lr-nat-add</code> <var>router</var> <var>type</var> <var>external_ip</var> <var>logical_ip</var></dt>
+ <dt>[<code>--may-exist</code>] <code>lr-nat-add</code> <var>router</var> <var>type</var> <var>external_ip</var> <var>logical_ip</var> [<var>logical_port</var> <var>external_mac</var>]</dt>
<dd>
<p>
Adds the specified NAT to <var>router</var>.
@@ -453,6 +453,13 @@
The <var>external_ip</var> is an IPv4 address.
The <var>logical_ip</var> is an IPv4 network (e.g 192.168.1.0/24)
or an IPv4 address.
+ The <var>logical_port</var> and <var>external_mac</var> are only
+ accepted when <var>router</var> is a distributed router (rather
+ than a gateway router) and <var>type</var> is
+ <code>dnat_and_snat</code>.
+ The <var>logical_port</var> is the name of an existing logical
+ switch port where the <var>logical_ip</var> resides.
+ The <var>external_mac</var> is an Ethernet address.
</p>
<p>
When <var>type</var> is <code>dnat</code>, the externally
@@ -475,8 +482,22 @@
the IP address in <var>external_ip</var>.
</p>
<p>
- It is an error if a NAT already exists,
- unless <code>--may-exist</code> is specified.
+ When the <var>logical_port</var> and <var>external_mac</var>
+ are specified, the NAT rule will be programmed on the chassis
+ where the <var>logical_port</var> resides. This includes
+ ARP replies for the <var>external_ip</var>, which return the
+ value of <var>external_mac</var>. All packets transmitted
+ with source IP address equal to <var>external_ip</var> will
+ be sent using the <var>external_mac</var>.
+ </p>
+ <p>
+ It is an error if a NAT already exists with the same values
+ of <var>router</var>, <var>type</var>, <var>external_ip</var>,
+ and <var>logical_ip</var>, unless <code>--may-exist</code> is
+ specified. When <code>--may-exist</code>,
+ <var>logical_port</var>, and <var>external_mac</var> are all
+ specified, the existing values of <var>logical_port</var> and
+ <var>external_mac</var> are overwritten.
</p>
</dd>
@@ -390,7 +390,7 @@ Route commands:\n\
lr-route-list ROUTER print routes for ROUTER\n\
\n\
NAT commands:\n\
- lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP\n\
+ lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]\n\
add a NAT to ROUTER\n\
lr-nat-del ROUTER [TYPE [IP]]\n\
remove NATs from ROUTER\n\
@@ -2239,6 +2239,30 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
new_logical_ip = normalize_ipv4_prefix(ipv4, plen);
}
+ const char *logical_port;
+ const char *external_mac;
+ if (ctx->argc == 6) {
+ ctl_fatal("lr-nat-add with logical_port "
+ "must also specify external_mac.");
+ } else if (ctx->argc == 7) {
+ if (strcmp(nat_type, "dnat_and_snat")) {
+ ctl_fatal("logical_port and external_mac are only valid when "
+ "type is \"dnat_and_snat\".");
+ }
+
+ logical_port = ctx->argv[5];
+ lsp_by_name_or_uuid(ctx, logical_port, true);
+
+ external_mac = ctx->argv[6];
+ struct eth_addr ea;
+ if (!eth_addr_from_string(external_mac, &ea)) {
+ ctl_fatal("invalid mac address %s.", external_mac);
+ }
+ } else {
+ logical_port = NULL;
+ external_mac = NULL;
+ }
+
bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
int is_snat = !strcmp("snat", nat_type);
for (size_t i = 0; i < lr->n_nat; i++) {
@@ -2249,6 +2273,10 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
if (!strcmp(is_snat ? external_ip : new_logical_ip,
is_snat ? nat->external_ip : nat->logical_ip)) {
if (may_exist) {
+ nbrec_nat_verify_logical_port(nat);
+ nbrec_nat_verify_external_mac(nat);
+ nbrec_nat_set_logical_port(nat, logical_port);
+ nbrec_nat_set_external_mac(nat, external_mac);
free(new_logical_ip);
return;
}
@@ -2271,6 +2299,10 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
nbrec_nat_set_type(nat, nat_type);
nbrec_nat_set_external_ip(nat, external_ip);
nbrec_nat_set_logical_ip(nat, new_logical_ip);
+ if (logical_port && external_mac) {
+ nbrec_nat_set_logical_port(nat, logical_port);
+ nbrec_nat_set_external_mac(nat, external_mac);
+ }
free(new_logical_ip);
/* Insert the NAT into the logical router. */
@@ -2353,17 +2385,24 @@ nbctl_lr_nat_list(struct ctl_context *ctx)
struct smap lr_nats = SMAP_INITIALIZER(&lr_nats);
for (size_t i = 0; i < lr->n_nat; i++) {
const struct nbrec_nat *nat = lr->nat[i];
- smap_add_format(&lr_nats, nat->type, "%-19.15s%s",
- nat->external_ip, nat->logical_ip);
+ const char *key = xasprintf("%-17.13s%s", nat->type, nat->external_ip);
+ if (nat->external_mac && nat->logical_port) {
+ smap_add_format(&lr_nats, key, "%-22.18s%-21.17s%s",
+ nat->logical_ip, nat->external_mac,
+ nat->logical_port);
+ } else {
+ smap_add_format(&lr_nats, key, "%s", nat->logical_ip);
+ }
}
const struct smap_node **nodes = smap_sort(&lr_nats);
if (nodes) {
- ds_put_format(&ctx->output, "%-17.13s%-19.15s%s\n",
- "TYPE", "EXTERNAL_IP", "LOGICAL_IP");
+ ds_put_format(&ctx->output, "%-17.13s%-19.15s%-22.18s%-21.17s%s\n",
+ "TYPE", "EXTERNAL_IP", "LOGICAL_IP", "EXTERNAL_MAC",
+ "LOGICAL_PORT");
for (size_t i = 0; i < smap_count(&lr_nats); i++) {
const struct smap_node *node = nodes[i];
- ds_put_format(&ctx->output, "%-17.13s%s\n",
+ ds_put_format(&ctx->output, "%-36.32s%s\n",
node->key, node->value);
}
free(nodes);
@@ -3314,7 +3353,8 @@ static const struct ctl_command_syntax nbctl_commands[] = {
"", RO },
/* NAT commands. */
- { "lr-nat-add", 4, 4, "ROUTER TYPE EXTERNAL_IP LOGICAL_IP", NULL,
+ { "lr-nat-add", 4, 6,
+ "ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]", NULL,
nbctl_lr_nat_add, NULL, "--may-exist", RW },
{ "lr-nat-del", 1, 3, "ROUTER [TYPE [IP]]", NULL,
nbctl_lr_nat_del, NULL, "--if-exists", RW },
@@ -283,15 +283,34 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2/24], [1], [],
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2/24], [1], [],
[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address.
])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0], [1], [],
+[ovn-nbctl: lr-nat-add with logical_port must also specify external_mac.
+])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02:03], [1], [],
+[ovn-nbctl: logical_port and external_mac are only valid when type is "dnat_and_snat".
+])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02:03], [1], [],
+[ovn-nbctl: logical_port and external_mac are only valid when type is "dnat_and_snat".
+])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02:03], [1], [],
+[ovn-nbctl: lp0: port name not found
+])
+AT_CHECK([ovn-nbctl ls-add ls0])
+AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02], [1], [],
+[ovn-nbctl: invalid mac address 00:00:00:01:02.
+])
dnl Add snat and dnat
AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24])
AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 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.2])
+AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:01:02:03])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP
+TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
dnat 30.0.0.1 192.168.1.2
dnat_and_snat 30.0.0.1 192.168.1.2
+dnat_and_snat 30.0.0.2 192.168.1.3 00:00:00:01:02:03 lp0
snat 30.0.0.1 192.168.1.0/24
])
AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [],
@@ -318,10 +337,26 @@ AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.
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) and 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
+TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
+dnat 30.0.0.1 192.168.1.2
+dnat_and_snat 30.0.0.1 192.168.1.2
+dnat_and_snat 30.0.0.2 192.168.1.3 00:00:00:04:05:06 lp0
+snat 30.0.0.1 192.168.1.0/24
+])
+AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3])
+AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
+dnat 30.0.0.1 192.168.1.2
+dnat_and_snat 30.0.0.1 192.168.1.2
+dnat_and_snat 30.0.0.2 192.168.1.3
+snat 30.0.0.1 192.168.1.0/24
+])
dnl Deletes the NATs
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.2], [1], [],
-[ovn-nbctl: no matching NAT with the type (dnat_and_snat) and external_ip (30.0.0.2)
+AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.3], [1], [],
+[ovn-nbctl: no matching NAT with the type (dnat_and_snat) and external_ip (30.0.0.3)
])
AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat 30.0.0.2], [1], [],
[ovn-nbctl: no matching NAT with the type (dnat) and external_ip (30.0.0.2)
@@ -333,14 +368,16 @@ AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 192.168.10.0/24])
AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.1])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP
+TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
dnat 30.0.0.1 192.168.1.2
+dnat_and_snat 30.0.0.2 192.168.1.3
snat 30.0.0.1 192.168.1.0/24
])
AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat])
AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP
+TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
+dnat_and_snat 30.0.0.2 192.168.1.3
snat 30.0.0.1 192.168.1.0/24
])
@@ -1145,20 +1145,11 @@ ovn-nbctl lsp-add alice alice1 \
-- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
# Add DNAT rules
-ovn-nbctl -- --id=@nat create nat type="dnat_and_snat" \
- logical_ip=192.168.1.2 external_ip=172.16.1.3 \
- external_mac=\"00:00:02:02:03:04\" logical_port=foo1 \
- -- add logical_router R1 nat @nat
-
-ovn-nbctl -- --id=@nat create nat type="dnat_and_snat" \
- logical_ip=192.168.1.3 external_ip=172.16.1.4 \
- external_mac=\"00:00:02:02:03:05\" logical_port=foo2 \
- -- add logical_router R1 nat @nat
+AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04])
+AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05])
# Add a SNAT rule
-ovn-nbctl -- --id=@nat create nat type="snat" \
- logical_ip=192.168.0.0/16 external_ip=172.16.1.1 \
- -- add logical_router R1 nat @nat
+AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep ct\( | grep nat])
@@ -1300,20 +1291,11 @@ ovn-nbctl lsp-add alice alice1 \
-- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
# Add DNAT rules
-ovn-nbctl -- --id=@nat create nat type="dnat_and_snat" \
- logical_ip=192.168.1.2 external_ip=172.16.1.3 \
- external_mac=\"00:00:02:02:03:04\" logical_port=foo1 \
- -- add logical_router R1 nat @nat
-
-ovn-nbctl -- --id=@nat create nat type="dnat_and_snat" \
- logical_ip=192.168.2.2 external_ip=172.16.1.4 \
- external_mac=\"00:00:02:02:03:05\" logical_port=bar1 \
- -- add logical_router R1 nat @nat
+AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04])
+AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.2.2 bar1 00:00:02:02:03:05])
# Add a SNAT rule
-ovn-nbctl -- --id=@nat create nat type="snat" \
- logical_ip=192.168.0.0/16 external_ip=172.16.1.1 \
- -- add logical_router R1 nat @nat
+AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep ct\( | grep nat])