Emergency flow cache: Add emergency flow cache framework
Emergency flow cache use completely separate flow table from working and its table ID for stats is 0xfe.
This commit is contained in:
1 parent
e4cbc1e74a
commit
58ee7f213f
24 files changed
+924
-124
No files matched your search
+4
-2
@@ -20,8 +20,9 @@ ofdatapath_sources = \
|
||||
forward.c \
|
||||
nx_act.c \
|
||||
nx_msg.c \
|
||||
private-msg.c \
|
||||
table-hash.c \
|
||||
table-linear.c
|
||||
table-linear.c
|
||||
|
||||
ofdatapath_headers = \
|
||||
chain.h \
|
||||
@@ -34,7 +35,8 @@ ofdatapath_headers = \
|
||||
dp_act.h \
|
||||
nx_act.h \
|
||||
nx_msg.h \
|
||||
table.h
|
||||
private-msg.h \
|
||||
table.h
|
||||
|
||||
dist_sources = $(foreach module,$(dist_modules),$($(module)_sources))
|
||||
dist_headers = $(foreach module,$(dist_modules),$($(module)_headers))
|
||||
|
||||
+62
-25
@@ -20,7 +20,7 @@ static DEFINE_SPINLOCK(hook_lock);
|
||||
/* Attempts to append 'table' to the set of tables in 'chain'. Returns 0 or
|
||||
* negative error. If 'table' is null it is assumed that table creation failed
|
||||
* due to out-of-memory. */
|
||||
static int add_table(struct sw_chain *chain, struct sw_table *table)
|
||||
static int add_table(struct sw_chain *chain, struct sw_table *table, int emerg)
|
||||
{
|
||||
if (table == NULL)
|
||||
return -ENOMEM;
|
||||
@@ -30,7 +30,10 @@ static int add_table(struct sw_chain *chain, struct sw_table *table)
|
||||
table->destroy(table);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
chain->tables[chain->n_tables++] = table;
|
||||
if (emerg)
|
||||
chain->emerg_table = table;
|
||||
else
|
||||
chain->tables[chain->n_tables++] = table;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -45,13 +48,15 @@ struct sw_chain *chain_create(struct datapath *dp)
|
||||
chain->owner = try_module_get(hw_table_owner) ? hw_table_owner : NULL;
|
||||
if (chain->owner && create_hw_table_hook) {
|
||||
struct sw_table *hwtable = create_hw_table_hook();
|
||||
if (!hwtable || add_table(chain, hwtable))
|
||||
if (!hwtable || add_table(chain, hwtable, 0))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
|
||||
0x741B8CD7, TABLE_HASH_MAX_FLOWS))
|
||||
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS)))
|
||||
0x741B8CD7, TABLE_HASH_MAX_FLOWS),
|
||||
0)
|
||||
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS), 0)
|
||||
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS), 1))
|
||||
goto error;
|
||||
return chain;
|
||||
|
||||
@@ -66,19 +71,29 @@ error:
|
||||
*
|
||||
* Caller must hold rcu_read_lock or dp_mutex. */
|
||||
struct sw_flow *chain_lookup(struct sw_chain *chain,
|
||||
const struct sw_flow_key *key)
|
||||
const struct sw_flow_key *key, int emerg)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(key->wildcards);
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
struct sw_flow *flow = t->lookup(t, key);
|
||||
t->n_lookup++;
|
||||
if (flow) {
|
||||
t->n_matched++;
|
||||
return flow;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
struct sw_flow *flow = t->lookup(t, key);
|
||||
t->n_lookup++;
|
||||
if (flow) {
|
||||
t->n_matched++;
|
||||
return flow;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -90,17 +105,22 @@ struct sw_flow *chain_lookup(struct sw_chain *chain,
|
||||
* by the caller.
|
||||
*
|
||||
* Caller must hold dp_mutex. */
|
||||
int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
int chain_insert(struct sw_chain *chain, struct sw_flow *flow, int emerg)
|
||||
{
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
if (t->insert(t, flow))
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (t->insert(t, flow))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
@@ -111,18 +131,25 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
* iterating through the entire contents of each table for keys that contain
|
||||
* wildcards. Relatively cheap for fully specified keys. */
|
||||
int
|
||||
chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t priority, int strict,
|
||||
const struct ofp_action_header *actions, size_t actions_len)
|
||||
chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t priority, int strict,
|
||||
const struct ofp_action_header *actions, size_t actions_len,
|
||||
int emerg)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
count += t->modify(t, key, priority, strict, actions, actions_len);
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
count += t->modify(t, key, priority, strict,
|
||||
actions, actions_len);
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
count += t->modify(t, key, priority, strict,
|
||||
actions, actions_len);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -137,17 +164,23 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
*
|
||||
* Caller must hold dp_mutex. */
|
||||
int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t out_port, uint16_t priority, int strict)
|
||||
uint16_t out_port, uint16_t priority, int strict, int emerg)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
count += t->delete(chain->dp, t, key, out_port, priority, strict);
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
count += t->delete(chain->dp, t, key,
|
||||
out_port, priority, strict);
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
count += t->delete(chain->dp, t, key,
|
||||
out_port, priority, strict);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -176,13 +209,17 @@ int chain_timeout(struct sw_chain *chain)
|
||||
void chain_destroy(struct sw_chain *chain)
|
||||
{
|
||||
int i;
|
||||
struct sw_table *t = NULL;
|
||||
|
||||
synchronize_rcu();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
t = chain->tables[i];
|
||||
if (t->destroy)
|
||||
t->destroy(t);
|
||||
}
|
||||
t = chain->emerg_table;
|
||||
if (t->destroy)
|
||||
t->destroy(t);
|
||||
module_put(chain->owner);
|
||||
kfree(chain);
|
||||
}
|
||||
|
||||
+8
-6
@@ -17,18 +17,20 @@ struct datapath;
|
||||
struct sw_chain {
|
||||
int n_tables;
|
||||
struct sw_table *tables[CHAIN_MAX_TABLES];
|
||||
struct sw_table *emerg_table;
|
||||
|
||||
struct datapath *dp;
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
struct sw_chain *chain_create(struct datapath *);
|
||||
struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
|
||||
int chain_insert(struct sw_chain *, struct sw_flow *);
|
||||
int chain_modify(struct sw_chain *, const struct sw_flow_key *,
|
||||
uint16_t, int, const struct ofp_action_header *, size_t);
|
||||
int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t,
|
||||
uint16_t, int);
|
||||
struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *,
|
||||
int);
|
||||
int chain_insert(struct sw_chain *, struct sw_flow *, int);
|
||||
int chain_modify(struct sw_chain *, const struct sw_flow_key *,
|
||||
uint16_t, int, const struct ofp_action_header *, size_t, int);
|
||||
int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t,
|
||||
uint16_t, int, int);
|
||||
int chain_timeout(struct sw_chain *);
|
||||
void chain_destroy(struct sw_chain *);
|
||||
|
||||
|
||||
+41
-18
@@ -1467,6 +1467,8 @@ struct flow_stats_state {
|
||||
int bytes_used, bytes_allocated;
|
||||
};
|
||||
|
||||
#define EMERG_TABLE_ID_FOR_STATS 0xfe
|
||||
|
||||
static int flow_stats_init(struct datapath *dp, const void *body, int body_len,
|
||||
void **state)
|
||||
{
|
||||
@@ -1541,18 +1543,27 @@ static int flow_stats_dump(struct datapath *dp, void *state,
|
||||
s->body = body;
|
||||
|
||||
flow_extract_match(&match_key, &s->rq->match);
|
||||
while (s->table_idx < dp->chain->n_tables
|
||||
&& (s->rq->table_id == 0xff || s->rq->table_id == s->table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[s->table_idx];
|
||||
if (s->rq->table_id == EMERG_TABLE_ID_FOR_STATS) {
|
||||
struct sw_table *table = dp->chain->emerg_table;
|
||||
|
||||
error = table->iterate(table, &match_key, s->rq->out_port,
|
||||
&s->position, flow_stats_dump_callback, s);
|
||||
if (error)
|
||||
break;
|
||||
error = table->iterate(table, &match_key, s->rq->out_port,
|
||||
&s->position, flow_stats_dump_callback,
|
||||
s);
|
||||
} else {
|
||||
while (s->table_idx < dp->chain->n_tables
|
||||
&& (s->rq->table_id == 0xff
|
||||
|| s->rq->table_id == s->table_idx)) {
|
||||
struct sw_table *table = dp->chain->tables[s->table_idx];
|
||||
|
||||
s->table_idx++;
|
||||
memset(&s->position, 0, sizeof s->position);
|
||||
error = table->iterate(table, &match_key,
|
||||
s->rq->out_port, &s->position,
|
||||
flow_stats_dump_callback, s);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
s->table_idx++;
|
||||
memset(&s->position, 0, sizeof s->position);
|
||||
}
|
||||
}
|
||||
*body_len = s->bytes_used;
|
||||
|
||||
@@ -1594,6 +1605,7 @@ static int aggregate_stats_dump(struct datapath *dp, void *state,
|
||||
struct sw_table_position position;
|
||||
struct sw_flow_key match_key;
|
||||
int table_idx;
|
||||
int error = 0;
|
||||
|
||||
if (*body_len < sizeof *rpy)
|
||||
return -ENOBUFS;
|
||||
@@ -1605,19 +1617,30 @@ static int aggregate_stats_dump(struct datapath *dp, void *state,
|
||||
flow_extract_match(&match_key, &rq->match);
|
||||
table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
|
||||
memset(&position, 0, sizeof position);
|
||||
while (table_idx < dp->chain->n_tables
|
||||
&& (rq->table_id == 0xff || rq->table_id == table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[table_idx];
|
||||
int error;
|
||||
|
||||
error = table->iterate(table, &match_key, rq->out_port, &position,
|
||||
if (rq->table_id == EMERG_TABLE_ID_FOR_STATS) {
|
||||
struct sw_table *table = dp->chain->emerg_table;
|
||||
|
||||
error = table->iterate(table, &match_key, rq->out_port,
|
||||
&position,
|
||||
aggregate_stats_dump_callback, rpy);
|
||||
if (error)
|
||||
return error;
|
||||
} else {
|
||||
while (table_idx < dp->chain->n_tables
|
||||
&& (rq->table_id == 0xff || rq->table_id == table_idx)) {
|
||||
struct sw_table *table = dp->chain->tables[table_idx];
|
||||
|
||||
table_idx++;
|
||||
memset(&position, 0, sizeof position);
|
||||
error = table->iterate(table, &match_key, rq->out_port,
|
||||
&position,
|
||||
aggregate_stats_dump_callback,
|
||||
rpy);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
table_idx++;
|
||||
memset(&position, 0, sizeof position);
|
||||
}
|
||||
}
|
||||
|
||||
rpy->packet_count = cpu_to_be64(rpy->packet_count);
|
||||
|
||||
+13
-6
@@ -16,6 +16,7 @@
|
||||
#include "openflow/private-ext.h"
|
||||
#include "dp_act.h"
|
||||
#include "nx_msg.h"
|
||||
#include "private-msg.h"
|
||||
#include "chain.h"
|
||||
#include "flow.h"
|
||||
|
||||
@@ -51,7 +52,7 @@ int run_flow_through_tables(struct sw_chain *chain, struct sk_buff *skb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
flow = chain_lookup(chain, &key);
|
||||
flow = chain_lookup(chain, &key, 0);
|
||||
if (likely(flow != NULL)) {
|
||||
struct sw_flow_actions *sf_acts = rcu_dereference(flow->sf_acts);
|
||||
flow_used(flow, skb);
|
||||
@@ -230,7 +231,8 @@ add_flow(struct sw_chain *chain, const struct sender *sender,
|
||||
flow_setup_actions(flow, ofm->actions, actions_len);
|
||||
|
||||
/* Act. */
|
||||
error = chain_insert(chain, flow);
|
||||
error = chain_insert(chain, flow,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0);
|
||||
if (error == -ENOBUFS) {
|
||||
dp_send_error_msg(chain->dp, sender, OFPET_FLOW_MOD_FAILED,
|
||||
OFPFMFC_ALL_TABLES_FULL, ofm, ntohs(ofm->header.length));
|
||||
@@ -284,7 +286,8 @@ mod_flow(struct sw_chain *chain, const struct sender *sender,
|
||||
|
||||
priority = key.wildcards ? ntohs(ofm->priority) : -1;
|
||||
strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
|
||||
chain_modify(chain, &key, priority, strict, ofm->actions, actions_len);
|
||||
chain_modify(chain, &key, priority, strict, ofm->actions, actions_len,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0);
|
||||
|
||||
if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
|
||||
struct sk_buff *skb = retrieve_skb(ntohl(ofm->buffer_id));
|
||||
@@ -318,14 +321,17 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
|
||||
} else if (command == OFPFC_DELETE) {
|
||||
struct sw_flow_key key;
|
||||
flow_extract_match(&key, &ofm->match);
|
||||
return chain_delete(chain, &key, ofm->out_port, 0, 0) ? 0 : -ESRCH;
|
||||
return chain_delete(chain, &key, ofm->out_port, 0, 0,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0)
|
||||
? 0 : -ESRCH;
|
||||
} else if (command == OFPFC_DELETE_STRICT) {
|
||||
struct sw_flow_key key;
|
||||
uint16_t priority;
|
||||
flow_extract_match(&key, &ofm->match);
|
||||
priority = key.wildcards ? ntohs(ofm->priority) : -1;
|
||||
return chain_delete(chain, &key, ofm->out_port,
|
||||
priority, 1) ? 0 : -ESRCH;
|
||||
return chain_delete(chain, &key, ofm->out_port, priority, 1,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0)
|
||||
? 0 : -ESRCH;
|
||||
} else {
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
@@ -342,6 +348,7 @@ recv_vendor(struct sw_chain *chain, const struct sender *sender,
|
||||
case NX_VENDOR_ID:
|
||||
return nx_recv_msg(chain, sender, msg);
|
||||
case PRIVATE_VENDOR_ID:
|
||||
return private_recv_msg(chain, sender, msg);
|
||||
default:
|
||||
if (net_ratelimit())
|
||||
printk(KERN_NOTICE "%s: unknown vendor: 0x%x\n",
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009
|
||||
* The Board of Trustees of The Leland Stanford Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation that
|
||||
* others will use, modify and enhance the Software and contribute those
|
||||
* enhancements back to the community. However, since we would like to make the
|
||||
* Software available for broadest use, with as few restrictions as possible
|
||||
* permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this Software to deal in the Software under the copyrights without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any derivatives
|
||||
* without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include "openflow/private-ext.h"
|
||||
|
||||
#include "chain.h"
|
||||
#include "datapath.h"
|
||||
#include "table.h"
|
||||
#include "private-msg.h"
|
||||
|
||||
struct emerg_flow_context {
|
||||
struct sw_chain *chain;
|
||||
};
|
||||
|
||||
static void flush_working(struct sw_chain *);
|
||||
static int protection_callback(struct sw_flow *, void *);
|
||||
static void do_protection(struct sw_chain *);
|
||||
|
||||
static void
|
||||
flush_working(struct sw_chain *chain)
|
||||
{
|
||||
struct sw_flow_key key;
|
||||
int num_deleted = 0;
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.wildcards = OFPFW_ALL;
|
||||
num_deleted = chain_delete(chain, &key, OFPP_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
protection_callback(struct sw_flow *flow, void *private_)
|
||||
{
|
||||
struct emerg_flow_context *private
|
||||
= (struct emerg_flow_context *)private_;
|
||||
struct sw_flow_actions *actions = flow->sf_acts;
|
||||
struct ofp_match match;
|
||||
struct sw_flow *tgtflow = NULL;
|
||||
int error = 0;
|
||||
|
||||
tgtflow = flow_alloc(actions->actions_len, GFP_ATOMIC);
|
||||
if (tgtflow == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Dup w/o idle and hard timeout. */
|
||||
memset(&match, 0, sizeof(match));
|
||||
flow_fill_match(&match, &flow->key);
|
||||
flow_extract_match(&tgtflow->key, &match);
|
||||
tgtflow->priority = flow->priority;
|
||||
tgtflow->idle_timeout = 0;
|
||||
tgtflow->hard_timeout = 0;
|
||||
flow_setup_actions(tgtflow, actions->actions, actions->actions_len);
|
||||
|
||||
error = chain_insert(private->chain, tgtflow, 0);
|
||||
if (error)
|
||||
flow_free(tgtflow);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
do_protection(struct sw_chain *chain)
|
||||
{
|
||||
struct emerg_flow_context private;
|
||||
struct sw_flow_key key;
|
||||
struct sw_table_position position;
|
||||
struct sw_table *table = chain->emerg_table;
|
||||
int error = 0;
|
||||
|
||||
memset(&private, 0, sizeof(private));
|
||||
private.chain = chain;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.wildcards = OFPFW_ALL;
|
||||
memset(&position, 0, sizeof(position));
|
||||
|
||||
error = table->iterate(table, &key, OFPP_NONE,
|
||||
&position, protection_callback, &private);
|
||||
}
|
||||
|
||||
int
|
||||
private_recv_msg(struct sw_chain *chain, const struct sender *sender,
|
||||
const void *ofph)
|
||||
{
|
||||
int error = 0;
|
||||
struct private_vxhdr *vxhdr = (struct private_vxhdr *)ofph;
|
||||
struct private_vxopt *vxopt = (struct private_vxopt *)(vxhdr + 1);
|
||||
|
||||
switch (ntohs(vxopt->pvo_type)) {
|
||||
case PRIVATEOPT_PROTOCOL_STATS_REQUEST:
|
||||
case PRIVATEOPT_PROTOCOL_STATS_REPLY:
|
||||
break;
|
||||
case PRIVATEOPT_EMERG_FLOW_PROTECTION:
|
||||
flush_working(chain);
|
||||
do_protection(chain);
|
||||
break;
|
||||
case PRIVATEOPT_EMERG_FLOW_RESTORATION:
|
||||
/* Nothing to do because we assume that a re-connected
|
||||
* controller will do flush current working flow table. */
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009
|
||||
* The Board of Trustees of The Leland Stanford Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation that
|
||||
* others will use, modify and enhance the Software and contribute those
|
||||
* enhancements back to the community. However, since we would like to make the
|
||||
* Software available for broadest use, with as few restrictions as possible
|
||||
* permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this Software to deal in the Software under the copyrights without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any derivatives
|
||||
* without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#ifndef PRIVATE_MSG_H_
|
||||
#define PRIVATE_MSG_H_
|
||||
|
||||
int private_recv_msg(struct sw_chain *, const struct sender *, const void *);
|
||||
|
||||
#endif
|
||||
@@ -520,6 +520,12 @@ OFP_ASSERT(sizeof(struct ofp_match) == 36);
|
||||
/* By default, choose a priority in the middle. */
|
||||
#define OFP_DEFAULT_PRIORITY 0x8000
|
||||
|
||||
enum ofp_flow_mod_flags {
|
||||
OFPFF_SEND_FLOW_EXP = 1 << 0, /* Send expiration message when flow expires. */
|
||||
OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */
|
||||
OFPFF_EMERG = 1 << 2 /* Ramark this is for emergency. */
|
||||
};
|
||||
|
||||
/* Flow setup and teardown (controller -> datapath). */
|
||||
struct ofp_flow_mod {
|
||||
struct ofp_header header;
|
||||
@@ -536,7 +542,7 @@ struct ofp_flow_mod {
|
||||
matching entries to include this as an
|
||||
output port. A value of OFPP_NONE
|
||||
indicates no restriction. */
|
||||
uint8_t pad[2]; /* Align to 32-bits. */
|
||||
uint16_t flags; /* One of OFPFF_*. */
|
||||
uint32_t reserved; /* Reserved for future use. */
|
||||
struct ofp_action_header actions[0]; /* The action length is inferred
|
||||
from the length field in the
|
||||
@@ -689,8 +695,8 @@ OFP_ASSERT(sizeof(struct ofp_desc_stats) == 800);
|
||||
/* Body for ofp_stats_request of type OFPST_FLOW. */
|
||||
struct ofp_flow_stats_request {
|
||||
struct ofp_match match; /* Fields to match. */
|
||||
uint8_t table_id; /* ID of table to read (from ofp_table_stats)
|
||||
or 0xff for all tables. */
|
||||
uint8_t table_id; /* ID of table to read (from ofp_table_stats),
|
||||
0xff for all tables or 0xfe for emergency. */
|
||||
uint8_t pad; /* Align to 32 bits. */
|
||||
uint16_t out_port; /* Require matching entries to include this
|
||||
as an output port. A value of OFPP_NONE
|
||||
@@ -720,7 +726,7 @@ OFP_ASSERT(sizeof(struct ofp_flow_stats) == 72);
|
||||
struct ofp_aggregate_stats_request {
|
||||
struct ofp_match match; /* Fields to match. */
|
||||
uint8_t table_id; /* ID of table to read (from ofp_table_stats)
|
||||
or 0xff for all tables. */
|
||||
0xff for all tables or 0xfe for emergency. */
|
||||
uint8_t pad; /* Align to 32 bits. */
|
||||
uint16_t out_port; /* Require matching entries to include this
|
||||
as an output port. A value of OFPP_NONE
|
||||
|
||||
@@ -50,24 +50,32 @@
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
#define PRIVATE_VENDOR_ID 0x00acde48
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x0123
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x0456
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x0001
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x0002
|
||||
#define PRIVATEOPT_EMERG_FLOW_PROTECTION 0x0003
|
||||
#define PRIVATEOPT_EMERG_FLOW_RESTORATION 0x0004
|
||||
#else
|
||||
#define PRIVATE_VENDOR_ID 0x48deac00
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x2301
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x5604
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x0100
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x0200
|
||||
#define PRIVATEOPT_EMERG_FLOW_PROTECTION 0x0300
|
||||
#define PRIVATEOPT_EMERG_FLOW_RESTORATION 0x0400
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define PRIVATE_VENDOR_ID 0x00acde48
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x0123
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x0456
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x0001
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x0002
|
||||
#define PRIVATEOPT_EMERG_FLOW_PROTECTION 0x0003
|
||||
#define PRIVATEOPT_EMERG_FLOW_RESTORATION 0x0004
|
||||
#else
|
||||
#define PRIVATE_VENDOR_ID 0x48deac00
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x2301
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x5604
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REQUEST 0x0100
|
||||
#define PRIVATEOPT_PROTOCOL_STATS_REPLY 0x0200
|
||||
#define PRIVATEOPT_EMERG_FLOW_PROTECTION 0x0300
|
||||
#define PRIVATEOPT_EMERG_FLOW_RESTORATION 0x0400
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@ VLOG_MODULE(dhcp_client)
|
||||
VLOG_MODULE(discovery)
|
||||
VLOG_MODULE(dpif)
|
||||
VLOG_MODULE(dpctl)
|
||||
VLOG_MODULE(emerg_flow)
|
||||
VLOG_MODULE(executer)
|
||||
VLOG_MODULE(fail_open)
|
||||
VLOG_MODULE(failover)
|
||||
|
||||
@@ -6,6 +6,8 @@ secchan_ofprotocol_SOURCES = \
|
||||
secchan/discovery.h \
|
||||
secchan/executer.c \
|
||||
secchan/executer.h \
|
||||
secchan/emerg-flow.c \
|
||||
secchan/emerg-flow.h \
|
||||
secchan/fail-open.c \
|
||||
secchan/fail-open.h \
|
||||
secchan/failover.c \
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009
|
||||
* The Board of Trustees of The Leland Stanford Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation that
|
||||
* others will use, modify and enhance the Software and contribute those
|
||||
* enhancements back to the community. However, since we would like to make the
|
||||
* Software available for broadest use, with as few restrictions as possible
|
||||
* permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this Software to deal in the Software under the copyrights without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any derivatives
|
||||
* without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "openflow/openflow.h"
|
||||
#include "openflow/private-ext.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "vconn.h"
|
||||
#include "rconn.h"
|
||||
#include "secchan.h"
|
||||
#include "status.h"
|
||||
#include "timeval.h"
|
||||
#include "sat-math.h"
|
||||
#include "ofpbuf.h"
|
||||
#include "emerg-flow.h"
|
||||
#define THIS_MODULE VLM_emerg_flow
|
||||
#include "vlog.h"
|
||||
|
||||
struct emerg_flow_context {
|
||||
const struct settings *settings;
|
||||
const struct secchan *secchan;
|
||||
struct rconn *local_rconn;
|
||||
struct rconn *remote_rconn;
|
||||
int prev_state;
|
||||
int state;
|
||||
};
|
||||
|
||||
static void emerg_flow_status_cb(struct status_reply *, void *);
|
||||
static void emerg_flow_periodic_cb(void *);
|
||||
|
||||
static void
|
||||
emerg_flow_status_cb(struct status_reply *status_reply, void *context_)
|
||||
{
|
||||
struct emerg_flow_context *context = context_;
|
||||
|
||||
status_reply_put(status_reply, "state=%s",
|
||||
context->state == PRIVATEOPT_EMERG_FLOW_RESTORATION
|
||||
? "restoration"
|
||||
: context->state == PRIVATEOPT_EMERG_FLOW_PROTECTION
|
||||
? "protection" : "unknown");
|
||||
}
|
||||
|
||||
static void
|
||||
emerg_flow_periodic_cb(void *context_)
|
||||
{
|
||||
struct emerg_flow_context *context = context_;
|
||||
struct ofpbuf *buf = NULL;
|
||||
struct private_vxhdr *vxhdr = NULL;
|
||||
struct private_vxopt *vxopt = NULL;
|
||||
int error = 0;
|
||||
|
||||
if (rconn_is_connected(context->remote_rconn)) {
|
||||
if (context->state == PRIVATEOPT_EMERG_FLOW_PROTECTION) {
|
||||
context->prev_state = context->state;
|
||||
context->state = PRIVATEOPT_EMERG_FLOW_RESTORATION;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (context->state == PRIVATEOPT_EMERG_FLOW_RESTORATION) {
|
||||
context->prev_state = context->state;
|
||||
context->state = PRIVATEOPT_EMERG_FLOW_PROTECTION;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vxhdr = (struct private_vxhdr *)make_openflow
|
||||
(sizeof(*vxhdr) + sizeof(*vxopt), OFPT_VENDOR, &buf);
|
||||
vxopt = (struct private_vxopt *)(vxhdr + 1);
|
||||
vxhdr->ofp_vxid = htonl(PRIVATE_VENDOR_ID);
|
||||
vxopt->pvo_type = htons(context->state);
|
||||
vxopt->pvo_len = htons(0);
|
||||
|
||||
error = rconn_send(context->local_rconn, buf, NULL);
|
||||
if (error && error != EAGAIN) {
|
||||
VLOG_WARN("send failed (%s)", strerror(error));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
emerg_flow_start(struct secchan *secchan, const struct settings *settings,
|
||||
struct switch_status *switch_status,
|
||||
struct rconn *local_rconn, struct rconn *remote_rconn)
|
||||
{
|
||||
struct emerg_flow_context *context = NULL;
|
||||
static struct hook_class emerg_flow_hook_class = {
|
||||
NULL, /* local_packet_cb */
|
||||
NULL, /* remote_packet_cb */
|
||||
emerg_flow_periodic_cb, /* periodic_cb */
|
||||
NULL, /* wait_cb */
|
||||
NULL, /* closing_cb */
|
||||
};
|
||||
|
||||
context = xmalloc(sizeof(*context));
|
||||
context->settings = settings;
|
||||
context->secchan = secchan;
|
||||
context->local_rconn = local_rconn;
|
||||
context->remote_rconn = remote_rconn;
|
||||
context->prev_state = PRIVATEOPT_EMERG_FLOW_PROTECTION;
|
||||
context->state = PRIVATEOPT_EMERG_FLOW_PROTECTION;
|
||||
|
||||
switch_status_register_category(switch_status, "emerg-flow",
|
||||
emerg_flow_status_cb, context);
|
||||
add_hook(secchan, &emerg_flow_hook_class, context);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009
|
||||
* The Board of Trustees of The Leland Stanford Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation that
|
||||
* others will use, modify and enhance the Software and contribute those
|
||||
* enhancements back to the community. However, since we would like to make the
|
||||
* Software available for broadest use, with as few restrictions as possible
|
||||
* permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this Software to deal in the Software under the copyrights without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any derivatives
|
||||
* without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#ifndef EMERG_FLOW_H_
|
||||
#define EMERG_FLOW_H_ 1
|
||||
|
||||
struct rconn;
|
||||
struct secchan;
|
||||
struct settings;
|
||||
struct switch_status;
|
||||
|
||||
void emerg_flow_start(struct secchan *, const struct settings *,
|
||||
struct switch_status *, struct rconn *, struct rconn *);
|
||||
|
||||
#endif
|
||||
@@ -375,6 +375,13 @@ The default will change to \fB--stp\fR at some point in the future.
|
||||
When flows end on the switch, send NetFlow v5 messages to
|
||||
\fIhost\fR on UDP \fIport\fR.
|
||||
|
||||
.TP
|
||||
\fB--emerg-flow\fR
|
||||
Enable emergecny flow protection and restration at the switch. If emergency
|
||||
flow enabled, \fBofprotocol\fR will attempt to switch flow table to emergency's
|
||||
one when a controller connection fails. The default is disabled in this
|
||||
distribution.
|
||||
|
||||
.SS "Rate-Limiting Options"
|
||||
|
||||
These options configure how the switch applies a ``token bucket'' to
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "dirs.h"
|
||||
#include "discovery.h"
|
||||
#include "executer.h"
|
||||
#include "emerg-flow.h"
|
||||
#include "fail-open.h"
|
||||
#include "failover.h"
|
||||
#include "fault.h"
|
||||
@@ -233,6 +234,9 @@ main(int argc, char *argv[])
|
||||
if (s.command_acl[0]) {
|
||||
executer_start(&secchan, &s);
|
||||
}
|
||||
if (s.emerg_flow) {
|
||||
emerg_flow_start(&secchan, &s, switch_status, local_rconn, remote_rconn);
|
||||
}
|
||||
|
||||
while (s.discovery || rconn_is_alive(remote_rconn)) {
|
||||
struct relay *r, *n;
|
||||
@@ -593,6 +597,7 @@ parse_options(int argc, char *argv[], struct settings *s)
|
||||
OPT_COMMAND_ACL,
|
||||
OPT_COMMAND_DIR,
|
||||
OPT_NETFLOW,
|
||||
OPT_EMERG_FLOW,
|
||||
VLOG_OPTION_ENUMS,
|
||||
LEAK_CHECKER_OPTION_ENUMS
|
||||
};
|
||||
@@ -614,6 +619,7 @@ parse_options(int argc, char *argv[], struct settings *s)
|
||||
{"command-acl", required_argument, 0, OPT_COMMAND_ACL},
|
||||
{"command-dir", required_argument, 0, OPT_COMMAND_DIR},
|
||||
{"netflow", required_argument, 0, OPT_NETFLOW},
|
||||
{"emerg-flow", no_argument, 0, OPT_EMERG_FLOW},
|
||||
{"verbose", optional_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
@@ -645,6 +651,7 @@ parse_options(int argc, char *argv[], struct settings *s)
|
||||
s->command_acl = "";
|
||||
s->command_dir = xasprintf("%s/commands", ofp_pkgdatadir);
|
||||
s->netflow_dst = NULL;
|
||||
s->emerg_flow = false;
|
||||
for (;;) {
|
||||
int c;
|
||||
|
||||
@@ -752,6 +759,10 @@ parse_options(int argc, char *argv[], struct settings *s)
|
||||
s->netflow_dst = optarg;
|
||||
break;
|
||||
|
||||
case OPT_EMERG_FLOW:
|
||||
s->emerg_flow = true;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (s->n_listeners >= MAX_MGMT) {
|
||||
ofp_fatal(0,
|
||||
@@ -889,6 +900,7 @@ usage(void)
|
||||
" --stp enable 802.1D Spanning Tree Protocol\n"
|
||||
" --no-stp disable 802.1D Spanning Tree Protocol\n"
|
||||
" --netflow=HOST:PORT send NetFlow v5 messages when flows end\n"
|
||||
" --emerg-flow enable emergency flow protection/restoration\n"
|
||||
"\nRate-limiting of \"packet-in\" messages to the controller:\n"
|
||||
" --rate-limit[=PACKETS] max rate, in packets/s (default: 1000)\n"
|
||||
" --burst-limit=BURST limit on packet credit for idle time\n"
|
||||
|
||||
@@ -91,6 +91,9 @@ struct settings {
|
||||
|
||||
/* NetFlow logging. */
|
||||
char *netflow_dst; /* Host and port to send NetFlow traffic. */
|
||||
|
||||
/* Emergency flow protection/restoration behavior. */
|
||||
bool emerg_flow;
|
||||
};
|
||||
|
||||
struct half {
|
||||
|
||||
@@ -15,6 +15,8 @@ udatapath_ofdatapath_SOURCES = \
|
||||
udatapath/nx_msg.c \
|
||||
udatapath/nx_msg.h \
|
||||
udatapath/udatapath.c \
|
||||
udatapath/private-msg.c \
|
||||
udatapath/private-msg.h \
|
||||
udatapath/switch-flow.c \
|
||||
udatapath/switch-flow.h \
|
||||
udatapath/table.h \
|
||||
|
||||
+55
-18
@@ -45,7 +45,7 @@
|
||||
/* Attempts to append 'table' to the set of tables in 'chain'. Returns 0 or
|
||||
* negative error. If 'table' is null it is assumed that table creation failed
|
||||
* due to out-of-memory. */
|
||||
static int add_table(struct sw_chain *chain, struct sw_table *table)
|
||||
static int add_table(struct sw_chain *chain, struct sw_table *table, int emerg)
|
||||
{
|
||||
if (table == NULL)
|
||||
return -ENOMEM;
|
||||
@@ -54,7 +54,10 @@ static int add_table(struct sw_chain *chain, struct sw_table *table)
|
||||
table->destroy(table);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
chain->tables[chain->n_tables++] = table;
|
||||
if (emerg)
|
||||
chain->emerg_table = table;
|
||||
else
|
||||
chain->tables[chain->n_tables++] = table;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -68,8 +71,10 @@ struct sw_chain *chain_create(struct datapath *dp)
|
||||
|
||||
chain->dp = dp;
|
||||
if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
|
||||
0x741B8CD7, TABLE_HASH_MAX_FLOWS))
|
||||
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) {
|
||||
0x741B8CD7, TABLE_HASH_MAX_FLOWS),
|
||||
0)
|
||||
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS), 0)
|
||||
|| add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS), 1)) {
|
||||
chain_destroy(chain);
|
||||
return NULL;
|
||||
}
|
||||
@@ -80,20 +85,32 @@ struct sw_chain *chain_create(struct datapath *dp)
|
||||
/* Searches 'chain' for a flow matching 'key', which must not have any wildcard
|
||||
* fields. Returns the flow if successful, otherwise a null pointer. */
|
||||
struct sw_flow *
|
||||
chain_lookup(struct sw_chain *chain, const struct sw_flow_key *key)
|
||||
chain_lookup(struct sw_chain *chain, const struct sw_flow_key *key, int emerg)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(!key->wildcards);
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
struct sw_flow *flow = t->lookup(t, key);
|
||||
t->n_lookup++;
|
||||
if (flow) {
|
||||
t->n_matched++;
|
||||
return flow;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
struct sw_flow *flow = t->lookup(t, key);
|
||||
t->n_lookup++;
|
||||
if (flow) {
|
||||
t->n_matched++;
|
||||
return flow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -103,14 +120,20 @@ chain_lookup(struct sw_chain *chain, const struct sw_flow_key *key)
|
||||
* If successful, 'flow' becomes owned by the chain, otherwise it is retained
|
||||
* by the caller. */
|
||||
int
|
||||
chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
chain_insert(struct sw_chain *chain, struct sw_flow *flow, int emerg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
if (t->insert(t, flow))
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (t->insert(t, flow))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOBUFS;
|
||||
@@ -125,14 +148,20 @@ chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
int
|
||||
chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t priority, int strict,
|
||||
const struct ofp_action_header *actions, size_t actions_len)
|
||||
const struct ofp_action_header *actions, size_t actions_len,
|
||||
int emerg)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
count += t->modify(t, key, priority, strict, actions, actions_len);
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
count += t->modify(t, key, priority, strict, actions, actions_len);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -147,15 +176,20 @@ chain_modify(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
* iterating through the entire contents of each table for keys that contain
|
||||
* wildcards. Relatively cheap for fully specified keys. */
|
||||
int
|
||||
chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t out_port, uint16_t priority, int strict)
|
||||
chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t out_port, uint16_t priority, int strict, int emerg)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (emerg) {
|
||||
struct sw_table *t = chain->emerg_table;
|
||||
count += t->delete(chain->dp, t, key, out_port, priority, strict);
|
||||
} else {
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
count += t->delete(chain->dp, t, key, out_port, priority, strict);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -183,10 +217,13 @@ void
|
||||
chain_destroy(struct sw_chain *chain)
|
||||
{
|
||||
int i;
|
||||
struct sw_table *t;
|
||||
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
t = chain->tables[i];
|
||||
t->destroy(t);
|
||||
}
|
||||
t = chain->emerg_table;
|
||||
t->destroy(t);
|
||||
free(chain);
|
||||
}
|
||||
+8
-6
@@ -51,19 +51,21 @@ struct datapath;
|
||||
/* Set of tables chained together in sequence from cheap to expensive. */
|
||||
#define CHAIN_MAX_TABLES 4
|
||||
struct sw_chain {
|
||||
int n_tables;
|
||||
int n_tables; /* Number of working tables, not includes
|
||||
* protection (emergency) table. */
|
||||
struct sw_table *tables[CHAIN_MAX_TABLES];
|
||||
struct sw_table *emerg_table;
|
||||
|
||||
struct datapath *dp;
|
||||
};
|
||||
|
||||
struct sw_chain *chain_create(struct datapath *);
|
||||
struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *);
|
||||
int chain_insert(struct sw_chain *, struct sw_flow *);
|
||||
int chain_modify(struct sw_chain *, const struct sw_flow_key *,
|
||||
uint16_t, int, const struct ofp_action_header *, size_t);
|
||||
struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *, int);
|
||||
int chain_insert(struct sw_chain *, struct sw_flow *, int);
|
||||
int chain_modify(struct sw_chain *, const struct sw_flow_key *,
|
||||
uint16_t, int, const struct ofp_action_header *, size_t, int);
|
||||
int chain_delete(struct sw_chain *, const struct sw_flow_key *, uint16_t,
|
||||
uint16_t, int);
|
||||
uint16_t, int, int);
|
||||
void chain_timeout(struct sw_chain *, struct list *deleted);
|
||||
void chain_destroy(struct sw_chain *);
|
||||
|
||||
|
||||
+53
-24
@@ -55,6 +55,7 @@
|
||||
#include "vconn.h"
|
||||
#include "xtoxll.h"
|
||||
#include "nx_msg.h"
|
||||
#include "private-msg.h"
|
||||
#include "dp_act.h"
|
||||
|
||||
#define THIS_MODULE VLM_datapath
|
||||
@@ -835,7 +836,8 @@ int run_flow_through_tables(struct datapath *dp, struct ofpbuf *buffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
flow = chain_lookup(dp->chain, &key);
|
||||
|
||||
flow = chain_lookup(dp->chain, &key, 0);
|
||||
if (flow != NULL) {
|
||||
flow_used(flow, buffer);
|
||||
execute_actions(dp, buffer, &key, flow->sf_acts->actions,
|
||||
@@ -986,14 +988,16 @@ add_flow(struct datapath *dp, const struct sender *sender,
|
||||
flow_setup_actions(flow, ofm->actions, actions_len);
|
||||
|
||||
/* Act. */
|
||||
error = chain_insert(dp->chain, flow);
|
||||
error = chain_insert(dp->chain, flow,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0);
|
||||
if (error == -ENOBUFS) {
|
||||
dp_send_error_msg(dp, sender, OFPET_FLOW_MOD_FAILED,
|
||||
OFPFMFC_ALL_TABLES_FULL, ofm, ntohs(ofm->header.length));
|
||||
goto error_free_flow;
|
||||
} else if (error) {
|
||||
goto error_free_flow;
|
||||
goto error_free_flow;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
if (ntohl(ofm->buffer_id) != UINT32_MAX) {
|
||||
struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
|
||||
@@ -1042,7 +1046,8 @@ mod_flow(struct datapath *dp, const struct sender *sender,
|
||||
|
||||
priority = key.wildcards ? ntohs(ofm->priority) : -1;
|
||||
strict = (ofm->command == htons(OFPFC_MODIFY_STRICT)) ? 1 : 0;
|
||||
chain_modify(dp->chain, &key, priority, strict, ofm->actions, actions_len);
|
||||
chain_modify(dp->chain, &key, priority, strict, ofm->actions, actions_len,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0);
|
||||
|
||||
if (ntohl(ofm->buffer_id) != UINT32_MAX) {
|
||||
struct ofpbuf *buffer = retrieve_buffer(ntohl(ofm->buffer_id));
|
||||
@@ -1078,14 +1083,17 @@ recv_flow(struct datapath *dp, const struct sender *sender,
|
||||
} else if (command == OFPFC_DELETE) {
|
||||
struct sw_flow_key key;
|
||||
flow_extract_match(&key, &ofm->match);
|
||||
return chain_delete(dp->chain, &key, ofm->out_port, 0, 0) ? 0 : -ESRCH;
|
||||
return chain_delete(dp->chain, &key, ofm->out_port, 0, 0,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0)
|
||||
? 0 : -ESRCH;
|
||||
} else if (command == OFPFC_DELETE_STRICT) {
|
||||
struct sw_flow_key key;
|
||||
uint16_t priority;
|
||||
flow_extract_match(&key, &ofm->match);
|
||||
priority = key.wildcards ? ntohs(ofm->priority) : -1;
|
||||
return chain_delete(dp->chain, &key, ofm->out_port,
|
||||
priority, 1) ? 0 : -ESRCH;
|
||||
return chain_delete(dp->chain, &key, ofm->out_port, priority, 1,
|
||||
(ntohs(ofm->flags) & OFPFF_EMERG) ? 1 : 0)
|
||||
? 0 : -ESRCH;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -1115,6 +1123,7 @@ struct flow_stats_state {
|
||||
};
|
||||
|
||||
#define MAX_FLOW_STATS_BYTES 4096
|
||||
#define EMERG_TABLE_ID_FOR_STATS 0xfe
|
||||
|
||||
static int
|
||||
flow_stats_init(const void *body, int body_len UNUSED, void **state)
|
||||
@@ -1144,17 +1153,25 @@ static int flow_stats_dump(struct datapath *dp, void *state,
|
||||
flow_extract_match(&match_key, &s->rq.match);
|
||||
s->buffer = buffer;
|
||||
s->now = time_msec();
|
||||
while (s->table_idx < dp->chain->n_tables
|
||||
&& (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[s->table_idx];
|
||||
|
||||
if (table->iterate(table, &match_key, s->rq.out_port,
|
||||
&s->position, flow_stats_dump_callback, s))
|
||||
break;
|
||||
if (s->rq.table_id == EMERG_TABLE_ID_FOR_STATS) {
|
||||
struct sw_table *table = dp->chain->emerg_table;
|
||||
|
||||
s->table_idx++;
|
||||
memset(&s->position, 0, sizeof s->position);
|
||||
table->iterate(table, &match_key, s->rq.out_port,
|
||||
&s->position, flow_stats_dump_callback, s);
|
||||
} else {
|
||||
while (s->table_idx < dp->chain->n_tables
|
||||
&& (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[s->table_idx];
|
||||
|
||||
if (table->iterate(table, &match_key, s->rq.out_port,
|
||||
&s->position, flow_stats_dump_callback, s))
|
||||
break;
|
||||
|
||||
s->table_idx++;
|
||||
memset(&s->position, 0, sizeof s->position);
|
||||
}
|
||||
}
|
||||
return s->buffer->size >= MAX_FLOW_STATS_BYTES;
|
||||
}
|
||||
@@ -1196,6 +1213,7 @@ static int aggregate_stats_dump(struct datapath *dp, void *state,
|
||||
struct sw_table_position position;
|
||||
struct sw_flow_key match_key;
|
||||
int table_idx;
|
||||
int error;
|
||||
|
||||
rpy = ofpbuf_put_uninit(buffer, sizeof *rpy);
|
||||
memset(rpy, 0, sizeof *rpy);
|
||||
@@ -1203,19 +1221,28 @@ static int aggregate_stats_dump(struct datapath *dp, void *state,
|
||||
flow_extract_match(&match_key, &rq->match);
|
||||
table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
|
||||
memset(&position, 0, sizeof position);
|
||||
while (table_idx < dp->chain->n_tables
|
||||
&& (rq->table_id == 0xff || rq->table_id == table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[table_idx];
|
||||
int error;
|
||||
|
||||
error = table->iterate(table, &match_key, rq->out_port, &position,
|
||||
if (rq->table_id == EMERG_TABLE_ID_FOR_STATS) {
|
||||
struct sw_table *table = dp->chain->emerg_table;
|
||||
|
||||
error = table->iterate(table, &match_key, rq->out_port, &position,
|
||||
aggregate_stats_dump_callback, rpy);
|
||||
if (error)
|
||||
return error;
|
||||
} else {
|
||||
while (table_idx < dp->chain->n_tables
|
||||
&& (rq->table_id == 0xff || rq->table_id == table_idx))
|
||||
{
|
||||
struct sw_table *table = dp->chain->tables[table_idx];
|
||||
|
||||
table_idx++;
|
||||
memset(&position, 0, sizeof position);
|
||||
error = table->iterate(table, &match_key, rq->out_port, &position,
|
||||
aggregate_stats_dump_callback, rpy);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
table_idx++;
|
||||
memset(&position, 0, sizeof position);
|
||||
}
|
||||
}
|
||||
|
||||
rpy->packet_count = htonll(rpy->packet_count);
|
||||
@@ -1582,6 +1609,8 @@ recv_vendor(struct datapath *dp, const struct sender *sender,
|
||||
return nx_recv_msg(dp, sender, oh);
|
||||
|
||||
case PRIVATE_VENDOR_ID:
|
||||
return private_recv_msg(dp, sender, oh);
|
||||
|
||||
default:
|
||||
VLOG_WARN_RL(&rl, "unknown vendor: 0x%x\n", ntohl(ovh->vendor));
|
||||
dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST,
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009
|
||||
* The Board of Trustees of The Leland Stanford Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation that
|
||||
* others will use, modify and enhance the Software and contribute those
|
||||
* enhancements back to the community. However, since we would like to make the
|
||||
* Software available for broadest use, with as few restrictions as possible
|
||||
* permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this Software to deal in the Software under the copyrights without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any derivatives
|
||||
* without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "openflow/private-ext.h"
|
||||
|
||||
#include "chain.h"
|
||||
#include "datapath.h"
|
||||
#include "switch-flow.h"
|
||||
#include "table.h"
|
||||
#include "private-msg.h"
|
||||
|
||||
struct emerg_flow_context {
|
||||
struct datapath *dp;
|
||||
};
|
||||
|
||||
static void flush_working(struct datapath *);
|
||||
static int protection_callback(struct sw_flow *, void *);
|
||||
static void do_protection(struct datapath *);
|
||||
|
||||
static void
|
||||
flush_working(struct datapath *dp)
|
||||
{
|
||||
struct sw_flow_key key;
|
||||
int num_deleted = 0;
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.wildcards = OFPFW_ALL;
|
||||
num_deleted = chain_delete(dp->chain, &key, OFPP_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
protection_callback(struct sw_flow *flow, void *private_)
|
||||
{
|
||||
struct emerg_flow_context *private
|
||||
= (struct emerg_flow_context *)private_;
|
||||
struct sw_flow_actions *actions = flow->sf_acts;
|
||||
struct ofp_match match;
|
||||
struct sw_flow *tgtflow = NULL;
|
||||
int error = 0;
|
||||
|
||||
tgtflow = flow_alloc(flow->sf_acts->actions_len);
|
||||
if (tgtflow == NULL)
|
||||
return -ENOBUFS;
|
||||
|
||||
/* Dup w/o idle and hard timeout. */
|
||||
memset(&match, 0, sizeof(match));
|
||||
flow_fill_match(&match, &flow->key.flow, flow->key.wildcards);
|
||||
flow_extract_match(&tgtflow->key, &match);
|
||||
tgtflow->priority = flow->priority;
|
||||
tgtflow->idle_timeout = 0;
|
||||
tgtflow->hard_timeout = 0;
|
||||
flow_setup_actions(tgtflow, actions->actions, actions->actions_len);
|
||||
|
||||
error = chain_insert(private->dp->chain, tgtflow, 0);
|
||||
if (error)
|
||||
flow_free(tgtflow);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
do_protection(struct datapath *dp)
|
||||
{
|
||||
struct emerg_flow_context private;
|
||||
struct sw_flow_key key;
|
||||
struct sw_table_position position;
|
||||
struct sw_table *table = dp->chain->emerg_table;
|
||||
int error = 0;
|
||||
|
||||
memset(&private, 0, sizeof(private));
|
||||
private.dp = dp;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.wildcards = OFPFW_ALL;
|
||||
memset(&position, 0, sizeof(position));
|
||||
|
||||
error = table->iterate(table, &key, OFPP_NONE,
|
||||
&position, protection_callback, &private);
|
||||
}
|
||||
|
||||
int
|
||||
private_recv_msg(struct datapath *dp, const struct sender *sender,
|
||||
const void *ofph)
|
||||
{
|
||||
struct private_vxhdr *vxhdr = ofph;
|
||||
struct private_vxopt *vxopt = (vxhdr + 1);
|
||||
int error = 0;
|
||||
|
||||
switch (ntohs(vxopt->pvo_type)) {
|
||||
case PRIVATEOPT_PROTOCOL_STATS_REQUEST:
|
||||
case PRIVATEOPT_PROTOCOL_STATS_REPLY:
|
||||
break;
|
||||
case PRIVATEOPT_EMERG_FLOW_PROTECTION:
|
||||
flush_working(dp);
|
||||
do_protection(dp);
|
||||
break;
|
||||
case PRIVATEOPT_EMERG_FLOW_RESTORATION:
|
||||
/* Nothing to do because we assume that a re-connected
|
||||
* controller will do flush current working flow table. */
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* Copyright (c) 2008, 2009
|
||||
* The Board of Trustees of The Leland Stanford Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation that
|
||||
* others will use, modify and enhance the Software and contribute those
|
||||
* enhancements back to the community. However, since we would like to make the
|
||||
* Software available for broadest use, with as few restrictions as possible
|
||||
* permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this Software to deal in the Software under the copyrights without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any derivatives
|
||||
* without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#ifndef PRIVATE_MSG_H_
|
||||
#define PRIVATE_MSG_H_
|
||||
|
||||
#include "datapath.h"
|
||||
|
||||
struct sender;
|
||||
|
||||
int private_recv_msg(struct datapath *, const struct sender *, const void *);
|
||||
|
||||
#endif
|
||||
+23
-7
@@ -186,6 +186,12 @@ Add flow entries as described in \fIfile\fR to the datapath \fIswitch\fR's
|
||||
tables. Each line in \fIfile\fR is a flow entry in the format
|
||||
described in \fBFLOW SYNTAX\fR, below.
|
||||
|
||||
.TP
|
||||
\fBadd-emerg-flow \fIswitch flow\fR
|
||||
Add the emergency flow entry as described by \fIflow\fR to the datapath
|
||||
\fIswitch\fR's tables. The flow entry is in the format described in
|
||||
\fBFLOW SYNTAX\fR, below.
|
||||
|
||||
.TP
|
||||
\fBmod-flows \fIswitch flow\fR
|
||||
Modify the actions in entries from the datapath \fIswitch\fR's tables
|
||||
@@ -202,6 +208,15 @@ omitted and the \fB--strict\fR option is not used, all flows in the
|
||||
datapath's tables are removed. See \fBFLOW SYNTAX\fR, below, for the
|
||||
syntax of \fIflows\fR.
|
||||
|
||||
.TP
|
||||
\fBdel-emerg-flows \fIswitch \fR[\fIflow\fR]
|
||||
Deletes emergency entries from the datapath \fIswitch\fR's tables that match
|
||||
\fIflow\fR. When invoked with the \fB--strict\fR option, wildcards are
|
||||
not treated as active for matching purposes. If \fIflow\fR is
|
||||
omitted and the \fB--strict\fR option is not used, all flows in the
|
||||
datapath's tables are removed. See \fBFLOW SYNTAX\fR, below, for the
|
||||
syntax of \fIflows\fR.
|
||||
|
||||
.TP
|
||||
\fBmonitor \fIswitch\fR
|
||||
Connects to \fIswitch\fR and prints to the console all OpenFlow
|
||||
@@ -339,7 +354,8 @@ Same as \fBdl_type=0x0800,nw_proto=17\fR.
|
||||
Same as \fBdl_type=0x0806\fR.
|
||||
|
||||
.PP
|
||||
The \fBadd-flow\fR and \fBadd-flows\fR commands require an additional field:
|
||||
The \fBadd-flow\fR, \fBadd-flows\fR and \fBadd-emerg-flow\fR commands
|
||||
require an additional field:
|
||||
|
||||
.IP \fIactions\fB=\fItarget\fR[\fB,\fItarget\fR...]\fR
|
||||
Specifies a comma-separated list of actions to take on a packet when the
|
||||
@@ -403,8 +419,8 @@ Strips the VLAN tag from a packet if it is present.
|
||||
not yet expose to the user.)
|
||||
|
||||
.PP
|
||||
The \fBadd-flow\fR, \fBadd-flows\fR, and \fBdel-flows\fR commands
|
||||
support an additional optional field:
|
||||
The \fBadd-flow\fR, \fBadd-flows\fR, \fBadd-emerg-flow\fR, \fBdel-flows\fR,
|
||||
and \fBdel-emerg-flows\fR commands support an additional optional field:
|
||||
|
||||
.IP \fBpriority=\fIvalue\fR
|
||||
Sets the priority of the flow to be added or deleted to \fIvalue\fR,
|
||||
@@ -412,8 +428,8 @@ which should be a number between 0 and 65535, inclusive. If this
|
||||
field is not specified, it defaults to 32768.
|
||||
|
||||
.PP
|
||||
The \fBadd-flow\fR and \fBadd-flows\fR commands support additional
|
||||
optional fields:
|
||||
The \fBadd-flow\fR, \fBadd-flows\fR and \fBadd-emerg-flow\fR commands support
|
||||
additional optional fields:
|
||||
|
||||
.TP
|
||||
\fBidle_timeout=\fIseconds\fR
|
||||
@@ -427,8 +443,8 @@ regardless of activity. A value of 0 (the default) gives the flow no
|
||||
hard expiration deadline.
|
||||
|
||||
.PP
|
||||
The \fBdump-flows\fR, \fBdump-aggregate\fR, \fBdel-flow\fR
|
||||
and \fBdel-flows\fR commands support the additional optional field:
|
||||
The \fBdump-flows\fR, \fBdump-aggregate\fR, \fBdel-flows\fR
|
||||
and \fBdel-emerg-flows\fR commands support the additional optional field:
|
||||
|
||||
.TP
|
||||
\fBout_port=\fIport\fR
|
||||
|
||||
@@ -237,8 +237,10 @@ usage(void)
|
||||
" dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n"
|
||||
" add-flow SWITCH FLOW add flow described by FLOW\n"
|
||||
" add-flows SWITCH FILE add flows from FILE\n"
|
||||
" add-emerg-flow SWITCH FLOW add emergency flow described by FLOW\n"
|
||||
" mod-flows SWITCH FLOW modify actions of matching FLOWs\n"
|
||||
" del-flows SWITCH [FLOW] delete matching FLOWs\n"
|
||||
" del-emerg-flows SWITCH [FLOW] delete matching emergency FLOWs\n"
|
||||
" monitor SWITCH print packets received from SWITCH\n"
|
||||
" execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
|
||||
"\nFor local datapaths, remote switches, and controllers:\n"
|
||||
@@ -1078,6 +1080,36 @@ do_add_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
do_add_emerg_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
{
|
||||
struct vconn *vconn;
|
||||
struct ofpbuf *buffer;
|
||||
struct ofp_flow_mod *ofm;
|
||||
uint16_t priority, idle_timeout, hard_timeout, flags;
|
||||
struct ofp_match match;
|
||||
|
||||
/* Parse and send. str_to_flow() will expand and reallocate the data in
|
||||
* 'buffer', so we can't keep pointers to across the str_to_flow() call. */
|
||||
make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
|
||||
str_to_flow(argv[2], &match, buffer,
|
||||
NULL, NULL, &priority, &idle_timeout, &hard_timeout);
|
||||
ofm = buffer->data;
|
||||
ofm->match = match;
|
||||
ofm->command = htons(OFPFC_ADD);
|
||||
ofm->idle_timeout = htons(idle_timeout);
|
||||
ofm->hard_timeout = htons(hard_timeout);
|
||||
ofm->buffer_id = htonl(UINT32_MAX);
|
||||
ofm->priority = htons(priority);
|
||||
flags = OFPFF_EMERG;
|
||||
ofm->flags = htons(flags);
|
||||
ofm->reserved = htonl(0);
|
||||
|
||||
open_vconn(argv[1], &vconn);
|
||||
send_openflow_buffer(vconn, buffer);
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
do_add_flows(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
{
|
||||
@@ -1188,6 +1220,37 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void do_del_emerg_flows(const struct settings *s, int argc, char *argv[])
|
||||
{
|
||||
struct vconn *vconn;
|
||||
uint16_t priority;
|
||||
uint16_t out_port;
|
||||
uint16_t flags;
|
||||
struct ofpbuf *buffer;
|
||||
struct ofp_flow_mod *ofm;
|
||||
|
||||
/* Parse and send. */
|
||||
ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
|
||||
str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL,
|
||||
&out_port, &priority, NULL, NULL);
|
||||
if (s->strict) {
|
||||
ofm->command = htons(OFPFC_DELETE_STRICT);
|
||||
} else {
|
||||
ofm->command = htons(OFPFC_DELETE);
|
||||
}
|
||||
ofm->idle_timeout = htons(0);
|
||||
ofm->hard_timeout = htons(0);
|
||||
ofm->buffer_id = htonl(UINT32_MAX);
|
||||
ofm->out_port = htons(out_port);
|
||||
ofm->priority = htons(priority);
|
||||
flags = OFPFF_EMERG;
|
||||
ofm->flags = htons(flags);
|
||||
ofm->reserved = htonl(0);
|
||||
|
||||
open_vconn(argv[1], &vconn);
|
||||
send_openflow_buffer(vconn, buffer);
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
do_monitor(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
@@ -1498,8 +1561,10 @@ static struct command all_commands[] = {
|
||||
{ "dump-aggregate", 1, 2, do_dump_aggregate },
|
||||
{ "add-flow", 2, 2, do_add_flow },
|
||||
{ "add-flows", 2, 2, do_add_flows },
|
||||
{ "add-emerg-flow", 2, 2, do_add_emerg_flow },
|
||||
{ "mod-flows", 2, 2, do_mod_flows },
|
||||
{ "del-flows", 1, 2, do_del_flows },
|
||||
{ "del-emerg-flows", 1, 2, do_del_emerg_flows },
|
||||
{ "dump-ports", 1, 1, do_dump_ports },
|
||||
{ "mod-port", 3, 3, do_mod_port },
|
||||
{ "probe", 1, 1, do_probe },
|
||||
|
||||
Reference in new issue
Block a user