[ovs-dev] [PATCH v5 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 May 4 15:13:54 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            |  72 +++++++++++++-
 ovn/utilities/ovn-trace.c |   4 +
 tests/ovn.at              |  40 +++++++-
 7 files changed, 528 insertions(+), 24 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index c1bfda5..9acc8ec 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -72,7 +72,11 @@ struct simap;
     OVNACT(PUT_DHCPV4_OPTS, ovnact_put_dhcp_opts)   \
     OVNACT(PUT_DHCPV6_OPTS, ovnact_put_dhcp_opts)   \
     OVNACT(SET_QUEUE,       ovnact_set_queue)       \
-    OVNACT(DNS_LOOKUP,      ovnact_dns_lookup)
+    OVNACT(DNS_LOOKUP,      ovnact_dns_lookup)      \
+    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 {
@@ -266,6 +270,31 @@ struct ovnact_dns_lookup {
     struct expr_field dst;      /* 1-bit destination field. */
 };
 
+/* 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);
@@ -407,6 +436,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. */
@@ -499,6 +558,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 9656675..5c0abba 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -75,7 +75,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);
 
@@ -952,8 +952,11 @@ process_packet_in(const struct ofp_header *msg, struct controller_ctx *ctx)
         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:
@@ -1738,7 +1741,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
@@ -1756,13 +1759,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
@@ -1771,20 +1776,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 d361c0e..7dbfd87 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
@@ -1811,7 +1851,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)
 {
@@ -1850,6 +2074,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")) {
@@ -1989,7 +2223,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 b3d2125..7fdabee 100644
--- a/ovn/lib/ovn-util.h
+++ b/ovn/lib/ovn-util.h
@@ -67,4 +67,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 8b2e9a8..213c2a2 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1324,13 +1324,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 dd0eaea..8a1984c 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1629,6 +1629,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 807161a..8e51edf 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.07.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.07.00.00.00.00.00.05.00.08.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.07.00.00.00.00.00.05.00.08.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.07.00.00.00.00.00.05.00.08.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.07.00.00.00.00.00.27.00.09.00.06.12.34.56.78.9a.bc.00.0a.00.04.00.00.05.00.00.0b.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.07.00.00.00.00.00.3c.00.09.00.06.12.34.56.78.9a.bc.00.0a.00.04.00.00.05.00.00.0b.00.11.40.fd.ad.12.34.56.78.00.00.00.00.00.00.00.00.00.00.00.0b.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