From patchwork Wed Oct 14 16:27:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Ivanov X-Patchwork-Id: 1382256 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.133; helo=hemlock.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=cambridgegreys.com Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CBHsK2wBsz9sVR for ; Thu, 15 Oct 2020 03:28:21 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 98AAC87E2C; Wed, 14 Oct 2020 16:28:19 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id W-gisHnCDxRB; Wed, 14 Oct 2020 16:28:14 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by hemlock.osuosl.org (Postfix) with ESMTP id DB4F687E1E; Wed, 14 Oct 2020 16:28:07 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id AE854C0891; Wed, 14 Oct 2020 16:28:07 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1C00CC1AD6 for ; Wed, 14 Oct 2020 16:28:06 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 0B3C98810B for ; Wed, 14 Oct 2020 16:28:06 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id vUUa+fOHbe0j for ; Wed, 14 Oct 2020 16:27:59 +0000 (UTC) X-Greylist: from auto-whitelisted by SQLgrey-1.7.6 Received: from www.kot-begemot.co.uk (ivanoab7.miniserver.com [37.128.132.42]) by whitealder.osuosl.org (Postfix) with ESMTPS id 9FD6B880B1 for ; Wed, 14 Oct 2020 16:27:51 +0000 (UTC) Received: from tun252.jain.kot-begemot.co.uk ([192.168.18.6] helo=jain.kot-begemot.co.uk) by www.kot-begemot.co.uk with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kSjcz-0000EK-UO; Wed, 14 Oct 2020 16:27:50 +0000 Received: from jain.kot-begemot.co.uk ([192.168.3.3]) by jain.kot-begemot.co.uk with esmtp (Exim 4.92) (envelope-from ) id 1kSjcw-0001Zd-R6; Wed, 14 Oct 2020 17:27:48 +0100 From: anton.ivanov@cambridgegreys.com To: dev@openvswitch.org Date: Wed, 14 Oct 2020 17:27:14 +0100 Message-Id: <20201014162714.5978-9-anton.ivanov@cambridgegreys.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201014162714.5978-1-anton.ivanov@cambridgegreys.com> References: <20201014162714.5978-1-anton.ivanov@cambridgegreys.com> MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett Cc: i.maximets@ovn.org, Anton Ivanov Subject: [ovs-dev] [PATCH ovn v5 9/9] ovn-northd: migrate lswitch arp responder to converged build X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Anton Ivanov Move the processing for arp responder, arp responder known ips and arp responder skip to next to helper functions. Move the invocation of the helper functions to the converged build. Signed-off-by: Anton Ivanov --- northd/ovn-northd.c | 396 +++++++++++++++++++++++--------------------- 1 file changed, 205 insertions(+), 191 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index bda83e106..9530dd55e 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -6837,198 +6837,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, struct ds match = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; struct ovn_datapath *od; - - /* Ingress table 13: ARP/ND responder, skip requests coming from localnet - * and vtep ports. (priority 100); see ovn-northd.8.xml for the - * rationale. */ struct ovn_port *op; - HMAP_FOR_EACH (op, key_node, ports) { - if (!op->nbsp) { - continue; - } - - if ((!strcmp(op->nbsp->type, "localnet")) || - (!strcmp(op->nbsp->type, "vtep"))) { - ds_clear(&match); - ds_put_format(&match, "inport == %s", op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, - 100, ds_cstr(&match), "next;", - &op->nbsp->header_); - } - } - - /* Ingress table 13: ARP/ND responder, reply for known IPs. - * (priority 50). */ - HMAP_FOR_EACH (op, key_node, ports) { - if (!op->nbsp) { - continue; - } - - if (!strcmp(op->nbsp->type, "virtual")) { - /* Handle - * - GARPs for virtual ip which belongs to a logical port - * of type 'virtual' and bind that port. - * - * - ARP reply from the virtual ip which belongs to a logical - * port of type 'virtual' and bind that port. - * */ - ovs_be32 ip; - const char *virtual_ip = smap_get(&op->nbsp->options, - "virtual-ip"); - const char *virtual_parents = smap_get(&op->nbsp->options, - "virtual-parents"); - if (!virtual_ip || !virtual_parents || - !ip_parse(virtual_ip, &ip)) { - continue; - } - - char *tokstr = xstrdup(virtual_parents); - char *save_ptr = NULL; - char *vparent; - for (vparent = strtok_r(tokstr, ",", &save_ptr); vparent != NULL; - vparent = strtok_r(NULL, ",", &save_ptr)) { - struct ovn_port *vp = ovn_port_find(ports, vparent); - if (!vp || vp->od != op->od) { - /* vparent name should be valid and it should belong - * to the same logical switch. */ - continue; - } - - ds_clear(&match); - ds_put_format(&match, "inport == \"%s\" && " - "((arp.op == 1 && arp.spa == %s && " - "arp.tpa == %s) || (arp.op == 2 && " - "arp.spa == %s))", - vparent, virtual_ip, virtual_ip, - virtual_ip); - ds_clear(&actions); - ds_put_format(&actions, - "bind_vport(%s, inport); " - "next;", - op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_ARP_ND_RSP, 100, - ds_cstr(&match), ds_cstr(&actions), - &vp->nbsp->header_); - } - - free(tokstr); - } else { - /* - * Add ARP/ND reply flows if either the - * - port is up and it doesn't have 'unknown' address defined or - * - port type is router or - * - port type is localport - */ - if (check_lsp_is_up && - !lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router") && - strcmp(op->nbsp->type, "localport")) { - continue; - } - - if (lsp_is_external(op->nbsp) || op->has_unknown) { - continue; - } - - 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++) { - ds_clear(&match); - ds_put_format(&match, "arp.tpa == %s && arp.op == 1", - op->lsp_addrs[i].ipv4_addrs[j].addr_s); - ds_clear(&actions); - ds_put_format(&actions, - "eth.dst = eth.src; " - "eth.src = %s; " - "arp.op = 2; /* ARP reply */ " - "arp.tha = arp.sha; " - "arp.sha = %s; " - "arp.tpa = arp.spa; " - "arp.spa = %s; " - "outport = inport; " - "flags.loopback = 1; " - "output;", - op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s, - op->lsp_addrs[i].ipv4_addrs[j].addr_s); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_ARP_ND_RSP, 50, - ds_cstr(&match), - ds_cstr(&actions), - &op->nbsp->header_); - - /* Do not reply to an ARP request from the port that owns - * the address (otherwise a DHCP client that ARPs to check - * for a duplicate address will fail). Instead, forward - * it the usual way. - * - * (Another alternative would be to simply drop the packet. - * If everything is working as it is configured, then this - * would produce equivalent results, since no one should - * reply to the request. But ARPing for one's own IP - * address is intended to detect situations where the - * network is not working as configured, so dropping the - * request would frustrate that intent.) */ - ds_put_format(&match, " && inport == %s", op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_ARP_ND_RSP, 100, - ds_cstr(&match), "next;", - &op->nbsp->header_); - } - - /* For ND solicitations, we need to listen for both the - * unicast IPv6 address and its all-nodes multicast address, - * but always respond with the unicast IPv6 address. */ - for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) { - ds_clear(&match); - ds_put_format(&match, - "nd_ns && ip6.dst == {%s, %s} && nd.target == %s", - op->lsp_addrs[i].ipv6_addrs[j].addr_s, - op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s, - op->lsp_addrs[i].ipv6_addrs[j].addr_s); - - ds_clear(&actions); - ds_put_format(&actions, - "%s { " - "eth.src = %s; " - "ip6.src = %s; " - "nd.target = %s; " - "nd.tll = %s; " - "outport = inport; " - "flags.loopback = 1; " - "output; " - "};", - !strcmp(op->nbsp->type, "router") ? - "nd_na_router" : "nd_na", - op->lsp_addrs[i].ea_s, - op->lsp_addrs[i].ipv6_addrs[j].addr_s, - op->lsp_addrs[i].ipv6_addrs[j].addr_s, - op->lsp_addrs[i].ea_s); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_ARP_ND_RSP, 50, - ds_cstr(&match), - ds_cstr(&actions), - &op->nbsp->header_); - - /* Do not reply to a solicitation from the port that owns - * the address (otherwise DAD detection will fail). */ - ds_put_format(&match, " && inport == %s", op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_ARP_ND_RSP, 100, - ds_cstr(&match), "next;", - &op->nbsp->header_); - } - } - } - } - - /* Ingress table 13: ARP/ND responder, by default goto next. - * (priority 0)*/ - HMAP_FOR_EACH (od, key_node, datapaths) { - if (!od->nbs) { - continue; - } - - ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;"); - } /* Ingress table 13: ARP/ND responder for service monitor source ip. * (priority 110)*/ @@ -7524,6 +7333,205 @@ static void } } +/* Ingress table 13: ARP/ND responder, skip requests coming from localnet + * and vtep ports. (priority 100); see ovn-northd.8.xml for the + * rationale. */ + +static void + build_lswitch_arp_nd_responder( + struct ovn_port *op, struct hmap *lflows, + struct ds *match) +{ + if (op->nbsp) { + if ((!strcmp(op->nbsp->type, "localnet")) || + (!strcmp(op->nbsp->type, "vtep"))) { + ds_clear(match); + ds_put_format(match, "inport == %s", op->json_key); + ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, + 100, ds_cstr(match), "next;", + &op->nbsp->header_); + } + } +} + +/* Ingress table 13: ARP/ND responder, reply for known IPs. + * (priority 50). */ +static void + build_lswitch_arp_nd_responder_known( + struct ovn_port *op, struct hmap *lflows, + struct hmap *ports, + struct ds *match, struct ds *actions) +{ + if (!op->nbsp) { + return; + } + if (!strcmp(op->nbsp->type, "virtual")) { + /* Handle + * - GARPs for virtual ip which belongs to a logical port + * of type 'virtual' and bind that port. + * + * - ARP reply from the virtual ip which belongs to a logical + * port of type 'virtual' and bind that port. + * */ + ovs_be32 ip; + const char *virtual_ip = smap_get(&op->nbsp->options, + "virtual-ip"); + const char *virtual_parents = smap_get(&op->nbsp->options, + "virtual-parents"); + if (!virtual_ip || !virtual_parents || + !ip_parse(virtual_ip, &ip)) { + return; + } + + char *tokstr = xstrdup(virtual_parents); + char *save_ptr = NULL; + char *vparent; + for (vparent = strtok_r(tokstr, ",", &save_ptr); vparent != NULL; + vparent = strtok_r(NULL, ",", &save_ptr)) { + struct ovn_port *vp = ovn_port_find(ports, vparent); + if (!vp || vp->od != op->od) { + /* vparent name should be valid and it should belong + * to the same logical switch. */ + continue; + } + + ds_clear(match); + ds_put_format(match, "inport == \"%s\" && " + "((arp.op == 1 && arp.spa == %s && " + "arp.tpa == %s) || (arp.op == 2 && " + "arp.spa == %s))", + vparent, virtual_ip, virtual_ip, + virtual_ip); + ds_clear(actions); + ds_put_format(actions, + "bind_vport(%s, inport); " + "next;", + op->json_key); + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_ARP_ND_RSP, 100, + ds_cstr(match), ds_cstr(actions), + &vp->nbsp->header_); + } + + free(tokstr); + } else { + /* + * Add ARP/ND reply flows if either the + * - port is up and it doesn't have 'unknown' address defined or + * - port type is router or + * - port type is localport + */ + if (check_lsp_is_up && + !lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router") && + strcmp(op->nbsp->type, "localport")) { + return; + } + + if (lsp_is_external(op->nbsp) || op->has_unknown) { + return; + } + + 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++) { + ds_clear(match); + ds_put_format(match, "arp.tpa == %s && arp.op == 1", + op->lsp_addrs[i].ipv4_addrs[j].addr_s); + ds_clear(actions); + ds_put_format(actions, + "eth.dst = eth.src; " + "eth.src = %s; " + "arp.op = 2; /* ARP reply */ " + "arp.tha = arp.sha; " + "arp.sha = %s; " + "arp.tpa = arp.spa; " + "arp.spa = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output;", + op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s, + op->lsp_addrs[i].ipv4_addrs[j].addr_s); + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_ARP_ND_RSP, 50, + ds_cstr(match), + ds_cstr(actions), + &op->nbsp->header_); + + /* Do not reply to an ARP request from the port that owns + * the address (otherwise a DHCP client that ARPs to check + * for a duplicate address will fail). Instead, forward + * it the usual way. + * + * (Another alternative would be to simply drop the packet. + * If everything is working as it is configured, then this + * would produce equivalent results, since no one should + * reply to the request. But ARPing for one's own IP + * address is intended to detect situations where the + * network is not working as configured, so dropping the + * request would frustrate that intent.) */ + ds_put_format(match, " && inport == %s", op->json_key); + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_ARP_ND_RSP, 100, + ds_cstr(match), "next;", + &op->nbsp->header_); + } + + /* For ND solicitations, we need to listen for both the + * unicast IPv6 address and its all-nodes multicast address, + * but always respond with the unicast IPv6 address. */ + for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) { + ds_clear(match); + ds_put_format(match, + "nd_ns && ip6.dst == {%s, %s} && nd.target == %s", + op->lsp_addrs[i].ipv6_addrs[j].addr_s, + op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s, + op->lsp_addrs[i].ipv6_addrs[j].addr_s); + + ds_clear(actions); + ds_put_format(actions, + "%s { " + "eth.src = %s; " + "ip6.src = %s; " + "nd.target = %s; " + "nd.tll = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output; " + "};", + !strcmp(op->nbsp->type, "router") ? + "nd_na_router" : "nd_na", + op->lsp_addrs[i].ea_s, + op->lsp_addrs[i].ipv6_addrs[j].addr_s, + op->lsp_addrs[i].ipv6_addrs[j].addr_s, + op->lsp_addrs[i].ea_s); + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_ARP_ND_RSP, 50, + ds_cstr(match), + ds_cstr(actions), + &op->nbsp->header_); + + /* Do not reply to a solicitation from the port that owns + * the address (otherwise DAD detection will fail). */ + ds_put_format(match, " && inport == %s", op->json_key); + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_ARP_ND_RSP, 100, + ds_cstr(match), "next;", + &op->nbsp->header_); + } + } + } +} + +/* Ingress table 13: ARP/ND responder, by default goto next. + * (priority 0)*/ +static void + build_lswitch_arp_nd_responder_next( + struct ovn_datapath *od, struct hmap *lflows) +{ + if (od->nbs) { + ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;"); + } +} + /* Returns a string of the IP address of the router port 'op' that * overlaps with 'ip_s". If one is not found, returns NULL. @@ -11374,6 +11382,7 @@ build_lswitch_and_lrouter_iterate_by_od( build_fwd_group_lflows(od, lsi->lflows); build_lswitch_lflows_admission_control(od, lsi->lflows); build_lswitch_input_port_sec_od(od, lsi->lflows); + build_lswitch_arp_nd_responder_next(od, lsi->lflows); /* Build Logical Router Flows. */ build_adm_ctrl_flows_for_lrouter(od, lsi->lflows); @@ -11406,6 +11415,11 @@ build_lswitch_and_lrouter_iterate_by_op( /* Build Logical Switch Flows. */ build_lswitch_input_port_sec_op(op, lsi->lflows, &lsi->actions, &lsi->match); + build_lswitch_input_port_sec_op(op, lsi->lflows, &lsi->actions, + &lsi->match); + build_lswitch_arp_nd_responder(op, lsi->lflows, &lsi->match); + build_lswitch_arp_nd_responder_known(op, lsi->lflows, lsi->ports, + &lsi->match, &lsi->actions); /* Build Logical Router Flows. */ build_adm_ctrl_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,