From patchwork Wed Feb 7 04:21:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guoshuai Li X-Patchwork-Id: 870207 X-Patchwork-Delegate: guru@ovn.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) 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 3zbp7Y5X41z9ryT for ; Wed, 7 Feb 2018 15:22:01 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id B5716134C; Wed, 7 Feb 2018 04:21:58 +0000 (UTC) X-Original-To: ovs-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 ECE3F1305 for ; Wed, 7 Feb 2018 04:21:56 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from smtp2203-239.mail.aliyun.com (smtp2203-239.mail.aliyun.com [121.197.203.239]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id EB8D22D4 for ; Wed, 7 Feb 2018 04:21:54 +0000 (UTC) X-Alimail-AntiSpam: AC=CONTINUE; BC=0.07444555|-1; CH=green; FP=0|0|0|0|0|-1|-1|-1; HT=e02c03275; MF=ligs@dtdream.com; NM=1; PH=DS; RN=2; RT=2; SR=0; TI=SMTPD_---.AnIifNk_1517977307; Received: from localhost.localdomain(mailfrom:ligs@dtdream.com fp:222.128.6.212) by smtp.aliyun-inc.com(10.147.41.143); Wed, 07 Feb 2018 12:21:48 +0800 From: Guoshuai Li To: ovs-dev@openvswitch.org Date: Wed, 7 Feb 2018 12:21:35 +0800 Message-Id: <20180207042135.11036-1-ligs@dtdream.com> X-Mailer: git-send-email 2.13.2.windows.1 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY 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] ovn-northd: Support logical router port with config CIDR, such as 192.168.1.0/24. 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 Support logical router port who is gateway can configure CIDR whitout IP address, so as to save the public network IP when connecting to the external network. With this configuration, the gateway's default snat functionality will not be available and VMs accessing the external network can only pass floatingip(snat_and_dnat). The modify: 1. Logical switch Ingress table 11: ARP reply for logical router port IPs. - Do not reply ARP when logical router port without IP. 2. Logical router ingress table 5: IP Routing. - Use floatingIp (sNat IP) for routeing source IP in static routing. - Use floatingIp (sNat IP) for routeing source IP in direct routing. 3. Local router ingress table 6: ARP Resolution. - Do not reply ARP when logical router port without IP. 4. Logical router ingress table 1: IP Input for IPv4. - Do not reply ARP for the router own when logical router port without IP. Signed-off-by: Guoshuai Li --- ovn/northd/ovn-northd.c | 114 +++++++++++++++++++++++++++++++++++++++++++----- ovn/ovn-nb.xml | 7 +++ tests/system-ovn.at | 8 ++++ 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 4d95a3d9d..c98bf2ab6 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -3548,6 +3548,17 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows) } } +/* Return to whether Port is only configured cidr. */ +static bool +op_is_v4_network_cidr(const struct ipv4_netaddr *ipv4_addrs) +{ + if ((ipv4_addrs->plen != 32) && + (ipv4_addrs->addr == ipv4_addrs->network)) { + return true; + } + return false; +} + static void build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, struct hmap *lflows, struct hmap *mcgroups) @@ -3679,6 +3690,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, for (size_t i = 0; i < op->n_lsp_addrs; i++) { for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) { + if (!strcmp(op->nbsp->type, "router") && + op_is_v4_network_cidr(&op->lsp_addrs[i].ipv4_addrs[j])) { + continue; + } ds_clear(&match); ds_put_format(&match, "arp.tpa == %s && arp.op == 1", op->lsp_addrs[i].ipv4_addrs[j].addr_s); @@ -4100,8 +4115,32 @@ lrport_is_enabled(const struct nbrec_logical_router_port *lrport) return !lrport->enabled || *lrport->enabled; } +/* Returns a string of the net external_ip of the router port 'op'. + * If one is not found, returns NULL. + * + * The caller must not free the returned string. */ +static const char * +find_lrp_external_ip(const struct ovn_port *op) +{ + for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { + const struct ipv4_netaddr *na = &op->lrp_networks.ipv4_addrs[i]; + for (size_t j = 0; j < op->od->nbr->n_nat; j++) { + const struct nbrec_nat *nat = op->od->nbr->nat[j]; + ovs_be32 external_ip; + if (!ip_parse(nat->external_ip, &external_ip)) { + continue; + } + if (!((na->network ^ external_ip) & na->mask)) { + return nat->external_ip; + } + } + } + return NULL; +} + /* Returns a string of the IP address of the router port 'op' that - * overlaps with 'ip_s". If one is not found, returns NULL. + * overlaps with 'ip_s". Or Returns a string of the nat external_ip + * address of the router port 'op', If one is not found, returns NULL. * * The caller must not free the returned string. */ static const char * @@ -4118,15 +4157,27 @@ find_lrp_member_ip(const struct ovn_port *op, const char *ip_s) return NULL; } + bool find_port = false; 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)) { + find_port = true; /* 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; + if (na->addr != na->network) { + return na->addr_s; + } + } + } + + if (find_port) { + /* Get NAT external IP addresses. */ + const char *external_ip = find_lrp_external_ip(op); + if (external_ip) { + return external_ip; } } } else { @@ -4156,6 +4207,20 @@ find_lrp_member_ip(const struct ovn_port *op, const char *ip_s) return NULL; } +static int +op_get_v4_ip_number(const struct ovn_port *op) +{ + int n = 0; + if (op->lrp_networks.n_ipv4_addrs) { + for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { + if (!op_is_v4_network_cidr(&op->lrp_networks.ipv4_addrs[i])) { + n++; + } + } + } + return n; +} + static void add_route(struct hmap *lflows, const struct ovn_port *op, const char *lrp_addr_s, const char *network_s, int plen, @@ -4296,12 +4361,19 @@ build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od, /* There are no IP networks configured on the router's port via * which 'route->nexthop' is theoretically reachable. But since * 'out_port' has been specified, we honor it by trying to reach - * 'route->nexthop' via the first IP address of 'out_port'. + * 'route->nexthop' via the first IP address of 'out_port' or + * nat external IP addresses of 'out_port'. * (There are cases, e.g in GCE, where each VM gets a /32 IP * address and the default gateway is still reachable from it.) */ if (is_ipv4) { if (out_port->lrp_networks.n_ipv4_addrs) { - lrp_addr_s = out_port->lrp_networks.ipv4_addrs[0].addr_s; + struct lport_addresses *address = &out_port->lrp_networks; + for (int i = 0; i < address->n_ipv4_addrs; i++) { + if (!op_is_v4_network_cidr(&address->ipv4_addrs[i])) { + lrp_addr_s = address->ipv4_addrs[i].addr_s; + break; + } + } } } else { if (out_port->lrp_networks.n_ipv6_addrs) { @@ -4328,7 +4400,7 @@ build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od, } } - if (!out_port || !lrp_addr_s) { + if (!out_port) { /* There is no matched out port. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s", @@ -4336,6 +4408,14 @@ build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od, goto free_prefix_s; } + if (!lrp_addr_s) { + /* There is no matched lrp_addr_s. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_DBG_RL(&rl, "No source ip for static route %s; next hop %s", + route->ip_prefix, route->nexthop); + goto free_prefix_s; + } + char *policy = route->policy ? route->policy : "dst-ip"; add_route(lflows, out_port, lrp_addr_s, prefix_s, plen, route->nexthop, policy); @@ -4347,14 +4427,16 @@ free_prefix_s: static void op_put_v4_networks(struct ds *ds, const struct ovn_port *op, bool add_bcast) { - if (!add_bcast && op->lrp_networks.n_ipv4_addrs == 1) { + if (!add_bcast && op_get_v4_ip_number(op) == 1) { ds_put_format(ds, "%s", op->lrp_networks.ipv4_addrs[0].addr_s); return; } ds_put_cstr(ds, "{"); for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { - ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s); + if (!op_is_v4_network_cidr(&op->lrp_networks.ipv4_addrs[i])) { + ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s); + } if (add_bcast) { ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].bcast_s); } @@ -4685,7 +4767,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } - if (op->lrp_networks.n_ipv4_addrs) { + if (op_get_v4_ip_number(op)) { /* L3 admission control: drop packets that originate from an * IPv4 address owned by the router or a broadcast address * known to the router (priority 100). */ @@ -4720,6 +4802,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, /* ARP reply. These flows reply to ARP requests for the router's own * IP address. */ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { + if (op_is_v4_network_cidr(&op->lrp_networks.ipv4_addrs[i])) { + continue; + } ds_clear(&match); ds_put_format(&match, "inport == %s && arp.tpa == %s && arp.op == 1", @@ -5660,7 +5745,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { - add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s, + const char *lrp_addr_s = op->lrp_networks.ipv4_addrs[i].addr_s; + if (op_is_v4_network_cidr(&op->lrp_networks.ipv4_addrs[i])) { + lrp_addr_s = find_lrp_member_ip(op, lrp_addr_s); + if (!lrp_addr_s) { + continue; + } + } + add_route(lflows, op, lrp_addr_s, op->lrp_networks.ipv4_addrs[i].network_s, op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL); } @@ -5709,7 +5801,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * The packet is still in peer's logical pipeline. So the match * should be on peer's outport. */ if (op->peer && op->nbrp->peer) { - if (op->lrp_networks.n_ipv4_addrs) { + if (op_get_v4_ip_number(op)) { ds_clear(&match); ds_put_format(&match, "outport == %s && reg0 == ", op->peer->json_key); @@ -5846,7 +5938,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } - if (router_port->lrp_networks.n_ipv4_addrs) { + if (op_get_v4_ip_number(router_port)) { ds_clear(&match); ds_put_format(&match, "outport == %s && reg0 == ", peer->json_key); diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index b7a5b6bf2..fc9fd42ff 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -1345,6 +1345,13 @@

+ A logical router port who is gateway port also can configure CIDR, + For example, 192.168.0.0/24. This means that traffic + to the external network can only pass NAT's external_ip. This + does not make sense for normal logical router port. +

+ +

A logical router port always adds a link-local IPv6 address (fe80::/64) automatically generated from the interface's MAC address using the modified EUI-64 format. diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 638c0b661..5bca5e3d2 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -1347,6 +1347,14 @@ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=,type=0,code=0),zone= ]) +ovn-nbctl set Logical_Router_Port alice networks=172.16.1.0/24 + +# North-South DNAT without gatewayIp: 'alice1' pings 'foo1' using 172.16.1.3. +NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + OVS_APP_EXIT_AND_WAIT([ovn-controller]) as ovn-sb