From patchwork Fri Dec 20 11:32:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1214016 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.136; helo=silver.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.b="iPToeEYc"; dkim-atps=neutral Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47fRSQ3TMWz9sPc for ; Fri, 20 Dec 2019 22:32:54 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id BEFAE2544B; Fri, 20 Dec 2019 11:32:52 +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 fjJiBdcX1A8h; Fri, 20 Dec 2019 11:32:46 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by silver.osuosl.org (Postfix) with ESMTP id 7A7A6251E1; Fri, 20 Dec 2019 11:32:46 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 569B7C1D83; Fri, 20 Dec 2019 11:32:46 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id A0498C077D for ; Fri, 20 Dec 2019 11:32:44 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 8782587267 for ; Fri, 20 Dec 2019 11:32:44 +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 OFUzDxlsZoY4 for ; Fri, 20 Dec 2019 11:32:41 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-1.mimecast.com (us-smtp-1.mimecast.com [205.139.110.61]) by fraxinus.osuosl.org (Postfix) with ESMTPS id CADE5871D8 for ; Fri, 20 Dec 2019 11:32:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1576841559; 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=jwPdquXKXq90CsvdKHsqFYfUrckHvG8hVpCT0RlOAD8=; b=iPToeEYcvXf8NLdt2xXBUnjm86kSW6Vj5q/x1IiP/Tl1TibUi6ag3+VS5oIUt28w3EVAwk wY/8wJ9hddd4ngaUOJ7Z9p+TStWRfQR4OTorxsi+uUT6K1YJ1UpoMnPxW1GLxO/+JTzadA Ppw98xsiJL9uOF5GWlzd4ShThaUjG4U= 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-138-mgL8UZnNP8OLWfaAsKZjWQ-1; Fri, 20 Dec 2019 06:32:37 -0500 Received: by mail-wm1-f69.google.com with SMTP id 7so1952963wmf.9 for ; Fri, 20 Dec 2019 03:32:37 -0800 (PST) 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=ECSm3H2QyvP53xbci2Nh5N2XcLF4S6tCZLId3f4aZpA=; b=Ol3pS2MJ3dOMz7bZTLSamVMosSM22fMuj+Quuc9BvDGfDIBHWFPG7CCcvmb/cnvvxj Fkx0fQVERxGAd5UQN6ZpfG0+5EsH4iKPTCws7cv9jbQ+SAmedY/eoyju4RExxATEfh2g iLaQz58VyfmD9eRadDG1t3B6n6gmt8mTajZc2FngG7Ptb0JmZS2OwpOaHFZFQJXdyb3F 9GYUAmNPIdJem/E0XmIpzLxbALcRguRbWS34tzhal7XnloCqsOtEmqD3QHF47hvVQgiS SW3FXj4UoZK4PcEtCJYxGTYI0nItbNG2FGGadL71Z/dv7NpUd7U+RYqOTVXWoI/7zs53 mpMQ== X-Gm-Message-State: APjAAAU3ukMwmfdfNRfQtTQThakSN+CJHKShoVIWxVcUulBpG0H84M6A FSUmdGLFFXMLZRTf8TJJTpJmMprQkutDWcgFqlg5taHY4Q/b7+x8YJBdS8fYW9S5E4CC7eJX1GN 3S4Y1m2g0R+dJ X-Received: by 2002:a5d:6a83:: with SMTP id s3mr14104074wru.99.1576841556131; Fri, 20 Dec 2019 03:32:36 -0800 (PST) X-Google-Smtp-Source: APXvYqwpemruPhpNUMDBI/4nck+kE71MFjEYQj87ZJiA7uu2KHEvoB0aNkVpBemhjOV7WYVsm9RE9g== X-Received: by 2002:a5d:6a83:: with SMTP id s3mr14104012wru.99.1576841555388; Fri, 20 Dec 2019 03:32:35 -0800 (PST) Received: from localhost.localdomain.com (nat-pool-mxp-t.redhat.com. [149.6.153.186]) by smtp.gmail.com with ESMTPSA id w17sm9648539wrt.89.2019.12.20.03.32.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Dec 2019 03:32:34 -0800 (PST) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Fri, 20 Dec 2019 12:32:24 +0100 Message-Id: <6aec0fb280f610a2083fbb6c61e251b1d237b21f.1576840560.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: References: MIME-Version: 1.0 X-MC-Unique: mgL8UZnNP8OLWfaAsKZjWQ-1 X-Mimecast-Spam-Score: 0 Subject: [ovs-dev] [PATCH v4 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 | 597 ++++++++++++++++++++++++++++++++++++++++++ include/ovn/actions.h | 8 +- lib/actions.c | 22 ++ lib/ovn-l7.h | 19 ++ ovn-sb.xml | 8 + tests/ovn.at | 6 + utilities/ovn-trace.c | 3 + 7 files changed, 662 insertions(+), 1 deletion(-) diff --git a/controller/pinctrl.c b/controller/pinctrl.c index c886b21d9..3eb82133d 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -270,6 +270,20 @@ static void pinctrl_ip_mcast_handle_igmp( 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_datapath, + 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); @@ -457,6 +471,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(); @@ -544,6 +559,49 @@ set_actions_and_enqueue_msg(struct rconn *swconn, ofpbuf_uninit(&ofpacts); } +static struct shash ipv6_prefixd; + +enum { + PREFIX_SOLICIT, + 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; + int8_t 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; struct dp_packet *p; @@ -967,6 +1025,251 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, dp_packet_uninit(&packet); } +static void +pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const struct flow *ip_flow, + struct dp_packet *pkt_in, const struct match *md, + struct ofpbuf *userdata) +{ + 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; + + 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: { + int orig_len = len, hdr_len = 0, size = sizeof *in_opt + 12; + + 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; + } + + 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_dst, ip_flow->dl_src, ETH_TYPE_IPV6, + IPV6_HEADER_LEN); + + struct udp_header *udp_h = compose_ipv6(&packet, IPPROTO_UDP, + &ip_flow->ipv6_src, + &ip_flow->ipv6_dst, 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); + } + + set_actions_and_enqueue_msg(swconn, &packet, md, userdata); + dp_packet_uninit(&packet); + +out: + free(data); +} + +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_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; + } +} + +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, + struct ofpbuf *userdata) +{ + 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, userdata); + break; + case DHCPV6_MSG_TYPE_REPLY: + ovs_mutex_lock(&pinctrl_mutex); + pinctrl_parse_dhcpv6_reply(pkt_in, ip_flow); + ovs_mutex_unlock(&pinctrl_mutex); + break; + default: + break; + } +} + /* Called with in the pinctrl_handler thread context. */ static void pinctrl_handle_put_dhcp_opts( @@ -1998,6 +2301,10 @@ 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: + pinctrl_handle_dhcp6_server(swconn, &headers, &packet, + &pin.flow_metadata, &userdata); + break; case ACTION_OPCODE_HANDLE_SVC_CHECK: ovs_mutex_lock(&pinctrl_mutex); @@ -2076,6 +2383,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); @@ -2129,6 +2437,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); @@ -2146,6 +2455,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); @@ -2197,6 +2507,9 @@ 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_datapath, + 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, @@ -2635,6 +2948,151 @@ send_ipv6_ras(struct rconn *swconn, long long int *send_ipv6_ra_time) } } +static void +compose_prefixd_solicit(struct dp_packet *b, + const struct eth_addr eth_src, + const struct eth_addr eth_dst, + const struct in6_addr *ipv6_src, + const struct in6_addr *ipv6_dst, + const struct eth_addr cmac, + unsigned aid) +{ + eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN); + + int len = UDP_HEADER_LEN + 4 + sizeof(struct dhcpv6_opt_server_id) + + sizeof(struct dhcpv6_opt_ia_na); + struct udp_header *udp_h = compose_ipv6(b, IPPROTO_UDP, ipv6_src, + 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 = cmac; + + 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); + ia_pd->opt.len = htons(sizeof(struct dhcpv6_opt_ia_na) - + sizeof(struct dhcpv6_opt_header)); + ia_pd->iaid = htonl(aid); + ia_pd->t1 = htonl(0xffffffff); + ia_pd->t2 = htonl(0xffffffff); + + 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->ea, eth_dst, &pfd->ipv6_addr, + &ipv6_dst, pfd->cmac, pfd->aid); + + 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); + + 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 == PREFIX_DONE && + pfd->next_announce < time_msec()) { + pfd->state = PREFIX_SOLICIT; + 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 @@ -2712,6 +3170,143 @@ prepare_ipv6_ras(const struct hmap *local_datapaths) } +static bool +fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_index *sbrec_port_binding_by_datapath, + const struct hmap *local_datapaths, + struct lport_addresses laddrs, int64_t tunnel_key, + int64_t dp_tunnel_key) + OVS_REQUIRES(pinctrl_mutex) +{ + const struct local_datapath *ld; + bool changed = false; + + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { + struct sbrec_port_binding *target = sbrec_port_binding_index_init_row( + sbrec_port_binding_by_datapath); + sbrec_port_binding_index_set_datapath(target, ld->datapath); + + struct sbrec_port_binding *pb; + SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, + sbrec_port_binding_by_datapath) { + if (!smap_get_bool(&pb->options, "ipv6_prefix", false)) { + continue; + } + + struct lport_addresses c_addrs; + int i; + for (i = 0; i < pb->n_mac; i++) { + if (extract_lsp_addresses(pb->mac[i], &c_addrs)) { + break; + } + } + + struct ipv6_prefixd_state *pfd = shash_find_data( + &ipv6_prefixd, pb->logical_port); + if (!pfd) { + pfd = xzalloc(sizeof *pfd); + pfd->ipv6_addr = laddrs.ipv6_addrs[0].addr; + pfd->ea = laddrs.ea; + pfd->cmac = c_addrs.ea; + pfd->aid = random_range(0xfffffff); + 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; + } 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", "\"%s\"/%d", + prefix_str, pfd->plen); + sbrec_port_binding_set_options(pb, &options); + smap_destroy(&options); + } + } + sbrec_port_binding_index_destroy_row(target); + } + + return changed; +} + +static void +prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_index *sbrec_port_binding_by_datapath, + 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; + int i; + + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { + struct sbrec_port_binding *target = sbrec_port_binding_index_init_row( + sbrec_port_binding_by_datapath); + sbrec_port_binding_index_set_datapath(target, ld->datapath); + + struct sbrec_port_binding *pb; + SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, + sbrec_port_binding_by_datapath) { + 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 lport_addresses laddrs; + for (i = 0; i < pb->n_mac; i++) { + if (extract_lsp_addresses(pb->mac[i], &laddrs) && + laddrs.n_ipv6_addrs > 0 && + !in6_is_lla(&laddrs.ipv6_addrs[0].addr)) { + break; + } + } + if (i == pb->n_mac) { + continue; + } + + changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, + sbrec_port_binding_by_datapath, + local_datapaths, laddrs, + peer->tunnel_key, + peer->datapath->tunnel_key); + } + sbrec_port_binding_index_destroy_row(target); + } + + if (changed) { + notify_pinctrl_handler(); + } +} + /* Called by pinctrl_run(). Runs with in the main ovn-controller * thread context. */ void @@ -2734,6 +3329,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(); @@ -4231,6 +4827,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/include/ovn/actions.h b/include/ovn/actions.h index 047a8d737..c3d719985 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -89,7 +89,8 @@ struct ovn_extend_table; OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) \ OVNACT(TRIGGER_EVENT, ovnact_controller_event) \ OVNACT(BIND_VPORT, ovnact_bind_vport) \ - OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) + OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) \ + OVNACT(DHCP6_REPLY, ovnact_nest) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -552,6 +553,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 051e6c875..b7bd219e4 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -2172,6 +2172,26 @@ ovnact_put_opts_free(struct ovnact_put_opts *pdo) free_gen_options(pdo->options, pdo->n_options); } +static void +parse_DHCP6_REPLY(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_DHCP6_REPLY, "ip6"); +} + +static void +format_DHCP6_REPLY(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "handle_dhcpv6_reply", s); +} + +static void +encode_DHCP6_REPLY(const struct ovnact_nest *on, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + encode_nested_actions(on, ep, ACTION_OPCODE_DHCP6_SERVER, ofpacts); +} + static void parse_SET_QUEUE(struct action_context *ctx) { @@ -2973,6 +2993,8 @@ parse_action(struct action_context *ctx) parse_bind_vport(ctx); } else if (lexer_match_id(ctx->lexer, "handle_svc_check")) { parse_handle_svc_check(ctx); + } else if (lexer_match_id(ctx->lexer, "handle_dhcpv6_reply")) { + parse_DHCP6_REPLY(ctx); } else { lexer_syntax_error(ctx->lexer, "expecting action"); } diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h index ae6dbfdfb..387e7eba1 100644 --- a/lib/ovn-l7.h +++ b/lib/ovn-l7.h @@ -174,8 +174,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") @@ -254,6 +257,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 82167c488..14fe249ec 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2114,6 +2114,14 @@ tcp.flags = RST;

