[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