* 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_STAGES \
+ /* Logical switch ingress stages. */ \
+ PIPELINE_STAGE(SWITCH, IN, PORT_SEC, 0, "ls_in_port_sec") \
+ PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 1, "ls_in_pre_acl") \
+ PIPELINE_STAGE(SWITCH, IN, ACL, 2, "ls_in_acl") \
+ PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 3, "ls_in_l2_lkup") \
+ \
+ /* Logical switch egress stages. */ \
+ PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 0, "ls_out_pre_acl") \
+ PIPELINE_STAGE(SWITCH, OUT, ACL, 1, "ls_out_acl") \
+ PIPELINE_STAGE(SWITCH, OUT, PORT_SEC, 2, "ls_out_port_sec") \
+ \
+ /* Logical router ingress stages. */ \
+ PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
+ PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 2, "lr_in_ip_routing") \
+ PIPELINE_STAGE(ROUTER, IN, ARP, 3, "lr_in_arp") \
+ \
+ /* Logical router egress stages. */ \
+ PIPELINE_STAGE(ROUTER, OUT, DELIVERY, 0, "lr_out_delivery")
#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
S_##DP_TYPE##_##PIPELINE##_##STAGE \
continue;
}
- char name[UUID_LEN + 1];
- snprintf(name, sizeof name, UUID_FMT,
- UUID_ARGS(&nbr->header_.uuid));
- struct ovn_port *op = ovn_port_find(ports, name);
+ struct ovn_port *op = ovn_port_find(ports, nbr->name);
if (op) {
if (op->nbs || op->nbr) {
static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl, "duplicate logical router port %s",
- name);
+ nbr->name);
continue;
}
op->nbr = nbr;
list_remove(&op->list);
list_push_back(both, &op->list);
} else {
- op = ovn_port_create(ports, name, NULL, nbr, NULL);
+ op = ovn_port_create(ports, nbr->name, NULL, nbr, NULL);
list_push_back(nb_only, &op->list);
}
sizeof *op->od->router_ports * (op->od->n_router_ports + 1));
op->od->router_ports[op->od->n_router_ports++] = op;
} else if (op->nbr && op->nbr->peer) {
- char peer_name[UUID_LEN + 1];
- snprintf(peer_name, sizeof peer_name, UUID_FMT,
- UUID_ARGS(&op->nbr->peer->header_.uuid));
- op->peer = ovn_port_find(ports, peer_name);
+ op->peer = ovn_port_find(ports, op->nbr->name);
}
}
}
ds_destroy(&match);
}
- /* Ingress table 2: Destination lookup, broadcast and multicast handling
+ /* Ingress table 3: Destination lookup, ARP reply for known IPs.
+ * (priority 150). */
+ HMAP_FOR_EACH (op, key_node, ports) {
+ if (!op->nbs) {
+ continue;
+ }
+
+ for (size_t i = 0; i < op->nbs->n_addresses; i++) {
+ struct eth_addr ea;
+ ovs_be32 ip;
+
+ if (ovs_scan(op->nbs->addresses[i],
+ ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT,
+ ETH_ADDR_SCAN_ARGS(ea), IP_SCAN_ARGS(&ip))) {
+ char *match = xasprintf(
+ "arp.tpa == "IP_FMT" && arp.op == 1", IP_ARGS(ip));
+ char *actions = xasprintf(
+ "eth.dst = eth.src; "
+ "eth.src = "ETH_ADDR_FMT"; "
+ "arp.op = 2; /* ARP reply */ "
+ "arp.tha = arp.sha; "
+ "arp.sha = "ETH_ADDR_FMT"; "
+ "arp.tpa = arp.spa; "
+ "arp.spa = "IP_FMT"; "
+ "outport = inport; "
+ "inport = \"\"; /* Allow sending out inport. */ "
+ "output;",
+ ETH_ADDR_ARGS(ea),
+ ETH_ADDR_ARGS(ea),
+ IP_ARGS(ip));
+ ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 150,
+ match, actions);
+ free(match);
+ free(actions);
+ }
+ }
+ }
+
+ /* Ingress table 3: Destination lookup, broadcast and multicast handling
* (priority 100). */
HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbs) {
IP_ARGS(network), IP_ARGS(mask));
struct ds actions = DS_EMPTY_INITIALIZER;
- ds_put_cstr(&actions, "ip4.ttl--; reg0 = ");
+ ds_put_cstr(&actions, "ip.ttl--; reg0 = ");
if (gateway) {
ds_put_format(&actions, IP_FMT, IP_ARGS(gateway));
} else {
/* This flow table structure is documented in ovn-northd(8), so please
* update ovn-northd.8.xml if you change anything. */
- /* XXX ICMP echo reply */
-
/* Logical router ingress table 0: Admission control framework. */
struct ovn_datapath *od;
HMAP_FOR_EACH (od, key_node, datapaths) {
ETH_ADDR_ARGS(op->mac), op->json_key);
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
match, "next;");
+ free(match);
}
/* Logical router ingress table 1: IP Input. */
match, "drop;");
free(match);
+ /* ICMP echo reply. These flows reply to ICMP echo requests
+ * received for the router's IP address. */
+ match = xasprintf(
+ "inport == %s && (ip4.dst == "IP_FMT" || ip4.dst == "IP_FMT") && "
+ "icmp4.type == 8 && icmp4.code == 0",
+ op->json_key, IP_ARGS(op->ip), IP_ARGS(op->bcast));
+ char *actions = xasprintf(
+ "ip4.dst = ip4.src; "
+ "ip4.src = "IP_FMT"; "
+ "ip.ttl = 255; "
+ "icmp4.type = 0; "
+ "inport = \"\"; /* Allow sending out inport. */ "
+ "next; ",
+ IP_ARGS(op->ip));
+ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+ match, actions);
+ free(match);
+ free(actions);
+
/* ARP reply. These flows reply to ARP requests for the router's own
* IP address. */
match = xasprintf(
"inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
op->json_key, IP_ARGS(op->ip));
- char *actions = xasprintf(
+ actions = xasprintf(
"eth.dst = eth.src; "
"eth.src = "ETH_ADDR_FMT"; "
"arp.op = 2; /* ARP reply */ "
op->json_key);
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
match, actions);
+ free(match);
+ free(actions);
/* Drop IP traffic to this router. */
match = xasprintf("ip4.dst == "IP_FMT, IP_ARGS(op->ip));
}
\f
static void
-ovnnb_db_changed(struct northd_context *ctx)
+ovnnb_db_run(struct northd_context *ctx)
{
- VLOG_DBG("ovn-nb db contents have changed.");
-
+ if (!ctx->ovnsb_txn) {
+ return;
+ }
+ VLOG_DBG("ovn-nb db contents may have changed.");
struct hmap datapaths, ports;
build_datapaths(ctx, &datapaths);
build_ports(ctx, &datapaths, &ports);
* need to set the corresponding logical port as 'up' in the northbound DB.
*/
static void
-ovnsb_db_changed(struct northd_context *ctx)
+ovnsb_db_run(struct northd_context *ctx)
{
+ if (!ctx->ovnnb_txn) {
+ return;
+ }
struct hmap lports_hmap;
const struct sbrec_port_binding *sb;
const struct nbrec_logical_port *nb;
main(int argc, char *argv[])
{
extern struct vlog_module VLM_reconnect;
- struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
- unsigned int ovnnb_seqno, ovn_seqno;
+ unsigned int ovnnb_seqno, ovnsb_seqno;
int res = EXIT_SUCCESS;
- struct northd_context ctx = {
- .ovnsb_txn = NULL,
- };
- bool ovnnb_changes_pending = false;
- bool ovn_changes_pending = false;
struct unixctl_server *unixctl;
int retval;
bool exiting;
sbrec_init();
/* We want to detect all changes to the ovn-nb db. */
- ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
- &nbrec_idl_class, true, true);
-
- ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
- &sbrec_idl_class, false, true);
-
- ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_logical_flow);
- add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_logical_datapath);
- add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_pipeline);
- add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_table_id);
- add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_priority);
- add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_match);
- add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_actions);
-
- ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_multicast_group);
- add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_datapath);
- add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_tunnel_key);
- add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_name);
- add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_ports);
-
- ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_datapath_binding);
- add_column_noalert(ovnsb_idl, &sbrec_datapath_binding_col_tunnel_key);
- add_column_noalert(ovnsb_idl, &sbrec_datapath_binding_col_external_ids);
-
- ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_port_binding);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_datapath);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_logical_port);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_tunnel_key);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_parent_port);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_tag);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_type);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_options);
- add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_mac);
- ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_chassis);
-
- /*
- * The loop here just runs the IDL in a loop waiting for the seqno to
- * change, which indicates that the contents of the db have changed.
- *
- * If the contents of the ovn-nb db change, the mappings to the ovn-sb
- * db must be recalculated.
- *
- * If the contents of the ovn-sb db change, it means the 'up' state of
- * a port may have changed, as that's the only type of change ovn-northd is
- * watching for.
- */
-
- ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
- ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
+ struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
+ ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
+
+ struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
+ ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));
+
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_logical_flow_col_logical_datapath);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions);
+
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_multicast_group_col_datapath);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_multicast_group_col_tunnel_key);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports);
+
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_datapath_binding_col_tunnel_key);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_datapath_binding_col_external_ids);
+
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_port_binding_col_logical_port);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_port_binding_col_tunnel_key);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_port_binding_col_parent_port);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac);
+ ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);
+
+ ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl_loop.idl);
+ ovnsb_seqno = ovsdb_idl_get_seqno(ovnsb_idl_loop.idl);
+
+ /* Main loop. */
exiting = false;
while (!exiting) {
- ovsdb_idl_run(ovnnb_idl);
- ovsdb_idl_run(ovnsb_idl);
- unixctl_server_run(unixctl);
-
- if (!ovsdb_idl_is_alive(ovnnb_idl)) {
- int retval = ovsdb_idl_get_last_error(ovnnb_idl);
- VLOG_ERR("%s: database connection failed (%s)",
- ovnnb_db, ovs_retval_to_string(retval));
- res = EXIT_FAILURE;
- break;
- }
-
- if (!ovsdb_idl_is_alive(ovnsb_idl)) {
- int retval = ovsdb_idl_get_last_error(ovnsb_idl);
- VLOG_ERR("%s: database connection failed (%s)",
- ovnsb_db, ovs_retval_to_string(retval));
- res = EXIT_FAILURE;
- break;
- }
-
- if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
- ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
- ovnnb_changes_pending = true;
- }
-
- if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
- ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
- ovn_changes_pending = true;
- }
+ struct northd_context ctx = {
+ .ovnnb_idl = ovnnb_idl_loop.idl,
+ .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
+ .ovnsb_idl = ovnsb_idl_loop.idl,
+ .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
+ };
- /*
- * If there are any pending changes, we delay recalculating the
- * necessary updates until after an existing transaction finishes.
- * This avoids the possibility of rapid updates causing ovn-northd to
- * never be able to successfully make the corresponding updates to the
- * other db. Instead, pending changes are batched up until the next
- * time we get a chance to calculate the new state and apply it.
- */
-
- if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
- /*
- * The OVN-nb db contents have changed, so create a transaction for
- * updating the OVN-sb DB.
- */
- ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
- ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
- "ovn-northd: northbound db changed");
- ovnnb_db_changed(&ctx);
- ovnnb_changes_pending = false;
+ if (ovnnb_seqno != ovsdb_idl_get_seqno(ctx.ovnnb_idl)) {
+ ovnnb_seqno = ovsdb_idl_get_seqno(ctx.ovnnb_idl);
+ ovnnb_db_run(&ctx);
}
-
- if (ovn_changes_pending && !ctx.ovnnb_txn) {
- /*
- * The OVN-sb db contents have changed, so create a transaction for
- * updating the northbound DB.
- */
- ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
- ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
- "ovn-northd: southbound db changed");
- ovnsb_db_changed(&ctx);
- ovn_changes_pending = false;
+ if (ovnsb_seqno != ovsdb_idl_get_seqno(ctx.ovnsb_idl)) {
+ ovnsb_seqno = ovsdb_idl_get_seqno(ctx.ovnsb_idl);
+ ovnsb_db_run(&ctx);
}
- if (ctx.ovnnb_txn) {
- enum ovsdb_idl_txn_status txn_status;
- txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
- switch (txn_status) {
- case TXN_UNCOMMITTED:
- case TXN_INCOMPLETE:
- /* Come back around and try to commit this transaction again */
- break;
- case TXN_ABORTED:
- case TXN_TRY_AGAIN:
- case TXN_NOT_LOCKED:
- case TXN_ERROR:
- /* Something went wrong, so try creating a new transaction. */
- ovn_changes_pending = true;
- case TXN_UNCHANGED:
- case TXN_SUCCESS:
- ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
- ctx.ovnnb_txn = NULL;
- }
- }
-
- if (ctx.ovnsb_txn) {
- enum ovsdb_idl_txn_status txn_status;
- txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
- switch (txn_status) {
- case TXN_UNCOMMITTED:
- case TXN_INCOMPLETE:
- /* Come back around and try to commit this transaction again */
- break;
- case TXN_ABORTED:
- case TXN_TRY_AGAIN:
- case TXN_NOT_LOCKED:
- case TXN_ERROR:
- /* Something went wrong, so try creating a new transaction. */
- ovnnb_changes_pending = true;
- case TXN_UNCHANGED:
- case TXN_SUCCESS:
- ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
- ctx.ovnsb_txn = NULL;
- }
+ unixctl_server_run(unixctl);
+ unixctl_server_wait(unixctl);
+ if (exiting) {
+ poll_immediate_wake();
}
+ ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
+ ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
- if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
- ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
- ovsdb_idl_wait(ovnnb_idl);
- ovsdb_idl_wait(ovnsb_idl);
- if (ctx.ovnnb_txn) {
- ovsdb_idl_txn_wait(ctx.ovnnb_txn);
- }
- if (ctx.ovnsb_txn) {
- ovsdb_idl_txn_wait(ctx.ovnsb_txn);
- }
- unixctl_server_wait(unixctl);
- if (exiting) {
- poll_immediate_wake();
- }
- poll_block();
- }
+ poll_block();
if (should_service_stop()) {
exiting = true;
}
}
unixctl_server_destroy(unixctl);
- ovsdb_idl_destroy(ovnsb_idl);
- ovsdb_idl_destroy(ovnnb_idl);
+ ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
+ ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
service_stop();
free(default_db_);
-
exit(res);
}