This won't really get used until the next commit. Signed-off-by: Ben Pfaff <b...@nicira.com> --- ovn/controller/ofctrl.c | 470 ++++++++++++++++++++++++++++------------ ovn/controller/ofctrl.h | 5 +- ovn/controller/ovn-controller.c | 6 +- ovn/controller/physical.h | 7 + 4 files changed, 348 insertions(+), 140 deletions(-)
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c index b1c421c..d8a0573 100644 --- a/ovn/controller/ofctrl.c +++ b/ovn/controller/ofctrl.c @@ -27,9 +27,10 @@ #include "openflow/openflow.h" #include "openvswitch/vlog.h" #include "ovn-controller.h" -#include "vswitch-idl.h" +#include "physical.h" #include "rconn.h" #include "socket-util.h" +#include "vswitch-idl.h" VLOG_DEFINE_THIS_MODULE(ofctrl); @@ -53,6 +54,9 @@ static char *ovn_flow_to_string(const struct ovn_flow *); static void ovn_flow_log(const struct ovn_flow *, const char *action); static void ovn_flow_destroy(struct ovn_flow *); +static ovs_be32 queue_msg(struct ofpbuf *); +static void queue_flow_mod(struct ofputil_flow_mod *); + /* OpenFlow connection to the switch. */ static struct rconn *swconn; @@ -60,6 +64,25 @@ static struct rconn *swconn; * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ static unsigned int seqno; +/* Connection state machine. */ +#define STATES \ + STATE(S_NEW) \ + STATE(S_GENEVE_TABLE_REQUESTED) \ + STATE(S_GENEVE_TABLE_MOD_SENT) \ + STATE(S_CLEAR_FLOWS) \ + STATE(S_UPDATE_FLOWS) +enum ofctrl_state { +#define STATE(NAME) NAME, + STATES +#undef STATE +}; + +/* Current state. */ +static enum ofctrl_state state; + +/* Transaction IDs for messages in flight to the switch. */ +static ovs_be32 xid, xid2; + /* Counter for in-flight OpenFlow messages on 'swconn'. We only send a new * round of flow table modifications to the switch when the counter falls to * zero, to avoid unbounded buffering. */ @@ -69,11 +92,15 @@ static struct rconn_packet_counter *tx_counter; * installed in the switch. */ static struct hmap installed_flows; +/* MFF_* field ID for our Geneve option. In S_GENEVE_TABLE_MOD_SENT, this is + * the option we requested (we don't know whether we obtained it yet). In + * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */ +static enum mf_field_id mff_ovn_geneve; + static void ovn_flow_table_clear(struct hmap *flow_table); static void ovn_flow_table_destroy(struct hmap *flow_table); -static void ofctrl_update_flows(struct hmap *desired_flows); -static void ofctrl_recv(const struct ofpbuf *msg); +static void ofctrl_recv(const struct ofp_header *, enum ofptype); void ofctrl_init(void) @@ -82,15 +109,244 @@ ofctrl_init(void) tx_counter = rconn_packet_counter_create(); hmap_init(&installed_flows); } + +/* S_NEW, for a new connection. + * + * Sends NXT_GENEVE_TABLE_REQUEST and transitions to + * S_GENEVE_TABLE_REQUESTED. */ -/* Attempts to update the OpenFlow flows in bridge 'br_int' to those in - * 'flow_table'. Removes all of the flows from 'flow_table' and frees them. +static void +run_S_NEW(void) +{ + struct ofpbuf *buf = ofpraw_alloc(OFPRAW_NXT_GENEVE_TABLE_REQUEST, + rconn_get_version(swconn), 0); + xid = queue_msg(buf); + state = S_GENEVE_TABLE_REQUESTED; +} + +static void +recv_S_NEW(const struct ofp_header *oh OVS_UNUSED, + enum ofptype type OVS_UNUSED) +{ + OVS_NOT_REACHED(); +} + +/* S_GENEVE_TABLE_REQUESTED, when NXT_GENEVE_TABLE_REQUEST has been sent + * and we're waiting for a reply. * - * The flow table will only be updated if we've got an OpenFlow connection to - * 'br_int' and it's not backlogged. Otherwise, it'll have to wait until the - * next iteration. */ -void -ofctrl_run(const struct ovsrec_bridge *br_int, struct hmap *flow_table) + * If we receive an NXT_GENEVE_TABLE_REPLY: + * + * - If it contains our tunnel metadata option, assign its field ID to + * mff_ovn_geneve and transition to S_CLEAR_FLOWS. + * + * - Otherwise, if there is an unused tunnel metadata field ID, send + * NXT_GENEVE_TABLE_MOD and OFPT_BARRIER_REQUEST, and transition to + * S_GENEVE_TABLE_MOD_SENT. + * + * - Otherwise, log an error, disable Geneve, and transition to + * S_CLEAR_FLOWS. + * + * If we receive an OFPT_ERROR: + * + * - Log an error, disable Geneve, and transition to S_CLEAR_FLOWS. */ + +static void +run_S_GENEVE_TABLE_REQUESTED(void) +{ +} + +static void +recv_S_GENEVE_TABLE_REQUESTED(const struct ofp_header *oh, enum ofptype type) +{ + if (oh->xid != xid) { + ofctrl_recv(oh, type); + } else if (type == OFPTYPE_NXT_GENEVE_TABLE_REPLY) { + struct ofputil_geneve_table_reply reply; + enum ofperr error = ofputil_decode_geneve_table_reply(oh, &reply); + if (error) { + VLOG_ERR("failed to decode Geneve table request (%s)", + ofperr_to_string(error)); + goto error; + } + + const struct ofputil_geneve_map *map; + uint64_t md_free = UINT64_MAX; + BUILD_ASSERT(TUN_METADATA_NUM_OPTS == 64); + + LIST_FOR_EACH (map, list_node, &reply.mappings) { + if (map->option_class == OVN_GENEVE_CLASS + && map->option_type == OVN_GENEVE_TYPE + && map->option_len == OVN_GENEVE_LEN) { + if (map->index >= TUN_METADATA_NUM_OPTS) { + VLOG_ERR("desired Geneve tunnel option 0x%"PRIx16"," + "%"PRIu8",%"PRIu8" already in use with " + "unsupported index %"PRIu16, + map->option_class, map->option_type, + map->option_len, map->index); + goto error; + } else { + mff_ovn_geneve = MFF_TUN_METADATA0 + map->index; + state = S_CLEAR_FLOWS; + return; + } + } + + if (map->index < TUN_METADATA_NUM_OPTS) { + md_free &= ~(UINT64_C(1) << map->index); + } + } + + VLOG_DBG("OVN Geneve option not found"); + if (!md_free) { + VLOG_ERR("no Geneve options free for use by OVN"); + goto error; + } + + unsigned int index = rightmost_1bit_idx(md_free); + mff_ovn_geneve = MFF_TUN_METADATA0 + index; + struct ofputil_geneve_map gm; + gm.option_class = OVN_GENEVE_CLASS; + gm.option_type = OVN_GENEVE_TYPE; + gm.option_len = OVN_GENEVE_LEN; + gm.index = index; + + struct ofputil_geneve_table_mod gtm; + gtm.command = NXGTMC_ADD; + list_init(>m.mappings); + list_push_back(>m.mappings, &gm.list_node); + + xid = queue_msg(ofputil_encode_geneve_table_mod(OFP13_VERSION, >m)); + xid2 = queue_msg(ofputil_encode_barrier_request(OFP13_VERSION)); + state = S_GENEVE_TABLE_MOD_SENT; + } else if (type == OFPTYPE_ERROR) { + VLOG_ERR("switch refused to allocate Geneve option (%s)", + ofperr_to_string(ofperr_decode_msg(oh, NULL))); + goto error; + } else { + char *s = ofp_to_string(oh, ntohs(oh->length), 1); + VLOG_ERR("unexpected reply to Geneve table request (%s)", + s); + free(s); + goto error; + } + return; + +error: + mff_ovn_geneve = 0; + state = S_CLEAR_FLOWS; +} + +/* S_GENEVE_TABLE_MOD_SENT, when NXT_GENEVE_TABLE_MOD and OFPT_BARRIER_REQUEST + * have been sent and we're waiting for a reply to one or the other. + * + * If we receive an OFPT_ERROR: + * + * - If the error is NXGTMFC_ALREADY_MAPPED or NXGTMFC_DUP_ENTRY, we + * raced with some other controller. Transition to S_NEW. + * + * - Otherwise, log an error, disable Geneve, and transition to + * S_CLEAR_FLOWS. + * + * If we receive OFPT_BARRIER_REPLY: + * + * - Set the tunnel metadata field ID to the one that we requested. + * Transition to S_CLEAR_FLOWS. + */ + +static void +run_S_GENEVE_TABLE_MOD_SENT(void) +{ +} + +static void +recv_S_GENEVE_TABLE_MOD_SENT(const struct ofp_header *oh, enum ofptype type) +{ + if (oh->xid != xid && oh->xid != xid2) { + ofctrl_recv(oh, type); + } else if (oh->xid == xid2 && type == OFPTYPE_BARRIER_REPLY) { + state = S_CLEAR_FLOWS; + } else if (oh->xid == xid && type == OFPTYPE_ERROR) { + enum ofperr error = ofperr_decode_msg(oh, NULL); + if (error == OFPERR_NXGTMFC_ALREADY_MAPPED || + error == OFPERR_NXGTMFC_DUP_ENTRY) { + VLOG_INFO("raced with another controller adding " + "Geneve option (%s); trying again", + ofperr_to_string(error)); + state = S_NEW; + } else { + VLOG_ERR("error adding Geneve option (%s)", + ofperr_to_string(error)); + goto error; + } + } else { + char *s = ofp_to_string(oh, ntohs(oh->length), 1); + VLOG_ERR("unexpected reply to Geneve option allocation request (%s)", + s); + free(s); + goto error; + } + return; + +error: + state = S_CLEAR_FLOWS; +} + +/* S_CLEAR_FLOWS, after we've established a Geneve metadata field ID and it's + * time to set up some flows. + * + * Sends an OFPT_TABLE_MOD to clear all flows, then transitions to + * S_UPDATE_FLOWS. */ + +static void +run_S_CLEAR_FLOWS(void) +{ + /* Send a flow_mod to delete all flows. */ + struct ofputil_flow_mod fm = { + .match = MATCH_CATCHALL_INITIALIZER, + .table_id = OFPTT_ALL, + .command = OFPFC_DELETE, + }; + queue_flow_mod(&fm); + VLOG_DBG("clearing all flows"); + + /* Clear installed_flows, to match the state of the switch. */ + ovn_flow_table_clear(&installed_flows); + + state = S_UPDATE_FLOWS; +} + +static void +recv_S_CLEAR_FLOWS(const struct ofp_header *oh, enum ofptype type) +{ + ofctrl_recv(oh, type); +} + +/* S_UPDATE_FLOWS, for maintaining the flow table over time. + * + * Compare the installed flows to the ones we want. Send OFPT_FLOW_MOD as + * necessary. + * + * This is a terminal state. We only transition out of it if the connection + * drops. */ + +static void +run_S_UPDATE_FLOWS(void) +{ +} + + +static void +recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type) +{ + ofctrl_recv(oh, type); +} + +/* Runs the OpenFlow state machine against 'br_int', which is local to the + * hypervisor on which we are running. Attempts to negotiate a Geneve option + * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE. If successful, + * returns the MFF_* field ID for the option, otherwise returns 0. */ +enum mf_field_id +ofctrl_run(const struct ovsrec_bridge *br_int) { if (br_int) { char *target; @@ -107,24 +363,56 @@ ofctrl_run(const struct ovsrec_bridge *br_int, struct hmap *flow_table) rconn_run(swconn); if (!rconn_is_connected(swconn)) { - goto exit; + return 0; } - if (!rconn_packet_counter_n_packets(tx_counter)) { - ofctrl_update_flows(flow_table); + if (seqno != rconn_get_connection_seqno(swconn)) { + seqno = rconn_get_connection_seqno(swconn); + state = S_NEW; } - for (int i = 0; i < 50; i++) { + enum ofctrl_state old_state; + do { + old_state = state; + switch (state) { +#define STATE(NAME) case NAME: run_##NAME(); break; + STATES +#undef STATE + default: + OVS_NOT_REACHED(); + } + } while (state != old_state); + + for (int i = 0; state == old_state && i < 50; i++) { struct ofpbuf *msg = rconn_recv(swconn); if (!msg) { break; } - ofctrl_recv(msg); + const struct ofp_header *oh = msg->data; + enum ofptype type; + enum ofperr error; + + error = ofptype_decode(&type, oh); + if (!error) { + switch (state) { +#define STATE(NAME) case NAME: recv_##NAME(oh, type); break; + STATES +#undef STATE + default: + OVS_NOT_REACHED(); + } + } else { + char *s = ofp_to_string(oh, ntohs(oh->length), 1); + VLOG_WARN("could not decode OpenFlow message (%s): %s", + ofperr_to_string(error), s); + free(s); + } + ofpbuf_delete(msg); } -exit: - ovn_flow_table_clear(flow_table); + return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS + ? mff_ovn_geneve : 0); } void @@ -142,109 +430,28 @@ ofctrl_destroy(void) rconn_packet_counter_destroy(tx_counter); } -static void +static ovs_be32 queue_msg(struct ofpbuf *msg) { + const struct ofp_header *oh = msg->data; + ovs_be32 xid = oh->xid; rconn_send(swconn, msg, tx_counter); + return xid; } static void -ofctrl_recv(const struct ofpbuf *msg) +ofctrl_recv(const struct ofp_header *oh, enum ofptype type) { - enum ofptype type; - struct ofpbuf b; - - b = *msg; - if (ofptype_pull(&type, &b)) { - return; - } - - switch (type) { - case OFPTYPE_ECHO_REQUEST: - queue_msg(make_echo_reply(msg->data)); - break; - - case OFPTYPE_ECHO_REPLY: - case OFPTYPE_PACKET_IN: - case OFPTYPE_PORT_STATUS: - case OFPTYPE_FLOW_REMOVED: - /* Nothing to do. */ - break; - - case OFPTYPE_HELLO: - case OFPTYPE_ERROR: - case OFPTYPE_FEATURES_REQUEST: - case OFPTYPE_FEATURES_REPLY: - case OFPTYPE_GET_CONFIG_REQUEST: - case OFPTYPE_GET_CONFIG_REPLY: - case OFPTYPE_SET_CONFIG: - case OFPTYPE_PACKET_OUT: - case OFPTYPE_FLOW_MOD: - case OFPTYPE_GROUP_MOD: - case OFPTYPE_PORT_MOD: - case OFPTYPE_TABLE_MOD: - case OFPTYPE_BARRIER_REQUEST: - case OFPTYPE_BARRIER_REPLY: - case OFPTYPE_QUEUE_GET_CONFIG_REQUEST: - case OFPTYPE_QUEUE_GET_CONFIG_REPLY: - case OFPTYPE_DESC_STATS_REQUEST: - case OFPTYPE_DESC_STATS_REPLY: - case OFPTYPE_FLOW_STATS_REQUEST: - case OFPTYPE_FLOW_STATS_REPLY: - case OFPTYPE_AGGREGATE_STATS_REQUEST: - case OFPTYPE_AGGREGATE_STATS_REPLY: - case OFPTYPE_TABLE_STATS_REQUEST: - case OFPTYPE_TABLE_STATS_REPLY: - case OFPTYPE_PORT_STATS_REQUEST: - case OFPTYPE_PORT_STATS_REPLY: - case OFPTYPE_QUEUE_STATS_REQUEST: - case OFPTYPE_QUEUE_STATS_REPLY: - case OFPTYPE_PORT_DESC_STATS_REQUEST: - case OFPTYPE_PORT_DESC_STATS_REPLY: - case OFPTYPE_ROLE_REQUEST: - case OFPTYPE_ROLE_REPLY: - case OFPTYPE_ROLE_STATUS: - case OFPTYPE_SET_FLOW_FORMAT: - case OFPTYPE_FLOW_MOD_TABLE_ID: - case OFPTYPE_SET_PACKET_IN_FORMAT: - case OFPTYPE_FLOW_AGE: - case OFPTYPE_SET_CONTROLLER_ID: - case OFPTYPE_FLOW_MONITOR_STATS_REQUEST: - case OFPTYPE_FLOW_MONITOR_STATS_REPLY: - case OFPTYPE_FLOW_MONITOR_CANCEL: - case OFPTYPE_FLOW_MONITOR_PAUSED: - case OFPTYPE_FLOW_MONITOR_RESUMED: - case OFPTYPE_GET_ASYNC_REQUEST: - case OFPTYPE_GET_ASYNC_REPLY: - case OFPTYPE_SET_ASYNC_CONFIG: - case OFPTYPE_METER_MOD: - case OFPTYPE_GROUP_STATS_REQUEST: - case OFPTYPE_GROUP_STATS_REPLY: - case OFPTYPE_GROUP_DESC_STATS_REQUEST: - case OFPTYPE_GROUP_DESC_STATS_REPLY: - case OFPTYPE_GROUP_FEATURES_STATS_REQUEST: - case OFPTYPE_GROUP_FEATURES_STATS_REPLY: - case OFPTYPE_METER_STATS_REQUEST: - case OFPTYPE_METER_STATS_REPLY: - case OFPTYPE_METER_CONFIG_STATS_REQUEST: - case OFPTYPE_METER_CONFIG_STATS_REPLY: - case OFPTYPE_METER_FEATURES_STATS_REQUEST: - case OFPTYPE_METER_FEATURES_STATS_REPLY: - case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: - case OFPTYPE_TABLE_FEATURES_STATS_REPLY: - case OFPTYPE_TABLE_DESC_REQUEST: - case OFPTYPE_TABLE_DESC_REPLY: - case OFPTYPE_BUNDLE_CONTROL: - case OFPTYPE_BUNDLE_ADD_MESSAGE: - case OFPTYPE_NXT_GENEVE_TABLE_MOD: - case OFPTYPE_NXT_GENEVE_TABLE_REQUEST: - case OFPTYPE_NXT_GENEVE_TABLE_REPLY: - default: - /* Messages that are generally unexpected. */ + if (type == OFPTYPE_ECHO_REQUEST) { + queue_msg(make_echo_reply(oh)); + } else if (type != OFPTYPE_ECHO_REPLY && + type != OFPTYPE_PACKET_IN && + type != OFPTYPE_PORT_STATUS && + type != OFPTYPE_FLOW_REMOVED) { if (VLOG_IS_DBG_ENABLED()) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); - char *s = ofp_to_string(msg->data, msg->size, 2); + char *s = ofp_to_string(oh, ntohs(oh->length), 2); VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s); free(s); } @@ -272,6 +479,7 @@ ofctrl_add_flow(struct hmap *desired_flows, f->match = *match; f->ofpacts = xmemdup(actions->data, actions->size); f->ofpacts_len = actions->size; + f->hmap_node.hash = ovn_flow_hash(f); if (ovn_flow_lookup(desired_flows, f)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); @@ -285,7 +493,7 @@ ofctrl_add_flow(struct hmap *desired_flows, return; } - hmap_insert(desired_flows, &f->hmap_node, ovn_flow_hash(f)); + hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash); } /* ovn_flow. */ @@ -359,6 +567,7 @@ ovn_flow_table_clear(struct hmap *flow_table) ovn_flow_destroy(f); } } + static void ovn_flow_table_destroy(struct hmap *flow_table) { @@ -377,26 +586,13 @@ queue_flow_mod(struct ofputil_flow_mod *fm) queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM)); } -static void -ofctrl_update_flows(struct hmap *desired_flows) +void +ofctrl_put(struct hmap *flow_table) { - /* If we've (re)connected, don't make any assumptions about the flows in - * the switch: delete all of them. (We'll immediately repopulate it - * below.) */ - if (seqno != rconn_get_connection_seqno(swconn)) { - seqno = rconn_get_connection_seqno(swconn); - - /* Send a flow_mod to delete all flows. */ - struct ofputil_flow_mod fm = { - .match = MATCH_CATCHALL_INITIALIZER, - .table_id = OFPTT_ALL, - .command = OFPFC_DELETE, - }; - queue_flow_mod(&fm); - VLOG_DBG("clearing all flows"); - - /* Clear installed_flows, to match the state of the switch. */ - ovn_flow_table_clear(&installed_flows); + if (state != S_UPDATE_FLOWS + || rconn_packet_counter_n_packets(tx_counter)) { + ovn_flow_table_clear(flow_table); + return; } /* Iterate through all of the installed flows. If any of them are no @@ -404,7 +600,7 @@ ofctrl_update_flows(struct hmap *desired_flows) * actions, update them. */ struct ovn_flow *i, *next; HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) { - struct ovn_flow *d = ovn_flow_lookup(desired_flows, i); + struct ovn_flow *d = ovn_flow_lookup(flow_table, i); if (!d) { /* Installed flow is no longer desirable. Delete it from the * switch and from installed_flows. */ @@ -442,16 +638,16 @@ ofctrl_update_flows(struct hmap *desired_flows) d->ofpacts_len = 0; } - hmap_remove(desired_flows, &d->hmap_node); + hmap_remove(flow_table, &d->hmap_node); ovn_flow_destroy(d); } } - /* The previous loop removed from 'desired_flows' all of the flows that are - * already installed. Thus, any flows remaining in 'desired_flows' need to + /* The previous loop removed from 'flow_table' all of the flows that are + * already installed. Thus, any flows remaining in 'flow_table' need to * be added to the flow table. */ struct ovn_flow *d; - HMAP_FOR_EACH_SAFE (d, next, hmap_node, desired_flows) { + HMAP_FOR_EACH_SAFE (d, next, hmap_node, flow_table) { /* Send flow_mod to add flow. */ struct ofputil_flow_mod fm = { .match = d->match, @@ -464,8 +660,8 @@ ofctrl_update_flows(struct hmap *desired_flows) queue_flow_mod(&fm); ovn_flow_log(d, "adding"); - /* Move 'd' from 'desired_flows' to installed_flows. */ - hmap_remove(desired_flows, &d->hmap_node); + /* Move 'd' from 'flow_table' to installed_flows. */ + hmap_remove(flow_table, &d->hmap_node); hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash); } } diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h index fc07d51..b43527a 100644 --- a/ovn/controller/ofctrl.h +++ b/ovn/controller/ofctrl.h @@ -19,6 +19,8 @@ #include <stdint.h> +#include "meta-flow.h" + struct controller_ctx; struct hmap; struct match; @@ -27,7 +29,8 @@ struct ovsrec_bridge; /* Interface for OVN main loop. */ void ofctrl_init(void); -void ofctrl_run(const struct ovsrec_bridge *br_int, struct hmap *flow_table); +enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int); +void ofctrl_put(struct hmap *flows); void ofctrl_wait(void); void ofctrl_destroy(void); diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 9d20245..488dce7 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -265,12 +265,14 @@ main(int argc, char *argv[]) } if (br_int) { + enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int); + struct hmap flow_table = HMAP_INITIALIZER(&flow_table); rule_run(&ctx, &flow_table); - if (chassis_id) { physical_run(&ctx, br_int, chassis_id, &flow_table); + if (chassis_id && mff_ovn_geneve) { } - ofctrl_run(br_int, &flow_table); + ofctrl_put(&flow_table); hmap_destroy(&flow_table); } diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h index 9de76de..82baa2f 100644 --- a/ovn/controller/physical.h +++ b/ovn/controller/physical.h @@ -29,6 +29,13 @@ struct hmap; struct ovsdb_idl; struct ovsrec_bridge; +/* OVN Geneve option information. + * + * Keep these in sync with the documentation in ovn-architecture(7). */ +#define OVN_GENEVE_CLASS 0xffff +#define OVN_GENEVE_TYPE 0 +#define OVN_GENEVE_LEN 4 + void physical_register_ovs_idl(struct ovsdb_idl *); void physical_run(struct controller_ctx *, const struct ovsrec_bridge *br_int, const char *chassis_id, -- 2.1.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev