[ovs-dev] [PATCH v4 5/6] ovn-controller: add 'put_nd_ra_addr_mode' and 'put_nd_opt_*' actions support
nusiddiq at redhat.com
nusiddiq at redhat.com
Thu Mar 23 06:07:37 UTC 2017
From: Zong Kai LI <zealokii at gmail.com>
This patch adds action 'nd_ra' specific inner actions 'put_nd_ra_addr_mode',
'put_nd_opt_prefix', 'put_nd_opt_mtu' and 'put_nd_opt_sll'.
Action put_nd_ra_addr_mode takes the RA address mode as argument and sets
the address configuration field in the Router Advertisement (RA) IPv6 packet.
e.g. put_nd_ra_addr_mode("slaac");
Action put_nd_ra_opt_sll will append Source Link-layer Address Option for RA
packet with inner ethernet address parameter.
e.g. put_nd_ra_opt_sll(12:34:56:78:9a:bc);
Action put_nd_ra_opt_mtu will append MTU Option for RA packet with inner
integer value(32-bit unsigned integer).
e.g. put_nd_ra_opt_mtu(1450);
Action put_nd_ra_opt_prefix will append Prefix Information Option with
following inner parameters for RA packet:
- prefix length(8-bit unsigned integer),
- prefix(128-bit IPv6 address prefix).
e.g. put_nd_ra_opt_prefix(fdad:a0f9:a012::/64);
Co-authored-by: Numan Siddique <nusiddiq at redhat.com>
Signed-off-by: Zongkai LI <zealokii at gmail.com>
Signed-off-by: Numan Siddique <nusiddiq at redhat.com>
---
include/ovn/actions.h | 64 ++++++++++++-
ovn/controller/pinctrl.c | 112 ++++++++++++++++++----
ovn/lib/actions.c | 238 +++++++++++++++++++++++++++++++++++++++++++++-
ovn/lib/ovn-util.h | 22 +++++
ovn/ovn-sb.xml | 74 +++++++++++++-
ovn/utilities/ovn-trace.c | 4 +
tests/ovn.at | 40 +++++++-
7 files changed, 529 insertions(+), 25 deletions(-)
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index d14b99c..31bb757 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -71,7 +71,11 @@ struct simap;
OVNACT(PUT_ND, ovnact_put_mac_bind) \
OVNACT(PUT_DHCPV4_OPTS, ovnact_put_dhcp_opts) \
OVNACT(PUT_DHCPV6_OPTS, ovnact_put_dhcp_opts) \
- OVNACT(SET_QUEUE, ovnact_set_queue)
+ OVNACT(SET_QUEUE, ovnact_set_queue) \
+ OVNACT(PUT_ND_RA_ADDR_MODE, ovnact_put_nd_ra_addr_mode) \
+ OVNACT(PUT_ND_OPT_SLL, ovnact_put_nd_opt_sll) \
+ OVNACT(PUT_ND_OPT_MTU, ovnact_put_nd_opt_mtu) \
+ OVNACT(PUT_ND_OPT_PREFIX, ovnact_put_nd_opt_prefix)
/* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
enum OVS_PACKED_ENUM ovnact_type {
@@ -259,6 +263,31 @@ struct ovnact_set_queue {
uint16_t queue_id;
};
+/* OVNACT_PUT_ND_RA_MODE. */
+struct ovnact_put_nd_ra_addr_mode {
+ struct ovnact ovnact;
+ uint8_t mode_flags; /* MO flags */
+};
+
+/* OVNACT_PUT_ND_OPT_SLL. */
+struct ovnact_put_nd_opt_sll {
+ struct ovnact ovnact;
+ struct eth_addr mac;
+};
+
+/* OVNACT_PUT_ND_OPT_MTU. */
+struct ovnact_put_nd_opt_mtu {
+ struct ovnact ovnact;
+ ovs_be32 mtu;
+};
+
+/* OVNACT_PUT_ND_OPT_PREFIX. */
+struct ovnact_put_nd_opt_prefix {
+ struct ovnact ovnact;
+ uint8_t prefix_len;
+ ovs_be128 prefix;
+};
+
/* Internal use by the helpers below. */
void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
@@ -392,6 +421,36 @@ enum action_opcode {
* The actions, in OpenFlow 1.3 format, follow the action_header.
*/
ACTION_OPCODE_ND_RA,
+
+ /* "put_nd_ra_addr_mode(mode)".
+ *
+ * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format:
+ * - mode_flag: 8-bit unsigned integer for Managed address configuration flag
+ * and Other configuration flag. (including 6-bit reserved 0)
+ */
+ ACTION_OPCODE_PUT_ND_RA_ADDR_MODE,
+
+ /* "put_nd_opt_sll(mac)".
+ *
+ * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format:
+ * - mac: 48-bit ethernet address.
+ */
+ ACTION_OPCODE_PUT_ND_OPT_SLL,
+
+ /* "put_nd_opt_mtu(mtu)".
+ *
+ * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format:
+ * - mtu: 32-bit unsigned integer MTU.
+ */
+ ACTION_OPCODE_PUT_ND_OPT_MTU,
+
+ /* "put_nd_opt_prefix(plen, prefix)".
+ *
+ * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format:
+ * - plen: 8-bit unsigned integer for prefix length.
+ * - prefix: 128-bit IPv6 address.
+ */
+ ACTION_OPCODE_PUT_ND_OPT_PREFIX,
};
/* Header. */
@@ -487,6 +546,9 @@ struct ovnact_encode_params {
resubmit. */
};
+void ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep,
+ struct ofpbuf *ofpacts);
+
void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
const struct ovnact_encode_params *,
struct ofpbuf *ofpacts);
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index e89d48f..be5dd61 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -74,7 +74,7 @@ static void send_garp_run(const struct ovsrec_bridge *,
struct hmap *local_datapaths);
static void pinctrl_handle_nd(const struct flow *ip_flow,
const struct match *md,
- struct ofpbuf *userdata);
+ struct ofpbuf *userdata, bool is_nd_na);
static void reload_metadata(struct ofpbuf *ofpacts,
const struct match *md);
@@ -707,8 +707,11 @@ process_packet_in(const struct ofp_header *msg)
break;
case ACTION_OPCODE_ND_NA:
+ pinctrl_handle_nd(&headers, &pin.flow_metadata, &userdata, true);
+ break;
+
case ACTION_OPCODE_ND_RA:
- pinctrl_handle_nd(&headers, &pin.flow_metadata, &userdata);
+ pinctrl_handle_nd(&headers, &pin.flow_metadata, &userdata, false);
break;
case ACTION_OPCODE_PUT_ND:
@@ -1370,7 +1373,7 @@ reload_metadata(struct ofpbuf *ofpacts, const struct match *md)
static void
pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md,
- struct ofpbuf *userdata)
+ struct ofpbuf *userdata, bool is_nd_na)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
/* This action only works for IPv6 ND packets, and only Neighbor
@@ -1388,13 +1391,15 @@ pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md,
enum ofp_version version = rconn_get_version(swconn);
enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- bool as_nd_na = ip_flow->tp_src == htons(ND_NEIGHBOR_SOLICIT);
- int packet_stub_size = as_nd_na ? 128 : 256;
+ int packet_stub_size = is_nd_na ? 128 : 256;
uint64_t packet_stub[packet_stub_size / 8];
struct dp_packet packet;
dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- if (as_nd_na) {
+ if (is_nd_na) {
+ if (ip_flow->tp_src != htons(ND_NEIGHBOR_SOLICIT)) {
+ goto exit;
+ }
/* xxx These flags are not exactly correct. Look at section 7.2.4
* xxx of RFC 4861. For example, we need to set ND_RSO_ROUTER for
* xxx router's interfaces and ND_RSO_SOLICITED only if it was
@@ -1403,20 +1408,95 @@ pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md,
&ip_flow->nd_target, &ip_flow->ipv6_src,
htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE));
} else {
- /* xxx Using hardcode data to compose RA packet.
- * xxx The following patch should fix this. */
- uint8_t cur_hop_limit = 64;
+ if (ip_flow->tp_src != htons(ND_ROUTER_SOLICIT)) {
+ goto exit;
+ }
+
+ ovs_be16 *args_len_p = ofpbuf_try_pull(userdata, sizeof(ovs_be16));
+ if (!args_len_p) {
+ goto exit;
+ }
+
+ uint16_t args_len = ntohs(*args_len_p);
+ if (!args_len || args_len > userdata->size) {
+ goto exit;
+ }
+
uint8_t mo_flags = 0;
- ovs_be16 router_lifetime = 0xffff;
- ovs_be32 reachable_time = 0;
- ovs_be32 retrans_timer = 0;
+ struct eth_addr sll = eth_addr_zero;
ovs_be32 mtu = 0;
+ struct prefix_data {
+ uint8_t prefix_len;
+ ovs_be128 prefix;
+ };
+
+ struct prefix_data *prefixes = NULL;
+ int num_prefixes = 0;
+
+ while (userdata->size - args_len > 0) {
+ struct ipv6_nd_ra_opt_header *opt =
+ ofpbuf_try_pull(userdata, sizeof(*opt));
+
+ uint16_t opt_len = ntohs(opt->len);
+ if (!opt_len) {
+ break;
+ }
+
+ uint8_t *opt_data = ofpbuf_try_pull(userdata, opt_len);
+ if (!opt_data) {
+ break;
+ }
+
+ switch(ntohs(opt->opcode)) {
+ case ACTION_OPCODE_PUT_ND_RA_ADDR_MODE:
+ mo_flags = *opt_data;
+ break;
+
+ case ACTION_OPCODE_PUT_ND_OPT_SLL:
+ if (opt_len == sizeof(struct eth_addr)) {
+ memcpy(&sll, opt_data, sizeof(struct eth_addr));
+ }
+ break;
+
+ case ACTION_OPCODE_PUT_ND_OPT_MTU:
+ if (opt_len == sizeof(ovs_be32)) {
+ mtu = *(ALIGNED_CAST(const ovs_be32 *, opt_data));
+ }
+ break;
+
+ case ACTION_OPCODE_PUT_ND_OPT_PREFIX:
+ if (opt_len == (sizeof(uint8_t) + sizeof(ovs_be128))) {
+ prefixes = xrealloc(
+ prefixes, sizeof(*prefixes) * (num_prefixes + 1));
+ prefixes[num_prefixes].prefix_len = *opt_data++;
+ memcpy(&prefixes[num_prefixes].prefix, opt_data,
+ sizeof(ovs_be128));
+ num_prefixes++;
+ }
+ break;
+ }
+ }
+
+ if (eth_addr_is_zero(sll)) {
+ free(prefixes);
+ goto exit;
+ }
+
compose_nd_ra_with_sll_mtu_opts(
- &packet, ip_flow->dl_dst, ip_flow->dl_src,
- &ip_flow->ipv6_dst, &ip_flow->ipv6_src,
- cur_hop_limit, mo_flags, router_lifetime, reachable_time,
- retrans_timer, mtu);
+ &packet, sll, ip_flow->dl_src, &ip_flow->ipv6_dst,
+ &ip_flow->ipv6_src, IPV6_ND_RA_CUR_HOP_LIMIT, mo_flags,
+ IPV6_ND_RA_LIFETIME, IPV6_ND_RA_REACHABLE_TIME,
+ IPV6_ND_RA_RETRANSMIT_TIMER, mtu);
+
+ for (int i = 0; i < num_prefixes; i++) {
+ packet_put_ra_prefix_opt(
+ &packet, prefixes[i].prefix_len, IPV6_ND_RA_OPT_PREFIX_FLAGS,
+ IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME,
+ IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME, prefixes[i].prefix);
+ }
+
+ free(prefixes);
}
/* Reload previous packet metadata. */
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index c39b14d..aa7bdd1 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -21,6 +21,7 @@
#include "byte-order.h"
#include "compiler.h"
#include "ovn-dhcp.h"
+#include "ovn-util.h"
#include "hash.h"
#include "logical-fields.h"
#include "nx-match.h"
@@ -1234,7 +1235,46 @@ encode_ND_RA(const struct ovnact_nest *on,
const struct ovnact_encode_params *ep,
struct ofpbuf *ofpacts)
{
- encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ND_RA, ofpacts);
+ uint64_t inner_ofpacts_stub[1024 / 8];
+ uint64_t inner_args_stub[1024 / 8];
+ struct ofpbuf inner_args = OFPBUF_STUB_INITIALIZER(inner_args_stub);
+ struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub);
+ const struct ovnact *a;
+ OVNACT_FOR_EACH (a, on->nested, on->nested_len) {
+ if (a->type == OVNACT_PUT_ND_RA_ADDR_MODE) {
+ encode_PUT_ND_RA_ADDR_MODE(
+ (const struct ovnact_put_nd_ra_addr_mode *)a, ep,
+ &inner_args);
+ } else if (a->type == OVNACT_PUT_ND_OPT_SLL) {
+ encode_PUT_ND_OPT_SLL(
+ (const struct ovnact_put_nd_opt_sll *)a, ep, &inner_args);
+ } else if (a->type == OVNACT_PUT_ND_OPT_MTU) {
+ encode_PUT_ND_OPT_MTU(
+ (const struct ovnact_put_nd_opt_mtu *)a, ep, &inner_args);
+ } else if (a->type == OVNACT_PUT_ND_OPT_PREFIX) {
+ encode_PUT_ND_OPT_PREFIX(
+ (const struct ovnact_put_nd_opt_prefix *)a, ep, &inner_args);
+ } else {
+ ovnact_encode(a, ep, &inner_ofpacts);
+ }
+ }
+ ovs_be16 inner_args_size = inner_args.size ? htons(inner_args.size) : 0;
+
+ /* Add a "controller" action with the data and actions parsed from nested
+ * actions inside "{...}", converted to OpenFlow, as its userdata.
+ * ovn-controller will convert the packet to RA and then send the packet
+ * and actions back to the switch inside an OFPT_PACKET_OUT message. */
+ size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_ND_RA,
+ false, ofpacts);
+ ofpbuf_put(ofpacts, &inner_args_size, sizeof(ovs_be16));
+ ofpbuf_put(ofpacts, inner_args.data, inner_args.size);
+ ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size,
+ ofpacts, OFP13_VERSION);
+ encode_finish_controller_op(oc_offset, ofpacts);
+
+ /* Free memory. */
+ ofpbuf_uninit(&inner_args);
+ ofpbuf_uninit(&inner_ofpacts);
}
static void
@@ -1759,7 +1799,191 @@ parse_set_action(struct action_context *ctx)
lexer_syntax_error(ctx->lexer, "expecting `=' or `<->'");
}
}
+
+/* Parse put_nd_ra, put_nd_opt_sll, put_nd_prefix, put_nd_mtu action. */
+static void
+parse_put_nd_ra_addr_mode(struct action_context *ctx,
+ struct ovnact_put_nd_ra_addr_mode *put_ra_mode)
+{
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+ if (ctx->lexer->token.type != LEX_T_STRING) {
+ lexer_syntax_error(ctx->lexer, "expecting string field");
+ return;
+ }
+
+ if (!strcmp(ctx->lexer->token.s, "slaac")) {
+ put_ra_mode->mode_flags = 0;
+ } else if (!strcmp(ctx->lexer->token.s, "dhcpv6_stateful")) {
+ put_ra_mode->mode_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
+ } else if (!strcmp(ctx->lexer->token.s, "dhcpv6_stateless")) {
+ put_ra_mode->mode_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
+ } else {
+ lexer_syntax_error(ctx->lexer, "Invalid ND RA mode value");
+ return;
+ }
+
+ lexer_get(ctx->lexer);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+parse_put_nd_opt_prefix(struct action_context *ctx,
+ struct ovnact_put_nd_opt_prefix *prefix)
+{
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+ if (!lexer_match(ctx->lexer, LEX_T_MASKED_INTEGER) ||
+ ctx->lexer->token.format != LEX_F_IPV6 ||
+ !ipv6_is_cidr(&ctx->lexer->token.mask.ipv6)) {
+ lexer_syntax_error(ctx->lexer, "Invalid IPv6 prefix");
+ return;
+ }
+
+ prefix->prefix_len = ipv6_count_cidr_bits(&ctx->lexer->token.mask.ipv6);
+ memcpy(&prefix->prefix, &ctx->lexer->token.value.be128_int,
+ sizeof(ovs_be128));
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+parse_put_nd_opt_sll(struct action_context *ctx,
+ struct ovnact_put_nd_opt_sll *sll)
+{
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+ if (!lexer_match(ctx->lexer, LEX_T_INTEGER)
+ || ctx->lexer->token.format != LEX_F_ETHERNET) {
+ lexer_syntax_error(ctx->lexer, "expecting ether address");
+ }
+ sll->mac = ctx->lexer->token.value.mac;
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+static void
+parse_put_nd_opt_mtu(struct action_context *ctx,
+ struct ovnact_put_nd_opt_mtu *mtu)
+{
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+ if (!lexer_match(ctx->lexer, LEX_T_INTEGER)
+ || ctx->lexer->token.format != LEX_F_DECIMAL) {
+ lexer_syntax_error(ctx->lexer, "expecting decimal integer");
+ }
+ mtu->mtu = ctx->lexer->token.value.be32_int;
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+format_PUT_ND_RA_ADDR_MODE(const struct ovnact_put_nd_ra_addr_mode *ra, struct ds *s)
+{
+ if (ra->mode_flags == 0) {
+ ds_put_cstr(s, "put_nd_ra_addr_mode(\"slaac\");");
+ } else if(ra->mode_flags == IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG) {
+ ds_put_cstr(s, "put_nd_ra_addr_mode(\"dhcpv6_stateful\");");
+ } else {
+ ds_put_cstr(s, "put_nd_ra_addr_mode(\"dhcpv6_stateless\");");
+ }
+}
+static void
+format_PUT_ND_OPT_PREFIX(const struct ovnact_put_nd_opt_prefix *p,
+ struct ds *s)
+{
+ ds_put_cstr(s, "put_nd_opt_prefix(");
+ const struct in6_addr *ip6_addr = (const struct in6_addr *)&p->prefix;
+ //ipv6_format_addr(CONST_CAST(const struct in6_addr *, p->prefix), s);
+ ipv6_format_addr(ip6_addr, s);
+ ds_put_format(s,"/%d);", p->prefix_len);
+}
+
+static void
+format_PUT_ND_OPT_SLL(const struct ovnact_put_nd_opt_sll *sll, struct ds *s)
+{
+ ds_put_cstr(s, "put_nd_opt_sll(");
+ ds_put_format(s, ETH_ADDR_FMT");", ETH_ADDR_ARGS(sll->mac));
+}
+
+static void
+format_PUT_ND_OPT_MTU(const struct ovnact_put_nd_opt_mtu *mtu, struct ds *s)
+{
+ ds_put_format(s, "put_nd_opt_mtu(%u);", ntohl(mtu->mtu));
+}
+
+static void
+encode_PUT_ND_RA_ADDR_MODE(const struct ovnact_put_nd_ra_addr_mode *ra,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ struct ipv6_nd_ra_opt_header opt = {
+ .opcode = htons(ACTION_OPCODE_PUT_ND_RA_ADDR_MODE),
+ .len = htons(sizeof(uint8_t)),
+ };
+ ofpbuf_put(ofpacts, &opt, sizeof opt);
+ ofpbuf_put(ofpacts, &ra->mode_flags, sizeof(uint8_t));
+}
+
+static void
+encode_PUT_ND_OPT_PREFIX(const struct ovnact_put_nd_opt_prefix *prefix,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ struct ipv6_nd_ra_opt_header opt = {
+ .opcode = htons(ACTION_OPCODE_PUT_ND_OPT_PREFIX),
+ .len = htons(sizeof(uint8_t) + sizeof(ovs_be128)),
+ };
+ ofpbuf_put(ofpacts, &opt, sizeof opt);
+ ofpbuf_put(ofpacts, &prefix->prefix_len, sizeof(uint8_t));
+ ofpbuf_put(ofpacts, &prefix->prefix, sizeof(ovs_be128));
+}
+
+static void
+encode_PUT_ND_OPT_SLL(const struct ovnact_put_nd_opt_sll *sll,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ struct ipv6_nd_ra_opt_header opt = {
+ .opcode = htons(ACTION_OPCODE_PUT_ND_OPT_SLL),
+ .len = htons(sizeof(struct eth_addr)),
+ };
+ ofpbuf_put(ofpacts, &opt, sizeof opt);
+ ofpbuf_put(ofpacts, &sll->mac, sizeof(struct eth_addr));
+}
+
+static void
+encode_PUT_ND_OPT_MTU(const struct ovnact_put_nd_opt_mtu *mtu,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ struct ipv6_nd_ra_opt_header opt = {
+ .opcode = htons(ACTION_OPCODE_PUT_ND_OPT_MTU),
+ .len = htons(sizeof(uint32_t)),
+ };
+ ofpbuf_put(ofpacts, &opt, sizeof opt);
+ ofpbuf_put(ofpacts, &mtu->mtu, sizeof(ovs_be32));
+}
+
+static void
+ovnact_put_nd_ra_addr_mode_free(
+ struct ovnact_put_nd_ra_addr_mode *nd_ra OVS_UNUSED)
+{
+
+}
+
+static void
+ovnact_put_nd_opt_sll_free(struct ovnact_put_nd_opt_sll *nd_opt_sll OVS_UNUSED)
+{
+
+}
+
+static void
+ovnact_put_nd_opt_mtu_free(struct ovnact_put_nd_opt_mtu *nd_opt_mtu OVS_UNUSED)
+{
+
+}
+
+static void
+ovnact_put_nd_opt_prefix_free(
+ struct ovnact_put_nd_opt_prefix *nd_opt_prefix OVS_UNUSED)
+{
+
+}
+
static bool
parse_action(struct action_context *ctx)
{
@@ -1798,6 +2022,16 @@ parse_action(struct action_context *ctx)
parse_ND_NA(ctx);
} else if (lexer_match_id(ctx->lexer, "nd_ra")) {
parse_ND_RA(ctx);
+ } else if (lexer_match_id(ctx->lexer, "put_nd_ra_addr_mode")) {
+ parse_put_nd_ra_addr_mode(ctx,
+ ovnact_put_PUT_ND_RA_ADDR_MODE(ctx->ovnacts));
+ } else if (lexer_match_id(ctx->lexer, "put_nd_opt_prefix")) {
+ parse_put_nd_opt_prefix(ctx,
+ ovnact_put_PUT_ND_OPT_PREFIX(ctx->ovnacts));
+ } else if (lexer_match_id(ctx->lexer, "put_nd_opt_sll")) {
+ parse_put_nd_opt_sll(ctx, ovnact_put_PUT_ND_OPT_SLL(ctx->ovnacts));
+ } else if (lexer_match_id(ctx->lexer, "put_nd_opt_mtu")) {
+ parse_put_nd_opt_mtu(ctx, ovnact_put_PUT_ND_OPT_MTU(ctx->ovnacts));
} else if (lexer_match_id(ctx->lexer, "get_arp")) {
parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts));
} else if (lexer_match_id(ctx->lexer, "put_arp")) {
@@ -1937,7 +2171,7 @@ ovnacts_format(const struct ovnact *ovnacts, size_t ovnacts_len,
/* Encoding ovnacts to OpenFlow. */
-static void
+void
ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep,
struct ofpbuf *ofpacts)
{
diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
index 30b27b1..766ca66 100644
--- a/ovn/lib/ovn-util.h
+++ b/ovn/lib/ovn-util.h
@@ -64,4 +64,26 @@ char *alloc_nat_zone_key(const struct uuid *key, const char *type);
const char *default_nb_db(void);
const char *default_sb_db(void);
+
+/* 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
+
+OVS_PACKED(
+struct ipv6_nd_ra_opt_header {
+ ovs_be16 opcode; /* One of ACTION_OPCODE_PUT_ND_* */
+ ovs_be16 len;
+});
+
#endif
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 63fa39e..b7c623f 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -311,7 +311,7 @@
transmitted and received with reasonable performance. It is a hint
to senders transmitting data to this chassis that they should use
checksums to protect OVN metadata. <code>ovn-controller</code>
- populates this key with the value defined in
+ populates this key with the value defined in
<ref table="Open_vSwitch" column="external_ids:ovn-encap-csum"/> column
of the Open_vSwitch database's <ref table="Open_vSwitch"
db="Open_vSwitch"/> table. Other applications should treat this key as
@@ -1322,13 +1322,79 @@
</ul>
<p>
- The ND packet has the same VLAN header, if any, as the IPv6 packet
- it replaces.
- </p>
+ The 'nd_ra' action supports the following inner actions.
+ </p>
+
+ <dl>
+ <dt>
+ <code>put_nd_ra_addr_mode(<var>M</var>);</code>
+ </dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>: Address mode string field <var>M</var>.
+ </p>
+ <p>
+ Puts the IPv6 address configuration mode defined in the
+ paramater <var>M</var> in the RA flags. <var>M</var> can be
+ "slaac", "dhcpv6_stateful" or "dhcpv6_stateless".
+ </p>
+ </dd>
+
+ <dt>
+ <code>put_nd_opt_mtu(<var>M</var>);</code>
+ </dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>: MTU int field <var>M</var>.
+ </p>
+
+ <p>
+ Puts the ICMPv6 MTU (type 5) option in the RA packet.
+ </p>
+ </dd>
+
+ <dt>
+ <code>put_nd_opt_sll(<var>M</var>);</code>
+ </dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>: Link layer address <var>M</var>.
+ </p>
+
+ <p>
+ Puts the ICMPv6 source link layer address (type 1) option in
+ the RA packet.
+ </p>
+ </dd>
+
+ <dt>
+ <code>put_nd_opt_prefix(<var>M</var>);</code>
+ </dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>: IPv6 prefix and prefix length as a
+ string field <var>M</var>.
+ </p>
+
+ <p>
+ Puts the ICMPv6 prefix information (type 3) option in the RA
+ packet.
+ </p>
+ </dd>
+ </dl>
<p>
<b>Prerequisite:</b> <code>nd_rs</code>
</p>
+
+ <p>
+ <b>Example:</b>
+ <code>nd_ra{put_nd_ra_addr_mode("slaac"); put_nd_opt_mtu(1450); put_nd_opt_sll(f0:00:00:00:01:10); put_nd_opt_prefix(aef0::/64); eth.src = eth.dst; outport=inport; output;};</code>
+ </p>
</dd>
<dt><code>get_nd(<var>P</var>, <var>A</var>);</code></dt>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 61ec728..687a30a 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1522,6 +1522,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
case OVNACT_PUT_ARP:
case OVNACT_PUT_ND:
+ case OVNACT_PUT_ND_RA_ADDR_MODE:
+ case OVNACT_PUT_ND_OPT_PREFIX:
+ case OVNACT_PUT_ND_OPT_SLL:
+ case OVNACT_PUT_ND_OPT_MTU:
/* Nothing to do for tracing. */
break;
diff --git a/tests/ovn.at b/tests/ovn.at
index dbbd228..6ae1b7a 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -968,9 +968,45 @@ nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inpor
has prereqs nd_ns
# nd_ra
-nd_ra{eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output;};
+nd_ra { eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output;};
formats as nd_ra { eth.src = 12:34:56:78:9a:bc; ip6.src = fdad:1234:5678::1; outport = inport; flags.loopback = 1; output; };
- encodes as controller(userdata=00.00.00.06.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.18.80.00.34.10.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.01.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.ff.ff.00.18.00.00.23.20.00.07.00.00.00.01.14.04.00.00.00.00.00.00.00.01.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.18.80.00.34.10.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.01.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.ff.ff.00.18.00.00.23.20.00.07.00.00.00.01.14.04.00.00.00.00.00.00.00.01.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
+ has prereqs nd_rs
+nd_ra { put_nd_ra_addr_mode("slaac");};
+ formats as nd_ra { put_nd_ra_addr_mode("slaac"); };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.05.00.07.00.01.00)
+ has prereqs nd_rs
+nd_ra { put_nd_ra_addr_mode("dhcpv6_stateful");};
+ formats as nd_ra { put_nd_ra_addr_mode("dhcpv6_stateful"); };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.05.00.07.00.01.80)
+ has prereqs nd_rs
+nd_ra { put_nd_ra_addr_mode("dhcpv6_stateless");};
+ formats as nd_ra { put_nd_ra_addr_mode("dhcpv6_stateless"); };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.05.00.07.00.01.40)
+ has prereqs nd_rs
+nd_ra { put_nd_ra_addr_mode("foo");};
+ Syntax error at `"foo"' Invalid ND RA mode value.
+nd_ra { put_nd_ra_addr_mode(foo);};
+ Syntax error at `foo' expecting string field.
+
+nd_ra { put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(fdad:1234:5678::/64);};
+ formats as nd_ra { put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(fdad:1234:5678::/64); };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.27.00.08.00.06.12.34.56.78.9a.bc.00.09.00.04.00.00.05.00.00.0a.00.11.40.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.00)
+ has prereqs nd_rs
+nd_ra { put_nd_opt_sll(foo);};
+ Syntax error at `foo' expecting ether address.
+nd_ra { put_nd_opt_sll(12:34:56:78:9a);};
+ Invalid numeric constant.
+nd_ra { put_nd_opt_mtu(foo);};
+ Syntax error at `foo' expecting decimal integer.
+nd_ra { put_nd_opt_prefix(foo);};
+ Syntax error at `foo' Invalid IPv6 prefix.
+nd_ra { put_nd_opt_prefix(aef0::/130);};
+ Value and mask have incompatible formats.
+
+nd_ra { eth.dst = eth.src; put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(fdad:1234:5678::/64); put_nd_opt_prefix(aef0::/64); eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output;};
+ formats as nd_ra { eth.dst = eth.src; put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(fdad:1234:5678::/64); put_nd_opt_prefix(aef0::/64); eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.3c.00.08.00.06.12.34.56.78.9a.bc.00.09.00.04.00.00.05.00.00.0a.00.11.40.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.00.00.0a.00.11.40.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.ff.ff.00.18.00.00.23.20.00.06.00.30.00.00.00.00.00.00.04.06.00.00.02.06.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
has prereqs nd_rs
# get_nd
--
2.9.3
More information about the dev
mailing list