@@ -157,6 +157,14 @@ struct ofp14_table_desc {
};
OFP_ASSERT(sizeof(struct ofp14_table_desc) == 8);
+/* A table config has changed in the datapath */
+struct ofp14_table_status {
+ uint8_t reason; /* One of OFPTR_*. */
+ uint8_t pad[7]; /* Pad to 64 bits */
+ /* Followed by struct ofp14_table_desc */
+};
+OFP_ASSERT(sizeof(struct ofp14_table_status) == 8);
+
/* ## ---------------- ## */
/* ## ofp14_port_stats ## */
/* ## ---------------- ## */
@@ -249,6 +249,9 @@ enum ofpraw {
/* OFPT 1.4+ (30): struct ofp14_role_status, uint8_t[8][]. */
OFPRAW_OFPT14_ROLE_STATUS,
+ /* OFPT 1.4+ (31): struct ofp14_table_status, uint8_t[8][]. */
+ OFPRAW_OFPT14_TABLE_STATUS,
+
/* OFPT 1.4+ (32): struct ofp14_requestforward, uint8_t[8][]. */
OFPRAW_OFPT14_REQUESTFORWARD,
@@ -575,6 +578,9 @@ enum ofptype {
/* Request forwarding by the switch. */
OFPTYPE_REQUESTFORWARD, /* OFPRAW_OFPT14_REQUESTFORWARD. */
+ /* Asynchronous messages. */
+ OFPTYPE_TABLE_STATUS, /* OFPRAW_OFPT14_TABLE_STATUS. */
+
OFPTYPE_BUNDLE_CONTROL, /* OFPRAW_OFPT14_BUNDLE_CONTROL. */
OFPTYPE_BUNDLE_ADD_MESSAGE, /* OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE. */
@@ -1085,6 +1085,28 @@ ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc *td)
}
static void
+ofp_print_table_status_message(struct ds *string, const struct ofp_header *oh)
+{
+ struct ofputil_table_status ts;
+ enum ofperr error;
+
+ error = ofputil_decode_table_status(oh, &ts);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ if (ts.reason == OFPTR_VACANCY_DOWN) {
+ ds_put_format(string, " reason=VACANCY_DOWN");
+ } else if (ts.reason == OFPTR_VACANCY_UP) {
+ ds_put_format(string, " reason=VACANCY_UP");
+ }
+
+ ds_put_format(string, "\ntable_desc:-");
+ ofp_print_table_desc(string, &ts.desc);
+}
+
+static void
ofp_print_queue_get_config_request(struct ds *string,
const struct ofp_header *oh)
{
@@ -3207,6 +3229,10 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
ofp_print_requestforward(string, oh);
break;
+ case OFPTYPE_TABLE_STATUS:
+ ofp_print_table_status_message(string, oh);
+ break;
+
case OFPTYPE_METER_STATS_REQUEST:
case OFPTYPE_METER_CONFIG_STATS_REQUEST:
ofp_print_stats(string, oh);
@@ -9236,6 +9236,7 @@ ofputil_is_bundlable(enum ofptype type)
case OFPTYPE_TABLE_DESC_REPLY:
case OFPTYPE_ROLE_STATUS:
case OFPTYPE_REQUESTFORWARD:
+ case OFPTYPE_TABLE_STATUS:
case OFPTYPE_NXT_TLV_TABLE_REQUEST:
case OFPTYPE_NXT_TLV_TABLE_REPLY:
break;
@@ -9812,3 +9813,91 @@ ofputil_async_cfg_default(enum ofp_version version)
.slave[OAM_PORT_STATUS] = OFPPR_BITS,
};
}
+
+static void
+ofputil_put_ofp14_table_desc(const struct ofputil_table_desc *td,
+ struct ofpbuf *b, enum ofp_version version)
+{
+ struct ofp14_table_desc *otd;
+ struct ofp14_table_mod_prop_vacancy *otv;
+ size_t start_otd;
+
+ start_otd = b->size;
+ ofpbuf_put_zeros(b, sizeof *otd);
+
+ ofpprop_put_u32(b, OFPTMPT14_EVICTION, td->eviction_flags);
+
+ otv = ofpbuf_put_zeros(b, sizeof *otv);
+ otv->type = htons(OFPTMPT14_VACANCY);
+ otv->length = htons(sizeof *otv);
+ otv->vacancy_down = td->table_vacancy.vacancy_down;
+ otv->vacancy_up = td->table_vacancy.vacancy_up;
+ otv->vacancy = td->table_vacancy.vacancy;
+
+ otd = ofpbuf_at_assert(b, start_otd, sizeof *otd);
+ otd->length = htons(b->size - start_otd);
+ otd->table_id = td->table_id;
+ otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT,
+ td->eviction, td->vacancy,
+ version);
+}
+
+/* Converts the abstract form of a "table status" message in '*ts' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in
+ * a buffer owned by the caller. */
+struct ofpbuf *
+ofputil_encode_table_status(const struct ofputil_table_status *ts,
+ enum ofputil_protocol protocol)
+{
+ enum ofp_version version;
+ struct ofpbuf *b;
+
+ version = ofputil_protocol_to_ofp_version(protocol);
+ if (version >= OFP14_VERSION) {
+ enum ofpraw raw;
+ struct ofp14_table_status *ots;
+
+ raw = OFPRAW_OFPT14_TABLE_STATUS;
+ b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
+ ots = ofpbuf_put_zeros(b, sizeof *ots);
+ ots->reason = ts->reason;
+ ofputil_put_ofp14_table_desc(&ts->desc, b, version);
+ ofpmsg_update_length(b);
+ return b;
+ } else {
+ return NULL;
+ }
+}
+
+/* Decodes the OpenFlow "table status" message in '*ots' into an abstract form
+ * in '*ts'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_table_status(const struct ofp_header *oh,
+ struct ofputil_table_status *ts)
+{
+ const struct ofp14_table_status *ots;
+ struct ofpbuf b;
+ enum ofperr error;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ ots = ofpbuf_pull(&b, sizeof *ots);
+
+ if (raw == OFPRAW_OFPT14_TABLE_STATUS)
+ {
+ if (ots->reason != OFPTR_VACANCY_DOWN
+ && ots->reason != OFPTR_VACANCY_UP) {
+ return OFPERR_OFPBPC_BAD_VALUE;
+ }
+ ts->reason = ots->reason;
+
+ error = ofputil_decode_table_desc(&b, &ts->desc, oh->version);
+ return error;
+
+ } else {
+ return OFPERR_OFPBRC_BAD_VERSION;
+ }
+
+ return 0;
+}
@@ -1356,4 +1356,16 @@ enum ofperr ofputil_decode_requestforward(const struct ofp_header *,
struct ofputil_requestforward *);
void ofputil_destroy_requestforward(struct ofputil_requestforward *);
+/* Abstract ofp14_table_status. */
+struct ofputil_table_status {
+ enum ofp14_table_reason reason; /* One of OFPTR_*. */
+ struct ofputil_table_desc desc; /* New table config. */
+};
+
+enum ofperr ofputil_decode_table_status(const struct ofp_header *oh,
+ struct ofputil_table_status *ts);
+
+struct ofpbuf *
+ofputil_encode_table_status(const struct ofputil_table_status *ts,
+ enum ofputil_protocol protocol);
#endif /* ofp-util.h */
@@ -1404,6 +1404,7 @@ is_admitted_msg(const struct ofpbuf *b)
case OFPTYPE_ROLE_REPLY:
case OFPTYPE_ROLE_STATUS:
case OFPTYPE_REQUESTFORWARD:
+ case OFPTYPE_TABLE_STATUS:
case OFPTYPE_SET_FLOW_FORMAT:
case OFPTYPE_FLOW_MOD_TABLE_ID:
case OFPTYPE_SET_PACKET_IN_FORMAT:
@@ -1648,6 +1648,37 @@ connmgr_send_flow_removed(struct connmgr *mgr,
}
}
+/* Sends an OFPT_TABLE_STATUS message with 'reason' to appropriate controllers
+ * managed by 'mgr'. When the table state changes, the controller needs to be
+ * informed with the OFPT_TABLE_STATUS message. The reason values
+ * OFPTR_VACANCY_DOWN and OFPTR_VACANCY_UP identify a vacancy message. The
+ * vacancy events are generated when the remaining space in the flow table
+ * changes and crosses one of the vacancy thereshold specified by
+ * OFPT_TABLE_MOD. */
+void
+connmgr_send_table_status(struct connmgr *mgr,
+ const struct ofputil_table_desc *td,
+ uint8_t reason)
+{
+ struct ofputil_table_status ts;
+ struct ofconn *ofconn;
+
+ ts.reason = reason;
+ ts.desc = *td;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ if (ofconn_receives_async_msg(ofconn, OAM_TABLE_STATUS, reason)) {
+ struct ofpbuf *msg;
+
+ msg = ofputil_encode_table_status(&ts,
+ ofconn_get_protocol(ofconn));
+ if (msg) {
+ ofconn_send(ofconn, msg, NULL);
+ }
+ }
+ }
+}
+
/* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as
* necessary according to their individual configurations.
*
@@ -219,4 +219,7 @@ void ofmonitor_compose_refresh_updates(struct rule_collection *rules,
struct ovs_list *msgs)
OVS_REQUIRES(ofproto_mutex);
+void connmgr_send_table_status(struct connmgr *,
+ const struct ofputil_table_desc *td,
+ uint8_t reason);
#endif /* connmgr.h */
@@ -265,6 +265,9 @@ struct oftable {
uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */
uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */
+ /* Vacancy Reason sent through TABLE_STATUS */
+ enum ofp14_table_reason vacancy_reason; /* One of OFPTR_*. */
+
atomic_ulong n_matched;
atomic_ulong n_missed;
};
@@ -3595,7 +3595,9 @@ query_table_desc__(struct ofputil_table_desc *td,
: OFPUTIL_TABLE_VACANCY_OFF);
td->table_vacancy.vacancy_down = ofproto->tables[table_id].vacancy_down;
td->table_vacancy.vacancy_up = ofproto->tables[table_id].vacancy_up;
- td->table_vacancy.vacancy = max_flows ? (count * 100) / max_flows : 0;
+ td->table_vacancy.vacancy = max_flows
+ ? ((long int)(max_flows - count) * 100) / max_flows
+ : 0;
}
/* This function queries the database for dumping table-desc. */
@@ -3635,6 +3637,34 @@ handle_table_desc_request(struct ofconn *ofconn,
return 0;
}
+/* This function determines and sends the vacancy event, based on the value
+ * of current vacancy and threshold vacancy. If the current vacancy is less
+ * than or equal to vacancy_down, vacancy up events must be enabled, and when
+ * the current vacancy is greater or equal to vacancy_up, vacancy down events
+ * must be enabled. */
+static void
+send_table_status(struct ofproto *ofproto, uint8_t table_id)
+{
+ struct ofputil_table_desc td;
+ enum ofp14_table_reason reason;
+
+ reason = ofproto->tables[table_id].vacancy_reason;
+ query_table_desc__(&td, ofproto, table_id);
+
+ if (td.table_vacancy.vacancy < td.table_vacancy.vacancy_down
+ && reason != OFPTR_VACANCY_DOWN) {
+
+ ofproto->tables[table_id].vacancy_reason = OFPTR_VACANCY_DOWN;
+ connmgr_send_table_status(ofproto->connmgr, &td, OFPTR_VACANCY_DOWN);
+
+ } else if (td.table_vacancy.vacancy > td.table_vacancy.vacancy_up
+ && reason != OFPTR_VACANCY_UP) {
+
+ ofproto->tables[table_id].vacancy_reason = OFPTR_VACANCY_UP;
+ connmgr_send_table_status(ofproto->connmgr, &td, OFPTR_VACANCY_UP);
+ }
+}
+
static void
append_port_stat(struct ofport *port, struct ovs_list *replies)
{
@@ -4670,6 +4700,13 @@ add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0,
req ? req->ofconn : NULL,
req ? req->request->xid : 0, NULL);
+
+ /* Send Vacancy Events for OF1.4+ when there is change in flow table.
+ * Provided OFPTC14_VACANCY_EVENTS is set for the table. */
+ if (ofproto->tables[new_rule->table_id].vacancy_enabled) {
+ send_table_status(ofproto, new_rule->table_id);
+ }
+
}
send_buffered_packet(req, fm->buffer_id, new_rule);
@@ -5065,6 +5102,14 @@ delete_flows_finish__(struct ofproto *ofproto,
ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED, reason,
req ? req->ofconn : NULL,
req ? req->request->xid : 0, NULL);
+
+ /* Send Vacancy Events for OF1.4+ when there is change in flow
+ * table. Provided OFPTC14_VACANCY_EVENTS is set for the table.
+ */
+ if (ofproto->tables[rule->table_id].vacancy_enabled) {
+ send_table_status(ofproto, rule->table_id);
+ }
+
ofproto_rule_remove__(ofproto, rule);
learned_cookies_dec(ofproto, rule_get_actions(rule),
&dead_cookies);
@@ -7310,6 +7355,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
case OFPTYPE_TABLE_DESC_REPLY:
case OFPTYPE_ROLE_STATUS:
case OFPTYPE_REQUESTFORWARD:
+ case OFPTYPE_TABLE_STATUS:
case OFPTYPE_NXT_TLV_TABLE_REPLY:
default:
if (ofpmsg_is_stat_request(oh)) {
@@ -1999,7 +1999,7 @@ AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d
AT_CHECK([ovs-ofctl -O Openflow14 mod-table br0 0 vacancy:20,80])
# Check that the configuration was updated.
mv expout orig-expout
-sed -e '3s/vacancy=off/vacancy=on vacancy_down=20% vacancy_up=80% vacancy=0%/' <orig-expout > expout
+sed -e '3s/vacancy=off/vacancy=on vacancy_down=20% vacancy_up=80% vacancy=100%/' <orig-expout > expout
AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d
/^OFPST_TABLE_DESC/d'], [0], [expout])
OVS_VSWITCHD_STOP
@@ -2907,7 +2907,7 @@ OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x
echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=delete table_id=0"
fi
- # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE
+ # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE
ovs-ofctl -O OpenFlow14 add-group br0 group_id=1234,type=all,bucket=output:10
ovs-ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=group:1234
ovs-ofctl -O OpenFlow14 --strict del-groups br0 group_id=1234
@@ -2915,6 +2915,22 @@ OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x
echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=group_delete table_id=0"
fi
+ # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP
+ if test X"$1" = X"OFPTR_VACANCY_UP"; then shift;
+ ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1
+ ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80
+ ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2
+ ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2
+ ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2
+ ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2
+ ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2
+ echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP
+table_desc:-
+ table 1:
+ eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME
+ vacancy=on vacancy_down=20% vacancy_up=80% vacancy=90%"
+ fi
+
AT_FAIL_IF([test X"$1" != X])
ovs-appctl -t ovs-ofctl ofctl/barrier
@@ -2940,8 +2956,8 @@ ovs-appctl -t ovs-ofctl ofctl/send 051800180000000200000003000000000000000000000
check_async 3 OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE
# Use OF 1.4 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages.
-ovs-appctl -t ovs-ofctl ofctl/send 051c0038000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c0005000800000005
-check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE
+ovs-appctl -t ovs-ofctl ofctl/send 051c0040000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c00050008000000050008000800000010
+check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE OFPTR_VACANCY_UP
# Set controller ID 123.
ovs-appctl -t ovs-ofctl ofctl/send 05040018000000030000232000000014000000000000007b