aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2015-10-07 13:12:34 -0700
committerBen Pfaff <blp@nicira.com>2015-10-16 23:52:41 -0700
commit880fcd147e2321b6f215fa368e726d81edb1f054 (patch)
tree39866258159a51d62bc51e15957c93c39fbf0d3d
parent614404518f67812dbf35d30aca2686ebfd8f6aaa (diff)
ovn-northd: Add stages for logical routers.
Until now, ovn-northd has only set up flows for logical switches. With the arrival of logical routers, it needs to set up flows for them too. The stages within logical routers are completely different from those for logical switches, so this prepares for that by adding logic for identifying those stages. Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Justin Pettit <jpettit@nicira.com>
-rw-r--r--ovn/northd/ovn-northd.c236
1 files changed, 136 insertions, 100 deletions
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 74eb5ccfa..1ed3cc751 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -53,39 +53,99 @@ static const char *ovnnb_db;
static const char *ovnsb_db;
static const char *default_db(void);
+
+/* Pipeline stages. */
+/* The two pipelines in an OVN logical flow table. */
+enum ovn_pipeline {
+ P_IN, /* Ingress pipeline. */
+ P_OUT /* Egress pipeline. */
+};
-/* Ingress pipeline stages.
- *
- * These must be listed in the order that the stages will be executed. */
-#define INGRESS_STAGES \
- INGRESS_STAGE(PORT_SEC, port_sec) \
- INGRESS_STAGE(PRE_ACL, pre_acl) \
- INGRESS_STAGE(ACL, acl) \
- INGRESS_STAGE(L2_LKUP, l2_lkup)
-
-enum ingress_stage {
-#define INGRESS_STAGE(NAME, STR) S_IN_##NAME,
- INGRESS_STAGES
-#undef INGRESS_STAGE
- INGRESS_N_STAGES
+/* The two purposes for which ovn-northd uses OVN logical datapaths. */
+enum ovn_datapath_type {
+ DP_SWITCH, /* OVN logical switch. */
+ DP_ROUTER /* OVN logical router. */
};
-/* Egress pipeline stages.
+/* Returns an "enum ovn_stage" built from the arguments.
+ *
+ * (It's better to use ovn_stage_build() for type-safety reasons, but inline
+ * functions can't be used in enums or switch cases.) */
+#define OVN_STAGE_BUILD(DP_TYPE, PIPELINE, TABLE) \
+ (((DP_TYPE) << 9) | ((PIPELINE) << 8) | (TABLE))
+
+/* A stage within an OVN logical switch or router.
*
- * These must be listed in the order that the stages will be executed. */
-#define EGRESS_STAGES \
- EGRESS_STAGE(PRE_ACL, pre_acl) \
- EGRESS_STAGE(ACL, acl) \
- EGRESS_STAGE(PORT_SEC, port_sec)
-
-enum egress_stage {
-#define EGRESS_STAGE(NAME, STR) S_OUT_##NAME,
- EGRESS_STAGES
-#undef EGRESS_STAGE
- EGRESS_N_STAGES
+ * An "enum ovn_stage" indicates whether the stage is part of a logical switch
+ * or router, whether the stage is part of the ingress or egress pipeline, and
+ * the table within that pipeline. The first three components are combined to
+ * form the stage's full name, e.g. S_SWITCH_IN_PORT_SEC,
+ * S_ROUTER_OUT_DELIVERY. */
+enum ovn_stage {
+#define PIPELINE_STAGES \
+ /* Logical switch ingress stages. */ \
+ PIPELINE_STAGE(SWITCH, IN, PORT_SEC, 0, "switch_in_port_sec") \
+ PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 1, "switch_in_pre_acl") \
+ PIPELINE_STAGE(SWITCH, IN, ACL, 2, "switch_in_acl") \
+ PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 3, "switch_in_l2_lkup") \
+ \
+ /* Logical switch egress stages. */ \
+ PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 0, "switch_out_pre_acl") \
+ PIPELINE_STAGE(SWITCH, OUT, ACL, 1, "switch_out_acl") \
+ PIPELINE_STAGE(SWITCH, OUT, PORT_SEC, 2, "switch_out_port_sec") \
+ \
+ /* Logical router ingress stages. */ \
+ PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "router_in_admission") \
+ PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "router_in_ip_input") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 2, "router_in_ip_routing") \
+ PIPELINE_STAGE(ROUTER, IN, ARP, 3, "router_in_arp") \
+ \
+ /* Logical router egress stages. */ \
+ PIPELINE_STAGE(ROUTER, OUT, DELIVERY, 0, "router_out_delivery")
+
+#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
+ S_##DP_TYPE##_##PIPELINE##_##STAGE \
+ = OVN_STAGE_BUILD(DP_##DP_TYPE, P_##PIPELINE, TABLE),
+ PIPELINE_STAGES
+#undef PIPELINE_STAGE
};
+/* Returns an "enum ovn_stage" built from the arguments. */
+static enum ovn_stage
+ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline,
+ uint8_t table)
+{
+ return OVN_STAGE_BUILD(dp_type, pipeline, table);
+}
+
+/* Returns the pipeline to which 'stage' belongs. */
+static enum ovn_pipeline
+ovn_stage_get_pipeline(enum ovn_stage stage)
+{
+ return (stage >> 8) & 1;
+}
+
+/* Returns the table to which 'stage' belongs. */
+static uint8_t
+ovn_stage_get_table(enum ovn_stage stage)
+{
+ return stage & 0xff;
+}
+
+/* Returns a string name for 'stage'. */
+static const char *
+ovn_stage_to_str(enum ovn_stage stage)
+{
+ switch (stage) {
+#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
+ case S_##DP_TYPE##_##PIPELINE##_##STAGE: return NAME;
+ PIPELINE_STAGES
+#undef PIPELINE_STAGE
+ default: return "<unknown>";
+ }
+}
+
static void
usage(void)
{
@@ -586,8 +646,7 @@ struct ovn_lflow {
struct hmap_node hmap_node;
struct ovn_datapath *od;
- enum ovn_pipeline { P_IN, P_OUT } pipeline;
- uint8_t table_id;
+ enum ovn_stage stage;
uint16_t priority;
char *match;
char *actions;
@@ -597,7 +656,7 @@ static size_t
ovn_lflow_hash(const struct ovn_lflow *lflow)
{
size_t hash = uuid_hash(&lflow->od->key);
- hash = hash_2words((lflow->table_id << 16) | lflow->priority, hash);
+ hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
hash = hash_string(lflow->match, hash);
return hash_string(lflow->actions, hash);
}
@@ -606,8 +665,7 @@ static bool
ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
{
return (a->od == b->od
- && a->pipeline == b->pipeline
- && a->table_id == b->table_id
+ && a->stage == b->stage
&& a->priority == b->priority
&& !strcmp(a->match, b->match)
&& !strcmp(a->actions, b->actions));
@@ -615,56 +673,35 @@ ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
static void
ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
- enum ovn_pipeline pipeline, uint8_t table_id, uint16_t priority,
+ enum ovn_stage stage, uint16_t priority,
char *match, char *actions)
{
lflow->od = od;
- lflow->pipeline = pipeline;
- lflow->table_id = table_id;
+ lflow->stage = stage;
lflow->priority = priority;
lflow->match = match;
lflow->actions = actions;
}
-static const char *
-ingress_stage_to_str(int stage) {
- switch (stage) {
-#define INGRESS_STAGE(NAME, STR) case S_IN_##NAME: return #STR;
- INGRESS_STAGES
-#undef INGRESS_STAGE
- default: return "<unknown>";
- }
-}
-
-static const char *
-egress_stage_to_str(int stage) {
- switch (stage) {
-#define EGRESS_STAGE(NAME, STR) case S_OUT_##NAME: return #STR;
- EGRESS_STAGES
-#undef EGRESS_STAGE
- default: return "<unknown>";
- }
-}
-
/* Adds a row with the specified contents to the Logical_Flow table. */
static void
ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od,
- enum ovn_pipeline pipeline, uint8_t table_id, uint16_t priority,
+ enum ovn_stage stage, uint16_t priority,
const char *match, const char *actions)
{
struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
- ovn_lflow_init(lflow, od, pipeline, table_id, priority,
+ ovn_lflow_init(lflow, od, stage, priority,
xstrdup(match), xstrdup(actions));
hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
}
static struct ovn_lflow *
ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
- enum ovn_pipeline pipeline, uint8_t table_id, uint16_t priority,
+ enum ovn_stage stage, uint16_t priority,
const char *match, const char *actions)
{
struct ovn_lflow target;
- ovn_lflow_init(&target, od, pipeline, table_id, priority,
+ ovn_lflow_init(&target, od, stage, priority,
CONST_CAST(char *, match), CONST_CAST(char *, actions));
struct ovn_lflow *lflow;
@@ -744,14 +781,14 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
/* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
* allowed by default. */
- ovn_lflow_add(lflows, od, P_IN, S_IN_PRE_ACL, 0, "1", "next;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_PRE_ACL, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;");
/* Ingress and Egress ACL Table (Priority 0): Packets are allowed by
* default. A related rule at priority 1 is added below if there
* are any stateful ACLs in this datapath. */
- ovn_lflow_add(lflows, od, P_IN, S_IN_ACL, 0, "1", "next;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_ACL, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 0, "1", "next;");
/* If there are any stateful ACL rules in this dapapath, we must
* send all IP packets through the conntrack action, which handles
@@ -762,10 +799,8 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
* Regardless of whether the ACL is "from-lport" or "to-lport",
* we need rules in both the ingress and egress table, because
* the return traffic needs to be followed. */
- ovn_lflow_add(lflows, od, P_IN, S_IN_PRE_ACL, 100,
- "ip", "ct_next;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_PRE_ACL, 100,
- "ip", "ct_next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 100, "ip", "ct_next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 100, "ip", "ct_next;");
/* Ingress and Egress ACL Table (Priority 1).
*
@@ -775,18 +810,18 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
* direction may not have any stateful rules, the server's may
* and then its return traffic would not have an associated
* conntrack entry and would return "+invalid". */
- ovn_lflow_add(lflows, od, P_IN, S_IN_ACL, 1, "ip",
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1, "ip",
"ct_commit; next;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_ACL, 1, "ip",
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1, "ip",
"ct_commit; next;");
/* Ingress and Egress ACL Table (Priority 65535).
*
* Always drop traffic that's in an invalid state. This is
* enforced at a higher priority than ACLs can be defined. */
- ovn_lflow_add(lflows, od, P_IN, S_IN_ACL, UINT16_MAX,
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
"ct.inv", "drop;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_ACL, UINT16_MAX,
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
"ct.inv", "drop;");
/* Ingress and Egress ACL Table (Priority 65535).
@@ -794,10 +829,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
* Always allow traffic that is established to a committed
* conntrack entry. This is enforced at a higher priority than
* ACLs can be defined. */
- ovn_lflow_add(lflows, od, P_IN, S_IN_ACL, UINT16_MAX,
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
"ct.est && !ct.rel && !ct.new && !ct.inv",
"next;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_ACL, UINT16_MAX,
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
"ct.est && !ct.rel && !ct.new && !ct.inv",
"next;");
@@ -811,10 +846,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
* a dynamically negotiated FTP data channel), but will allow
* related traffic such as an ICMP Port Unreachable through
* that's generated from a non-listening UDP port. */
- ovn_lflow_add(lflows, od, P_IN, S_IN_ACL, UINT16_MAX,
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
"!ct.est && ct.rel && !ct.new && !ct.inv",
"next;");
- ovn_lflow_add(lflows, od, P_OUT, S_OUT_ACL, UINT16_MAX,
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
"!ct.est && ct.rel && !ct.new && !ct.inv",
"next;");
}
@@ -823,8 +858,7 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
for (size_t i = 0; i < od->nb->n_acls; i++) {
struct nbrec_acl *acl = od->nb->acls[i];
bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
- enum ovn_pipeline pipeline = ingress ? P_IN : P_OUT;
- uint8_t stage = ingress ? S_IN_ACL : S_OUT_ACL;
+ enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
if (!strcmp(acl->action, "allow")) {
/* If there are any stateful flows, we must even commit "allow"
@@ -833,7 +867,7 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
* may and then its return traffic would not have an
* associated conntrack entry and would return "+invalid". */
const char *actions = has_stateful ? "ct_commit; next;" : "next;";
- ovn_lflow_add(lflows, od, pipeline, stage, acl->priority,
+ ovn_lflow_add(lflows, od, stage, acl->priority,
acl->match, actions);
} else if (!strcmp(acl->action, "allow-related")) {
struct ds match = DS_EMPTY_INITIALIZER;
@@ -842,17 +876,17 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
* other traffic related to this entry to flow due to the
* 65535 priority flow defined earlier. */
ds_put_format(&match, "ct.new && (%s)", acl->match);
- ovn_lflow_add(lflows, od, pipeline, stage, acl->priority,
+ ovn_lflow_add(lflows, od, stage, acl->priority,
ds_cstr(&match), "ct_commit; next;");
ds_destroy(&match);
} else if (!strcmp(acl->action, "drop")) {
- ovn_lflow_add(lflows, od, pipeline, stage, acl->priority,
+ ovn_lflow_add(lflows, od, stage, acl->priority,
acl->match, "drop;");
} else if (!strcmp(acl->action, "reject")) {
/* xxx Need to support "reject". */
VLOG_INFO("reject is not a supported action");
- ovn_lflow_add(lflows, od, pipeline, stage, acl->priority,
+ ovn_lflow_add(lflows, od, stage, acl->priority,
acl->match, "drop;");
}
}
@@ -874,11 +908,11 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
struct ovn_datapath *od;
HMAP_FOR_EACH (od, key_node, datapaths) {
/* Logical VLANs not supported. */
- ovn_lflow_add(&lflows, od, P_IN, S_IN_PORT_SEC, 100, "vlan.present",
+ ovn_lflow_add(&lflows, od, S_SWITCH_IN_PORT_SEC, 100, "vlan.present",
"drop;");
/* Broadcast/multicast source address is invalid. */
- ovn_lflow_add(&lflows, od, P_IN, S_IN_PORT_SEC, 100, "eth.src[40]",
+ ovn_lflow_add(&lflows, od, S_SWITCH_IN_PORT_SEC, 100, "eth.src[40]",
"drop;");
/* Port security flows have priority 50 (see below) and will continue
@@ -900,7 +934,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
build_port_security("eth.src",
op->nb->port_security, op->nb->n_port_security,
&match);
- ovn_lflow_add(&lflows, op->od, P_IN, S_IN_PORT_SEC, 50,
+ ovn_lflow_add(&lflows, op->od, S_SWITCH_IN_PORT_SEC, 50,
ds_cstr(&match), "next;");
ds_destroy(&match);
}
@@ -913,7 +947,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
}
}
HMAP_FOR_EACH (od, key_node, datapaths) {
- ovn_lflow_add(&lflows, od, P_IN, S_IN_L2_LKUP, 100, "eth.mcast",
+ ovn_lflow_add(&lflows, od, S_SWITCH_IN_L2_LKUP, 100, "eth.mcast",
"outport = \""MC_FLOOD"\"; output;");
}
@@ -932,7 +966,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
ds_put_cstr(&actions, "outport = ");
json_string_escape(op->nb->name, &actions);
ds_put_cstr(&actions, "; output;");
- ovn_lflow_add(&lflows, op->od, P_IN, S_IN_L2_LKUP, 50,
+ ovn_lflow_add(&lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
ds_cstr(&match), ds_cstr(&actions));
ds_destroy(&actions);
ds_destroy(&match);
@@ -954,7 +988,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
/* Ingress table 3: Destination lookup for unknown MACs (priority 0). */
HMAP_FOR_EACH (od, key_node, datapaths) {
if (od->has_unknown) {
- ovn_lflow_add(&lflows, od, P_IN, S_IN_L2_LKUP, 0, "1",
+ ovn_lflow_add(&lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1",
"outport = \""MC_UNKNOWN"\"; output;");
}
}
@@ -962,7 +996,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
/* Egress table 2: Egress port security multicast/broadcast (priority
* 100). */
HMAP_FOR_EACH (od, key_node, datapaths) {
- ovn_lflow_add(&lflows, od, P_OUT, S_OUT_PORT_SEC, 100, "eth.mcast",
+ ovn_lflow_add(&lflows, od, S_SWITCH_OUT_PORT_SEC, 100, "eth.mcast",
"output;");
}
@@ -982,10 +1016,10 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
build_port_security("eth.dst",
op->nb->port_security, op->nb->n_port_security,
&match);
- ovn_lflow_add(&lflows, op->od, P_OUT, S_OUT_PORT_SEC, 50,
+ ovn_lflow_add(&lflows, op->od, S_SWITCH_OUT_PORT_SEC, 50,
ds_cstr(&match), "output;");
} else {
- ovn_lflow_add(&lflows, op->od, P_OUT, S_OUT_PORT_SEC, 150,
+ ovn_lflow_add(&lflows, op->od, S_SWITCH_OUT_PORT_SEC, 150,
ds_cstr(&match), "drop;");
}
@@ -1008,10 +1042,12 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
continue;
}
+ enum ovn_datapath_type dp_type = DP_SWITCH; /* XXX no routers yet. */
+ enum ovn_pipeline pipeline
+ = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
struct ovn_lflow *lflow = ovn_lflow_find(
- &lflows, od, (!strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT),
- sbflow->table_id, sbflow->priority,
- sbflow->match, sbflow->actions);
+ &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
+ sbflow->priority, sbflow->match, sbflow->actions);
if (lflow) {
ovn_lflow_destroy(&lflows, lflow);
} else {
@@ -1020,20 +1056,20 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
}
struct ovn_lflow *lflow, *next_lflow;
HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
+ enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage);
+ uint8_t table = ovn_stage_get_table(lflow->stage);
+
sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
- sbrec_logical_flow_set_pipeline(
- sbflow, lflow->pipeline == P_IN ? "ingress" : "egress");
- sbrec_logical_flow_set_table_id(sbflow, lflow->table_id);
+ sbrec_logical_flow_set_pipeline(sbflow,
+ pipeline ? "ingress" : "egress");
+ sbrec_logical_flow_set_table_id(sbflow, table);
sbrec_logical_flow_set_priority(sbflow, lflow->priority);
sbrec_logical_flow_set_match(sbflow, lflow->match);
sbrec_logical_flow_set_actions(sbflow, lflow->actions);
- const struct smap ids = SMAP_CONST1(
- &ids, "stage-name",
- (lflow->pipeline == P_IN
- ? ingress_stage_to_str(lflow->table_id)
- : egress_stage_to_str(lflow->table_id)));
+ const struct smap ids = SMAP_CONST1(&ids, "stage-name",
+ ovn_stage_to_str(lflow->stage));
sbrec_logical_flow_set_external_ids(sbflow, &ids);
ovn_lflow_destroy(&lflows, lflow);