[ovs-dev] [PATCH] WIP OVN ND for Logical_Port

Zong Kai LI zealokii at gmail.com
Fri May 27 02:32:56 UTC 2016


From: lzklibj <lzklibj at cn.ibm.com>

This patch tries to implement ND for Logical_Port in OVN.

Signed-off-by: lzklibj <lzklibj at cn.ibm.com>
---
 lib/packets.c            |   2 +-
 lib/packets.h            |   3 +
 ovn/controller/pinctrl.c | 156 +++++++++++++++++++++++++++++++++++++++--------
 ovn/lib/actions.c        |  50 +++++++++++++++
 ovn/lib/actions.h        |   6 ++
 ovn/lib/expr.c           |  47 +-------------
 ovn/lib/expr.h           |  43 +++++++++++++
 ovn/northd/ovn-northd.c  |  30 ++++++++-
 8 files changed, 266 insertions(+), 71 deletions(-)

diff --git a/lib/packets.c b/lib/packets.c
index 6a55d6f..ad7c389 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1301,7 +1301,7 @@ compose_arp__(struct dp_packet *b)
 
 /* This function expect packet with ethernet header with correct
  * l3 pointer set. */
-static void *
+void *
 compose_ipv6(struct dp_packet *packet, uint8_t proto, const ovs_be32 src[4],
              const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
              uint8_t key_hl, int size)
diff --git a/lib/packets.h b/lib/packets.h
index 5945940..82d793c 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -274,6 +274,9 @@ static inline uint32_t hash_mac(const struct eth_addr ea,
 bool eth_addr_is_reserved(const struct eth_addr);
 bool eth_addr_from_string(const char *, struct eth_addr *);
 
+void *compose_ipv6(struct dp_packet *packet, uint8_t proto, const ovs_be32 src[4],
+                   const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+                   uint8_t key_hl, int size);
 void compose_rarp(struct dp_packet *, const struct eth_addr);
 
 void eth_push_vlan(struct dp_packet *, ovs_be16 tpid, ovs_be16 tci);
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index bc57c40..5265207 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -14,6 +14,7 @@
  */
 
 #include <config.h>
+#include <netinet/icmp6.h>
 
 #include "pinctrl.h"
 
@@ -23,6 +24,8 @@
 #include "flow.h"
 #include "lport.h"
 #include "ovn-controller.h"
+#include "lib/csum.h"
+#include "lib/packets.h"
 #include "lib/sset.h"
 #include "openvswitch/ofp-actions.h"
 #include "openvswitch/ofp-msgs.h"
@@ -64,6 +67,14 @@ 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_nd(const struct flow *ip_flow,
+                              const struct match *md,
+                              struct ofpbuf *userdata);
+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 *);
+void reload_metadata(struct ofpbuf *ofpacts,
+                     const struct match *md);
 
 COVERAGE_DEFINE(pinctrl_drop_put_arp);
 
@@ -153,31 +164,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 +229,10 @@ process_packet_in(const struct ofp_header *msg)
         pinctrl_handle_put_arp(&pin.flow_metadata.flow, &headers);
         break;
 
+    case ACTION_OPCODE_ND:
+        pinctrl_handle_nd(&headers, &pin.flow_metadata, &userdata);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
@@ -734,3 +725,118 @@ send_garp_run(const struct ovsrec_bridge *br_int, const char *chassis_id,
     sset_destroy(&localnet_vifs);
     simap_destroy(&localnet_ofports);
 }
+
+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)
+{
+    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;
+    //TODO(lizk): RSO ?
+    na->rco_flags.hi = (ovs_be16)0x60;
+
+    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));
+}
+
+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_nd(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_reply = ofpbuf_try_pull(userdata,
+                                                      sizeof *dl_reply);
+    if (!dl_reply) {
+        goto exit;
+    }
+
+    //TODO(lizk): Validate the NS packet.
+
+    // Frame the NA packet.
+    uint64_t packet_stub[128 / 8];
+    struct dp_packet packet;
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+    compose_na(&packet,
+               *dl_reply, ip_flow->dl_src,
+               &(ip_flow->nd_target), &(ip_flow->ipv6_src));
+
+    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..2fce543 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_put_nd_action(struct action_context *ctx)
+{
+    if (!lexer_match(ctx->lexer, LEX_T_LPAREN)) {
+        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_RPAREN)) {
+        if (!parse_action(ctx)) {
+            break;
+        }
+    }
+
+    ctx->ofpacts = outer_ofpacts;
+
+    /* controller. */
+    size_t oc_offset = start_controller_op(ctx->ofpacts, ACTION_OPCODE_ND);
+    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, "put_nd_tll")) {
+        parse_put_nd_action(ctx);
     } else {
         action_syntax_error(ctx, "expecting action");
     }
diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h
index 29af06f..c5cc84c 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,
+
+    /* "put_nd_tll(mac; ...actions... )".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_ND
 };
 
 /* 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/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 44e9430..38796a1 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1381,6 +1381,15 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, struct hmap *ports)
         bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
         enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
 
+        //NOTE(lizk)
+        /* why it's allow-related for icmp6 ? */
+        if (strstr(acl->match, "ip6 && icmp6")) {
+            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
+                          acl->match, "output;");
+            continue;
+        }
+
         if (!strcmp(acl->action, "allow")) {
             /* If there are any stateful flows, we must even commit "allow"
              * actions.  This is because, while the initiater's
@@ -1531,7 +1540,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
         for (size_t i = 0; i < op->nbs->n_addresses; i++) {
             struct lport_addresses laddrs;
             if (!extract_lport_addresses(op->nbs->addresses[i], &laddrs,
-                                         false)) {
+                                         true)) {
                 continue;
             }
             for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
@@ -1557,8 +1566,27 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                 free(match);
                 free(actions);
             }
+            char ip6_str[INET6_ADDRSTRLEN + 1];
+            for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) {
+                ipv6_string_mapped(ip6_str, &(laddrs.ipv6_addrs[i].addr));
+
+                struct ds match = DS_EMPTY_INITIALIZER;
+                ds_put_cstr(&match, "ip6 && nd && icmp6.type == 135 && nd.target == ");
+                ds_put_format(&match, "%s", ip6_str);
+                struct ds actions = DS_EMPTY_INITIALIZER;
+                ds_put_cstr(&actions, "put_nd_tll(");
+                ds_put_format(&actions, ETH_ADDR_FMT, ETH_ADDR_ARGS(laddrs.ea));
+                ds_put_cstr(&actions, ";outport = inport; inport = \"\";output;);");
+
+                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_RSP, 50,
+                              ds_cstr(&match), ds_cstr(&actions));
+
+                ds_destroy(&actions);
+                ds_destroy(&match);
+            }
 
             free(laddrs.ipv4_addrs);
+            free(laddrs.ipv6_addrs);
         }
     }
 
-- 
1.9.1




More information about the dev mailing list