On Thu, Nov 28, 2019 at 3:53 PM Numan Siddique <num...@ovn.org> wrote: > > 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
Oops. Please ignore this. I meant to reply to your patches related to RFC 4191. 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