diff mbox

[ovs-dev,v2,1/2] ovn-controller: Add 'na' action for ND

Message ID 1464930723-23811-1-git-send-email-zealokii@gmail.com
State Superseded
Headers show

Commit Message

Zong Kai LI June 3, 2016, 5:12 a.m. UTC
This patch adds a new OVN action 'na' to support ND versus ARP.

When ovn-controller received a ND packet, it frames a NA packet for
reply, with mac address parsed from userdata as eth.dst. Then it
reloads metadata info from previous packet to framed packet, and
finally send the framed packet back with left actions.

Eg. na{12:34:56:78:9a:bc; reg0 = 0x1; outport = inport; inport = ""; output;};

Since patch port for IPv6 router interface is not ready yet, this
patch will only try to deal with ND from VM. This patch will set
RSO flags to 011 for NA packets.

The next patch will do logical flows works for this action.

Signed-off-by: Zong Kai LI <zealokii@gmail.com>
---
 lib/packets.c            |  33 +++++++++++++
 lib/packets.h            |   4 ++
 ovn/controller/pinctrl.c | 119 +++++++++++++++++++++++++++++++++++++----------
 ovn/lib/actions.c        |  50 ++++++++++++++++++++
 ovn/lib/actions.h        |   6 +++
 ovn/lib/expr.c           |  47 ++-----------------
 ovn/lib/expr.h           |  43 +++++++++++++++++
 tests/ovn.at             |   3 ++
 8 files changed, 236 insertions(+), 69 deletions(-)
diff mbox

Patch

diff --git a/lib/packets.c b/lib/packets.c
index 6a55d6f..cbc086e 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1355,6 +1355,39 @@  compose_nd(struct dp_packet *b, const struct eth_addr eth_src,
                                                       ND_MSG_LEN + ND_OPT_LEN));
 }
 
+void
+compose_na(struct dp_packet *b,
+           const struct eth_addr eth_src, const struct eth_addr eth_dst,
+           const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst,
+           ovs_be16 rco_flags)
+{
+    struct ovs_nd_msg *na;
+    struct ovs_nd_opt *nd_opt;
+    uint32_t icmp_csum;
+
+    eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+    na = compose_ipv6(b, IPPROTO_ICMPV6,
+                      ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+                      ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr),
+                      0, 0, 255,
+                      ND_MSG_LEN + ND_OPT_LEN);
+
+    na->icmph.icmp6_type = ND_NEIGHBOR_ADVERT;
+    na->icmph.icmp6_code = 0;
+    na->rco_flags.hi = rco_flags;
+
+    nd_opt = &na->options[0];
+    nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+    nd_opt->nd_opt_len = 1;
+
+    packet_set_nd(b, ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+                  eth_addr_zero, eth_src);
+    na->icmph.icmp6_cksum = 0;
+    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    na->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, na,
+                                                      ND_MSG_LEN + ND_OPT_LEN));
+}
+
 uint32_t
 packet_csum_pseudoheader(const struct ip_header *ip)
 {
diff --git a/lib/packets.h b/lib/packets.h
index 5945940..11b3b6d 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1069,6 +1069,10 @@  void compose_arp(struct dp_packet *, uint16_t arp_op,
                  ovs_be32 arp_spa, ovs_be32 arp_tpa);
 void compose_nd(struct dp_packet *, const struct eth_addr eth_src,
                 struct in6_addr *, struct in6_addr *);
+void compose_na(struct dp_packet *,
+                const struct eth_addr eth_src, const struct eth_addr eth_dst,
+                const struct in6_addr *, const struct in6_addr *,
+                ovs_be16 rco_flags);
 uint32_t packet_csum_pseudoheader(const struct ip_header *);
 void IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6);
 
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index bc57c40..1af9e89 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -23,6 +23,7 @@ 
 #include "flow.h"
 #include "lport.h"
 #include "ovn-controller.h"
+#include "lib/packets.h"
 #include "lib/sset.h"
 #include "openvswitch/ofp-actions.h"
 #include "openvswitch/ofp-msgs.h"
@@ -64,6 +65,11 @@  static void send_garp_run(const struct ovsrec_bridge *,
                           const char *chassis_id,
                           const struct lport_index *lports,
                           struct hmap *local_datapaths);
