From patchwork Thu Jan 26 19:33:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mickey Spiegel X-Patchwork-Id: 720352 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3v8XDh6kkPz9ssP for ; Fri, 27 Jan 2017 06:34:44 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Gki8DsY6"; dkim-atps=neutral Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id EE02EBF0; Thu, 26 Jan 2017 19:33:29 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 57A62BC6 for ; Thu, 26 Jan 2017 19:33:28 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pg0-f68.google.com (mail-pg0-f68.google.com [74.125.83.68]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 38AA610A for ; Thu, 26 Jan 2017 19:33:27 +0000 (UTC) Received: by mail-pg0-f68.google.com with SMTP id 194so23062236pgd.0 for ; Thu, 26 Jan 2017 11:33:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=KxxttNZy1ZDTkpBEs0InxF5NQqRe263G6SXxc6hdyeE=; b=Gki8DsY6/eI0I6qurCeeO899+CmKo9nKka5keNV+0JP0+h8QcSemjv6wRJWnRK2zO2 0SU+xlzUif+xBua+AR+p2PTDOgXJrQ4BgNU8UQ2sQMUoxA4OlAGUr7jyFlfmzaTpeEji cVMFPpUYFuMlbN4Xn3sv5UXlcMh1KePoyho6+KcfZaBGOFCI+VdvIHA8YtSg5j9tbnTP sLq7k5sSnVLRzqjw3RD5Pq0yoY4xcAW1OKrxjYiX+hqEZTB+W48wQptHLGhpet8ieWGH 9KzBtIAx8tPTsxJe2/9JPqTEZyuZstqIykpXQBBLfPdilMpp2+LPnPyHb98BwEbHvcq3 84Dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=KxxttNZy1ZDTkpBEs0InxF5NQqRe263G6SXxc6hdyeE=; b=sl74w4ghfPERmiCma4QVJmx7uZXylfIwTlTHPNCO3PoQJbaFtXDsJDH45jIbQFpGS+ R2OFql1Bc3BGgCx2QYyERLN+/VzvX7mLZyEz8Nvj1Gshq0wL1eiy3/rKP3gyNe2eg0OE Ne2VNkQ5+eIbt4/LThw7Vp6YR7u34KiFXoAO5lXR70vRQ+gJuYu59A4bew520Ejzm+yB XK+5htrW1wELVH1Lt7+mzxwt+qbfctZzxnaBGYUvS+/PNnf/Ss2rpWKiH60UeV6uxupP HtFzGHgCLS0yCSBA0Y7X7almHxONIGHcVAf+tMPIw4OyNImk5LgrkiS7yORsggu9fO7U armQ== X-Gm-Message-State: AIkVDXJIpDSXGuc7CXHjmEy+rk8jRwrw8A58h6N7qdjgq4FFwdF4OWCiL0IndU9EdajDdg== X-Received: by 10.84.217.85 with SMTP id e21mr6515310plj.109.1485459206678; Thu, 26 Jan 2017 11:33:26 -0800 (PST) Received: from localhost.localdomain (c-73-202-53-195.hsd1.ca.comcast.net. [73.202.53.195]) by smtp.gmail.com with ESMTPSA id c204sm5315751pfb.51.2017.01.26.11.33.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 26 Jan 2017 11:33:26 -0800 (PST) From: Mickey Spiegel To: dev@openvswitch.org Date: Thu, 26 Jan 2017 11:33:10 -0800 Message-Id: <1485459192-9017-5-git-send-email-mickeys.dev@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1485459192-9017-1-git-send-email-mickeys.dev@gmail.com> References: <1485459192-9017-1-git-send-email-mickeys.dev@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v13 4/6] ovn: ovn-nbctl commands for distributed NAT X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch adds the new optional arguments "logical_port" and "external_mac" to lr-nat-add, and displays that information in lr-nat-list. Signed-off-by: Mickey Spiegel Acked-by: Gurucharan Shetty --- ovn/utilities/ovn-nbctl.8.xml | 27 +++++++++++++++++++--- ovn/utilities/ovn-nbctl.c | 54 +++++++++++++++++++++++++++++++++++++------ tests/ovn-nbctl.at | 47 +++++++++++++++++++++++++++++++++---- tests/system-ovn.at | 30 +++++------------------- 4 files changed, 119 insertions(+), 39 deletions(-) diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index f95b88d..d81e99f 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -444,7 +444,7 @@

NAT Commands

-
[--may-exist] lr-nat-add router type external_ip logical_ip
+
[--may-exist] lr-nat-add router type external_ip logical_ip [logical_port external_mac]

Adds the specified NAT to router. @@ -453,6 +453,13 @@ The external_ip is an IPv4 address. The logical_ip is an IPv4 network (e.g 192.168.1.0/24) or an IPv4 address. + The logical_port and external_mac are only + accepted when router is a distributed router (rather + than a gateway router) and type is + dnat_and_snat. + The logical_port is the name of an existing logical + switch port where the logical_ip resides. + The external_mac is an Ethernet address.

When type is dnat, the externally @@ -475,8 +482,22 @@ the IP address in external_ip.

- It is an error if a NAT already exists, - unless --may-exist is specified. + When the logical_port and external_mac + are specified, the NAT rule will be programmed on the chassis + where the logical_port resides. This includes + ARP replies for the external_ip, which return the + value of external_mac. All packets transmitted + with source IP address equal to external_ip will + be sent using the external_mac. +

+

+ It is an error if a NAT already exists with the same values + of router, type, external_ip, + and logical_ip, unless --may-exist is + specified. When --may-exist, + logical_port, and external_mac are all + specified, the existing values of logical_port and + external_mac are overwritten.

diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index f0ff27a..3dac434 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -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 }, diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 164c81a..cec516f 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -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 ]) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 307cbb3..638ac56 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -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])