[ovs-dev] [PATCH v8 2/4] ovn-controller: Add a new action - 'put_nd_ra_opts'
Numan Siddique
nusiddiq at redhat.com
Mon Oct 2 16:10:54 UTC 2017
Thanks Mark for the review. I will respin another patch set with your
comments addressed.
Numan
On Fri, Sep 29, 2017 at 7:17 PM, Mark Michelson <mmichels at redhat.com> wrote:
>
>
> On Thu, Sep 21, 2017 at 11:10 AM <nusiddiq at redhat.com> wrote:
>
>> From: Numan Siddique <nusiddiq at redhat.com>
>>
>> This patch adds a new OVN action 'put_nd_ra_opts' to support native
>> IPv6 Router Advertisement in OVN. This action can be used to respond
>> to the IPv6 Router Solicitation requests.
>>
>> ovn-controller parses this action and adds a NXT_PACKET_IN2 OF flow
>> with 'pause' flag set and the RA options stored in 'userdata' field.
>> This action is similar to 'put_dhcp_opts' and 'put_dhcpv6_opts'.
>>
>> When a valid IPv6 RS packet is received by the pinctrl module of
>> ovn-controller, it frames a new RA packet and sets the RA options
>> from the 'userdata' field and resumes the packet storing 1 in the
>> 1-bit result sub-field. If the packet is invalid, it resumes the
>> packet without any modifications storing 0 in the 1-bit result
>> sub-field.
>>
>> Eg. reg0[5] = put_nd_ra_opts(address_mode = "slaac", mtu = 1450,
>> slla = 01:02:03:04:05:06, prefix = aef0::/64)
>>
>> Note that unlike DHCPv4/v6, a new table to store the supported IPv6 ND RA
>> options is not added in SB DB since there are only 3 ND RA options.
>>
>> Co-authored-by: Zongkai LI <zealokii at gmail.com>
>> Signed-off-by: Zongkai LI <zealokii at gmail.com>
>> Signed-off-by: Numan Siddique <nusiddiq at redhat.com>
>> ---
>> include/ovn/actions.h | 14 +++-
>> ovn/controller/lflow.c | 13 +++-
>> ovn/controller/pinctrl.c | 96 +++++++++++++++++++++++
>> ovn/lib/actions.c | 194 ++++++++++++++++++++++++++++++
>> +++++++++++++++-
>> ovn/lib/ovn-l7.h | 48 ++++++++++++
>> ovn/ovn-sb.xml | 77 ++++++++++++++++++
>> ovn/utilities/ovn-trace.c | 51 ++++++++----
>> tests/ovn.at | 31 ++++++++
>> tests/test-ovn.c | 13 +++-
>> 9 files changed, 516 insertions(+), 21 deletions(-)
>>
>> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
>> index d13a3747b..15cee478d 100644
>> --- a/include/ovn/actions.h
>> +++ b/include/ovn/actions.h
>> @@ -72,7 +72,8 @@ struct simap;
>> OVNACT(PUT_DHCPV6_OPTS, ovnact_put_opts) \
>> OVNACT(SET_QUEUE, ovnact_set_queue) \
>> OVNACT(DNS_LOOKUP, ovnact_dns_lookup) \
>> - OVNACT(LOG, ovnact_log)
>> + OVNACT(LOG, ovnact_log) \
>> + OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts)
>>
>> /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
>> enum OVS_PACKED_ENUM ovnact_type {
>> @@ -418,6 +419,14 @@ enum action_opcode {
>> * - A variable length string containing the name.
>> */
>> ACTION_OPCODE_LOG,
>> +
>> + /* "result = put_nd_ra_opts(option, ...)".
>> + * Arguments follow the action_header, in this format:
>> + * - A 32-bit or 64-bit OXM header designating the result field.
>> + * - A 32-bit integer specifying a bit offset within the result
>> field.
>> + * - Any number of ICMPv6 options.
>> + */
>> + ACTION_OPCODE_PUT_ND_RA_OPTS,
>> };
>>
>> /* Header. */
>> @@ -438,6 +447,9 @@ struct ovnact_parse_params {
>> /* hmap of 'struct gen_opts_map' to support 'put_dhcpv6_opts'
>> action */
>> const struct hmap *dhcpv6_opts;
>>
>> + /* hmap of 'struct gen_opts_map' to support 'put_nd_ra_opts' action
>> */
>> + const struct hmap *nd_ra_opts;
>> +
>> /* Each OVN flow exists in a logical table within a logical pipeline.
>> * These parameters express this context for a set of OVN actions
>> being
>> * parsed:
>> diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
>> index 6b6b91abc..a62ec6ebe 100644
>> --- a/ovn/controller/lflow.c
>> +++ b/ovn/controller/lflow.c
>> @@ -65,6 +65,7 @@ static void consider_logical_flow(struct controller_ctx
>> *ctx,
>> const struct sbrec_chassis *chassis,
>> struct hmap *dhcp_opts,
>> struct hmap *dhcpv6_opts,
>> + struct hmap *nd_ra_opts,
>> uint32_t *conj_id_ofs,
>> const struct shash *addr_sets,
>> struct hmap *flow_table,
>> @@ -167,17 +168,21 @@ add_logical_flows(struct controller_ctx *ctx,
>> dhcpv6_opt_row->type);
>> }
>>
>> + struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
>> + nd_ra_opts_init(&nd_ra_opts);
>> +
>> SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
>> consider_logical_flow(ctx, chassis_index,
>> lflow, local_datapaths,
>> group_table, chassis,
>> - &dhcp_opts, &dhcpv6_opts, &conj_id_ofs,
>> - addr_sets, flow_table, active_tunnels,
>> - local_lport_ids);
>> + &dhcp_opts, &dhcpv6_opts, &nd_ra_opts,
>> + &conj_id_ofs, addr_sets, flow_table,
>> + active_tunnels, local_lport_ids);
>> }
>>
>> dhcp_opts_destroy(&dhcp_opts);
>> dhcp_opts_destroy(&dhcpv6_opts);
>> + nd_ra_opts_destroy(&nd_ra_opts);
>> }
>>
>> static void
>> @@ -189,6 +194,7 @@ consider_logical_flow(struct controller_ctx *ctx,
>> const struct sbrec_chassis *chassis,
>> struct hmap *dhcp_opts,
>> struct hmap *dhcpv6_opts,
>> + struct hmap *nd_ra_opts,
>> uint32_t *conj_id_ofs,
>> const struct shash *addr_sets,
>> struct hmap *flow_table,
>> @@ -224,6 +230,7 @@ consider_logical_flow(struct controller_ctx *ctx,
>> .symtab = &symtab,
>> .dhcp_opts = dhcp_opts,
>> .dhcpv6_opts = dhcpv6_opts,
>> + .nd_ra_opts = nd_ra_opts,
>>
>> .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
>> .n_tables = LOG_PIPELINE_LEN,
>> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
>> index 43e3cba23..6dbea4efa 100644
>> --- a/ovn/controller/pinctrl.c
>> +++ b/ovn/controller/pinctrl.c
>> @@ -81,6 +81,10 @@ static void pinctrl_handle_nd_na(const struct flow
>> *ip_flow,
>> struct ofpbuf *userdata);
>> static void reload_metadata(struct ofpbuf *ofpacts,
>> const struct match *md);
>> +static void pinctrl_handle_put_nd_ra_opts(
>> + const struct flow *ip_flow, struct dp_packet *pkt_in,
>> + struct ofputil_packet_in *pin, struct ofpbuf *userdata,
>> + struct ofpbuf *continuation OVS_UNUSED);
>>
>
> I'm curious why the continuation parameter has the OVS_UNUSED attribute
> when it is actually used in the function.
>
>
>> COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
>>
>> @@ -985,6 +989,11 @@ process_packet_in(const struct ofp_header *msg,
>> struct controller_ctx *ctx)
>> handle_acl_log(&headers, &userdata);
>> break;
>>
>> + case ACTION_OPCODE_PUT_ND_RA_OPTS:
>> + pinctrl_handle_put_nd_ra_opts(&headers, &packet, &pin,
>> &userdata,
>> + &continuation);
>> + break;
>> +
>> default:
>> VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
>> ntohl(ah->opcode));
>> @@ -1848,3 +1857,90 @@ exit:
>> dp_packet_uninit(&packet);
>> ofpbuf_uninit(&ofpacts);
>> }
>> +
>> +static void
>> +pinctrl_handle_put_nd_ra_opts(
>> + const struct flow *in_flow, struct dp_packet *pkt_in,
>> + struct ofputil_packet_in *pin, struct ofpbuf *userdata,
>> + struct ofpbuf *continuation OVS_UNUSED)
>> +{
>> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>> + enum ofp_version version = rconn_get_version(swconn);
>> + enum ofputil_protocol proto = ofputil_protocol_from_ofp_
>> version(version);
>> + struct dp_packet *pkt_out_ptr = NULL;
>> + uint32_t success = 0;
>> +
>> + /* Parse result field. */
>> + const struct mf_field *f;
>> + enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
>> + if (ofperr) {
>> + VLOG_WARN_RL(&rl, "bad result OXM (%s)",
>> ofperr_to_string(ofperr));
>> + goto exit;
>> + }
>> +
>> + /* Parse result offset. */
>> + ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
>> + if (!ofsp) {
>> + VLOG_WARN_RL(&rl, "offset not present in the userdata");
>> + goto exit;
>> + }
>> +
>> + /* Check that the result is valid and writable. */
>> + struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits
>> = 1 };
>> + ofperr = mf_check_dst(&dst, NULL);
>> + if (ofperr) {
>> + VLOG_WARN_RL(&rl, "bad result bit (%s)",
>> ofperr_to_string(ofperr));
>> + goto exit;
>> + }
>> +
>> + if (!userdata->size) {
>> + VLOG_WARN_RL(&rl, "IPv6 ND RA options not present in the
>> userdata");
>> + goto exit;
>> + }
>> +
>> + if (!is_icmpv6(in_flow, NULL) || in_flow->tp_dst != htons(0) ||
>> + in_flow->tp_src != htons(ND_ROUTER_SOLICIT)) {
>> + VLOG_WARN_RL(&rl, "put_nd_ra action on invalid or unsupported
>> packet");
>> + goto exit;
>> + }
>> +
>> + size_t new_packet_size = pkt_in->l4_ofs + userdata->size;
>> + struct dp_packet pkt_out;
>> + dp_packet_init(&pkt_out, new_packet_size);
>> + dp_packet_clear(&pkt_out);
>> + dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
>> + pkt_out_ptr = &pkt_out;
>> +
>> + /* Copy L2 and L3 headers from pkt_in. */
>> + dp_packet_put(&pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs),
>> + pkt_in->l4_ofs);
>> +
>> + pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
>> + pkt_out.l2_pad_size = pkt_in->l2_pad_size;
>> + pkt_out.l3_ofs = pkt_in->l3_ofs;
>> + pkt_out.l4_ofs = pkt_in->l4_ofs;
>> +
>> + /* Copy the ICMPv6 Router Advertisement data from 'userdata' field.
>> */
>> + dp_packet_put(&pkt_out, userdata->data, userdata->size);
>> +
>> + /* Set the IPv6 payload length and calculate the ICMPv6 checksum. */
>> + struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
>> + nh->ip6_plen = htons(userdata->size);
>> + struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out);
>> + ra->icmph.icmp6_cksum = 0;
>> + uint32_t icmp_csum = packet_csum_pseudoheader6(nh);
>> + ra->icmph.icmp6_cksum = csum_finish(csum_continue(
>> + icmp_csum, ra, userdata->size));
>> + pin->packet = dp_packet_data(&pkt_out);
>> + pin->packet_len = dp_packet_size(&pkt_out);
>> + success = 1;
>> +
>> +exit:
>> + if (!ofperr) {
>> + union mf_subvalue sv;
>> + sv.u8_val = success;
>> + mf_write_subfield(&dst, &sv, &pin->flow_metadata);
>> + }
>> + queue_msg(ofputil_encode_resume(pin, continuation, proto));
>> + dp_packet_uninit(pkt_out_ptr);
>> +}
>> diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
>> index b2559cd07..2981d89f6 100644
>> --- a/ovn/lib/actions.c
>> +++ b/ovn/lib/actions.c
>> @@ -22,6 +22,7 @@
>> #include "compiler.h"
>> #include "ovn-l7.h"
>> #include "hash.h"
>> +#include "lib/packets.h"
>> #include "logical-fields.h"
>> #include "nx-match.h"
>> #include "openvswitch/dynamic-string.h"
>> @@ -1438,7 +1439,7 @@ parse_put_opts(struct action_context *ctx, const
>> struct expr_field *dst,
>> struct ovnact_put_opts *po, const struct hmap *gen_opts,
>> const char *opts_type)
>> {
>> - lexer_get(ctx->lexer); /* Skip put_dhcp[v6]_opts. */
>> + lexer_get(ctx->lexer); /* Skip put_dhcp[v6]_opts / put_nd_ra_opts. */
>> lexer_get(ctx->lexer); /* Skip '('. */
>>
>> /* Validate that the destination is a 1-bit, modifiable field. */
>> @@ -1771,6 +1772,193 @@ static void
>> ovnact_dns_lookup_free(struct ovnact_dns_lookup *dl OVS_UNUSED)
>> {
>> }
>> +
>> +/* Parses the "put_nd_ra_opts" action.
>> + * The caller has already consumed "<dst> =", so this just parses the
>> rest. */
>> +static void
>> +parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field
>> *dst,
>> + struct ovnact_put_opts *po)
>> +{
>> + parse_put_opts(ctx, dst, po, ctx->pp->nd_ra_opts, "IPv6 ND RA");
>> +
>> + if (ctx->lexer->error) {
>> + return;
>> + }
>> +
>> + bool addr_mode_stateful = false;
>> + bool prefix_set = false;
>> + bool slla_present = false;
>> + /* Let's validate the options. */
>> + for (struct ovnact_gen_option *o = po->options;
>> + o < &po->options[po->n_options]; o++) {
>> + const union expr_constant *c = o->value.values;
>> + if (o->value.n_values > 1) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts -Invalid value
>> for"
>> + " the option %s.", o->option->name);
>> + return;
>> + }
>> +
>> + switch (o->option->code) {
>> + case ND_RA_FLAG_ADDR_MODE:
>> + if (!c->string || (strcmp(c->string, "slaac") &&
>> + strcmp(c->string, "dhcpv6_stateful") &&
>> + strcmp(c->string, "dhcpv6_stateless"))) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts -Invalid
>> value "
>> + "for the option %s.", o->option->name);
>> + return;
>> + }
>> +
>> + if (!strcmp(c->string, "dhcpv6_stateful")) {
>> + addr_mode_stateful = true;
>> + }
>> + break;
>> +
>> + case ND_OPT_SOURCE_LINKADDR:
>> + if (c->format != LEX_F_ETHERNET) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts -Invalid
>> value "
>> + "for the option %s.", o->option->name);
>> + }
>> + slla_present = true;
>> + break;
>> +
>> + case ND_OPT_PREFIX_INFORMATION:
>> + if (c->format != LEX_F_IPV6 || !c->masked) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts -Invalid
>> value "
>> + "for the option %s.", o->option->name);
>> + }
>> + prefix_set = true;
>> + break;
>> +
>> + case ND_OPT_MTU:
>> + if (c->format != LEX_F_DECIMAL) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts -Invalid
>> value "
>> + "for the option %s.", o->option->name);
>> + }
>> + break;
>> + }
>> + }
>> +
>> + if (ctx->lexer->error) {
>> + return;
>> + }
>> +
>> + if (!slla_present) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts - slla option not"
>> + " present.");
>> + return;
>> + }
>> +
>> + if (addr_mode_stateful && prefix_set) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts - prefix option
>> can't be"
>> + " set when address mode is dhcpv6_stateful.");
>> + return;
>> + }
>> +
>> + if (!addr_mode_stateful && !prefix_set) {
>> + lexer_error(ctx->lexer, "parse_put_nd_ra_opts - prefix option
>> needs "
>> + "to be set when address mode is
>> slaac/dhcpv6_stateless.");
>> + return;
>> + }
>> +
>> + add_prerequisite(ctx, "ip6");
>> +}
>> +
>> +static void
>> +format_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po,
>> + struct ds *s)
>> +{
>> + format_put_opts("put_nd_ra_opts", po, s);
>> +}
>> +
>> +static void
>> +encode_put_nd_ra_option(const struct ovnact_gen_option *o,
>> + struct ofpbuf *ofpacts, struct ovs_ra_msg *ra)
>> +{
>> + const union expr_constant *c = o->value.values;
>> +
>> + switch (o->option->code) {
>> + case ND_RA_FLAG_ADDR_MODE:
>> + if (!strcmp(c->string, "dhcpv6_stateful")) {
>> + ra->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
>> + } else if (!strcmp(c->string, "dhcpv6_stateless")) {
>> + ra->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
>> + }
>> + break;
>> +
>> + case ND_OPT_SOURCE_LINKADDR:
>> + {
>> + struct ovs_nd_lla_opt *lla_opt =
>> + ofpbuf_put_uninit(ofpacts, sizeof *lla_opt);
>> + lla_opt->type = ND_OPT_SOURCE_LINKADDR;
>> + lla_opt->len = 1;
>> + lla_opt->mac = c->value.mac;
>> + break;
>> + }
>> +
>> + case ND_OPT_MTU:
>> + {
>> + struct ovs_nd_mtu_opt *mtu_opt =
>> + ofpbuf_put_uninit(ofpacts, sizeof *mtu_opt);
>> + mtu_opt->type = ND_OPT_MTU;
>> + mtu_opt->len = 1;
>> + mtu_opt->reserved = 0;
>> + put_16aligned_be32(&mtu_opt->mtu, c->value.be32_int);
>> + break;
>> + }
>> +
>> + case ND_OPT_PREFIX_INFORMATION:
>> + {
>> + struct ovs_nd_prefix_opt *prefix_opt =
>> + ofpbuf_put_uninit(ofpacts, sizeof *prefix_opt);
>> + uint8_t prefix_len = ipv6_count_cidr_bits(&c->mask.ipv6);
>> + prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
>> + prefix_opt->len = 4;
>> + prefix_opt->prefix_len = prefix_len;
>> + prefix_opt->la_flags = IPV6_ND_RA_OPT_PREFIX_FLAGS;
>> + put_16aligned_be32(&prefix_opt->valid_lifetime,
>> + htonl(IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME));
>> + put_16aligned_be32(&prefix_opt->preferred_lifetime,
>> + htonl(IPV6_ND_RA_OPT_PREFIX_
>> PREFERRED_LIFETIME));
>> + put_16aligned_be32(&prefix_opt->reserved, 0);
>> + memcpy(prefix_opt->prefix.be32, &c->value.be128[7].be32,
>> + sizeof(ovs_be32[4]));
>> + break;
>> + }
>> + }
>> +}
>> +
>> +static void
>> +encode_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po OVS_UNUSED,
>> + const struct ovnact_encode_params *ep OVS_UNUSED,
>> + struct ofpbuf *ofpacts OVS_UNUSED)
>>
>
> I have the same question regarding OVS_UNUSED here as well.
>
>
>> +{
>> + struct mf_subfield dst = expr_resolve_field(&po->dst);
>> +
>> + size_t oc_offset = encode_start_controller_op(
>> + ACTION_OPCODE_PUT_ND_RA_OPTS, true, ofpacts);
>> + nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
>> + ovs_be32 ofs = htonl(dst.ofs);
>> + ofpbuf_put(ofpacts, &ofs, sizeof ofs);
>> +
>> + /* Frame the complete ICMPv6 Router Advertisement data encoding
>> + * the ND RA options in it, in the userdata field, so that when
>> + * pinctrl module receives the ICMPv6 Router Solicitation packet
>> + * it can copy the userdata field AS IS and resume the packet.
>> + */
>> + struct ovs_ra_msg *ra = ofpbuf_put_zeros(ofpacts, sizeof *ra);
>> + ra->icmph.icmp6_type = ND_ROUTER_ADVERT;
>> + ra->cur_hop_limit = IPV6_ND_RA_CUR_HOP_LIMIT;
>> + ra->mo_flags = 0;
>> + ra->router_lifetime = htons(IPV6_ND_RA_LIFETIME);
>> +
>> + for (const struct ovnact_gen_option *o = po->options;
>> + o < &po->options[po->n_options]; o++) {
>> + encode_put_nd_ra_option(o, ofpacts, ra);
>> + }
>> +
>> + encode_finish_controller_op(oc_offset, ofpacts);
>> +}
>> +
>>
>> static void
>> parse_log_arg(struct action_context *ctx, struct ovnact_log *log)
>> @@ -1910,6 +2098,10 @@ parse_set_action(struct action_context *ctx)
>> } else if (!strcmp(ctx->lexer->token.s, "dns_lookup")
>> && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
>> parse_dns_lookup(ctx, &lhs, ovnact_put_DNS_LOOKUP(ctx->
>> ovnacts));
>> + } else if (!strcmp(ctx->lexer->token.s, "put_nd_ra_opts")
>> + && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
>> + parse_put_nd_ra_opts(ctx, &lhs,
>> + ovnact_put_PUT_ND_RA_OPTS(
>> ctx->ovnacts));
>> } else {
>> parse_assignment_action(ctx, false, &lhs);
>> }
>> diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h
>> index 40bd75461..41cdacdfc 100644
>> --- a/ovn/lib/ovn-l7.h
>> +++ b/ovn/lib/ovn-l7.h
>> @@ -18,6 +18,7 @@
>> #define OVN_DHCP_H 1
>>
>> #include <netinet/in.h>
>> +#include <netinet/icmp6.h>
>> #include "openvswitch/hmap.h"
>> #include "hash.h"
>>
>> @@ -206,4 +207,51 @@ struct dhcpv6_opt_ia_na {
>> #define DHCPV6_OPT_PAYLOAD(opt) \
>> (void *)((char *)opt + sizeof(struct dhcpv6_opt_header))
>>
>> +static inline struct gen_opts_map *
>> +nd_ra_opts_find(const struct hmap *nd_ra_opts, char *opt_name)
>> +{
>> + return gen_opts_find(nd_ra_opts, opt_name);
>> +}
>> +
>> +static inline void
>> +nd_ra_opt_add(struct hmap *nd_ra_opts, char *opt_name, size_t code,
>> + char *type)
>> +{
>> + gen_opt_add(nd_ra_opts, opt_name, code, type);
>> +}
>> +
>> +static inline void
>> +nd_ra_opts_destroy(struct hmap *nd_ra_opts)
>> +{
>> + gen_opts_destroy(nd_ra_opts);
>> +}
>> +
>> +
>> +#define ND_RA_FLAG_ADDR_MODE 0
>> +
>> +
>> +/* Default values of various IPv6 Neighbor Discovery protocol options and
>> + * flags. See RFC 4861 for more information.
>> + * */
>> +#define IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG 0x80
>> +#define IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG 0x40
>> +
>> +#define IPV6_ND_RA_CUR_HOP_LIMIT 255
>> +#define IPV6_ND_RA_LIFETIME 0xffff
>> +#define IPV6_ND_RA_REACHABLE_TIME 0
>> +#define IPV6_ND_RA_RETRANSMIT_TIMER 0
>> +
>> +#define IPV6_ND_RA_OPT_PREFIX_FLAGS 0xc0
>> +#define IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME 0xffffffff
>> +#define IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME 0xffffffff
>> +
>> +static inline void
>> +nd_ra_opts_init(struct hmap *nd_ra_opts)
>> +{
>> + nd_ra_opt_add(nd_ra_opts, "addr_mode", ND_RA_FLAG_ADDR_MODE, "str");
>> + nd_ra_opt_add(nd_ra_opts, "slla", ND_OPT_SOURCE_LINKADDR, "mac");
>> + nd_ra_opt_add(nd_ra_opts, "prefix", ND_OPT_PREFIX_INFORMATION,
>> "ipv6");
>> + nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32");
>> +}
>> +
>> #endif /* OVN_DHCP_H */
>> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
>> index 0a894f8cb..fab3f9de6 100644
>> --- a/ovn/ovn-sb.xml
>> +++ b/ovn/ovn-sb.xml
>> @@ -1516,6 +1516,83 @@
>> <b>Prerequisite:</b> <code>udp</code>
>> </p>
>> </dd>
>> +
>> + <dt>
>> + <code><var>R</var> = put_nd_ra_opts(<var>D1</var> =
>> <var>V1</var>, <var>D2</var> = <var>V2</var>, ..., <var>Dn</var> =
>> <var>Vn</var>);</code>
>> + </dt>
>> +
>> + <dd>
>> + <p>
>> + <b>Parameters</b>: The following IPv6 ND Router Advertisement
>> + option/value pairs as defined in RFC 4861.
>> +
>> + <ul>
>> + <li>
>> + <code>addr_mode</code>
>> + <p>
>> + Mandatory parameter which specifies the address mode
>> flag to
>> + be set in the RA flag options field. The value of this
>> option
>> + is a string and the following values can be defined -
>> + "slaac", "dhcpv6_stateful" and "dhcpv6_stateless".
>> + </p>
>> + </li>
>> +
>> + <li>
>> + <code>slla</code>
>> + <p>
>> + Mandatory parameter which specifies the link-layer
>> address of
>> + the interface from which the Router Advertisement is
>> sent.
>> + </p>
>> + </li>
>> +
>> + <li>
>> + <code>mtu</code>
>> + <p>
>> + Optional parameter which specifies the MTU.
>> + </p>
>> + </li>
>> +
>> + <li>
>> + <code>prefix</code>
>> + <p>
>> + Optional parameter which should be specified if the
>> addr_mode
>> + is "slaac" or "dhcpv6_stateless". The value should be
>> an IPv6
>> + prefix which will be used for stateless IPv6 address
>> + configuration. This option can be defined multiple
>> times.
>> + </p>
>> + </li>
>> + </ul>
>> + </p>
>> +
>> + <p>
>> + <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
>> + </p>
>> +
>> + <p>
>> + Valid only in the ingress pipeline.
>> + </p>
>> +
>> + <p>
>> + When this action is applied to an IPv6 Router solicitation
>> request
>> + packet, it changes the packet into an IPv6 Router
>> Advertisement
>> + reply and adds the options specified in the parameters, and
>> stores
>> + 1 in <var>R</var>.
>> + </p>
>> +
>> + <p>
>> + When this action is applied to a non-IPv6 Router solicitation
>> + packet or an invalid IPv6 request packet , it leaves the
>> packet
>> + unchanged and stores 0 in <var>R</var>.
>> + </p>
>> +
>> + <p>
>> + <b>Example:</b>
>> + <code>
>> + reg0[3] = put_nd_ra_opts(addr_mode = "slaac",
>> + slla = 00:00:00:00:10:02, prefix = aef0::/64, mtu = 1450);
>> + </code>
>> + </p>
>> + </dd>
>> </dl>
>>
>> <dl>
>> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
>> index d9465c90c..211148b8b 100644
>> --- a/ovn/utilities/ovn-trace.c
>> +++ b/ovn/utilities/ovn-trace.c
>> @@ -420,6 +420,7 @@ static struct shash address_sets;
>> /* DHCP options. */
>> static struct hmap dhcp_opts; /* Contains "struct gen_opts_map"s. */
>> static struct hmap dhcpv6_opts; /* Contains "struct gen_opts_map"s. */
>> +static struct hmap nd_ra_opts; /* Contains "struct gen_opts_map"s. */
>>
>> static struct ovntrace_datapath *
>> ovntrace_datapath_find_by_sb_uuid(const struct uuid *sb_uuid)
>> @@ -806,6 +807,7 @@ read_flows(void)
>> .symtab = &symtab,
>> .dhcp_opts = &dhcp_opts,
>> .dhcpv6_opts = &dhcpv6_opts,
>> + .nd_ra_opts = &nd_ra_opts,
>> .pipeline = (!strcmp(sblf->pipeline, "ingress")
>> ? OVNACT_P_INGRESS
>> : OVNACT_P_EGRESS),
>> @@ -881,6 +883,9 @@ read_gen_opts(void)
>> SBREC_DHCPV6_OPTIONS_FOR_EACH(sdo6, ovnsb_idl) {
>> dhcp_opt_add(&dhcpv6_opts, sdo6->name, sdo6->code, sdo6->type);
>> }
>> +
>> + hmap_init(&nd_ra_opts);
>> + nd_ra_opts_init(&nd_ra_opts);
>> }
>>
>> static void
>> @@ -1541,19 +1546,15 @@ execute_get_mac_bind(const struct
>> ovnact_get_mac_bind *bind,
>> }
>>
>> static void
>> -execute_put_dhcp_opts(const struct ovnact_put_opts *pdo,
>> - const char *name, struct flow *uflow,
>> - struct ovs_list *super)
>> +execute_put_opts(const struct ovnact_put_opts *po,
>> + const char *name, struct flow *uflow,
>> + struct ovs_list *super)
>> {
>> - ovntrace_node_append(
>> - super, OVNTRACE_NODE_ERROR,
>> - "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST.
>> */");
>> -
>> /* Format the put_dhcp_opts action. */
>> struct ds s = DS_EMPTY_INITIALIZER;
>> - for (const struct ovnact_gen_option *o = pdo->options;
>> - o < &pdo->options[pdo->n_options]; o++) {
>> - if (o != pdo->options) {
>> + for (const struct ovnact_gen_option *o = po->options;
>> + o < &po->options[po->n_options]; o++) {
>> + if (o != po->options) {
>> ds_put_cstr(&s, ", ");
>> }
>> ds_put_format(&s, "%s = ", o->option->name);
>> @@ -1562,22 +1563,41 @@ execute_put_dhcp_opts(const struct
>> ovnact_put_opts *pdo,
>> ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s(%s)",
>> name, ds_cstr(&s));
>>
>> - struct mf_subfield dst = expr_resolve_field(&pdo->dst);
>> + struct mf_subfield dst = expr_resolve_field(&po->dst);
>> if (!mf_is_register(dst.field->id)) {
>> /* Format assignment. */
>> ds_clear(&s);
>> - expr_field_format(&pdo->dst, &s);
>> + expr_field_format(&po->dst, &s);
>> ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
>> "%s = 1", ds_cstr(&s));
>> }
>> ds_destroy(&s);
>>
>> - struct mf_subfield sf = expr_resolve_field(&pdo->dst);
>> + struct mf_subfield sf = expr_resolve_field(&po->dst);
>> union mf_subvalue sv = { .u8_val = 1 };
>> mf_write_subfield_flow(&sf, &sv, uflow);
>> }
>>
>> static void
>> +execute_put_dhcp_opts(const struct ovnact_put_opts *pdo,
>> + const char *name, struct flow *uflow,
>> + struct ovs_list *super)
>> +{
>> + ovntrace_node_append(
>> + super, OVNTRACE_NODE_ERROR,
>> + "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST.
>> */");
>> + execute_put_opts(pdo, name, uflow, super);
>> +}
>> +
>> +static void
>> +execute_put_nd_ra_opts(const struct ovnact_put_opts *pdo,
>> + const char *name, struct flow *uflow,
>> + struct ovs_list *super)
>> +{
>> + execute_put_opts(pdo, name, uflow, super);
>> +}
>> +
>> +static void
>> execute_next(const struct ovnact_next *next,
>> const struct ovntrace_datapath *dp, struct flow *uflow,
>> enum ovnact_pipeline pipeline, struct ovs_list *super)
>> @@ -1814,6 +1834,11 @@ trace_actions(const struct ovnact *ovnacts, size_t
>> ovnacts_len,
>> "put_dhcpv6_opts", uflow, super);
>> break;
>>
>> + case OVNACT_PUT_ND_RA_OPTS:
>> + execute_put_nd_ra_opts(ovnact_get_PUT_DHCPV6_OPTS(a),
>> + "put_nd_ra_opts", uflow, super);
>> + break;
>> +
>> case OVNACT_SET_QUEUE:
>> /* The set_queue action is slippery from a logical
>> perspective. It
>> * has no visible effect as long as the packet remains on
>> the same
>> diff --git a/tests/ovn.at b/tests/ovn.at
>> index 6c38b973f..e56dc6232 100644
>> --- a/tests/ovn.at
>> +++ b/tests/ovn.at
>> @@ -1066,6 +1066,37 @@ reg1[0] = dns_lookup();
>> reg1[0] = dns_lookup("foo");
>> dns_lookup doesn't take any parameters
>>
>> +# put_nd_ra_opts
>> +reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix =
>> aef0::/64, slla = ae:01:02:03:04:05);
>> + encodes as controller(userdata=00.00.00.
>> 08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.00.ff.
>> ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.
>> c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.
>> 00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
>> + has prereqs ip6
>> +reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", slla =
>> ae:01:02:03:04:10, mtu = 1450);
>> + encodes as controller(userdata=00.00.00.
>> 08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.
>> ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10.05.01.00.
>> 00.00.00.05.aa,pause)
>> + has prereqs ip6
>> +reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla =
>> ae:01:02:03:04:06, prefix = aef0::/64);
>> + encodes as controller(userdata=00.00.00.
>> 08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.40.ff.
>> ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.
>> c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.
>> 00.00.00.00.00.00.00.00.00,pause)
>> + has prereqs ip6
>> +reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix =
>> aef0::/64);
>> + parse_put_nd_ra_opts - slla option not present.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", mtu = 1450,
>> prefix = aef0::/64, prefix = bef0::/64, slla = ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts - prefix option can't be set when address mode
>> is dhcpv6_stateful.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", mtu = 1450,
>> prefix = aef0::/64, prefix = bef0::/64, slla = ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts - prefix option can't be set when address mode
>> is dhcpv6_stateful.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "slaac", slla = ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts - prefix option needs to be set when address
>> mode is slaac/dhcpv6_stateless.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla =
>> ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts - prefix option needs to be set when address
>> mode is slaac/dhcpv6_stateless.
>> +reg1[0] = put_nd_ra_opts(addr_mode = dhcpv6_stateless, prefix =
>> aef0::/64, slla = ae:01:02:03:04:10);
>> + Syntax error at `dhcpv6_stateless' expecting constant.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix =
>> aef0::, slla = ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts -Invalid value for the option prefix.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "foo", mtu = 1500, slla =
>> ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts -Invalid value for the option addr_mode.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = "1500", slla =
>> ae:01:02:03:04:10);
>> + IPv6 ND RA option mtu requires numeric value.
>> +reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 10.0.0.4, slla =
>> ae:01:02:03:04:10);
>> + parse_put_nd_ra_opts -Invalid value for the option mtu.
>> +
>> # Contradictionary prerequisites (allowed but not useful):
>> ip4.src = ip6.src[0..31];
>> encodes as move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[]
>> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
>> index 67221ea50..f9a5085f7 100644
>> --- a/tests/test-ovn.c
>> +++ b/tests/test-ovn.c
>> @@ -155,7 +155,8 @@ create_symtab(struct shash *symtab)
>> }
>>
>> static void
>> -create_dhcp_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts)
>> +create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
>> + struct hmap *nd_ra_opts)
>> {
>> hmap_init(dhcp_opts);
>> dhcp_opt_add(dhcp_opts, "offerip", 0, "ipv4");
>> @@ -187,6 +188,10 @@ create_dhcp_opts(struct hmap *dhcp_opts, struct hmap
>> *dhcpv6_opts)
>> dhcp_opt_add(dhcpv6_opts, "ia_addr", 5, "ipv6");
>> dhcp_opt_add(dhcpv6_opts, "dns_server", 23, "ipv6");
>> dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str");
>> +
>> + /* IPv6 ND RA options. */
>> + hmap_init(nd_ra_opts);
>> + nd_ra_opts_init(nd_ra_opts);
>> }
>>
>> static void
>> @@ -1193,12 +1198,13 @@ test_parse_actions(struct ovs_cmdl_context *ctx
>> OVS_UNUSED)
>> struct shash symtab;
>> struct hmap dhcp_opts;
>> struct hmap dhcpv6_opts;
>> + struct hmap nd_ra_opts;
>> struct simap ports;
>> struct ds input;
>> bool ok = true;
>>
>> create_symtab(&symtab);
>> - create_dhcp_opts(&dhcp_opts, &dhcpv6_opts);
>> + create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts);
>>
>> /* Initialize group ids. */
>> struct group_table group_table;
>> @@ -1226,6 +1232,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx
>> OVS_UNUSED)
>> .symtab = &symtab,
>> .dhcp_opts = &dhcp_opts,
>> .dhcpv6_opts = &dhcpv6_opts,
>> + .nd_ra_opts = &nd_ra_opts,
>> .n_tables = 24,
>> .cur_ltable = 10,
>> };
>> @@ -1310,7 +1317,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx
>> OVS_UNUSED)
>> shash_destroy(&symtab);
>> dhcp_opts_destroy(&dhcp_opts);
>> dhcp_opts_destroy(&dhcpv6_opts);
>> -
>> + nd_ra_opts_destroy(&nd_ra_opts);
>> exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
>> }
>>
>> --
>> 2.13.3
>>
>> _______________________________________________
>> dev mailing list
>> dev at openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>
More information about the dev
mailing list