+static void pinctrl_handle_na(const struct flow *ip_flow,
+                              const struct match *md,
+                              struct ofpbuf *userdata);
+static void reload_metadata(struct ofpbuf *ofpacts,
+                            const struct match *md);
 
 COVERAGE_DEFINE(pinctrl_drop_put_arp);
 
@@ -153,31 +159,7 @@  pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
     enum ofp_version version = rconn_get_version(swconn);
 
-    enum mf_field_id md_fields[] = {
-#if FLOW_N_REGS == 8
-        MFF_REG0,
-        MFF_REG1,
-        MFF_REG2,
-        MFF_REG3,
-        MFF_REG4,
-        MFF_REG5,
-        MFF_REG6,
-        MFF_REG7,
-#else
-#error
-#endif
-        MFF_METADATA,
-    };
-    for (size_t i = 0; i < ARRAY_SIZE(md_fields); i++) {
-        const struct mf_field *field = mf_from_id(md_fields[i]);
-        if (!mf_is_all_wild(field, &md->wc)) {
-            struct ofpact_set_field *sf = ofpact_put_SET_FIELD(&ofpacts);
-            sf->field = field;
-            sf->flow_has_vlan = false;
-            mf_get_value(field, &md->flow, &sf->value);
-            memset(&sf->mask, 0xff, field->n_bytes);
-        }
-    }
+    reload_metadata(&ofpacts, md);
     enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
                                                       version, &ofpacts);
     if (error) {
@@ -242,6 +224,10 @@  process_packet_in(const struct ofp_header *msg)
         pinctrl_handle_put_arp(&pin.flow_metadata.flow, &headers);
         break;
 
+    case ACTION_OPCODE_NA:
+        pinctrl_handle_na(&headers, &pin.flow_metadata, &userdata);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
@@ -734,3 +720,86 @@  send_garp_run(const struct ovsrec_bridge *br_int, const char *chassis_id,
     sset_destroy(&localnet_vifs);
     simap_destroy(&localnet_ofports);
 }
+
+static void
+reload_metadata(struct ofpbuf *ofpacts, const struct match *md)
+{
+    enum mf_field_id md_fields[] = {
+#if FLOW_N_REGS == 8
+        MFF_REG0,
+        MFF_REG1,
+        MFF_REG2,
+        MFF_REG3,
+        MFF_REG4,
+        MFF_REG5,
+        MFF_REG6,
+        MFF_REG7,
+#else
+#error
+#endif
+        MFF_METADATA,
+    };
+    for (size_t i = 0; i < ARRAY_SIZE(md_fields); i++) {
+        const struct mf_field *field = mf_from_id(md_fields[i]);
+        if (!mf_is_all_wild(field, &md->wc)) {
+            struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
+            sf->field = field;
+            sf->flow_has_vlan = false;
+            mf_get_value(field, &md->flow, &sf->value);
+            memset(&sf->mask, 0xff, field->n_bytes);
+        }
+    }
+}
+
+static void
+pinctrl_handle_na(const struct flow *ip_flow,
+                  const struct match *md,
+                  struct ofpbuf *userdata)
+{
+    enum ofp_version version = rconn_get_version(swconn);
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+
+    const struct eth_addr *dl_na_reply = ofpbuf_try_pull(userdata,
+                                                         sizeof *dl_na_reply);
+    if (!dl_na_reply) {
+        goto exit;
+    }
+
+    // Frame the NA packet with RSO=011.
+    uint64_t packet_stub[128 / 8];
+    struct dp_packet packet;
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+    compose_na(&packet,
+               *dl_na_reply, ip_flow->dl_src,
+               &(ip_flow->nd_target), &(ip_flow->ipv6_src),
+               htons(0x6000));
+
+    // Reload previous packet metadata.
+    uint64_t ofpacts_stub[4096 / 8];
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+    reload_metadata(&ofpacts, md);
+
+    enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
+                                                      version, &ofpacts);
+    if (error) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "failed to parse actions for put_nd_tll (%s)",
+                     ofperr_to_string(error));
+        goto exit;
+    }
+
+    struct ofputil_packet_out po = {
+        .packet = dp_packet_data(&packet),
+        .packet_len = dp_packet_size(&packet),
+        .buffer_id = UINT32_MAX,
+        .in_port = OFPP_CONTROLLER,
+        .ofpacts = ofpacts.data,
+        .ofpacts_len = ofpacts.size,
+    };
+
+    queue_msg(ofputil_encode_packet_out(&po, proto));
+
+exit:
+    dp_packet_uninit(&packet);
+    ofpbuf_uninit(&ofpacts);
+}
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 5f0bf19..649171f 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -442,6 +442,54 @@  emit_ct(struct action_context *ctx, bool recirc_next, bool commit)
     add_prerequisite(ctx, "ip");
 }
 
