From patchwork Thu Nov 7 08:47:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1190985 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.169.12; helo=mail.linuxfoundation.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="eOesmDp0"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 477xrN1CXPz9sP6 for ; Thu, 7 Nov 2019 19:48:18 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 9CAEDD94; Thu, 7 Nov 2019 08:47:25 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id D47A7B8F for ; Thu, 7 Nov 2019 08:47:22 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 10646756 for ; Thu, 7 Nov 2019 08:47:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1573116440; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hPpOQ3IyH30yS2r3AXxxO1KdixBqUknd5vpReng7lJw=; b=eOesmDp0vhIDnqeEj/4+9GktoDoVqL3UoqbUrX8wySvK9bi7tjE0uCvW82CgAlpsmL6js7 XO/i1NYeJ1gs2jnKolV2es341TQZJnLToVVYRxCGiTnyydVT0DKbgMC0jAUdzl/auehS/5 BE5DcHWFVMxzzIYD+QCN5kwPDqkT8BE= 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-117-eMv9HRc0MqO8OsR6fMnVGQ-1; Thu, 07 Nov 2019 03:47:18 -0500 Received: by mail-wr1-f72.google.com with SMTP id g17so605080wru.4 for ; Thu, 07 Nov 2019 00:47:18 -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:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mV6etesCYNJ47t6MoBSAKTnVC7KF+Zs/sXiXMsZ3PFU=; b=KCDzAx28ck/6/P0nLao/JkQpCnWI0pH7wvYGbuLHLgIsYzAdcf4fuWv9VBaDSO4cY1 FgfJCYCpaFP3V8PZ7nNuckg5cjPf4y8K40zoL1wDRHqE2bAJdo1x68bCfLgsGkvOUDDe QMB9pCVp6iWS1dP3oJ4pAIyZuLVjSK2yRWUnPJ/AzK/kJA95cfVMxu87ga+cEgCVkuZj LTwO+nCqHd8epFy1Ro7KFyUj7CO5nSlIsgPhKzN2bnCQDlnzcxnozPqJGrjoVwiPV6AU 1PSHCLdTo02HoYGjhmCOSoNPuifjk0LhQT4wmMIbcOrabsgHZn3BI9dfFNFLS3n1Aktv MkTQ== X-Gm-Message-State: APjAAAXjcrWPoFct1ZfNeS6z9fY0Frbz2SHj5uhevz0W3eQi4e5InwY2 RcprbdxwspMupHuQJHhpAb1Becrehn+5suJQHYoe8rDKpiI2YVsQbfNb8ubub4cvyVM5BcVvHfE i7EL4U4sWU79+ X-Received: by 2002:a5d:574d:: with SMTP id q13mr1776459wrw.263.1573116437222; Thu, 07 Nov 2019 00:47:17 -0800 (PST) X-Google-Smtp-Source: APXvYqxfXPIzR+Ivjo86Q9vwnunJ7ZY2s0cfAuqXkCSqCM86Iy+guwPIWCEmbj6WLDS4M87k94KIwQ== X-Received: by 2002:a5d:574d:: with SMTP id q13mr1776413wrw.263.1573116436483; Thu, 07 Nov 2019 00:47:16 -0800 (PST) Received: from localhost.localdomain.com ([176.229.194.15]) by smtp.gmail.com with ESMTPSA id y9sm1216621wma.3.2019.11.07.00.47.15 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Nov 2019 00:47:15 -0800 (PST) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Thu, 7 Nov 2019 10:47:01 +0200 Message-Id: X-Mailer: git-send-email 2.21.0 In-Reply-To: References: MIME-Version: 1.0 X-MC-Unique: eMv9HRc0MqO8OsR6fMnVGQ-1 X-Mimecast-Spam-Score: 0 X-Spam-Status: No, score=-4.3 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH ovn 1/2] controller: add ipv6 prefix delegation state machine X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Introduce IPv6 Prefix delegation state machine according to RFC 3633 https://tools.ietf.org/html/rfc3633. Add dhcp6_server_pkt 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 ported marked with ipv6_prefix_delegationn set to true. 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 | 553 ++++++++++++++++++++++++++++++++++++++++++ include/ovn/actions.h | 9 +- lib/actions.c | 22 ++ lib/ovn-l7.h | 19 ++ tests/ovn.at | 6 + utilities/ovn-trace.c | 2 + 6 files changed, 610 insertions(+), 1 deletion(-) diff --git a/controller/pinctrl.c b/controller/pinctrl.c index a90ee73d6..b15c9e530 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -269,6 +269,19 @@ 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_prefix_req(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_prefix_msg(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); @@ -440,6 +453,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(); @@ -526,6 +540,55 @@ 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; + int64_t port_key; + int64_t metadata; + struct in6_addr prefix; + unsigned plife_time; + unsigned vlife_time; + unsigned t1; + unsigned t2; + int8_t plen; + int state; +}; + +static void +init_ipv6_prefixd(void) +{ + shash_init(&ipv6_prefixd); +} + +static void +ipv6_prefixd_delete(struct ipv6_prefixd_state *pfd) +{ + if (pfd) { + free(pfd); + } +} + +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; + ipv6_prefixd_delete(pfd); + shash_delete(&ipv6_prefixd, iter); + } + shash_destroy(&ipv6_prefixd); +} + struct buffer_info { struct ofpbuf ofpacts; struct dp_packet *p; @@ -949,6 +1012,251 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, dp_packet_uninit(&packet); } +static void +pinctrl_parse_dhcv6_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); + 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)); + uint8_t *data, *end = (uint8_t *)udp_in + dlen; + int len = 0; + + data = xmalloc(dlen); + if (!data) { + return; + } + + 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) +{ + 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)) { + return pfd; + } + } + return NULL; +} + +static void +pinctrl_prefixd_state_handler(const struct flow *ip_flow, + struct in6_addr addr, 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); + 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_dhcv6_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; + + memset(&ipv6, 0, sizeof (struct in6_addr)); + 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; + + 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, 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_dhcv6_advt(swconn, ip_flow, pkt_in, md, userdata); + break; + case DHCPV6_MSG_TYPE_REPLY: + ovs_mutex_lock(&pinctrl_mutex); + pinctrl_parse_dhcv6_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( @@ -1980,6 +2288,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; default: VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, @@ -2050,6 +2362,7 @@ pinctrl_handler(void *arg_) static long long int send_garp_rarp_time = LLONG_MAX; /* Next multicast query (IGMP) in ms. */ static long long int send_mcast_query_time = LLONG_MAX; + static long long int send_prefixd_time = LLONG_MAX; swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); @@ -2103,6 +2416,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_prefix_msg(swconn, &send_prefixd_time); send_mac_binding_buffered_pkts(swconn); ovs_mutex_unlock(&pinctrl_mutex); @@ -2115,6 +2429,7 @@ pinctrl_handler(void *arg_) send_garp_rarp_wait(send_garp_rarp_time); ipv6_ra_wait(send_ipv6_ra_time); ip_mcast_querier_wait(send_mcast_query_time); + ipv6_prefixd_wait(send_prefixd_time); new_seq = seq_read(pinctrl_handler_seq); seq_wait(pinctrl_handler_seq, new_seq); @@ -2165,6 +2480,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_prefix_req(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, @@ -2507,6 +2825,149 @@ 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) +{ + 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 = eth_src; + + 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(1); + 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); + + 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 + 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_prefix_msg(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 @@ -2584,6 +3045,96 @@ prepare_ipv6_ras(const struct hmap *local_datapaths) } +static void +prepare_ipv6_prefix_req(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; + struct ipv6_prefixd_state *pfd; + 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; + } + + 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->metadata = peer->datapath->tunnel_key; + pfd->port_key = peer->tunnel_key; + shash_add(&ipv6_prefixd, pb->logical_port, pfd); + pfd->next_announce = time_msec() + IPV6_PREFIXD_TIMEOUT; + changed = true; + } else if (pfd->state == PREFIX_PENDING) { + 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); + } + + if (changed) { + notify_pinctrl_handler(); + } +} + /* Called by pinctrl_run(). Runs with in the main ovn-controller * thread context. */ void @@ -2606,6 +3157,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(); @@ -4093,6 +4645,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 f4997e9c9..578292a16 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -88,7 +88,8 @@ struct ovn_extend_table; OVNACT(OVNFIELD_LOAD, ovnact_load) \ OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) \ OVNACT(TRIGGER_EVENT, ovnact_controller_event) \ - OVNACT(BIND_VPORT, ovnact_bind_vport) + OVNACT(BIND_VPORT, ovnact_bind_vport) \ + OVNACT(DHCP6_SERVER_PKT, ovnact_nest) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -537,6 +538,12 @@ enum action_opcode { * MFF_LOG_INPORT. */ ACTION_OPCODE_BIND_VPORT, + + /* dhcp6_server_pkt { ...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 a999a4fda..31fce0685 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_SERVER_PKT(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_DHCP6_SERVER_PKT, "ip6"); +} + +static void +format_DHCP6_SERVER_PKT(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "dhcp6_server_pkt", s); +} + +static void +encode_DHCP6_SERVER_PKT(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) { @@ -2931,6 +2951,8 @@ parse_action(struct action_context *ctx) parse_trigger_event(ctx, ovnact_put_TRIGGER_EVENT(ctx->ovnacts)); } else if (lexer_match_id(ctx->lexer, "bind_vport")) { parse_bind_vport(ctx); + } else if (lexer_match_id(ctx->lexer, "dhcp6_server_pkt")) { + parse_DHCP6_SERVER_PKT(ctx); } else { lexer_syntax_error(ctx->lexer, "expecting action"); } diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h index 5fc370bf5..3178f475c 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") @@ -242,6 +245,22 @@ struct ovs_nd_dnssl { }; BUILD_ASSERT_DECL(ND_DNSSL_OPT_LEN == sizeof(struct ovs_nd_dnssl)); +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/tests/ovn.at b/tests/ovn.at index 410f4b514..1f28af906 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1468,6 +1468,12 @@ bind_vport("xyzzy",; bind_vport("xyzzy", inport; Syntax error at `;' expecting `)'. +# prefix delegation +dhcp6_server_pkt{}; + formats as dhcp6_server_pkt { drop; }; + encodes as controller(userdata=00.00.00.12.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 ea64dc673..1d99651d2 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -2221,6 +2221,8 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_BIND_VPORT: break; + case OVNACT_DHCP6_SERVER_PKT: + break; } } ds_destroy(&s); From patchwork Thu Nov 7 08:47:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1190993 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.169.12; helo=mail.linuxfoundation.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="KV/9tdaD"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 477xsV45yfz9sPT for ; Thu, 7 Nov 2019 19:49:18 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8FEB2DA4; Thu, 7 Nov 2019 08:47:29 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 00388D9D for ; Thu, 7 Nov 2019 08:47:28 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 5F9D7712 for ; Thu, 7 Nov 2019 08:47:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1573116446; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IcfzUf6D5ObnvP91CdWGVXV1dDhQgqEHJA2SJBIGWfY=; b=KV/9tdaDj7SvkY4p5UZ0xjR1n4D9vO5+Sz1+tOTHZyFkeZkT/+/ZrEjLEB2PmqYRUfCfSs /Umr7e38aLSYXZ4e/Cv8NuCmr58KqHeAEnuaqrCtkmNCa7DwTfoKsEeTnSx820RwjpzhCS DZ/0wPOe2ihKIZcDWYFQ7wykiWdZqwI= 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-318-0uu1ho3UNK6eSZOcHYAQdA-1; Thu, 07 Nov 2019 03:47:20 -0500 Received: by mail-wr1-f69.google.com with SMTP id h7so331291wrb.2 for ; Thu, 07 Nov 2019 00:47:19 -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:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Osq6fv/tKCQlekUNWs5I5BAj1bVTxuTQBT5x14K6E8Y=; b=jO0RRJf9VTCfrmB1KEXns3Ru+/QfZTnln2vA7N7Aq+EtswjAKJf9MrOxuLEf5PgbjX QrtWjXmcMDs6QWwJP+76SZGw4g6y3kttsHBmIFDwlTLRqLhkbVZoi7Wz+CLJbITJSixJ /NVv4UERw8BLWHTKxLQUVh9ybJkjrJp7dMBKBEvOUFTnp5wjQANM7j30/eaVkusNgpYD PKG0kwwJHqotkdcbDSiezdht1S68l+5suSfwiKzZ8PjNmmkDaYsbmSBOWgrOOHrXfo5v VWqQkbVud66Ov7tQoMtjBVpZsCNvkoGLf5EfeL+dO/9ItThP/g5UWIGiXEsL7Az+f5Xz qWvA== X-Gm-Message-State: APjAAAVotUEgiP91caJu+ghl0DU4aRIr0mdwclPUjTVhVRpIecHfo5xs 3oqvb3eqBsVYT/CoVWSJ3+MyTnVXZeykTMs9fkR4r7oUUp/hjAA9nnZl4PhhQbum9IQUqdoV7pU 5jgcCcxPjYUsT X-Received: by 2002:adf:f342:: with SMTP id e2mr1794047wrp.61.1573116438621; Thu, 07 Nov 2019 00:47:18 -0800 (PST) X-Google-Smtp-Source: APXvYqzrYaFUJxxNz0+qXGSXHausQ0zBbjGOakraU/018ei8Pxff9qi3WLtf1SVMsBzMRR1t3Q3qjw== X-Received: by 2002:adf:f342:: with SMTP id e2mr1794029wrp.61.1573116438300; Thu, 07 Nov 2019 00:47:18 -0800 (PST) Received: from localhost.localdomain.com ([176.229.194.15]) by smtp.gmail.com with ESMTPSA id y9sm1216621wma.3.2019.11.07.00.47.17 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Nov 2019 00:47:17 -0800 (PST) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Thu, 7 Nov 2019 10:47:02 +0200 Message-Id: <45c468a73799df83be0e9e3e4129450545b846ca.1573116139.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: References: MIME-Version: 1.0 X-MC-Unique: 0uu1ho3UNK6eSZOcHYAQdA-1 X-Mimecast-Spam-Score: 0 X-Spam-Status: No, score=-4.3 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH ovn 2/2] northd: add logical flows for dhcpv6 pfd parsing X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org 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 | 66 ++++++++++++++++++++++++++++++++++++++++++++- ovn-nb.xml | 10 +++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index c23c270dc..e1af9828b 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -2588,6 +2588,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". */ @@ -2710,6 +2712,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); @@ -2759,6 +2767,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)) { @@ -2808,6 +2822,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 { @@ -7242,7 +7262,38 @@ 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; + } + + bool prefix_delegation = smap_get_bool(&op->nbrp->options, + "prefix_delegation", false); + if (!prefix_delegation) { + 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, "inport == %s && ip6.dst == %s" + " && udp.src == 547 && udp.dst == 546", + op->json_key, lrp_networks.ipv6_addrs[i].addr_s); + ds_put_format(&actions, "reg0 = 0; dhcp6_server_pkt { " + "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; @@ -8037,6 +8088,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } + /* enable IPv6 prefix delegation */ + bool prefix_delegation = smap_get_bool(&op->nbrp->options, + "prefix_delegation", false); + if (prefix_delegation) { + struct smap options; + + smap_clone(&options, &op->sb->options); + smap_add(&options, "ipv6_prefix_delegation", "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 d8f3237fc..7468d37ec 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -2048,6 +2048,16 @@ 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. +

+