[ovs-dev] [PATCH v2 6/8] ovn-controller: add 'put_nd_ra' and 'put_nd_opt_*' actions support
Zongkai LI
zealokii at gmail.com
Sat Sep 3 05:09:54 UTC 2016
This patch adds action 'nd_ra' specific inner actions 'put_nd_ra',
'put_nd_opt_prefix', 'put_nd_opt_mtu', 'put_nd_opt_sll'.
Action put_nd_ra will set following fields in Router Advertisement (RA) packet:
- cur_hop_limit(8-bit unsigned integer),
- "Managed address configuration" and "Other configuration"
flags(8-bit unsigned integer, with 6-bit 0 in low endian),
- router lifetime(16-bit unsigned integer),
- reachable time(32-bit unsigned integer),
- retrans timer(32-bit unsigned integer).
e.g. put_nd_ra(64,0,10800,0,0);
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),
- on-link flag and autonomous address-configuration flag
(8-bit unsigned integer, with 6-bit 0 in low endian),
- valid lifetime(32-bit unsigned integer),
- preferred lifetime(32-bit unsigned integer),
- prefix(128-bit IPv6 address prefix).
e.g. put_nd_ra_opt_prefix(64,192,10800,10800,fdad:a0f9:a012::);
v1 -> v2
rebased, method parse_put_nd_ra and parse_put_nd_opt_prefix updated.
---
include/ovn/actions.h | 79 +++++++++++++-
ovn/controller/pinctrl.c | 163 +++++++++++++++++++++++++++-
ovn/lib/actions.c | 264 +++++++++++++++++++++++++++++++++++++++++++++-
ovn/ovn-sb.xml | 39 +++++++
ovn/utilities/ovn-trace.c | 4 +
tests/ovn.at | 16 ++-
6 files changed, 558 insertions(+), 7 deletions(-)
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index a776a26..6792192 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -67,7 +67,11 @@ struct simap;
OVNACT(GET_ND, ovnact_get_mac_bind) \
OVNACT(PUT_ND, ovnact_put_mac_bind) \
OVNACT(PUT_DHCPV4_OPTS, ovnact_put_dhcp_opts) \
- OVNACT(PUT_DHCPV6_OPTS, ovnact_put_dhcp_opts)
+ OVNACT(PUT_DHCPV6_OPTS, ovnact_put_dhcp_opts) \
+ OVNACT(PUT_ND_RA, ovnact_put_nd_ra) \
+ 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 {
@@ -220,6 +224,38 @@ struct ovnact_put_dhcp_opts {
size_t n_options;
};
+/* OVNACT_PUT_ND_RA. */
+struct ovnact_put_nd_ra {
+ struct ovnact ovnact;
+ uint8_t cur_hop_limit;
+ uint8_t mo_flags;
+ uint16_t router_lifetime;
+ uint32_t reachable_time;
+ uint32_t retrans_timer;
+};
+
+/* 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;
+ uint32_t mtu;
+};
+
+/* OVNACT_PUT_ND_OPT_PREFIX. */
+struct ovnact_put_nd_opt_prefix {
+ struct ovnact ovnact;
+ uint8_t prefix_len;
+ uint8_t la_flags;
+ uint32_t valid_lifetime;
+ uint32_t preferred_lifetime;
+ struct in6_addr 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);
@@ -351,6 +387,44 @@ enum action_opcode {
* The actions, in OpenFlow 1.3 format, follow the action_header.
*/
ACTION_OPCODE_ND_RA,
+
+ /* "put_nd_ra(c, mo, router_lt, reach_t, retrans_t)".
+ *
+ * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format:
+ * - c: 8-bit unsigned integer for current hop limit.
+ * - mo: 8-bit unsigned integer for Managed address configuration flag
+ * and Other configuration flag. (including 6-bit reserved 0)
+ * - router_lt: 16-bit unsigned integer for router lifetime.
+ * - reach_t: 32-bit unisgned integer for reachable time.
+ * - retrans_t: 32-bit unsigned integer for retrans timer.
+ */
+ ACTION_OPCODE_PUT_ND_RA,
+
+ /* "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, la, valid_lt, preferred_lt, prefix)".
+ *
+ * Specific inner action for ACTION_OPCODE_ND_RA. Arguments in this format:
+ * - plen: 8-bit unsigned integer for prefix length.
+ * - la: 8-bit unsigned integer for on-link flag and autonomous
+ * address-configuration flag. (including 6-bit reserved 0)
+ * - valid_lt: 32-bit unsigned integer for valid_lifetime.
+ * - preferred_lt: 32-bit unsigned integer for preferred_lifetime.
+ * - prefix: 128-bit IPv6 address.
+ */
+ ACTION_OPCODE_PUT_ND_OPT_PREFIX,
};
/* Header. */
@@ -433,6 +507,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 ae3a58b..64c0959 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -1350,6 +1350,116 @@ reload_metadata(struct ofpbuf *ofpacts, const struct match *md)
}
}
+static bool parse_nd_ra_args(struct ofpbuf *userdata, uint8_t *chl,
+ uint8_t *mo, ovs_be16 *rlt,
+ ovs_be32 *rcht, ovs_be32 *rtrt,
+ uint16_t *args_left_size)
+{
+ int put_nd_ra_args_len =
+ sizeof(uint8_t) * 2 + sizeof(uint16_t) + sizeof(uint32_t) * 2;
+ if (*args_left_size < put_nd_ra_args_len) {
+ return false;
+ }
+ uint8_t *cur_hop_limit = ofpbuf_try_pull(userdata, sizeof(uint8_t));
+ if (!cur_hop_limit) {
+ return false;
+ }
+ uint8_t *mo_flags = ofpbuf_try_pull(userdata, sizeof(uint8_t));
+ if (!mo_flags) {
+ return false;
+ }
+ ovs_be16 *router_lifetime = ofpbuf_try_pull(userdata, sizeof(ovs_be16));
+ if (!router_lifetime) {
+ return false;
+ }
+ ovs_be32 *reachable_time = ofpbuf_try_pull(userdata, sizeof(ovs_be32));
+ if (!reachable_time) {
+ return false;
+ }
+ ovs_be32 *retrans_timer = ofpbuf_try_pull(userdata, sizeof(ovs_be32));
+ if (!retrans_timer) {
+ return false;
+ }
+ *chl = *cur_hop_limit;
+ *mo = *mo_flags;
+ *rlt = *router_lifetime;
+ *rcht = *reachable_time;
+ *rtrt = *retrans_timer;
+ *args_left_size -= put_nd_ra_args_len;
+ return true;
+}
+
+static bool parse_nd_prefix_opt_args(struct ofpbuf *userdata,
+ uint8_t *plen, uint8_t *la, ovs_be32 *vl,
+ ovs_be32 *pl, ovs_be32 *p,
+ uint16_t *args_left_size)
+{
+ int put_nd_prefix_opt_args_len =
+ sizeof(uint8_t) * 2 + sizeof(uint32_t) *2 + sizeof(struct in6_addr);
+ if (*args_left_size < put_nd_prefix_opt_args_len) {
+ return false;
+ }
+ uint8_t *prefix_len = ofpbuf_try_pull(userdata, sizeof(uint8_t));
+ if (!prefix_len || *prefix_len == 0 || *prefix_len == 128) {
+ return false;
+ }
+ uint8_t *la_flags = ofpbuf_try_pull(userdata, sizeof(uint8_t));
+ if (!la_flags) {
+ return false;
+ }
+ ovs_be32 *valid_lifetime = ofpbuf_try_pull(userdata, sizeof(ovs_be32));
+ if (!valid_lifetime || *valid_lifetime == 0) {
+ return false;
+ }
+ ovs_be32 *preferred_lifetime = ofpbuf_try_pull(userdata, sizeof(ovs_be32));
+ if (!preferred_lifetime || *preferred_lifetime == 0) {
+ return false;
+ }
+ struct in6_addr *prefix = ofpbuf_try_pull(userdata,
+ sizeof(struct in6_addr));
+ if (!prefix) {
+ return false;
+ }
+ *plen = *prefix_len;
+ *la = *la_flags;
+ *vl = *valid_lifetime;
+ *pl = *preferred_lifetime;
+ memcpy(p, prefix, sizeof(struct in6_addr));
+ *args_left_size -= put_nd_prefix_opt_args_len;
+ return true;
+}
+
+static bool parse_nd_sll_opt_arg(struct ofpbuf *userdata,
+ struct eth_addr *e, uint16_t *args_left_size)
+{
+ if (*args_left_size < sizeof(struct eth_addr)) {
+ return false;
+ }
+ struct eth_addr *sll = ofpbuf_try_pull(userdata, sizeof(struct eth_addr));
+ if (!sll) {
+ return false;
+ }
+ memcpy(e, sll, sizeof(struct eth_addr));
+ *args_left_size -= sizeof(struct eth_addr);
+ return true;
+}
+
+static bool parse_nd_mtu_opt_arg(struct ofpbuf *userdata,
+ ovs_be32 *m, uint16_t *args_left_size)
+{
+ if (*args_left_size < sizeof(ovs_be32)) {
+ return false;
+ }
+ ovs_be32 *mtu = ofpbuf_try_pull(userdata, sizeof(ovs_be32));
+ /* Per RFC 2460, consider 1280 as minimum IPv6 MTU. */
+ if (!mtu || *mtu < 1280) {
+ return false;
+ }
+ *m = *mtu;
+ *args_left_size -= sizeof(ovs_be32);
+ return true;
+}
+
static void
pinctrl_handle_nd(const struct flow *ip_flow, const struct match *md,
struct ofpbuf *userdata)
@@ -1383,18 +1493,67 @@ 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;
uint8_t mo_flags = 0;
ovs_be16 router_lifetime = htons(0x2a30); /* 3 hours. */
ovs_be32 reachable_time = 0;
ovs_be32 retrans_timer = 0;
+ uint16_t *args_len_p = ofpbuf_try_pull(userdata, sizeof(uint16_t));
+ uint16_t args_len = args_len_p ? ntohs(*args_len_p) : 0;
+ uint8_t *arg = NULL;
+ bool continue_parse = false;
+ if (args_len >= sizeof(uint8_t)) {
+ arg = ofpbuf_try_pull(userdata, sizeof(uint8_t));
+ args_len -= sizeof(uint8_t);
+ if (arg && *arg == ACTION_OPCODE_PUT_ND_RA) {
+ continue_parse = parse_nd_ra_args(
+ userdata, &cur_hop_limit, &mo_flags, &router_lifetime,
+ &reachable_time, &retrans_timer, &args_len);
+ }
+ }
compose_nd_ra(&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);
+ if (continue_parse && args_len > sizeof(uint8_t)) {
+ bool sll_opt_parsed = false;
+ bool mtu_opt_parsed = false;
+ struct eth_addr sll;
+ ovs_be32 mtu;
+ uint8_t prefix_len = 0;
+ uint8_t la_flags = 0;
+ ovs_be32 valid_lifetime = 0;
+ ovs_be32 preferred_lifetime = 0;
+ ovs_be32 prefix[4];
+ while (args_len > 0) {
+ arg = ofpbuf_try_pull(userdata, sizeof(uint8_t));
+ args_len -= sizeof(uint8_t);
+ if (arg && *arg == ACTION_OPCODE_PUT_ND_OPT_PREFIX
+ && parse_nd_prefix_opt_args(userdata, &prefix_len,
+ &la_flags, &valid_lifetime,
+ &preferred_lifetime, prefix,
+ &args_len)) {
+ packet_put_ra_prefix_opt(&packet, prefix_len, la_flags,
+ valid_lifetime, preferred_lifetime,
+ prefix);
+ } else if (arg && *arg == ACTION_OPCODE_PUT_ND_OPT_SLL
+ && !sll_opt_parsed
+ && parse_nd_sll_opt_arg(userdata, &sll, &args_len)) {
+ packet_put_ra_sll_opt(&packet, sll);
+ sll_opt_parsed = true;
+ } else if (arg && *arg == ACTION_OPCODE_PUT_ND_OPT_MTU
+ && !mtu_opt_parsed
+ && parse_nd_mtu_opt_arg(userdata, &mtu, &args_len)) {
+ packet_put_ra_mtu_opt(&packet, mtu);
+ mtu_opt_parsed = true;
+ } else {
+ VLOG_WARN_RL(&rl, "Unknown to parse inner arguments for"
+ " 'nd_ra'");
+ break;
+ }
+ }
+ }
}
/* Reload previous packet metadata. */
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 5568475..0ad0637 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1143,7 +1143,45 @@ encode_ND_RA(const struct ovnact_nest *on,
const struct ovnact_encode_params *ep,
struct ofpbuf *ofpacts)
{
- encode_nested_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) {
+ encode_PUT_ND_RA(
+ (const struct ovnact_put_nd_ra *)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);
+ }
+ }
+ uint16_t 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(uint16_t));
+ 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
@@ -1662,7 +1700,220 @@ 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(struct action_context *ctx,
+ struct ovnact_put_nd_ra *put_ra)
+{
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+ int field = 0;
+ int n_fields = 5;
+ while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+ if (!lexer_match(ctx->lexer, LEX_T_INTEGER)
+ || ctx->lexer->token.format != LEX_F_DECIMAL) {
+ lexer_syntax_error(ctx->lexer, "expecting demical integer");
+ }
+ switch (field) {
+ case 0:
+ put_ra->cur_hop_limit = ctx->lexer->token.value.u8_val;
+ break;
+ case 1:
+ put_ra->mo_flags = ctx->lexer->token.value.u8_val;
+ break;
+ case 2:
+ put_ra->router_lifetime = ntohs(
+ ctx->lexer->token.value.be16_int);
+ break;
+ case 3:
+ put_ra->reachable_time = ntohl(
+ ctx->lexer->token.value.be32_int);
+ break;
+ case 4:
+ put_ra->retrans_timer = ntohl(
+ ctx->lexer->token.value.be32_int);
+ break;
+ default:
+ lexer_syntax_error(ctx->lexer, "not many fields parsed");
+ }
+ lexer_match(ctx->lexer, LEX_T_COMMA);
+ field++;
+ }
+ if (field != n_fields) {
+ lexer_syntax_error(ctx->lexer, "not enough fields parsed");
+ }
+}
+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);
+
+ int field = 0;
+ int n_fields = 5;
+ while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+ if (!lexer_match(ctx->lexer, LEX_T_INTEGER)) {
+ lexer_syntax_error(ctx->lexer, "expecting integer");
+ } else {
+ if (field < 4 && ctx->lexer->token.format != LEX_F_DECIMAL) {
+ lexer_syntax_error(ctx->lexer, "expecting demical integer");
+ } else if (field == 4 && ctx->lexer->token.format != LEX_F_IPV6) {
+ lexer_syntax_error(ctx->lexer, "expecting IPv6 address");
+ }
+ }
+
+ switch (field) {
+ case 0:
+ prefix->prefix_len = ctx->lexer->token.value.u8_val;
+ break;
+ case 1:
+ prefix->la_flags = ctx->lexer->token.value.u8_val;
+ break;
+ case 2:
+ prefix->valid_lifetime =
+ ntohl(ctx->lexer->token.value.be32_int);
+ break;
+ case 3:
+ prefix->preferred_lifetime = ntohl(
+ ctx->lexer->token.value.be32_int);
+ break;
+ case 4:
+ memcpy(&prefix->prefix, &ctx->lexer->token.value.ipv6,
+ sizeof(struct in6_addr));
+ break;
+ default:
+ lexer_syntax_error(ctx->lexer, "not many fields parsed");
+ }
+ lexer_match(ctx->lexer, LEX_T_COMMA);
+ field++;
+ }
+ if (field != n_fields) {
+ lexer_syntax_error(ctx->lexer, "not enough fields parsed");
+ }
+}
+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 = ntohl(ctx->lexer->token.value.be32_int);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+format_PUT_ND_RA(const struct ovnact_put_nd_ra *ra, struct ds *s)
+{
+ ds_put_format(s, "put_nd_ra(%u, %u, %u, %u, %u);",
+ ra->cur_hop_limit, ra->mo_flags, ra->router_lifetime,
+ ra->reachable_time, ra->retrans_timer);
+}
+static void
+format_PUT_ND_OPT_PREFIX(const struct ovnact_put_nd_opt_prefix *p,
+ struct ds *s)
+{
+ ds_put_format(s, "put_nd_opt_prefix(%u, %u, %u, %u, ",
+ p->prefix_len, p->la_flags, p->valid_lifetime,
+ p->preferred_lifetime);
+ ipv6_format_addr(&p->prefix, s);
+ ds_put_cstr(s, ");");
+}
+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));
+ ds_put_cstr(s, ");");
+}
+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);", mtu->mtu);
+}
+
+static void
+encode_PUT_ND_RA(const struct ovnact_put_nd_ra *ra,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ uint8_t inner_code = ACTION_OPCODE_PUT_ND_RA;
+ ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t));
+ ofpbuf_put(ofpacts, &ra->cur_hop_limit, sizeof(uint8_t));
+ ofpbuf_put(ofpacts, &ra->mo_flags, sizeof(uint8_t));
+ uint16_t rlt = htons(ra->router_lifetime);
+ uint32_t rt = htonl(ra->reachable_time);
+ uint32_t rtt = htonl(ra->retrans_timer);
+ ofpbuf_put(ofpacts, &rlt, sizeof(uint16_t));
+ ofpbuf_put(ofpacts, &rt, sizeof(uint32_t));
+ ofpbuf_put(ofpacts, &rtt, sizeof(uint32_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)
+{
+ uint8_t inner_code = ACTION_OPCODE_PUT_ND_OPT_PREFIX;
+ ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t));
+ ofpbuf_put(ofpacts, &prefix->prefix_len, sizeof(uint8_t));
+ ofpbuf_put(ofpacts, &prefix->la_flags, sizeof(uint8_t));
+ uint32_t vlt = htonl(prefix->valid_lifetime);
+ uint32_t plt = htonl(prefix->preferred_lifetime);
+ ofpbuf_put(ofpacts, &vlt, sizeof(uint32_t));
+ ofpbuf_put(ofpacts, &plt, sizeof(uint32_t));
+ ofpbuf_put(ofpacts, &prefix->prefix, sizeof(struct in6_addr));
+}
+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)
+{
+ uint8_t inner_code = ACTION_OPCODE_PUT_ND_OPT_SLL;
+ ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t));
+ 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)
+{
+ uint8_t inner_code = ACTION_OPCODE_PUT_ND_OPT_MTU;
+ ofpbuf_put(ofpacts, &inner_code, sizeof(uint8_t));
+ uint32_t m = htonl(mtu->mtu);
+ ofpbuf_put(ofpacts, &m, sizeof(uint32_t));
+}
+static void
+free_PUT_ND_RA(struct ovnact_put_nd_ra *ra OVS_UNUSED)
+{
+}
+static void
+free_PUT_ND_OPT_PREFIX(struct ovnact_put_nd_opt_prefix *prefix OVS_UNUSED)
+{
+}
+static void
+free_PUT_ND_OPT_SLL(struct ovnact_put_nd_opt_sll *sll OVS_UNUSED)
+{
+}
+static void
+free_PUT_ND_OPT_MTU(struct ovnact_put_nd_opt_mtu *mtu OVS_UNUSED)
+{
+}
+
static bool
parse_action(struct action_context *ctx)
{
@@ -1697,6 +1948,15 @@ 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")) {
+ parse_put_nd_ra(ctx, ovnact_put_PUT_ND_RA(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")) {
@@ -1834,7 +2094,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/ovn-sb.xml b/ovn/ovn-sb.xml
index 7b17d9d..1f60863 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1242,6 +1242,45 @@
</ul>
<p>
+ The 'nd_ra' action has specific inner action
+ <code>put_nd_ra(h,mo,lt,ct,tr)</code> to hold values for fields
+ and flags in RA message.
+ <b>Parameters</b>: 8-bit unsigned integer cur_hop_limit field
+ <var>h</var>, 8-bit unsigned integer for "Managed address
+ configuration" and "Other configuration" flags with 6-bit reserved
+ 0 in field <var>mo</var>, 16-bit unsigned integer router_lifetime
+ field <var>lt</var>, 32-bit unsigned integer reachable_time
+ field <var>ct</var>, 32-bit unsigned integer retrans_timer
+ field <var>tr</var>.
+ </p>
+
+ <p>
+ The 'nd_ra' action has specific inner action
+ <code>put_nd_opt_prefix(pl,la,vt,pt,p)</code> to hold values for
+ fields and flags in Prefix Information Option for RA message.
+ <b>Parameters</b>: 8-bit unsigned integer prefix lenght field
+ <var>pl</var>, 8-bit unsigned integer for on-link flag and
+ autonomous address-configuration flag with 6-bit reserved 0 in
+ field <var>mo</var>, 32-bit unsigned integer valid_lifetime field
+ <var>vt</var>, 32-bit unsigned integer preferred_lifetime field
+ <var>pt</var>, 128-bit IPv6 prefix field <var>p</var>.
+ </p>
+
+ <p>
+ The 'nd_ra' action has specific inner action
+ <code>put_nd_opt_mtu(m)</code> to hold value for mtu field in MTU
+ Option for RA message.
+ <b>Parameters</b>: 32-bit unsigned integer mtu field <var>m</var>.
+ </p>
+
+ <p>
+ The 'nd_ra' action has specific inner action
+ <code>put_nd_opt_sll(s)</code> to hold value for link-layer address
+ field in Source Link-layer Address Option for RA message.
+ <b>Parameters</b>: 48-bit Ethernet address field <var>s</var>.
+ </p>
+
+ <p>
The ND packet has the same VLAN header, if any, as the IPv6 packet
it replaces.
</p>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index fabecc6..e9dfe90 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1281,6 +1281,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
case OVNACT_PUT_ARP:
case OVNACT_PUT_ND:
+ case OVNACT_PUT_ND_RA:
+ 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 ec16d8e..53528ac 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -886,9 +886,21 @@ 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(64,0,10800,0,0);};
+ formats as nd_ra { put_nd_ra(64, 0, 10800, 0, 0); };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.0d.07.40.00.2a.30.00.00.00.00.00.00.00.00)
+ has prereqs nd_rs
+nd_ra { put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64,0,10800,10800,fdad:1234:5678::);};
+ formats as nd_ra { put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64, 0, 10800, 10800, fdad:1234:5678::); };
+ encodes as controller(userdata=00.00.00.06.00.00.00.00.00.27.08.12.34.56.78.9a.bc.09.00.00.05.00.0a.40.00.00.00.2a.30.00.00.2a.30.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.00)
+ has prereqs nd_rs
+nd_ra { put_nd_ra(64,0,10800,0,0); put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64,0,10800,10800,fdad:1234:5678::); 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 { put_nd_ra(64, 0, 10800, 0, 0); put_nd_opt_sll(12:34:56:78:9a:bc); put_nd_opt_mtu(1280); put_nd_opt_prefix(64, 0, 10800, 10800, fdad:1234:5678::); 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.34.07.40.00.2a.30.00.00.00.00.00.00.00.00.08.12.34.56.78.9a.bc.09.00.00.05.00.0a.40.00.00.00.2a.30.00.00.2a.30.fd.ad.12.34.56.78.00.00.00.00.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.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.7.4
More information about the dev
mailing list