mirror of
https://github.com/openvswitch/ovs
synced 2025-10-25 15:07:05 +00:00
ofctrl: Negotiate OVN Geneve option.
This won't really get used until the next commit. Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Justin Pettit <jpettit@nicira.com>
This commit is contained in:
@@ -27,9 +27,10 @@
|
|||||||
#include "openflow/openflow.h"
|
#include "openflow/openflow.h"
|
||||||
#include "openvswitch/vlog.h"
|
#include "openvswitch/vlog.h"
|
||||||
#include "ovn-controller.h"
|
#include "ovn-controller.h"
|
||||||
#include "vswitch-idl.h"
|
#include "physical.h"
|
||||||
#include "rconn.h"
|
#include "rconn.h"
|
||||||
#include "socket-util.h"
|
#include "socket-util.h"
|
||||||
|
#include "vswitch-idl.h"
|
||||||
|
|
||||||
VLOG_DEFINE_THIS_MODULE(ofctrl);
|
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_log(const struct ovn_flow *, const char *action);
|
||||||
static void ovn_flow_destroy(struct ovn_flow *);
|
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. */
|
/* OpenFlow connection to the switch. */
|
||||||
static struct rconn *swconn;
|
static struct rconn *swconn;
|
||||||
|
|
||||||
@@ -60,6 +64,25 @@ static struct rconn *swconn;
|
|||||||
* rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
|
* rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
|
||||||
static unsigned int seqno;
|
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
|
/* 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
|
* round of flow table modifications to the switch when the counter falls to
|
||||||
* zero, to avoid unbounded buffering. */
|
* zero, to avoid unbounded buffering. */
|
||||||
@@ -69,11 +92,15 @@ static struct rconn_packet_counter *tx_counter;
|
|||||||
* installed in the switch. */
|
* installed in the switch. */
|
||||||
static struct hmap installed_flows;
|
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_clear(struct hmap *flow_table);
|
||||||
static void ovn_flow_table_destroy(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 ofp_header *, enum ofptype);
|
||||||
static void ofctrl_recv(const struct ofpbuf *msg);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ofctrl_init(void)
|
ofctrl_init(void)
|
||||||
@@ -82,15 +109,246 @@ ofctrl_init(void)
|
|||||||
tx_counter = rconn_packet_counter_create();
|
tx_counter = rconn_packet_counter_create();
|
||||||
hmap_init(&installed_flows);
|
hmap_init(&installed_flows);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attempts to update the OpenFlow flows in bridge 'br_int' to those in
|
/* S_NEW, for a new connection.
|
||||||
* 'flow_table'. Removes all of the flows from 'flow_table' and frees them.
|
|
||||||
*
|
*
|
||||||
* The flow table will only be updated if we've got an OpenFlow connection to
|
* Sends NXT_GENEVE_TABLE_REQUEST and transitions to
|
||||||
* 'br_int' and it's not backlogged. Otherwise, it'll have to wait until the
|
* S_GENEVE_TABLE_REQUESTED. */
|
||||||
* next iteration. */
|
|
||||||
void
|
static void
|
||||||
ofctrl_run(const struct ovsrec_bridge *br_int, struct hmap *flow_table)
|
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.
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
/* Nothing to do here.
|
||||||
|
*
|
||||||
|
* Being in this state enables ofctrl_put() to work, however. */
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
if (br_int) {
|
||||||
char *target;
|
char *target;
|
||||||
@@ -107,24 +365,56 @@ ofctrl_run(const struct ovsrec_bridge *br_int, struct hmap *flow_table)
|
|||||||
rconn_run(swconn);
|
rconn_run(swconn);
|
||||||
|
|
||||||
if (!rconn_is_connected(swconn)) {
|
if (!rconn_is_connected(swconn)) {
|
||||||
goto exit;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!rconn_packet_counter_n_packets(tx_counter)) {
|
if (seqno != rconn_get_connection_seqno(swconn)) {
|
||||||
ofctrl_update_flows(flow_table);
|
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);
|
struct ofpbuf *msg = rconn_recv(swconn);
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
break;
|
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);
|
ofpbuf_delete(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS
|
||||||
ovn_flow_table_clear(flow_table);
|
? mff_ovn_geneve : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -142,109 +432,29 @@ ofctrl_destroy(void)
|
|||||||
rconn_packet_counter_destroy(tx_counter);
|
rconn_packet_counter_destroy(tx_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static ovs_be32
|
||||||
queue_msg(struct ofpbuf *msg)
|
queue_msg(struct ofpbuf *msg)
|
||||||
{
|
{
|
||||||
|
const struct ofp_header *oh = msg->data;
|
||||||
|
ovs_be32 xid = oh->xid;
|
||||||
rconn_send(swconn, msg, tx_counter);
|
rconn_send(swconn, msg, tx_counter);
|
||||||
|
return xid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ofctrl_recv(const struct ofpbuf *msg)
|
ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
|
||||||
{
|
{
|
||||||
enum ofptype type;
|
if (type == OFPTYPE_ECHO_REQUEST) {
|
||||||
struct ofpbuf b;
|
queue_msg(make_echo_reply(oh));
|
||||||
|
} else if (type != OFPTYPE_ECHO_REPLY &&
|
||||||
b = *msg;
|
type != OFPTYPE_BARRIER_REPLY &&
|
||||||
if (ofptype_pull(&type, &b)) {
|
type != OFPTYPE_PACKET_IN &&
|
||||||
return;
|
type != OFPTYPE_PORT_STATUS &&
|
||||||
}
|
type != OFPTYPE_FLOW_REMOVED) {
|
||||||
|
|
||||||
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 (VLOG_IS_DBG_ENABLED()) {
|
if (VLOG_IS_DBG_ENABLED()) {
|
||||||
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
|
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);
|
VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
@@ -360,6 +570,7 @@ ovn_flow_table_clear(struct hmap *flow_table)
|
|||||||
ovn_flow_destroy(f);
|
ovn_flow_destroy(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ovn_flow_table_destroy(struct hmap *flow_table)
|
ovn_flow_table_destroy(struct hmap *flow_table)
|
||||||
{
|
{
|
||||||
@@ -378,26 +589,26 @@ queue_flow_mod(struct ofputil_flow_mod *fm)
|
|||||||
queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM));
|
queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
/* Replaces the flow table on the switch, if possible, by the flows in
|
||||||
ofctrl_update_flows(struct hmap *desired_flows)
|
* 'flow_table', which should have been added with ofctrl_add_flow().
|
||||||
|
* Regardless of whether the flow table is updated, this deletes all of the
|
||||||
|
* flows from 'flow_table' and frees them. (The hmap itself isn't
|
||||||
|
* destroyed.)
|
||||||
|
*
|
||||||
|
* This called be called be ofctrl_run() within the main loop. */
|
||||||
|
void
|
||||||
|
ofctrl_put(struct hmap *flow_table)
|
||||||
{
|
{
|
||||||
/* If we've (re)connected, don't make any assumptions about the flows in
|
/* The flow table can be updated if the connection to the switch is up and
|
||||||
* the switch: delete all of them. (We'll immediately repopulate it
|
* in the correct state and not backlogged with existing flow_mods. (Our
|
||||||
* below.) */
|
* criteria for being backlogged appear very conservative, but the socket
|
||||||
if (seqno != rconn_get_connection_seqno(swconn)) {
|
* between ovn-controller and OVS provides some buffering.) Otherwise,
|
||||||
seqno = rconn_get_connection_seqno(swconn);
|
* discard the flows. A solution to either of those problems will cause us
|
||||||
|
* to wake up and retry. */
|
||||||
/* Send a flow_mod to delete all flows. */
|
if (state != S_UPDATE_FLOWS
|
||||||
struct ofputil_flow_mod fm = {
|
|| rconn_packet_counter_n_packets(tx_counter)) {
|
||||||
.match = MATCH_CATCHALL_INITIALIZER,
|
ovn_flow_table_clear(flow_table);
|
||||||
.table_id = OFPTT_ALL,
|
return;
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iterate through all of the installed flows. If any of them are no
|
/* Iterate through all of the installed flows. If any of them are no
|
||||||
@@ -405,7 +616,7 @@ ofctrl_update_flows(struct hmap *desired_flows)
|
|||||||
* actions, update them. */
|
* actions, update them. */
|
||||||
struct ovn_flow *i, *next;
|
struct ovn_flow *i, *next;
|
||||||
HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) {
|
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) {
|
if (!d) {
|
||||||
/* Installed flow is no longer desirable. Delete it from the
|
/* Installed flow is no longer desirable. Delete it from the
|
||||||
* switch and from installed_flows. */
|
* switch and from installed_flows. */
|
||||||
@@ -443,16 +654,16 @@ ofctrl_update_flows(struct hmap *desired_flows)
|
|||||||
d->ofpacts_len = 0;
|
d->ofpacts_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
hmap_remove(desired_flows, &d->hmap_node);
|
hmap_remove(flow_table, &d->hmap_node);
|
||||||
ovn_flow_destroy(d);
|
ovn_flow_destroy(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The previous loop removed from 'desired_flows' all of the flows that are
|
/* The previous loop removed from 'flow_table' all of the flows that are
|
||||||
* already installed. Thus, any flows remaining in 'desired_flows' need to
|
* already installed. Thus, any flows remaining in 'flow_table' need to
|
||||||
* be added to the flow table. */
|
* be added to the flow table. */
|
||||||
struct ovn_flow *d;
|
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. */
|
/* Send flow_mod to add flow. */
|
||||||
struct ofputil_flow_mod fm = {
|
struct ofputil_flow_mod fm = {
|
||||||
.match = d->match,
|
.match = d->match,
|
||||||
@@ -465,8 +676,8 @@ ofctrl_update_flows(struct hmap *desired_flows)
|
|||||||
queue_flow_mod(&fm);
|
queue_flow_mod(&fm);
|
||||||
ovn_flow_log(d, "adding");
|
ovn_flow_log(d, "adding");
|
||||||
|
|
||||||
/* Move 'd' from 'desired_flows' to installed_flows. */
|
/* Move 'd' from 'flow_table' to installed_flows. */
|
||||||
hmap_remove(desired_flows, &d->hmap_node);
|
hmap_remove(flow_table, &d->hmap_node);
|
||||||
hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash);
|
hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "meta-flow.h"
|
||||||
|
|
||||||
struct controller_ctx;
|
struct controller_ctx;
|
||||||
struct hmap;
|
struct hmap;
|
||||||
struct match;
|
struct match;
|
||||||
@@ -27,12 +29,12 @@ struct ovsrec_bridge;
|
|||||||
|
|
||||||
/* Interface for OVN main loop. */
|
/* Interface for OVN main loop. */
|
||||||
void ofctrl_init(void);
|
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_wait(void);
|
||||||
void ofctrl_destroy(void);
|
void ofctrl_destroy(void);
|
||||||
|
|
||||||
/* Flow table interface to the rest of ovn-controller. */
|
/* Flow table interface to the rest of ovn-controller. */
|
||||||
void ofctrl_clear_flows(void);
|
|
||||||
void ofctrl_add_flow(struct hmap *flows, uint8_t table_id, uint16_t priority,
|
void ofctrl_add_flow(struct hmap *flows, uint8_t table_id, uint16_t priority,
|
||||||
const struct match *, const struct ofpbuf *ofpacts);
|
const struct match *, const struct ofpbuf *ofpacts);
|
||||||
|
|
||||||
|
|||||||
@@ -279,12 +279,14 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (br_int) {
|
if (br_int) {
|
||||||
|
ofctrl_run(br_int);
|
||||||
|
|
||||||
struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
|
struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
|
||||||
lflow_run(&ctx, &flow_table);
|
lflow_run(&ctx, &flow_table);
|
||||||
if (chassis_id) {
|
if (chassis_id) {
|
||||||
physical_run(&ctx, br_int, chassis_id, &flow_table);
|
physical_run(&ctx, br_int, chassis_id, &flow_table);
|
||||||
}
|
}
|
||||||
ofctrl_run(br_int, &flow_table);
|
ofctrl_put(&flow_table);
|
||||||
hmap_destroy(&flow_table);
|
hmap_destroy(&flow_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ struct hmap;
|
|||||||
struct ovsdb_idl;
|
struct ovsdb_idl;
|
||||||
struct ovsrec_bridge;
|
struct ovsrec_bridge;
|
||||||
|
|
||||||
|
/* OVN Geneve option information.
|
||||||
|
*
|
||||||
|
* These are placeholders until OVS is assigned a Geneve option class.
|
||||||
|
*
|
||||||
|
* Keep these in sync with the documentation in ovn-architecture(7). */
|
||||||
|
#define OVN_GENEVE_CLASS 0xffff /* Geneve experimental class. */
|
||||||
|
#define OVN_GENEVE_TYPE 0
|
||||||
|
#define OVN_GENEVE_LEN 4
|
||||||
|
|
||||||
void physical_register_ovs_idl(struct ovsdb_idl *);
|
void physical_register_ovs_idl(struct ovsdb_idl *);
|
||||||
void physical_run(struct controller_ctx *, const struct ovsrec_bridge *br_int,
|
void physical_run(struct controller_ctx *, const struct ovsrec_bridge *br_int,
|
||||||
const char *chassis_id, struct hmap *flow_table);
|
const char *chassis_id, struct hmap *flow_table);
|
||||||
|
|||||||
Reference in New Issue
Block a user