aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);