Example: handle_svc_check(inport);

+ +
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 1d5369341..1cc8458e1 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1481,6 +1481,12 @@ handle_svc_check(); handle_svc_check(reg0); Cannot use numeric field reg0 where string field is required. +# prefix delegation +handle_dhcpv6_reply{}; + formats as handle_dhcpv6_reply { drop; }; + encodes as controller(userdata=00.00.00.13.00.00.00.00) + has prereqs ip6 + # Miscellaneous negative tests. ; Syntax error at `;'. diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index 264543876..3b132a2eb 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -2224,6 +2224,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_HANDLE_SVC_CHECK: break; + + case OVNACT_DHCP6_REPLY: + break; } } ds_destroy(&s); From patchwork Fri Dec 20 11:32:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1214015 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.b="Pk46rzX/"; 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 47fRSQ4WkCz9sPn for ; Fri, 20 Dec 2019 22:32:54 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 1688286D61; Fri, 20 Dec 2019 11:32:53 +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 98sa_1MsdU6c; Fri, 20 Dec 2019 11:32:49 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id 356F6809D4; Fri, 20 Dec 2019 11:32:49 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 030E1C1D8A; Fri, 20 Dec 2019 11:32:49 +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 CD3F8C1796 for ; Fri, 20 Dec 2019 11:32:45 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id B44E38695F for ; Fri, 20 Dec 2019 11:32:45 +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 FL2xfaG2uwkc for ; Fri, 20 Dec 2019 11:32:42 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-1.mimecast.com (us-smtp-1.mimecast.com [207.211.31.81]) by whitealder.osuosl.org (Postfix) with ESMTPS id 643F486429 for ; Fri, 20 Dec 2019 11:32:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1576841561; 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=lGolfH837cjK4MObW5V+9s6Rj7lkbfpP+7cuWzBa/WE=; b=Pk46rzX/2XrvawKhZs2DdBu9/SdBJ8hz5JOcHGXF0QpSHP2H1v+k2X4X4/EdVPn6Ws6+5S Mq3Ntobgoc4jHLbnvzC5ujSqW1fw4+dUyATKsoO2tcG+6bq5b6v1TsIxiQMCPkWSLsq01X C65sY/JX+6ge0AkK0thu1YNfiXqfk5w= Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-203--jk65TYJMLqB-3xaZVaC2Q-1; Fri, 20 Dec 2019 06:32:38 -0500 Received: by mail-wr1-f69.google.com with SMTP id v17so2101328wrm.17 for ; Fri, 20 Dec 2019 03:32:38 -0800 (PST) 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=l1S95cKl0pFEW/0mQ2Q5cW78pLab5A0XySjiGspSfr8=; b=Shm9E8TDfG7UuXQZK9H6mY4iMQsafRqbRmajiZg4pm+ESI12VDrIu2kQOio2XkeRHx dAE78XJDWSRyJkzrvqAzbEtmc+5JDXbe1ZkgIbRYXObEjH/T1wqmAyKT7MQO2+EVEcKQ QtdyVTT4N9zDway+a1jEBkcdOW2/Xhr+7oBC1SGgspQvJeWwCVjRqvPKZxdCMlArzw5h wIwavuy21Hp3lgkUV+7esmx7ZU9zOOa7SjqF+jpenClcT6oxdD7rAQjXJ0FOC2t1ljqw Ne/fmn1c8tze1tryB4cr6G64QB4N6LVIi31GYA/CoMvgh7vpQfqi911ttaGcYh+T5W4G A9Sw== X-Gm-Message-State: APjAAAWDRogVfTON2Cifs2jb3UmuNoCbU0DdnzgYa/MNgMBThSYd2jkK 6oYbAdXoPFaV9vrYJ9PomF4NixAVUSzE/cep18gHuXK5P3CEp/8W/M4eK1dkReTw8aM2tTzqL4I S5SoN09/4IoJl X-Received: by 2002:a1c:9d81:: with SMTP id g123mr15799200wme.29.1576841557192; Fri, 20 Dec 2019 03:32:37 -0800 (PST) X-Google-Smtp-Source: APXvYqwL+dFXeTGjBP+IdoQwUZiypu0WcC4LZ+cHMmgKUkb/i237uAapb9v53r34+RB5TUQuHc/QWA== X-Received: by 2002:a1c:9d81:: with SMTP id g123mr15799163wme.29.1576841556731; Fri, 20 Dec 2019 03:32:36 -0800 (PST) Received: from localhost.localdomain.com (nat-pool-mxp-t.redhat.com. [149.6.153.186]) by smtp.gmail.com with ESMTPSA id w17sm9648539wrt.89.2019.12.20.03.32.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Dec 2019 03:32:36 -0800 (PST) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Fri, 20 Dec 2019 12:32:25 +0100 Message-Id: <6fef3a5c90b1bf0363df104b74c8a3083d1c6267.1576840560.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: References: MIME-Version: 1.0 X-MC-Unique: -jk65TYJMLqB-3xaZVaC2Q-1 X-Mimecast-Spam-Score: 0 Subject: [ovs-dev] [PATCH v4 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 Signed-off-by: Lorenzo Bianconi --- northd/ovn-northd.c | 69 +++++++++++++++++++++++- ovn-nb.xml | 17 ++++++ tests/atlocal.in | 5 +- tests/system-ovn.at | 124 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 2 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 3a5cb7c91..cc3d950bc 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -2653,6 +2653,8 @@ ovn_port_update_sbrec(struct northd_context *ctx, struct sset *active_ha_chassis_grps) { sbrec_port_binding_set_datapath(op->sb, op->od->sb); + const char *ipv6_pd_list = NULL; + if (op->nbrp) { /* If the router is for l3 gateway, it resides on a chassis * and its port type is "l3gateway". */ @@ -2775,6 +2777,12 @@ ovn_port_update_sbrec(struct northd_context *ctx, smap_add(&new, "l3gateway-chassis", chassis_name); } } + + 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); @@ -2824,6 +2832,12 @@ ovn_port_update_sbrec(struct northd_context *ctx, smap_add_format(&options, "qdisc_queue_id", "%d", queue_id); } + + ipv6_pd_list = smap_get(&op->sb->options, "ipv6_ra_pd_list"); + if (ipv6_pd_list) { + smap_add(&options, "ipv6_ra_pd_list", ipv6_pd_list); + } + sbrec_port_binding_set_options(op->sb, &options); smap_destroy(&options); if (ovn_is_known_nb_lsp_type(op->nbsp->type)) { @@ -2873,6 +2887,12 @@ ovn_port_update_sbrec(struct northd_context *ctx, if (chassis) { smap_add(&new, "l3gateway-chassis", chassis); } + + 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); } else { @@ -7842,7 +7862,36 @@ 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 { " + "eth.dst <-> eth.src; ip6.dst <-> ip6.src; " + "outport <-> inport; output; };"); + 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; @@ -8643,6 +8692,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"); diff --git a/ovn-nb.xml b/ovn-nb.xml index 5ae52bbde..d7fddcae2 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -2142,6 +2142,23 @@ to true.

