From patchwork Fri Mar 27 10:10:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1262687 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.138; helo=whitealder.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=g4o9C4V8; dkim-atps=neutral Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48pd1X06Rzz9sSH for ; Fri, 27 Mar 2020 21:11:43 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 56BAA88655; Fri, 27 Mar 2020 10:11:41 +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 Sacb888gPB-w; Fri, 27 Mar 2020 10:11:27 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by whitealder.osuosl.org (Postfix) with ESMTP id 84D6988693; Fri, 27 Mar 2020 10:11:27 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 54C9FC18DA; Fri, 27 Mar 2020 10:11:27 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 3A2BEC1D7C for ; Fri, 27 Mar 2020 10:11:26 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 1FDA18951D for ; Fri, 27 Mar 2020 10:11:26 +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 y4vqBR7oNRTU for ; Fri, 27 Mar 2020 10:11:24 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-74.mimecast.com (us-smtp-delivery-74.mimecast.com [216.205.24.74]) by hemlock.osuosl.org (Postfix) with ESMTPS id 01920894D6 for ; Fri, 27 Mar 2020 10:11:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1585303882; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=N5GJ0DsteaRnsvYus/R43NL2dDaOKqiu2lCVKVNpVPc=; b=g4o9C4V8elJjyo6wWmXTh0nXtLwheHFq8Q/ceiH/WeoNVUe/xsAQxHNFwxBkqfewWZCjgc 8dF9Cl+yHSPq4Zkq4a/quFXWrXcDKdJ8X6Z7c0gelrEya+xMlJNHgxTPlqKAKerKfEdxOP //SgCh4QDGHkKrA8/oA2BNOeZ58iNUk= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-219-LIu7GFlIMS2HfMirNz-FGA-1; Fri, 27 Mar 2020 06:11:18 -0400 X-MC-Unique: LIu7GFlIMS2HfMirNz-FGA-1 Received: by mail-wm1-f69.google.com with SMTP id m4so5419314wme.0 for ; Fri, 27 Mar 2020 03:11:18 -0700 (PDT) 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:mime-version:content-transfer-encoding; bh=tRNnNHCvt/eglBv1XIfkA6aPTZhe5xBsAkPChAdEZ1o=; b=JnsLKHYLyZTZd7e5OXYeWvcrW3roi+AIboPrJa474gjNLHUEQZbY07HjK5NfQwXSYQ RVeRPKMcxmF+blg9sA4UyNnMEzIhN3eoDXbH/WnHw0IYtWi4RjMuU7r1dsyPFgHZOjRd YAK5xgt2+B6Tdss5M+3R9m1ikVatihYumNlGXBI41+4fgsrsmxGydJNCklPa8zQrp2Ij 1s81e8zv+ige2zWkHAouldsMV/6xuIg0tbNNqEQebkVGUUZP5tWRijca5QGSmmz2LBoN yWp0kyYHLxzhmv9v+TxMKPI0MIvnmTTRI02WHRgLcyAfDtf4+x89uI5VG8FH9Vh0bHMh RACQ== X-Gm-Message-State: ANhLgQ1FJHQK3jLLg8Y7ZzQ4BXE2maWXGB4p9rrT4aMlKJA+cY0TbK8N Xc2jjG97rsg9aJUHKbeiY9lGaaAIdd8EYnS+F55sjgnfoH8PP7yb8Wyr9ZcK9ATY2RJg9gqSDaF G76nXDNMov/IQ+7nmJA== X-Received: by 2002:a1c:2c85:: with SMTP id s127mr4616866wms.18.1585303876807; Fri, 27 Mar 2020 03:11:16 -0700 (PDT) X-Google-Smtp-Source: ADFU+vtsLZydCimwsC/6+bd1F1CXJ7hs/TnR/d5EDPa1GMPO/WUIKyoHHlLZhVZfevDpnMUZqnfZAQ== X-Received: by 2002:a1c:2c85:: with SMTP id s127mr4616806wms.18.1585303875990; Fri, 27 Mar 2020 03:11:15 -0700 (PDT) Received: from lore-desk-wlan.redhat.com ([151.48.139.19]) by smtp.gmail.com with ESMTPSA id k15sm7637455wrm.55.2020.03.27.03.11.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Mar 2020 03:11:15 -0700 (PDT) From: Lorenzo Bianconi To: ovs-dev@openvswitch.org Date: Fri, 27 Mar 2020 11:10:59 +0100 Message-Id: <4ee8517f47d0946b8c562705b9302792726c3059.1585303423.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH v8 ovn 1/2] controller: add ipv6 prefix delegation state machine 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" Introduce IPv6 Prefix delegation state machine according to RFC 3633 https://tools.ietf.org/html/rfc3633. Add handle_dhcpv6_reply controller action to parse advertise/reply from IPv6 delegation server. Advertise/reply are parsed running respectively: - pinctrl_parse_dhcv6_advt - pinctrl_parse_dhcv6_reply The IPv6 requesting router starts sending dhcpv6 solicit through the logical router port marked with ipv6_prefix_delegation set to true. An IPv6 prefix will be requested for each logical router port marked with "prefix" set to true in option column of logical router port table. Save IPv6 prefix received by IPv6 delegation router in the options columns of SB port binding table in order to be reused by Router Advertisement framework run by ovn logical router pipeline. IPv6 Prefix delegation state machine is enabled on Gateway Router or on a Gateway Router Port Signed-off-by: Lorenzo Bianconi --- controller/pinctrl.c | 673 +++++++++++++++++++++++++++++++++++++++++- controller/pinctrl.h | 1 + include/ovn/actions.h | 8 +- lib/actions.c | 16 + lib/ovn-l7.h | 19 ++ ovn-sb.xml | 18 ++ tests/ovn.at | 4 + utilities/ovn-trace.c | 2 + 8 files changed, 733 insertions(+), 8 deletions(-) diff --git a/controller/pinctrl.c b/controller/pinctrl.c index 8bf19776c..37be05cb5 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -270,6 +270,19 @@ static void pinctrl_ip_mcast_handle( const struct match *md, struct ofpbuf *userdata); +static void init_ipv6_prefixd(void); +static void destroy_ipv6_prefixd(void); +static void ipv6_prefixd_wait(long long int timeout); +static void +prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct hmap *local_datapaths, + const struct sbrec_chassis *chassis, + const struct sset *active_tunnels) + OVS_REQUIRES(pinctrl_mutex); +static void +send_ipv6_prefixd(struct rconn *swconn, long long int *send_prefixd_time) + OVS_REQUIRES(pinctrl_mutex); static bool may_inject_pkts(void); static void init_put_vport_bindings(void); @@ -313,6 +326,10 @@ static void pinctrl_compose_ipv6(struct dp_packet *packet, uint8_t ip_proto, uint8_t ttl, uint16_t ip_payload_len); +static void +put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, + struct ofpbuf *ofpacts); + COVERAGE_DEFINE(pinctrl_drop_put_mac_binding); COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map); COVERAGE_DEFINE(pinctrl_drop_controller_event); @@ -470,6 +487,7 @@ pinctrl_init(void) init_put_mac_bindings(); init_send_garps_rarps(); init_ipv6_ras(); + init_ipv6_prefixd(); init_buffered_packets_map(); init_event_table(); ip_mcast_snoop_init(); @@ -557,6 +575,50 @@ set_actions_and_enqueue_msg(struct rconn *swconn, ofpbuf_uninit(&ofpacts); } +static struct shash ipv6_prefixd; + +enum { + PREFIX_SOLICIT, + PREFIX_REQUEST, + PREFIX_PENDING, + PREFIX_DONE, +}; + +struct ipv6_prefixd_state { + long long int next_announce; + struct in6_addr ipv6_addr; + struct eth_addr ea; + struct eth_addr cmac; + int64_t port_key; + int64_t metadata; + struct in6_addr prefix; + unsigned plife_time; + unsigned vlife_time; + unsigned aid; + unsigned t1; + unsigned t2; + unsigned plen; + int state; +}; + +static void +init_ipv6_prefixd(void) +{ + shash_init(&ipv6_prefixd); +} + +static void +destroy_ipv6_prefixd(void) +{ + struct shash_node *iter, *next; + SHASH_FOR_EACH_SAFE (iter, next, &ipv6_prefixd) { + struct ipv6_prefixd_state *pfd = iter->data; + free(pfd); + shash_delete(&ipv6_prefixd, iter); + } + shash_destroy(&ipv6_prefixd); +} + struct buffer_info { struct ofpbuf ofpacts; ofp_port_t ofp_port; @@ -972,6 +1034,285 @@ is_dhcp_flags_broadcast(ovs_be16 flags) return flags & htons(DHCP_BROADCAST_FLAG); } +static struct ipv6_prefixd_state * +pinctrl_find_prefixd_state(const struct flow *ip_flow, unsigned aid) +{ + struct shash_node *iter; + + SHASH_FOR_EACH (iter, &ipv6_prefixd) { + struct ipv6_prefixd_state *pfd = iter->data; + if (IN6_ARE_ADDR_EQUAL(&pfd->ipv6_addr, &ip_flow->ipv6_dst) && + eth_addr_equals(pfd->ea, ip_flow->dl_dst) && + pfd->aid == aid) { + return pfd; + } + } + return NULL; +} + +static void +pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const struct flow *ip_flow, + struct dp_packet *pkt_in, const struct match *md) +{ + struct udp_header *udp_in = dp_packet_l4(pkt_in); + size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in)); + unsigned char *in_dhcpv6_data = (unsigned char *)(udp_in + 1); + uint8_t *data, *end = (uint8_t *)udp_in + dlen; + int len = 0, aid = 0; + + data = xmalloc(dlen); + /* skip DHCPv6 common header */ + in_dhcpv6_data += 4; + while (in_dhcpv6_data < end) { + struct dhcpv6_opt_header *in_opt = + (struct dhcpv6_opt_header *)in_dhcpv6_data; + int opt_len = sizeof *in_opt + ntohs(in_opt->len); + + if (dlen < opt_len + len) { + goto out; + } + + switch (ntohs(in_opt->code)) { + case DHCPV6_OPT_IA_PD: { + struct dhcpv6_opt_ia_na *ia_na = (struct dhcpv6_opt_ia_na *)in_opt; + int orig_len = len, hdr_len = 0, size = sizeof *in_opt + 12; + + aid = ntohl(ia_na->iaid); + memcpy(&data[len], in_opt, size); + in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size); + len += size; + + while (size < opt_len) { + int flen = sizeof *in_opt + ntohs(in_opt->len); + + if (dlen < flen + len) { + goto out; + } + + if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) { + memcpy(&data[len], in_opt, flen); + hdr_len += flen; + len += flen; + } + if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) { + struct dhcpv6_opt_status *status; + + status = (struct dhcpv6_opt_status *)in_opt; + if (ntohs(status->status_code)) { + goto out; + } + } + size += flen; + in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size); + } + in_opt = (struct dhcpv6_opt_header *)&data[orig_len]; + in_opt->len = htons(hdr_len + 12); + break; + } + case DHCPV6_OPT_SERVER_ID_CODE: + case DHCPV6_OPT_CLIENT_ID_CODE: + memcpy(&data[len], in_opt, opt_len); + len += opt_len; + break; + default: + break; + } + in_dhcpv6_data += opt_len; + } + + struct ipv6_prefixd_state *pfd = pinctrl_find_prefixd_state(ip_flow, aid); + if (!pfd) { + goto out; + } + + pfd->state = PREFIX_REQUEST; + + uint64_t packet_stub[256 / 8]; + struct dp_packet packet; + + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); + eth_compose(&packet, ip_flow->dl_src, ip_flow->dl_dst, ETH_TYPE_IPV6, + IPV6_HEADER_LEN); + + struct udp_header *udp_h = compose_ipv6(&packet, IPPROTO_UDP, + &ip_flow->ipv6_dst, + &ip_flow->ipv6_src, 0, 0, 255, + len + UDP_HEADER_LEN + 4); + udp_h->udp_len = htons(len + UDP_HEADER_LEN + 4); + udp_h->udp_csum = 0; + packet_set_udp_port(&packet, htons(546), htons(547)); + + unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1); + *dhcp_hdr = DHCPV6_MSG_TYPE_REQUEST; + memcpy(dhcp_hdr + 4, data, len); + + uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(&packet)); + csum = csum_continue(csum, udp_h, dp_packet_size(&packet) - + ((const unsigned char *)udp_h - + (const unsigned char *)dp_packet_eth(&packet))); + udp_h->udp_csum = csum_finish(csum); + if (!udp_h->udp_csum) { + udp_h->udp_csum = htons(0xffff); + } + + if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) { + eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q), + ip_flow->vlans[0].tci); + } + + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + enum ofp_version version = rconn_get_version(swconn); + put_load(ntohll(md->flow.metadata), MFF_LOG_DATAPATH, 0, 64, &ofpacts); + put_load(md->flow.regs[MFF_LOG_INPORT - MFF_REG0], MFF_LOG_OUTPORT, + 0, 32, &ofpacts); + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts); + resubmit->in_port = OFPP_CONTROLLER; + resubmit->table_id = OFTABLE_REMOTE_OUTPUT; + + struct ofputil_packet_out po = { + .packet = dp_packet_data(&packet), + .packet_len = dp_packet_size(&packet), + .buffer_id = UINT32_MAX, + .ofpacts = ofpacts.data, + .ofpacts_len = ofpacts.size, + }; + match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); + queue_msg(swconn, ofputil_encode_packet_out(&po, proto)); + dp_packet_uninit(&packet); + ofpbuf_uninit(&ofpacts); + +out: + free(data); +} + +/* Called with in the pinctrl_handler thread context. */ +static void +notify_pinctrl_main(void) +{ + seq_change(pinctrl_main_seq); +} + +static void +pinctrl_prefixd_state_handler(const struct flow *ip_flow, + struct in6_addr addr, unsigned aid, + char prefix_len, unsigned t1, unsigned t2, + unsigned plife_time, unsigned vlife_time) +{ + struct ipv6_prefixd_state *pfd; + + pfd = pinctrl_find_prefixd_state(ip_flow, aid); + if (pfd) { + pfd->state = PREFIX_PENDING; + pfd->plife_time = plife_time; + pfd->vlife_time = vlife_time; + pfd->plen = prefix_len; + pfd->prefix = addr; + pfd->t1 = t1; + pfd->t2 = t2; + notify_pinctrl_main(); + } +} + +static void +pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in, + const struct flow *ip_flow) + OVS_REQUIRES(pinctrl_mutex) +{ + struct udp_header *udp_in = dp_packet_l4(pkt_in); + unsigned char *in_dhcpv6_data = (unsigned char *)(udp_in + 1); + size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in)); + unsigned t1 = 0, t2 = 0, vlife_time = 0, plife_time = 0; + uint8_t *end = (uint8_t *)udp_in + dlen; + uint8_t prefix_len = 0; + struct in6_addr ipv6; + bool status = false; + unsigned aid = 0; + + memset(&ipv6, 0, sizeof (struct in6_addr)); + /* skip DHCPv6 common header */ + in_dhcpv6_data += 4; + while (in_dhcpv6_data < end) { + struct dhcpv6_opt_header *in_opt = + (struct dhcpv6_opt_header *)in_dhcpv6_data; + int opt_len = sizeof *in_opt + ntohs(in_opt->len); + + if (in_dhcpv6_data + opt_len > end) { + break; + } + + switch (ntohs(in_opt->code)) { + case DHCPV6_OPT_IA_PD: { + int size = sizeof *in_opt + 12; + in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size); + struct dhcpv6_opt_ia_na *ia_na = + (struct dhcpv6_opt_ia_na *)in_dhcpv6_data; + + aid = ntohl(ia_na->iaid); + t1 = ntohl(ia_na->t1); + t2 = ntohl(ia_na->t2); + if (t1 > t2 && t2 > 0) { + break; + } + + while (size < opt_len) { + if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) { + struct dhcpv6_opt_ia_prefix *ia_hdr = + (struct dhcpv6_opt_ia_prefix *)(in_dhcpv6_data + size); + + prefix_len = ia_hdr->plen; + plife_time = ntohl(ia_hdr->plife_time); + vlife_time = ntohl(ia_hdr->vlife_time); + memcpy(&ipv6, &ia_hdr->ipv6, sizeof (struct in6_addr)); + } + if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) { + struct dhcpv6_opt_status *status_hdr; + + status_hdr = (struct dhcpv6_opt_status *)in_opt; + status = ntohs(status_hdr->status_code) == 0; + } + size += sizeof *in_opt + ntohs(in_opt->len); + in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size); + } + break; + } + default: + break; + } + in_dhcpv6_data += opt_len; + } + if (status) { + pinctrl_prefixd_state_handler(ip_flow, ipv6, aid, prefix_len, + t1, t2, plife_time, vlife_time); + } +} + +static void +pinctrl_handle_dhcp6_server(struct rconn *swconn, const struct flow *ip_flow, + struct dp_packet *pkt_in, const struct match *md) + OVS_REQUIRES(pinctrl_mutex) +{ + if (ip_flow->dl_type != htons(ETH_TYPE_IPV6) || + ip_flow->nw_proto != IPPROTO_UDP) { + return; + } + + struct udp_header *udp_in = dp_packet_l4(pkt_in); + unsigned char *dhcp_hdr = (unsigned char *)(udp_in + 1); + + switch (*dhcp_hdr) { + case DHCPV6_MSG_TYPE_ADVT: + pinctrl_parse_dhcpv6_advt(swconn, ip_flow, pkt_in, md); + break; + case DHCPV6_MSG_TYPE_REPLY: + pinctrl_parse_dhcpv6_reply(pkt_in, ip_flow); + break; + default: + break; + } +} + /* Called with in the pinctrl_handler thread context. */ static void pinctrl_handle_put_dhcp_opts( @@ -2012,6 +2353,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) pinctrl_handle_bind_vport(&pin.flow_metadata.flow, &userdata); ovs_mutex_unlock(&pinctrl_mutex); break; + case ACTION_OPCODE_DHCP6_SERVER: + ovs_mutex_lock(&pinctrl_mutex); + pinctrl_handle_dhcp6_server(swconn, &headers, &packet, + &pin.flow_metadata); + ovs_mutex_unlock(&pinctrl_mutex); + break; case ACTION_OPCODE_HANDLE_SVC_CHECK: ovs_mutex_lock(&pinctrl_mutex); @@ -2062,13 +2409,6 @@ notify_pinctrl_handler(void) seq_change(pinctrl_handler_seq); } -/* Called with in the pinctrl_handler thread context. */ -static void -notify_pinctrl_main(void) -{ - seq_change(pinctrl_main_seq); -} - /* pinctrl_handler pthread function. */ static void * pinctrl_handler(void *arg_) @@ -2090,6 +2430,7 @@ pinctrl_handler(void *arg_) /* Next multicast query (IGMP) in ms. */ static long long int send_mcast_query_time = LLONG_MAX; static long long int svc_monitors_next_run_time = LLONG_MAX; + static long long int send_prefixd_time = LLONG_MAX; swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); @@ -2143,6 +2484,7 @@ pinctrl_handler(void *arg_) ovs_mutex_lock(&pinctrl_mutex); send_garp_rarp_run(swconn, &send_garp_rarp_time); send_ipv6_ras(swconn, &send_ipv6_ra_time); + send_ipv6_prefixd(swconn, &send_prefixd_time); send_mac_binding_buffered_pkts(swconn); ovs_mutex_unlock(&pinctrl_mutex); @@ -2160,6 +2502,7 @@ pinctrl_handler(void *arg_) ipv6_ra_wait(send_ipv6_ra_time); ip_mcast_querier_wait(send_mcast_query_time); svc_monitors_wait(svc_monitors_next_run_time); + ipv6_prefixd_wait(send_prefixd_time); new_seq = seq_read(pinctrl_handler_seq); seq_wait(pinctrl_handler_seq, new_seq); @@ -2211,6 +2554,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, sbrec_port_binding_by_name, br_int, chassis, local_datapaths, active_tunnels); prepare_ipv6_ras(local_datapaths); + prepare_ipv6_prefixd(ovnsb_idl_txn, sbrec_port_binding_by_name, + local_datapaths, chassis, active_tunnels); sync_dns_cache(dns_table); controller_event_run(ovnsb_idl_txn, ce_table, chassis); ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths, @@ -2658,6 +3003,168 @@ send_ipv6_ras(struct rconn *swconn, long long int *send_ipv6_ra_time) } } +static void +compose_prefixd_solicit(struct dp_packet *b, + struct ipv6_prefixd_state *pfd, + const struct eth_addr eth_dst, + const struct in6_addr *ipv6_dst) +{ + eth_compose(b, eth_dst, pfd->ea, ETH_TYPE_IPV6, IPV6_HEADER_LEN); + + int payload = sizeof(struct dhcpv6_opt_server_id) + + sizeof(struct dhcpv6_opt_ia_na); + if (ipv6_addr_is_set(&pfd->prefix)) { + payload += sizeof(struct dhcpv6_opt_ia_prefix); + } + int len = UDP_HEADER_LEN + 4 + payload; + struct udp_header *udp_h = compose_ipv6(b, IPPROTO_UDP, &pfd->ipv6_addr, + ipv6_dst, 0, 0, 255, len); + udp_h->udp_len = htons(len); + udp_h->udp_csum = 0; + packet_set_udp_port(b, htons(546), htons(547)); + + unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1); + *dhcp_hdr = DHCPV6_MSG_TYPE_SOLICIT; + + struct dhcpv6_opt_server_id *opt_client_id = + (struct dhcpv6_opt_server_id *)(dhcp_hdr + 4); + opt_client_id->opt.code = htons(DHCPV6_OPT_CLIENT_ID_CODE); + opt_client_id->opt.len = htons(sizeof(struct dhcpv6_opt_server_id) - + sizeof(struct dhcpv6_opt_header)); + opt_client_id->duid_type = htons(DHCPV6_DUID_LL); + opt_client_id->hw_type = htons(DHCPV6_HW_TYPE_ETH); + opt_client_id->mac = pfd->cmac; + + if (!ipv6_addr_is_set(&pfd->prefix)) { + pfd->aid = random_uint16(); + } + struct dhcpv6_opt_ia_na *ia_pd = + (struct dhcpv6_opt_ia_na *)(opt_client_id + 1); + ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD); + int opt_len = sizeof(struct dhcpv6_opt_ia_na) - + sizeof(struct dhcpv6_opt_header); + if (ipv6_addr_is_set(&pfd->prefix)) { + opt_len += sizeof(struct dhcpv6_opt_ia_prefix); + } + ia_pd->opt.len = htons(opt_len); + ia_pd->iaid = htonl(pfd->aid); + ia_pd->t1 = OVS_BE32_MAX; + ia_pd->t2 = OVS_BE32_MAX; + if (ipv6_addr_is_set(&pfd->prefix)) { + struct dhcpv6_opt_ia_prefix *ia_prefix = + (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1); + ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX); + ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) - + sizeof(struct dhcpv6_opt_header)); + ia_prefix->plife_time = OVS_BE32_MAX; + ia_prefix->vlife_time = OVS_BE32_MAX; + ia_prefix->plen = pfd->plen; + ia_prefix->ipv6 = pfd->prefix; + } + + uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + csum = csum_continue(csum, udp_h, dp_packet_size(b) - + ((const unsigned char *)udp_h - + (const unsigned char *)dp_packet_eth(b))); + udp_h->udp_csum = csum_finish(csum); + if (!udp_h->udp_csum) { + udp_h->udp_csum = htons(0xffff); + } +} + +#define IPV6_PREFIXD_TIMEOUT 3000LL +static long long int +ipv6_prefixd_send(struct rconn *swconn, struct ipv6_prefixd_state *pfd) +{ + long long int cur_time = time_msec(); + if (cur_time < pfd->next_announce) { + return pfd->next_announce; + } + + uint64_t packet_stub[256 / 8]; + struct dp_packet packet; + + struct eth_addr eth_dst; + eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02); + struct in6_addr ipv6_dst; + ipv6_parse("ff02::1:2", &ipv6_dst); + + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); + compose_prefixd_solicit(&packet, pfd, eth_dst, &ipv6_dst); + + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + + /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ + uint32_t dp_key = pfd->metadata; + uint32_t port_key = pfd->port_key; + put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); + put_load(port_key, MFF_LOG_INPORT, 0, 32, &ofpacts); + put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY_BIT, 1, &ofpacts); + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts); + resubmit->in_port = OFPP_CONTROLLER; + resubmit->table_id = OFTABLE_LOG_INGRESS_PIPELINE; + + struct ofputil_packet_out po = { + .packet = dp_packet_data(&packet), + .packet_len = dp_packet_size(&packet), + .buffer_id = UINT32_MAX, + .ofpacts = ofpacts.data, + .ofpacts_len = ofpacts.size, + }; + + match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); + enum ofp_version version = rconn_get_version(swconn); + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); + queue_msg(swconn, ofputil_encode_packet_out(&po, proto)); + dp_packet_uninit(&packet); + ofpbuf_uninit(&ofpacts); + pfd->next_announce = cur_time + random_range(IPV6_PREFIXD_TIMEOUT); + pfd->state = PREFIX_SOLICIT; + + return pfd->next_announce; +} + +static bool ipv6_prefixd_should_inject(void) +{ + struct shash_node *iter; + + SHASH_FOR_EACH (iter, &ipv6_prefixd) { + struct ipv6_prefixd_state *pfd = iter->data; + if (pfd->state == PREFIX_SOLICIT) { + return true; + } + if (pfd->state && pfd->next_announce < time_msec()) { + return true; + } + } + return false; +} + +static void +ipv6_prefixd_wait(long long int timeout) +{ + if (ipv6_prefixd_should_inject()) { + poll_timer_wait_until(timeout); + } +} + +static void +send_ipv6_prefixd(struct rconn *swconn, long long int *send_prefixd_time) + OVS_REQUIRES(pinctrl_mutex) +{ + struct shash_node *iter; + + *send_prefixd_time = LLONG_MAX; + SHASH_FOR_EACH (iter, &ipv6_prefixd) { + struct ipv6_prefixd_state *pfd = iter->data; + long long int next_msg = ipv6_prefixd_send(swconn, pfd); + if (*send_prefixd_time > next_msg) { + *send_prefixd_time = next_msg; + } + } +} + /* Called by pinctrl_run(). Runs with in the main ovn-controller * thread context. */ static void @@ -2735,6 +3242,156 @@ prepare_ipv6_ras(const struct hmap *local_datapaths) } +static bool +fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn, + const struct local_datapath *ld, + struct eth_addr ea, struct in6_addr ipv6_addr, + int64_t tunnel_key, int64_t dp_tunnel_key) + OVS_REQUIRES(pinctrl_mutex) +{ + bool changed = false; + + for (size_t i = 0; i < ld->n_peer_ports; i++) { + const struct sbrec_port_binding *pb = ld->peer_ports[i].local; + struct ipv6_prefixd_state *pfd; + + if (!smap_get_bool(&pb->options, "ipv6_prefix", false)) { + pfd = shash_find_and_delete(&ipv6_prefixd, pb->logical_port); + if (pfd) { + free(pfd); + } + continue; + } + + struct lport_addresses c_addrs; + for (size_t j = 0; j < pb->n_mac; j++) { + if (extract_lsp_addresses(pb->mac[j], &c_addrs)) { + break; + } + } + + pfd = shash_find_data(&ipv6_prefixd, pb->logical_port); + if (!pfd) { + pfd = xzalloc(sizeof *pfd); + pfd->ipv6_addr = ipv6_addr; + pfd->ea = ea; + pfd->cmac = c_addrs.ea; + pfd->metadata = dp_tunnel_key; + pfd->port_key = tunnel_key; + shash_add(&ipv6_prefixd, pb->logical_port, pfd); + pfd->next_announce = time_msec() + + random_range(IPV6_PREFIXD_TIMEOUT); + changed = true; + + char prefix_s[IPV6_SCAN_LEN + 6]; + const char *ipv6_pd_list = smap_get(&pb->options, + "ipv6_ra_pd_list"); + if (!ipv6_pd_list || + !ovs_scan(ipv6_pd_list, "%u:"IPV6_SCAN_FMT"/%d", + &pfd->aid, prefix_s, &pfd->plen) || + !ipv6_parse(prefix_s, &pfd->prefix)) { + pfd->prefix = in6addr_any; + } + } else if (pfd->state == PREFIX_PENDING && ovnsb_idl_txn) { + char prefix_str[INET6_ADDRSTRLEN + 1] = {}; + struct smap options; + + pfd->state = PREFIX_DONE; + pfd->next_announce = time_msec() + pfd->t1 * 1000; + ipv6_string_mapped(prefix_str, &pfd->prefix); + smap_clone(&options, &pb->options); + smap_add_format(&options, "ipv6_ra_pd_list", "%d:%s/%d", + pfd->aid, prefix_str, pfd->plen); + sbrec_port_binding_set_options(pb, &options); + smap_destroy(&options); + } + } + + return changed; +} + +static void +prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct hmap *local_datapaths, + const struct sbrec_chassis *chassis, + const struct sset *active_tunnels) + OVS_REQUIRES(pinctrl_mutex) +{ + const struct local_datapath *ld; + bool changed = false; + + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { + if (!smap_get(&ld->datapath->external_ids, "logical-router")) { + /* logical switch */ + continue; + } + + for (size_t i = 0; i < ld->n_peer_ports; i++) { + const struct sbrec_port_binding *pb = ld->peer_ports[i].local; + int j; + + if (!smap_get_bool(&pb->options, "ipv6_prefix_delegation", + false)) { + continue; + } + + const char *peer_s = smap_get(&pb->options, "peer"); + if (!peer_s) { + continue; + } + + const struct sbrec_port_binding *peer + = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s); + if (!peer) { + continue; + } + + char *redirect_name = xasprintf("cr-%s", pb->logical_port); + bool resident = lport_is_chassis_resident( + sbrec_port_binding_by_name, chassis, active_tunnels, + redirect_name); + free(redirect_name); + if (!resident && strcmp(pb->type, "l3gateway")) { + continue; + } + + struct in6_addr ip6_addr; + struct eth_addr ea; + for (j = 0; j < pb->n_mac; j++) { + struct lport_addresses laddrs; + + if (!extract_lsp_addresses(pb->mac[j], &laddrs)) { + continue; + } + + ea = laddrs.ea; + if (laddrs.n_ipv6_addrs > 0) { + ip6_addr = laddrs.ipv6_addrs[0].addr; + break; + } + } + + if (eth_addr_is_zero(ea)) { + continue; + } + + if (j == pb->n_mac) { + in6_generate_lla(ea, &ip6_addr); + } + + changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, ld, + ea, ip6_addr, + peer->tunnel_key, + peer->datapath->tunnel_key); + } + } + + if (changed) { + notify_pinctrl_handler(); + } +} + /* Called by pinctrl_run(). Runs with in the main ovn-controller * thread context. */ void @@ -2757,6 +3414,7 @@ pinctrl_destroy(void) free(pinctrl.br_int_name); destroy_send_garps_rarps(); destroy_ipv6_ras(); + destroy_ipv6_prefixd(); destroy_buffered_packets_map(); event_table_destroy(); destroy_put_mac_bindings(); @@ -4471,6 +5129,7 @@ may_inject_pkts(void) { return (!shash_is_empty(&ipv6_ras) || !shash_is_empty(&send_garp_rarp_data) || + ipv6_prefixd_should_inject() || !ovs_list_is_empty(&mcast_query_list) || !ovs_list_is_empty(&buffered_mac_bindings)); } diff --git a/controller/pinctrl.h b/controller/pinctrl.h index 8fa4baae9..b76d1b5ca 100644 --- a/controller/pinctrl.h +++ b/controller/pinctrl.h @@ -26,6 +26,7 @@ struct hmap; struct lport_index; struct ovsdb_idl_index; struct ovsdb_idl_txn; +struct ovsdb_idl; struct ovsrec_bridge; struct sbrec_chassis; struct sbrec_dns_table; diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 9b014925b..e3dec99b2 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -91,7 +91,8 @@ struct ovn_extend_table; OVNACT(TRIGGER_EVENT, ovnact_controller_event) \ OVNACT(BIND_VPORT, ovnact_bind_vport) \ OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) \ - OVNACT(FWD_GROUP, ovnact_fwd_group) + OVNACT(FWD_GROUP, ovnact_fwd_group) \ + OVNACT(DHCP6_REPLY, ovnact_null) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -577,6 +578,11 @@ enum action_opcode { * MFF_LOG_INPORT = port */ ACTION_OPCODE_HANDLE_SVC_CHECK, + /* handle_dhcpv6_reply { ...actions ...}." + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_DHCP6_SERVER, }; /* Header. */ diff --git a/lib/actions.c b/lib/actions.c index f22acddff..e64b16bf0 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -2311,6 +2311,20 @@ ovnact_put_opts_free(struct ovnact_put_opts *pdo) free_gen_options(pdo->options, pdo->n_options); } +static void +format_DHCP6_REPLY(const struct ovnact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "handle_dhcpv6_reply;"); +} + +static void +encode_DHCP6_REPLY(const struct ovnact_null *a OVS_UNUSED, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + encode_controller_op(ACTION_OPCODE_DHCP6_SERVER, ofpacts); +} + static void parse_SET_QUEUE(struct action_context *ctx) { @@ -3258,6 +3272,8 @@ parse_action(struct action_context *ctx) parse_handle_svc_check(ctx); } else if (lexer_match_id(ctx->lexer, "fwd_group")) { parse_fwd_group_action(ctx); + } else if (lexer_match_id(ctx->lexer, "handle_dhcpv6_reply")) { + ovnact_put_DHCP6_REPLY(ctx->ovnacts); } else { lexer_syntax_error(ctx->lexer, "expecting action"); } diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h index 931e6ffcf..cbea2a0c8 100644 --- a/lib/ovn-l7.h +++ b/lib/ovn-l7.h @@ -178,8 +178,11 @@ struct dhcp_opt6_header { #define DHCPV6_OPT_SERVER_ID_CODE 2 #define DHCPV6_OPT_IA_NA_CODE 3 #define DHCPV6_OPT_IA_ADDR_CODE 5 +#define DHCPV6_OPT_STATUS_CODE 13 #define DHCPV6_OPT_DNS_SERVER_CODE 23 #define DHCPV6_OPT_DOMAIN_SEARCH_CODE 24 +#define DHCPV6_OPT_IA_PD 25 +#define DHCPV6_OPT_IA_PREFIX 26 #define DHCPV6_OPT_SERVER_ID \ DHCP_OPTION("server_id", DHCPV6_OPT_SERVER_ID_CODE, "mac") @@ -258,6 +261,22 @@ struct ovs_nd_route_info { }; BUILD_ASSERT_DECL(ND_ROUTE_INFO_OPT_LEN == sizeof(struct ovs_nd_route_info)); +OVS_PACKED( +struct dhcpv6_opt_ia_prefix { + struct dhcpv6_opt_header opt; + ovs_be32 plife_time; + ovs_be32 vlife_time; + uint8_t plen; + struct in6_addr ipv6; +}); + +OVS_PACKED( +struct dhcpv6_opt_status { + struct dhcpv6_opt_header opt; + ovs_be16 status_code; + uint8_t msg[]; +}); + #define DHCPV6_DUID_LL 3 #define DHCPV6_HW_TYPE_ETH 1 diff --git a/ovn-sb.xml b/ovn-sb.xml index 3ae9d4f92..72466b97e 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2149,6 +2149,17 @@ tcp.flags = RST;

