Skip to content

Commit

Permalink
ovn: Add tunnel_key concept to Bindings table, assign in ovn-northd.
Browse files Browse the repository at this point in the history
When packets travel among nodes in OVN over tunnels, a tunnel key value is
needed to convey the logical port to which the packet is destined.  This
commit adds a tunnel_key column to the Bindings table and adds code to
ovn-northd to assign a unique tunnel_key value to each logical port.

Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Justin Pettit <jpettit@nicira.com>
  • Loading branch information
blp committed Apr 30, 2015
1 parent 7d292b6 commit eb00399
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 22 deletions.
104 changes: 83 additions & 21 deletions ovn/northd/ovn-northd.c
Expand Up @@ -444,6 +444,45 @@ tags_equal(const struct sbrec_bindings *binding,
return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
}

struct binding_hash_node {
struct hmap_node lp_node; /* In 'lp_map', by binding->logical_port. */
struct hmap_node tk_node; /* In 'tk_map', by binding->tunnel_key. */
const struct sbrec_bindings *binding;
};

static bool
tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key)
{
const struct binding_hash_node *hash_node;

HMAP_FOR_EACH_IN_BUCKET (hash_node, tk_node, hash_int(tunnel_key, 0),
tk_hmap) {
if (hash_node->binding->tunnel_key == tunnel_key) {
return true;
}
}
return false;
}

/* Chooses and returns a positive tunnel key that is not already in use in
* 'tk_hmap'. Returns 0 if all tunnel keys are in use. */
static uint16_t
choose_tunnel_key(const struct hmap *tk_hmap)
{
static uint16_t prev;

for (uint16_t key = prev + 1; key != prev; key++) {
if (!tunnel_key_in_use(tk_hmap, key)) {
prev = key;
return key;
}
}

static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rl, "all tunnel keys exhausted");
return 0;
}

/*
* When a change has occurred in the OVN_Northbound database, we go through and
* make sure that the contents of the Bindings table in the OVN_Southbound
Expand All @@ -453,38 +492,41 @@ tags_equal(const struct sbrec_bindings *binding,
static void
set_bindings(struct northd_context *ctx)
{
struct hmap bindings_hmap;
const struct sbrec_bindings *binding;
const struct nbrec_logical_port *lport;

struct binding_hash_node {
struct hmap_node node;
const struct sbrec_bindings *binding;
} *hash_node, *hash_node_next;

/*
* We will need to look up a binding for every logical port. We don't want
* to have to do an O(n) search for every binding, so start out by hashing
* them on the logical port.
*
* As we go through every logical port, we will update the binding if it
* exists or create one otherwise. When the update is done, we'll remove it
* from the hashmap. At the end, any bindings left in the hashmap are for
* logical ports that have been deleted.
* exists or create one otherwise. When the update is done, we'll remove
* it from the hashmap. At the end, any bindings left in the hashmap are
* for logical ports that have been deleted.
*
* We index the logical_port column because that's the shared key between
* the OVN_NB and OVN_SB databases. We index the tunnel_key column to
* allow us to choose a unique tunnel key for any Binding rows we have to
* add.
*/
hmap_init(&bindings_hmap);
struct hmap lp_hmap = HMAP_INITIALIZER(&lp_hmap);
struct hmap tk_hmap = HMAP_INITIALIZER(&tk_hmap);

SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
hash_node = xzalloc(sizeof *hash_node);
struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
hash_node->binding = binding;
hmap_insert(&bindings_hmap, &hash_node->node,
hash_string(binding->logical_port, 0));
hmap_insert(&lp_hmap, &hash_node->lp_node,
hash_string(binding->logical_port, 0));
hmap_insert(&tk_hmap, &hash_node->tk_node,
hash_int(binding->tunnel_key, 0));
}

NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
struct binding_hash_node *hash_node;
binding = NULL;
HMAP_FOR_EACH_WITH_HASH(hash_node, node,
hash_string(lport->name, 0), &bindings_hmap) {
HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
hash_string(lport->name, 0), &lp_hmap) {
if (!strcmp(lport->name, hash_node->binding->logical_port)) {
binding = hash_node->binding;
break;
Expand All @@ -502,9 +544,7 @@ set_bindings(struct northd_context *ctx)
/* We found an existing binding for this logical port. Update its
* contents. */

hmap_remove(&bindings_hmap, &hash_node->node);
free(hash_node);
hash_node = NULL;
hmap_remove(&lp_hmap, &hash_node->lp_node);

if (!macs_equal(binding->mac, binding->n_mac,
lport->macs, lport->n_macs)) {
Expand All @@ -524,6 +564,11 @@ set_bindings(struct northd_context *ctx)
} else {
/* There is no binding for this logical port, so create one. */

uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
if (!tunnel_key) {
continue;
}

binding = sbrec_bindings_insert(ctx->ovnsb_txn);
sbrec_bindings_set_logical_port(binding, lport->name);
sbrec_bindings_set_mac(binding,
Expand All @@ -533,16 +578,32 @@ set_bindings(struct northd_context *ctx)
sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
}

sbrec_bindings_set_tunnel_key(binding, tunnel_key);
sbrec_bindings_set_logical_datapath(binding, logical_datapath);

/* Add the tunnel key to the tk_hmap so that we don't try to use it
* for another port. (We don't want it in the lp_hmap because that
* would just get the Bindings record deleted later.) */
struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
hash_node->binding = binding;
hmap_insert(&tk_hmap, &hash_node->tk_node,
hash_int(binding->tunnel_key, 0));
}
}

HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
hmap_remove(&bindings_hmap, &hash_node->node);
struct binding_hash_node *hash_node;
HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) {
hmap_remove(&lp_hmap, &hash_node->lp_node);
sbrec_bindings_delete(hash_node->binding);
}
hmap_destroy(&lp_hmap);

struct binding_hash_node *hash_node_next;
HMAP_FOR_EACH_SAFE (hash_node, hash_node_next, tk_node, &tk_hmap) {
hmap_remove(&tk_hmap, &hash_node->tk_node);
free(hash_node);
}
hmap_destroy(&bindings_hmap);
hmap_destroy(&tk_hmap);
}

static void
Expand Down Expand Up @@ -733,6 +794,7 @@ main(int argc, char *argv[])
ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag);
ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port);
ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_datapath);
ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tunnel_key);
ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
Expand Down
6 changes: 5 additions & 1 deletion ovn/ovn-sb.ovsschema
Expand Up @@ -47,6 +47,10 @@
"columns": {
"logical_datapath": {"type": "uuid"},
"logical_port": {"type": "string"},
"tunnel_key": {
"type": {"key": {"type": "integer",
"minInteger": 1,
"maxInteger": 65535}}},
"parent_port": {"type": {"key": "string", "min": 0, "max": 1}},
"tag": {
"type": {"key": {"type": "integer",
Expand All @@ -57,6 +61,6 @@
"mac": {"type": {"key": "string",
"min": 0,
"max": "unlimited"}}},
"indexes": [["logical_port"]],
"indexes": [["logical_port"], ["tunnel_key"]],
"isRoot": true}},
"version": "1.0.0"}
14 changes: 14 additions & 0 deletions ovn/ovn-sb.xml
Expand Up @@ -665,6 +665,20 @@
prescribe a particular format for the logical port ID.
</column>

<column name="tunnel_key">
<p>
A number that represents the logical port in the key (e.g. VXLAN VNI or
STT key) field carried within tunnel protocol packets. (This avoids
wasting space for a whole UUID in tunneled packets. It also allows OVN
to support encapsulations that cannot fit an entire UUID in their
tunnel keys.)
</p>

<p>
Tunnel ID 0 is reserved for internal use within OVN.
</p>
</column>

<column name="parent_port">
For containers created inside a VM, this is taken from
<ref table="Logical_Port" column="parent_name" db="OVN_Northbound"/>
Expand Down

0 comments on commit eb00399

Please sign in to comment.