+static void
+parse_na_action(struct action_context *ctx)
+{
+    if (!lexer_match(ctx->lexer, LEX_T_LCURLY)) {
+        action_syntax_error(ctx, "expecting `{'");
+        return;
+    }
+
+    struct expr_constant_set cs;
+    struct expr_context expr_ctx = {
+       .lexer = ctx->lexer,
+       .symtab = NULL,
+    };
+    if (!parse_constant_set(&expr_ctx, &cs)) {
+        action_syntax_error(ctx, "expecting NA mac");
+        return;
+    }
+    if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) {
+        action_syntax_error(ctx, "expecting ';'");
+        return;
+    }
+
+    struct ofpbuf *outer_ofpacts = ctx->ofpacts;
+    uint64_t inner_ofpacts_stub[1024 / 8];
+    struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub);
+    ctx->ofpacts = &inner_ofpacts;
+
+    /* Parse inner actions. */
+    while (!lexer_match(ctx->lexer, LEX_T_RCURLY)) {
+        if (!parse_action(ctx)) {
+            break;
+        }
+    }
+
+    ctx->ofpacts = outer_ofpacts;
+
+    /* controller. */
+    size_t oc_offset = start_controller_op(ctx->ofpacts, ACTION_OPCODE_NA);
+    ofpbuf_put(ctx->ofpacts, &cs.values[0].value.mac, sizeof(struct eth_addr));
+    ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size,
+                                 ctx->ofpacts, OFP13_VERSION);
+    finish_controller_op(ctx->ofpacts, oc_offset);
+
+    /* Free memory. */
+    expr_constant_set_destroy(&cs);
+    ofpbuf_uninit(&inner_ofpacts);
+}
+
 static bool
 parse_action(struct action_context *ctx)
 {
@@ -475,6 +523,8 @@  parse_action(struct action_context *ctx)
         parse_get_arp_action(ctx);
     } else if (lexer_match_id(ctx->lexer, "put_arp")) {
         parse_put_arp_action(ctx);
+    } else if (lexer_match_id(ctx->lexer, "na")) {
+        parse_na_action(ctx);
     } else {
         action_syntax_error(ctx, "expecting action");
     }
diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h
index 29af06f..4ae8928 100644
--- a/ovn/lib/actions.h
+++ b/ovn/lib/actions.h
@@ -44,6 +44,12 @@  enum action_opcode {
      *     MFF_ETH_SRC = mac
      */
     ACTION_OPCODE_PUT_ARP,
+
+    /* "na { mac; ...actions... }".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_NA,
 };
 
 /* Header. */
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index f274ab4..b89b207 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -405,39 +405,6 @@  expr_print(const struct expr *e)
 
 /* Parsing. */
 
-/* Type of a "union expr_constant" or "struct expr_constant_set". */
-enum expr_constant_type {
-    EXPR_C_INTEGER,
-    EXPR_C_STRING
-};
-
-/* A string or integer constant (one must know which from context). */
-union expr_constant {
-    /* Integer constant.
-     *
-     * The width of a constant isn't always clear, e.g. if you write "1",
-     * there's no way to tell whether you mean for that to be a 1-bit constant
-     * or a 128-bit constant or somewhere in between. */
-    struct {
-        union mf_subvalue value;
-        union mf_subvalue mask; /* Only initialized if 'masked'. */
-        bool masked;
-
-        enum lex_format format; /* From the constant's lex_token. */
-    };
-
-    /* Null-terminated string constant. */
-    char *string;
-};
-
-/* A collection of "union expr_constant"s of the same type. */
-struct expr_constant_set {
-    union expr_constant *values;  /* Constants. */
-    size_t n_values;              /* Number of constants. */
-    enum expr_constant_type type; /* Type of the constants. */
-    bool in_curlies;              /* Whether the constants were in {}. */
-};
-
 /* A reference to a symbol or a subfield of a symbol.
  *
  * For string fields, ofs and n_bits are 0. */
@@ -447,17 +414,9 @@  struct expr_field {
     int n_bits;                       /* Number of bits. */
 };
 
-/* Context maintained during expr_parse(). */
-struct expr_context {
-    struct lexer *lexer;        /* Lexer for pulling more tokens. */
-    const struct shash *symtab; /* Symbol table. */
-    char *error;                /* Error, if any, otherwise NULL. */
-    bool not;                   /* True inside odd number of NOT operators. */
-};
-
 struct expr *expr_parse__(struct expr_context *);
 static void expr_not(struct expr *);
-static void expr_constant_set_destroy(struct expr_constant_set *);
+void expr_constant_set_destroy(struct expr_constant_set *);
 static bool parse_field(struct expr_context *, struct expr_field *);
 
 static bool
@@ -812,7 +771,7 @@  parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
  * which the caller need not have initialized.  Returns true on success, in
  * which case the caller owns 'cs', false on failure, in which case 'cs' is
  * indeterminate. */
-static bool
+bool
 parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
 {
     size_t allocated_values = 0;
@@ -838,7 +797,7 @@  parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
     return ok;
 }
 
-static void
+void
 expr_constant_set_destroy(struct expr_constant_set *cs)
 {
     if (cs) {
diff --git a/ovn/lib/expr.h b/ovn/lib/expr.h
index 1327789..2742d2a 100644
--- a/ovn/lib/expr.h
+++ b/ovn/lib/expr.h
@@ -391,4 +391,47 @@  char *expr_parse_field(struct lexer *, int n_bits, bool rw,
                        const struct shash *symtab, struct mf_subfield *,
                        struct expr **prereqsp);
 
+/* Context maintained during expr_parse(). */
+struct expr_context {
+    struct lexer *lexer;        /* Lexer for pulling more tokens. */
+    const struct shash *symtab; /* Symbol table. */
+    char *error;                /* Error, if any, otherwise NULL. */
+    bool not;                   /* True inside odd number of NOT operators. */
+};
+
+/* Type of a "union expr_constant" or "struct expr_constant_set". */
+enum expr_constant_type {
+    EXPR_C_INTEGER,
+    EXPR_C_STRING
+};
+
+/* A string or integer constant (one must know which from context). */
+union expr_constant {
+    /* Integer constant.
+     *
+     * The width of a constant isn't always clear, e.g. if you write "1",
+     * there's no way to tell whether you mean for that to be a 1-bit constant
+     * or a 128-bit constant or somewhere in between. */
+    struct {
+        union mf_subvalue value;
+        union mf_subvalue mask; /* Only initialized if 'masked'. */
+        bool masked;
+
+        enum lex_format format; /* From the constant's lex_token. */
+    };
+
+    /* Null-terminated string constant. */
+    char *string;
+};
+
+/* A collection of "union expr_constant"s of the same type. */
+struct expr_constant_set {
+    union expr_constant *values;  /* Constants. */
+    size_t n_values;              /* Number of constants. */
+    enum expr_constant_type type; /* Type of the constants. */
+    bool in_curlies;              /* Whether the constants were in {}. */
+};
+
+bool parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs);
+void expr_constant_set_destroy(struct expr_constant_set *cs);
 #endif /* ovn/expr.h */
diff --git a/tests/ovn.at b/tests/ovn.at
index e6ac1d7..3c2ecc6 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -525,6 +525,9 @@  get_arp(reg0, ip4.dst); => Cannot use numeric field reg0 where string field is r
 # put_arp
 put_arp(inport, arp.spa, arp.sha); => actions=push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.01.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[], prereqs=eth.type == 0x806 && eth.type == 0x806
 
+# na
+na{fa:16:3e:13:81:98; reg0 = 0x1; outport = inport; inport = ""; output;}; => actions=controller(userdata=00.00.00.02.00.00.00.00.fa.16.3e.13.81.98.ff.ff.00.18.00.00.23.20.00.07.08.1f.80.01.00.08.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.0c.04.00.01.0e.04.00.19.00.10.00.01.0c.04.00.00.00.00.00.00.00.00.00.19.00.10.00.00.00.02.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00), prereqs=1
+
 # Contradictionary prerequisites (allowed but not useful):
 ip4.src = ip6.src[0..31]; => actions=move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd
 ip4.src <-> ip6.src[0..31]; => actions=push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd