[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