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

Numan Siddique numans at ovn.org
Thu Nov 28 10:25:03 UTC 2019


On Thu, Nov 28, 2019 at 3:53 PM Numan Siddique <numans at ovn.org> wrote:
>
> 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

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
> > dev at openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >


More information about the dev mailing list