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 <[email protected]> wrote: > > > On Thu, Sep 21, 2017 at 11:10 AM <[email protected]> wrote: > >> From: Numan Siddique <[email protected]> >> >> 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 <[email protected]> >> Signed-off-by: Zongkai LI <[email protected]> >> Signed-off-by: Numan Siddique <[email protected]> >> --- >> 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 >> [email protected] >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev >> > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