Example: handle_svc_check(inport);

+
handle_dhcpv6_reply;
+
+

+ Handle DHCPv6 prefix delegation advertisements/replies from + a IPv6 delegation server. ovn-controller will + add an entry ipv6_ra_pd_list in the + table for each + prefix received from the delegation server +

+
+
R = select(N1[=W1], N2[=W2], ...);

@@ -2182,6 +2193,13 @@ tcp.flags = RST;

+
handle_dhcpv6_reply;
+
+

+ This action is used to parse DHCPv6 replies from IPv6 + Delegation Router and managed IPv6 Prefix delegation state machine +

+
diff --git a/tests/ovn.at b/tests/ovn.at index 9a44f0a6f..bc1731e4d 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1525,6 +1525,10 @@ fwd_group(); fwd_group(liveness="false", childports="eth0", "lsp1"); Syntax error at `"false"' expecting `,'. +# prefix delegation +handle_dhcpv6_reply; + encodes as controller(userdata=00.00.00.13.00.00.00.00) + # Miscellaneous negative tests. ; Syntax error at `;'. diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index 84e5f2b5c..e59698ec4 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -2292,6 +2292,8 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_FWD_GROUP: break; + case OVNACT_DHCP6_REPLY: + break; } } ds_destroy(&s); From patchwork Fri Mar 27 10:11:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1262688 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.137; helo=fraxinus.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=aDrkL3hV; dkim-atps=neutral Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48pd1Z0zPZz9sSH for ; Fri, 27 Mar 2020 21:11:45 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 0BA9187645; Fri, 27 Mar 2020 10:11:43 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3tMSF0TFt-Te; Fri, 27 Mar 2020 10:11:41 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id 81C0C871FD; Fri, 27 Mar 2020 10:11:41 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 6B711C0177; Fri, 27 Mar 2020 10:11:41 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id C51CDC18DA for ; Fri, 27 Mar 2020 10:11:39 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id C2AF5263CE for ; Fri, 27 Mar 2020 10:11:39 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id heS-uA7l-p8n for ; Fri, 27 Mar 2020 10:11:37 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-74.mimecast.com (us-smtp-delivery-74.mimecast.com [216.205.24.74]) by silver.osuosl.org (Postfix) with ESMTPS id 0CA9B26393 for ; Fri, 27 Mar 2020 10:11:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1585303895; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tktnCiU/FWgW6HfyJ7/Pjzxxxxumk22yCBxUXbIGwjM=; b=aDrkL3hVaMhodRdBm92WyMvsMJSGYHBHh9phbKRN9w8JbUXLObSvJhxcL7n1EUzglc6Tnt 9cA/3iuAanZt5F3wlfto9om6pMuXLrcjY4/hlBLg0hF2hRVBYRLfIW7AV3FRqjZpgVnZSB f3vVGmG+5t4M4iGnQs9xpgg2srOhFik= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-387-6fmZ9tRaMSS6hilx28rLZQ-1; Fri, 27 Mar 2020 06:11:20 -0400 X-MC-Unique: 6fmZ9tRaMSS6hilx28rLZQ-1 Received: by mail-wr1-f72.google.com with SMTP id b2so3877926wrq.8 for ; Fri, 27 Mar 2020 03:11:20 -0700 (PDT) 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:mime-version:content-transfer-encoding; bh=TG89VLLuwYAQgzfglbl5baEEzpmYC95FlLvq4uMzg3Y=; b=F+OF76yPF99mSmIWy9yIrGdzgVW1nwVpRS2ExqeReAZIDPAaeb4Uw6ab312NiGIAnK ZIMAsZKNEsmUKDnkASvW/fJM2y+7utnmUYb3nLu8uOibsWIofd5vdIbKS/GEaaWpnTVV aDIcTGiwVLi93gW7JHs+P0rRzC6SHvSFn8IP8hwIqUjUU6UPZDkqv1Gk31Y5S0t3B689 mEn/NND7VEbBlmkE4UzmfPJ/QlWCBuIZI94Q6CU3MPJxfh8VFrVBfA+/GOHcspQkJMD2 e4jfZdRHJkhWxXD5hIJADwjmnddv5KHnEzYlElNNj1SHi6ckurRx2Erfsd4AXuhBwDyf whOQ== X-Gm-Message-State: ANhLgQ3O7Kv1X8KRlOe7KFfrdDVeUoO+9JzVSIdPfYO/sBGRm4/swN2w 1T073nXvfO3ql99+1Jgb5JI9VY379xADs8cC5dna4zQjnUDAHX1A/yaQkHQlmLKz7H3wy9brDlE v4wBuUlLKykIQ2LiG+Q== X-Received: by 2002:a05:600c:2254:: with SMTP id a20mr4780405wmm.104.1585303878445; Fri, 27 Mar 2020 03:11:18 -0700 (PDT) X-Google-Smtp-Source: ADFU+vtip9WrNZzCHbm+kat2DKdexcbdRTz4GXJ668QLH9K4oaLIT0mkRdCw9G/E9NFZ/oOGXxH6YQ== X-Received: by 2002:a05:600c:2254:: with SMTP id a20mr4780360wmm.104.1585303877822; Fri, 27 Mar 2020 03:11:17 -0700 (PDT) Received: from lore-desk-wlan.redhat.com ([151.48.139.19]) by smtp.gmail.com with ESMTPSA id k15sm7637455wrm.55.2020.03.27.03.11.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Mar 2020 03:11:17 -0700 (PDT) From: Lorenzo Bianconi To: ovs-dev@openvswitch.org Date: Fri, 27 Mar 2020 11:11:00 +0100 Message-Id: <07900137d4eff3ca71e5bf7281b090e9b095279b.1585303423.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH v8 ovn 2/2] northd: add logical flows for dhcpv6 pfd parsing 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" Introduce logical flows in ovn router pipeline in order to parse dhcpv6 advertise/reply from IPv6 prefix delegation router. Do not overwrite ipv6_ra_pd_list info in options column of SB port_binding table written by ovn-controller Introduce ipv6_prefix column in NB Logical_router_port table to report IPv6 prefix received from delegation router to the CMS Signed-off-by: Lorenzo Bianconi --- northd/ovn-northd.8.xml | 8 +++ northd/ovn-northd.c | 95 ++++++++++++++++++++++++-- ovn-nb.ovsschema | 7 +- ovn-nb.xml | 21 ++++++ tests/atlocal.in | 5 +- tests/system-ovn.at | 144 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 272 insertions(+), 8 deletions(-) diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index b5e4d6d84..82c86f636 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -1660,6 +1660,14 @@ next; +
  • + A priority-100 flow parses DHCPv6 replies from IPv6 prefix + delegation routers (udp.src == 547 && + udp.dst == 546). The handle_dhcpv6_reply + is used to send IPv6 prefix delegation messages to the delegation + router. +
  • +
  • A priority-87 flow explicitly allows IPv6 multicast traffic that is diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 7ef049310..7ea181a07 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -2692,6 +2692,33 @@ op_get_name(const struct ovn_port *op) return name; } +static void +ovn_update_ipv6_prefix(struct hmap *ports) +{ + const struct ovn_port *op; + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbrp) { + continue; + } + + char prefix[IPV6_SCAN_LEN + 6]; + unsigned aid; + const char *ipv6_pd_list = smap_get(&op->sb->options, + "ipv6_ra_pd_list"); + if (!ipv6_pd_list || + !ovs_scan(ipv6_pd_list, "%u:%s", &aid, prefix)) { + continue; + } + + struct sset ipv6_prefix_set = SSET_INITIALIZER(&ipv6_prefix_set); + sset_add(&ipv6_prefix_set, prefix); + nbrec_logical_router_port_set_ipv6_prefix(op->nbrp, + sset_array(&ipv6_prefix_set), + sset_count(&ipv6_prefix_set)); + sset_destroy(&ipv6_prefix_set); + } +} + static void ovn_port_update_sbrec(struct northd_context *ctx, struct ovsdb_idl_index *sbrec_chassis_by_name, @@ -2822,6 +2849,13 @@ ovn_port_update_sbrec(struct northd_context *ctx, smap_add(&new, "l3gateway-chassis", chassis_name); } } + + const char *ipv6_pd_list = smap_get(&op->sb->options, + "ipv6_ra_pd_list"); + if (ipv6_pd_list) { + smap_add(&new, "ipv6_ra_pd_list", ipv6_pd_list); + } + sbrec_port_binding_set_options(op->sb, &new); smap_destroy(&new); @@ -4676,12 +4710,12 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows) * unreachable packets. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110, "nd || nd_rs || nd_ra || icmp4.type == 3 || " - "icmp6.type == 1 || (tcp && tcp.flags == 20)", - "next;"); + "icmp6.type == 1 || (tcp && tcp.flags == 20) || " + "(udp && udp.src == 546 && udp.dst == 547)", "next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, "nd || nd_rs || nd_ra || icmp4.type == 3 || " - "icmp6.type == 1 || (tcp && tcp.flags == 20)", - "next;"); + "icmp6.type == 1 || (tcp && tcp.flags == 20) ||" + "(udp && udp.src == 546 && udp.dst == 547)", "next;"); /* Ingress and Egress Pre-ACL Table (Priority 100). * @@ -7735,6 +7769,11 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode) } ds_put_format(&s, "%s/%u ", addrs->network_s, addrs->plen); } + + const char *ra_pd_list = smap_get(&op->sb->options, "ipv6_ra_pd_list"); + if (ra_pd_list) { + ds_put_cstr(&s, ra_pd_list); + } /* Remove trailing space */ ds_chomp(&s, ' '); smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s)); @@ -8479,7 +8518,34 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, free(snat_ips); } - /* Logical router ingress table 3: IP Input for IPv6. */ + /* DHCPv6 reply handling */ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbrp) { + continue; + } + + if (op->derived) { + continue; + } + + struct lport_addresses lrp_networks; + if (!extract_lrp_networks(op->nbrp, &lrp_networks)) { + continue; + } + + for (size_t i = 0; i < lrp_networks.n_ipv6_addrs; i++) { + ds_clear(&actions); + ds_clear(&match); + ds_put_format(&match, "ip6.dst == %s && udp.src == 547 &&" + " udp.dst == 546", + lrp_networks.ipv6_addrs[i].addr_s); + ds_put_format(&actions, "reg0 = 0; handle_dhcpv6_reply;"); + ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100, + ds_cstr(&match), ds_cstr(&actions)); + } + } + + /* Logical router ingress table 1: IP Input for IPv6. */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbrp) { continue; @@ -9243,6 +9309,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } + struct smap options; + /* enable IPv6 prefix delegation */ + bool prefix_delegation = smap_get_bool(&op->nbrp->options, + "prefix_delegation", false); + if (prefix_delegation) { + smap_clone(&options, &op->sb->options); + smap_add(&options, "ipv6_prefix_delegation", "true"); + sbrec_port_binding_set_options(op->sb, &options); + smap_destroy(&options); + } + + if (smap_get_bool(&op->nbrp->options, "prefix", false)) { + smap_clone(&options, &op->sb->options); + smap_add(&options, "ipv6_prefix", "true"); + sbrec_port_binding_set_options(op->sb, &options); + smap_destroy(&options); + } + const char *address_mode = smap_get( &op->nbrp->ipv6_ra_configs, "address_mode"); @@ -10942,6 +11026,7 @@ ovnnb_db_run(struct northd_context *ctx, build_meter_groups(ctx, &meter_groups); build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups, &igmp_groups, &meter_groups, &lbs); + ovn_update_ipv6_prefix(ports); sync_address_sets(ctx); sync_port_groups(ctx); diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 843e979db..76b93691c 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "5.20.0", - "cksum": "2846067333 25243", + "version": "5.21.0", + "cksum": "1457743848 25409", "tables": { "NB_Global": { "columns": { @@ -342,6 +342,9 @@ "ipv6_ra_configs": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, + "ipv6_prefix": {"type": {"key": "string", + "min": 0, + "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, diff --git a/ovn-nb.xml b/ovn-nb.xml index f4ecc1c1e..5c71b3a8b 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -2181,6 +2181,11 @@ + + This column contains IPv6 prefix obtained by prefix delegation + router according to RFC 3633 + +

    This column defines the IPv6 ND RA address mode and ND MTU Option to be @@ -2303,6 +2308,22 @@ ovn-northd honors the configured value. + +

    + If set to true, enable IPv6 prefix delegation state + machine on this logical router port (RFC3633). IPv6 prefix + delegation is available just on a gateway router or on a gateway + router port. +

    + + + +

    + If set to true, this interface will receive an IPv6 + prefix according to RFC3663 +

    +
    diff --git a/tests/atlocal.in b/tests/atlocal.in index 5f14c3da0..8f3ff03b9 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -157,7 +157,7 @@ find_command() { which $1 > /dev/null 2>&1 status=$? - var=HAVE_`echo "$1" | tr '[a-z]' '[A-Z]'` + var=HAVE_`echo "$1" | tr '-' '_' | tr '[a-z]' '[A-Z]'` if test "$status" = "0"; then eval ${var}="yes" else @@ -192,6 +192,9 @@ else DIFF_SUPPORTS_NORMAL_FORMAT=no fi +# Set HAVE_DIBBLER-SERVER +find_command dibbler-server + # Turn off proxies. unset http_proxy unset https_proxy diff --git a/tests/system-ovn.at b/tests/system-ovn.at index f1ae69b20..b48e5ffb7 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -3793,3 +3793,147 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP + +AT_SETUP([ovn -- IPv6 prefix delegation]) +AT_SKIP_IF([test $HAVE_DIBBLER_SERVER = no]) +AT_SKIP_IF([test $HAVE_TCPDUMP = no]) +AT_KEYWORDS([ovn-ipv6-prefix_d]) + +ovn_start +OVS_TRAFFIC_VSWITCHD_START() + +ADD_BR([br-int]) +ADD_BR([br-ext]) + +ovs-ofctl add-flow br-ext action=normal +# Set external-ids in br-int needed for ovn-controller +ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +# Start ovn-controller +start_daemon ovn-controller + +ovn-nbctl lr-add R1 + +ovn-nbctl ls-add sw0 +ovn-nbctl ls-add sw1 +ovn-nbctl ls-add public + +ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24 +ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24 +ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24 \ + -- set Logical_Router_Port rp-public options:redirect-chassis=hv1 + +ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \ + type=router options:router-port=rp-sw0 \ + -- lsp-set-addresses sw0-rp router +ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \ + type=router options:router-port=rp-sw1 \ + -- lsp-set-addresses sw1-rp router + +ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \ + type=router options:router-port=rp-public \ + -- lsp-set-addresses public-rp router + +ADD_NAMESPACES(sw01) +ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ + "192.168.1.1") +ovn-nbctl lsp-add sw0 sw01 \ + -- lsp-set-addresses sw01 "f0:00:00:01:02:03 192.168.1.2" + +ADD_NAMESPACES(sw11) +ADD_VETH(sw11, sw11, br-int, "192.168.2.2/24", "f0:00:00:02:02:03", \ + "192.168.2.1") +ovn-nbctl lsp-add sw1 sw11 \ + -- lsp-set-addresses sw11 "f0:00:00:02:02:03 192.168.2.2" + +ADD_NAMESPACES(server) +ADD_VETH(s1, server, br-ext, "2001:1db8:3333::2/64", "f0:00:00:01:02:05", \ + "2001:1db8:3333::1") + +OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep 2001:1db8:3333::2 | grep tentative)" = ""]) +OVS_WAIT_UNTIL([test "$(ip netns exec server ip a | grep fe80 | grep tentative)" = ""]) + +AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext]) +ovn-nbctl lsp-add public public1 \ + -- lsp-set-addresses public1 unknown \ + -- lsp-set-type public1 localnet \ + -- lsp-set-options public1 network_name=phynet + +ovn-nbctl set logical_router_port rp-public options:prefix_delegation=true +ovn-nbctl set logical_router_port rp-public options:prefix=true +ovn-nbctl set logical_router_port rp-sw0 options:prefix=true +ovn-nbctl set logical_router_port rp-sw1 options:prefix=true + +# reset dibbler state +sed s/eth0/s1/g -i /etc/dibbler/server.conf +sed s/"pd-pool 2001:db8:3333"/"pd-pool 2001:1db8:3333"/g -i /etc/dibbler/server.conf +sed s/"t1 1800-2000"/"t1 10"/g -i /etc/dibbler/server.conf +sed s/"t2 2700-3000"/"t2 15"/g -i /etc/dibbler/server.conf +cat > /var/lib/dibbler/server-AddrMgr.xml < + 1575481348 + 0 + +EOF +cat > /var/lib/dibbler/server-CfgMgr.xml < + /var/lib/dibbler + Server + 8 + 0 + 0 + +EOF + +NS_CHECK_EXEC([server], [dibbler-server run > dibbler.log &]) +ovn-nbctl --wait=hv sync + +sleep 10 +kill $(pidof dibbler-server) + +OVS_WAIT_UNTIL([ovn-nbctl list logical_router_port rp-public | grep ipv6_prefix]) +OVS_WAIT_UNTIL([ovn-nbctl list logical_router_port rp-sw0 | grep ipv6_prefix]) +OVS_WAIT_UNTIL([ovn-nbctl list logical_router_port rp-sw1 | grep ipv6_prefix]) +AT_CHECK([ovn-nbctl get logical_router_port rp-public ipv6_prefix | cut -c3-16], [0], [dnl +[2001:1db8:3333] +]) +AT_CHECK([ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16], [0], [dnl +[2001:1db8:3333] +]) +AT_CHECK([ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c3-16], [0], [dnl +[2001:1db8:3333] +]) + +sleep 8 +prefix=$(ovn-nbctl list logical_router_port rp-public | awk -F/ '/ipv6_prefix/{print substr($1,25,9)}' | sed 's/://g') +ovn-nbctl set logical_router_port rp-sw0 options:prefix=false +ovn-nbctl set logical_router_port rp-sw1 options:prefix=false + +NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[95:4]]=0x${prefix} > public.pcap &]) + +OVS_WAIT_UNTIL([ + total_pkts=$(cat public.pcap | wc -l) + test "${total_pkts}" = "1" +]) + +kill $(pidof tcpdump) +kill $(pidof ovn-controller) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d +/.*terminating with signal 15.*/d"]) +AT_CLEANUP