+ + +

+ 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 a56d358ea..b6a0aac6f 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -3426,3 +3426,127 @@ 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 + +# Logical network: +# One LR R1 with switches foo (192.168.1.0/24), bar (192.168.2.0/24), +# and alice (172.16.1.0/24) connected to it. The port between R1 and +# alice is the router gateway port where the R1 NAT rules are applied. +# +# R1 -- join -- dibbler-server +# | +# bar + +ovn-nbctl lr-add R1 + +ovn-nbctl ls-add bar +ovn-nbctl ls-add join + +ovn-nbctl lrp-add R1 bar 00:00:01:01:02:03 192.168.1.1/24 +ovn-nbctl lrp-add R1 join 00:00:02:01:02:03 172.16.1.1/24 2001:db8:3333::1/64 \ + -- set Logical_Router_Port join options:redirect-chassis=hv1 + +# Connect bar to R1 +ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ + type=router options:router-port=bar \ + -- lsp-set-addresses rp-bar router + +# Connect join to R1 +ovn-nbctl lsp-add join rp-join -- set Logical_Switch_Port rp-join \ + type=router options:router-port=join \ + -- lsp-set-addresses rp-join router + +# Logical port 'bar1' in switch 'bar'. +ADD_NAMESPACES(bar1) +ADD_VETH(bar1, bar1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ + "192.168.1.1") +ovn-nbctl lsp-add bar bar1 \ +-- lsp-set-addresses bar1 "f0:00:00:01:02:03 192.168.1.2" + +# Logical port 'join1' in switch 'join'. +ADD_NAMESPACES(join1) +ADD_VETH(join1, join1, br-ext, "2001:db8:3333::2/64", "f0:00:00:01:02:05", \ + "2001:db8:3333::1") + +NS_CHECK_EXEC([join1], [tcpdump -ni join1 > dhcp.pcap &]) + +OVS_WAIT_UNTIL([test "$(ip netns exec join1 ip a | grep 2001:db8:3333::2 | grep tentative)" = ""]) +OVS_WAIT_UNTIL([test "$(ip netns exec join1 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 join join1 \ + -- lsp-set-addresses join1 unknown \ + -- lsp-set-type join1 localnet \ + -- lsp-set-options join1 network_name=phynet + +ovn-nbctl set logical_router_port join options:prefix_delegation=true +ovn-nbctl set logical_router_port join options:prefix=true +ovn-nbctl set logical_router_port bar options:prefix=true + +# reset dibbler state +sed s/eth0/join1/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([join1], [dibbler-server run > dibbler.log &]) +ovn-nbctl --wait=hv sync + +sleep 10 +kill $(pidof tcpdump) +kill $(pidof dibbler-server) + +OVS_WAIT_UNTIL([ + total_reply=`grep -c "dhcp6 reply" dhcp.pcap` + test ${total_reply} -gt 1 +]) + +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