[ovs-dev] [PATCH v3 ovn 1/2] controller: add ipv6 prefix delegation state machine
Numan Siddique
numans at ovn.org
Mon Dec 16 22:38:26 UTC 2019
Hi Lorenzo,
Thanks for the series. Few comments below. Otherwise LGTM.
Thanks
Numan
On Thu, Dec 5, 2019 at 5:51 AM Lorenzo Bianconi
<lorenzo.bianconi at 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 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 <lorenzo.bianconi at redhat.com>
> ---
> 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 c886b21d9..0ef3aef31 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;
I think we can initialize shash here ? so that we don't need
init_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);
> + }
There is no need for the if condition. free(pfd) should work.
> +}
> +
> +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) {
xmalloc() takes care of failure in memory allocation. so there is no
need to check if the returned ptr is NULL or not.
> + return;
> + }
> +
> + in_dhcpv6_data += 4;
A comment on why 4 bytes were skipped would be helpful.
Or It would be hlepful If you could describe how dhcpv6 reply/Adv looks like.
Example - https://github.com/ovn-org/ovn/blob/master/controller/pinctrl.c#L1018
> +
> + 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);
I think you can rename this function to send_ipv6_ras() to be consistent with
other prefixd functions.
> 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,
I think you can rename this function to prepare_ipv6_prefixd_req()
There are similar other functions which needs to be renamed to *_prefixd_*()
> + sbrec_port_binding_by_name, local_datapaths,
> + chassis, active_tunnels);
> sync_dns_cache(dns_table);
> controller_event_run(ovnsb_idl_txn, ce_table, chassis);
> ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths,
> @@ -2635,6 +2958,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
> @@ -2712,6 +3180,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
> @@ -2734,6 +3339,7 @@ pinctrl_destroy(void)
> free(pinctrl.br_int_name);
> destroy_send_garps_rarps();
> destroy_ipv6_ras();
> + destroy_ipv6_prefixd();
> destroy_buffered_packets_map();
> event_table_destroy();
> destroy_put_mac_bindings();
> @@ -4231,6 +4837,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 051e6c875..b7bd219e4 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -2172,6 +2172,26 @@ ovnact_put_opts_free(struct ovnact_put_opts *pdo)
> free_gen_options(pdo->options, pdo->n_options);
> }
>
> +static void
> +parse_DHCP6_REPLY(struct action_context *ctx)
> +{
> + parse_nested_action(ctx, OVNACT_DHCP6_REPLY, "ip6");
> +}
> +
> +static void
> +format_DHCP6_REPLY(const struct ovnact_nest *nest, struct ds *s)
> +{
> + format_nested_action(nest, "handle_dhcpv6_reply", s);
> +}
> +
> +static void
> +encode_DHCP6_REPLY(const struct ovnact_nest *on,
> + const struct ovnact_encode_params *ep,
> + struct ofpbuf *ofpacts)
> +{
> + encode_nested_actions(on, ep, ACTION_OPCODE_DHCP6_SERVER, ofpacts);
> +}
> +
> static void
> parse_SET_QUEUE(struct action_context *ctx)
> {
> @@ -2973,6 +2993,8 @@ parse_action(struct action_context *ctx)
> parse_bind_vport(ctx);
> } else if (lexer_match_id(ctx->lexer, "handle_svc_check")) {
> parse_handle_svc_check(ctx);
> + } else if (lexer_match_id(ctx->lexer, "handle_dhcpv6_reply")) {
> + parse_DHCP6_REPLY(ctx);
> } else {
> lexer_syntax_error(ctx->lexer, "expecting action");
> }
> diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> index ae6dbfdfb..387e7eba1 100644
> --- a/lib/ovn-l7.h
> +++ b/lib/ovn-l7.h
> @@ -174,8 +174,11 @@ struct dhcp_opt6_header {
> #define DHCPV6_OPT_SERVER_ID_CODE 2
> #define DHCPV6_OPT_IA_NA_CODE 3
> #define DHCPV6_OPT_IA_ADDR_CODE 5
> +#define DHCPV6_OPT_STATUS_CODE 13
> #define DHCPV6_OPT_DNS_SERVER_CODE 23
> #define DHCPV6_OPT_DOMAIN_SEARCH_CODE 24
> +#define DHCPV6_OPT_IA_PD 25
> +#define DHCPV6_OPT_IA_PREFIX 26
>
> #define DHCPV6_OPT_SERVER_ID \
> DHCP_OPTION("server_id", DHCPV6_OPT_SERVER_ID_CODE, "mac")
> @@ -254,6 +257,22 @@ struct ovs_nd_route_info {
> };
> BUILD_ASSERT_DECL(ND_ROUTE_INFO_OPT_LEN == sizeof(struct ovs_nd_route_info));
>
> +OVS_PACKED(
> +struct dhcpv6_opt_ia_prefix {
> + struct dhcpv6_opt_header opt;
> + ovs_be32 plife_time;
> + ovs_be32 vlife_time;
> + uint8_t plen;
> + struct in6_addr ipv6;
> +});
> +
> +OVS_PACKED(
> +struct dhcpv6_opt_status {
> + struct dhcpv6_opt_header opt;
> + ovs_be16 status_code;
> + uint8_t msg[];
> +});
> +
> #define DHCPV6_DUID_LL 3
> #define DHCPV6_HW_TYPE_ETH 1
>
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index 82167c488..14fe249ec 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -2114,6 +2114,14 @@ tcp.flags = RST;
>
> <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 8f4d9a440..0dfec8dc5 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -1481,6 +1481,12 @@ handle_svc_check();
> handle_svc_check(reg0);
> Cannot use numeric field reg0 where string field is required.
>
> +# prefix delegation
> +handle_dhcpv6_reply{};
> + formats as handle_dhcpv6_reply { drop; };
> + encodes as controller(userdata=00.00.00.13.00.00.00.00)
> + has prereqs ip6
> +
> # Miscellaneous negative tests.
> ;
> Syntax error at `;'.
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index 264543876..3b132a2eb 100644
> --- a/utilities/ovn-trace.c
> +++ b/utilities/ovn-trace.c
> @@ -2224,6 +2224,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
>
> case OVNACT_HANDLE_SVC_CHECK:
> break;
> +
> + case OVNACT_DHCP6_REPLY:
> + break;
> }
> }
> ds_destroy(&s);
> --
> 2.21.0
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
More information about the dev
mailing list