On Tue, Nov 26, 2019 at 5:21 PM Lorenzo Bianconi <lorenzo.bianc...@redhat.com> wrote: > > 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 column 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 <lorenzo.bianc...@redhat.com>
Thanks Lorenzo for the patches. The patch LGTM. A couple of comments 1. Can you please change the option name in the OVN NB DB from "prf" to "router_preference" Using "ipv6_ra_prf" in south db is fine with me. 2. Can you please enhance the IPv6 RA test case and this option as well. Thanks Numan > --- > controller/pinctrl.c | 607 ++++++++++++++++++++++++++++++++++++++++++ > 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, 672 insertions(+), 1 deletion(-) > > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index 8fc31d38a..a51fdaaca 100644 > --- a/controller/pinctrl.c > +++ b/controller/pinctrl.c > @@ -270,6 +270,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); > @@ -457,6 +470,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 +558,57 @@ 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 > +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; > @@ -967,6 +1032,254 @@ 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, 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_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; > + unsigned aid = 0; > + > + 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; > + > + 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_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( > @@ -1998,6 +2311,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 +2393,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 +2447,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); > > @@ -2146,6 +2465,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 +2517,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, > @@ -2541,6 +2864,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 10000LL > +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_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 > @@ -2618,6 +3086,143 @@ prepare_ipv6_ras(const struct hmap *local_datapaths) > > } > > +static bool > +fill_ipv6_prefix_state(struct ovsdb_idl_index > *sbrec_port_binding_by_datapath, > + const struct hmap *local_datapaths, struct eth_addr > ea, > + 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); > + if (c_addrs.n_ipv6_addrs > 0) { > + pfd->ipv6_addr = c_addrs.ipv6_addrs[0].addr; > + } else { > + in6_generate_lla(c_addrs.ea, &pfd->ipv6_addr); > + } > + pfd->ea = 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) { > + 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_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; > + 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(sbrec_port_binding_by_datapath, > + local_datapaths, laddrs.ea, > + 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 > @@ -2640,6 +3245,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(); > @@ -4137,6 +4743,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_<ENUM> 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 586d7b75d..cdec22ba9 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 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/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; > > <p><b>Example:</b> <code>handle_svc_check(inport);</code></p> > </dd> > + > + <dt><code>handle_dhcpv6_reply;</code></dt> > + <dd> > + <p> > + This action is used to parse DHCPv6 replies from IPv6 > + Delegation Router and managed IPv6 Prefix delegation state > machine > + </p> > + </dd> > </dl> > </column> > > diff --git a/tests/ovn.at b/tests/ovn.at > index f54823b5d..66f8ae791 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 19b82e6a4..e69844a0c 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); > -- > 2.21.0 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev