[ovs-dev] [PATCH v2 ovn 1/2] controller: add ipv6 prefix delegation state machine

Numan Siddique numans at ovn.org
Thu Nov 28 10:23:01 UTC 2019


On Tue, Nov 26, 2019 at 5:21 PM 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 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.bianconi at 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
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>


More information about the dev mailing list