This updates the OpenFlow protocol version and changes the names of
structure members, so any software that uses OpenFlow will need to be
updated to match.
16 files changed:
-dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow)
+dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow,
+ enum ofp_flow_expired_reason reason)
{
struct sk_buff *skb;
struct ofp_flow_expired *ofe;
{
struct sk_buff *skb;
struct ofp_flow_expired *ofe;
- unsigned long duration_j;
+
+ if (!(dp->flags & OFPC_SEND_FLOW_EXP))
+ return 0;
ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb);
if (!ofe)
ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb);
if (!ofe)
flow_fill_match(&ofe->match, &flow->key);
flow_fill_match(&ofe->match, &flow->key);
- memset(ofe->pad, 0, sizeof ofe->pad);
ofe->priority = htons(flow->priority);
ofe->priority = htons(flow->priority);
+ ofe->reason = reason;
+ memset(ofe->pad, 0, sizeof ofe->pad);
- duration_j = (flow->timeout - HZ * flow->max_idle) - flow->init_time;
- ofe->duration = htonl(duration_j / HZ);
+ ofe->duration = htonl((jiffies - flow->init_time) / HZ);
+ memset(ofe->pad2, 0, sizeof ofe->pad2);
ofe->packet_count = cpu_to_be64(flow->packet_count);
ofe->byte_count = cpu_to_be64(flow->byte_count);
ofe->packet_count = cpu_to_be64(flow->packet_count);
ofe->byte_count = cpu_to_be64(flow->byte_count);
ofs->match.tp_src = flow->key.tp_src;
ofs->match.tp_dst = flow->key.tp_dst;
ofs->duration = htonl((jiffies - flow->init_time) / HZ);
ofs->match.tp_src = flow->key.tp_src;
ofs->match.tp_dst = flow->key.tp_dst;
ofs->duration = htonl((jiffies - flow->init_time) / HZ);
+ ofs->priority = htons(flow->priority);
+ ofs->idle_timeout = htons(flow->idle_timeout);
+ ofs->hard_timeout = htons(flow->hard_timeout);
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
ofs->packet_count = cpu_to_be64(flow->packet_count);
ofs->byte_count = cpu_to_be64(flow->byte_count);
ofs->packet_count = cpu_to_be64(flow->packet_count);
ofs->byte_count = cpu_to_be64(flow->byte_count);
- ofs->priority = htons(flow->priority);
- ofs->max_idle = htons(flow->max_idle);
memcpy(ofs->actions, flow->actions, actions_length);
s->bytes_used += length;
memcpy(ofs->actions, flow->actions, actions_length);
s->bytes_used += length;
int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *);
int dp_send_features_reply(struct datapath *, const struct sender *);
int dp_send_config_reply(struct datapath *, const struct sender *);
int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *);
int dp_send_features_reply(struct datapath *, const struct sender *);
int dp_send_config_reply(struct datapath *, const struct sender *);
-int dp_send_flow_expired(struct datapath *, struct sw_flow *);
+int dp_send_flow_expired(struct datapath *, struct sw_flow *,
+ enum ofp_flow_expired_reason);
int dp_send_error_msg(struct datapath *, const struct sender *,
uint16_t, uint16_t, const uint8_t *, size_t);
int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
int dp_send_error_msg(struct datapath *, const struct sender *,
uint16_t, uint16_t, const uint8_t *, size_t);
int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
#include <linux/if_vlan.h>
#include <net/llc_pdu.h>
#include <linux/ip.h>
#include <linux/if_vlan.h>
#include <net/llc_pdu.h>
#include <linux/ip.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tcp.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tcp.h>
memset(to->pad, '\0', sizeof(to->pad));
}
memset(to->pad, '\0', sizeof(to->pad));
}
+int flow_timeout(struct sw_flow *flow)
+{
+ if (flow->idle_timeout != OFP_FLOW_PERMANENT
+ && time_after(jiffies, flow->used + flow->idle_timeout * HZ))
+ return OFPER_IDLE_TIMEOUT;
+ else if (flow->hard_timeout != OFP_FLOW_PERMANENT
+ && time_after(jiffies,
+ flow->init_time + flow->hard_timeout * HZ))
+ return OFPER_HARD_TIMEOUT;
+ else
+ return -1;
+}
+EXPORT_SYMBOL(flow_timeout);
+
/* Allocates and returns a new flow with 'n_actions' action, using allocation
* flags 'flags'. Returns the new flow or a null pointer on failure. */
struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
/* Allocates and returns a new flow with 'n_actions' action, using allocation
* flags 'flags'. Returns the new flow or a null pointer on failure. */
struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
struct sw_flow {
struct sw_flow_key key;
struct sw_flow {
struct sw_flow_key key;
- uint16_t max_idle; /* Idle time before discarding (seconds). */
uint16_t priority; /* Only used on entries with wildcards. */
uint16_t priority; /* Only used on entries with wildcards. */
- unsigned long timeout; /* Expiration time (in jiffies). */
+ uint16_t idle_timeout; /* Idle time before discarding (seconds). */
+ uint16_t hard_timeout; /* Hard expiration time (seconds) */
+ unsigned long used; /* Last used time (in jiffies). */
/* FIXME? Probably most flows have only a single action. */
unsigned int n_actions;
/* FIXME? Probably most flows have only a single action. */
unsigned int n_actions;
int flow_extract(struct sk_buff *, uint16_t in_port, struct sw_flow_key *);
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
int flow_extract(struct sk_buff *, uint16_t in_port, struct sw_flow_key *);
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
+int flow_timeout(struct sw_flow *);
void print_flow(const struct sw_flow_key *);
void print_flow(const struct sw_flow_key *);
-#include <linux/jiffies.h>
-static inline int flow_timeout(struct sw_flow *flow)
-{
- if (flow->max_idle == OFP_FLOW_PERMANENT)
- return 0;
-
- return time_after(jiffies, flow->timeout);
-}
-
static inline void flow_used(struct sw_flow *flow, struct sk_buff *skb)
{
unsigned long flags;
static inline void flow_used(struct sw_flow *flow, struct sk_buff *skb)
{
unsigned long flags;
- if (flow->max_idle != OFP_FLOW_PERMANENT)
- flow->timeout = jiffies + HZ * flow->max_idle;
spin_lock_irqsave(&flow->lock, flags);
flow->packet_count++;
spin_lock_irqsave(&flow->lock, flags);
flow->packet_count++;
/* Fill out flow. */
flow_extract_match(&flow->key, &ofm->match);
/* Fill out flow. */
flow_extract_match(&flow->key, &ofm->match);
- flow->max_idle = ntohs(ofm->max_idle);
flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1;
flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1;
- flow->timeout = jiffies + flow->max_idle * HZ;
+ flow->idle_timeout = ntohs(ofm->idle_timeout);
+ flow->hard_timeout = ntohs(ofm->hard_timeout);
+ flow->used = jiffies;
flow->n_actions = n_acts;
flow->init_time = jiffies;
flow->byte_count = 0;
flow->n_actions = n_acts;
flow->init_time = jiffies;
flow->byte_count = 0;
flow->timeout = jiffies + HZ * flow->max_idle;
}
flow->timeout = jiffies + HZ * flow->max_idle;
}
- if (flow_timeout(flow)) {
+ reason = flow_timeout(flow);
+ if (reason >= 0) {
if (dp->flags & OFPC_SEND_FLOW_EXP) {
/* xxx Get byte count */
flow->byte_count = 0;
if (dp->flags & OFPC_SEND_FLOW_EXP) {
/* xxx Get byte count */
flow->byte_count = 0;
- dp_send_flow_expired(dp, flow);
+ dp_send_flow_expired(dp, flow, reason);
}
del_count += do_delete(swt, flow);
}
}
del_count += do_delete(swt, flow);
}
for (i = 0; i <= th->bucket_mask; i++) {
struct sw_flow **bucket = &th->buckets[i];
struct sw_flow *flow = *bucket;
for (i = 0; i <= th->bucket_mask; i++) {
struct sw_flow **bucket = &th->buckets[i];
struct sw_flow *flow = *bucket;
- if (flow && flow_timeout(flow)) {
- count += do_delete(bucket, flow);
- if (dp->flags & OFPC_SEND_FLOW_EXP)
- dp_send_flow_expired(dp, flow);
+ if (flow) {
+ int reason = flow_timeout(flow);
+ if (reason >= 0) {
+ count += do_delete(bucket, flow);
+ dp_send_flow_expired(dp, flow, reason);
+ }
}
}
th->n_flows -= count;
}
}
th->n_flows -= count;
mutex_lock(&dp_mutex);
list_for_each_entry (flow, &tl->flows, node) {
mutex_lock(&dp_mutex);
list_for_each_entry (flow, &tl->flows, node) {
- if (flow_timeout(flow)) {
+ int reason = flow_timeout(flow);
+ if (reason >= 0) {
count += do_delete(swt, flow);
count += do_delete(swt, flow);
- if (dp->flags & OFPC_SEND_FLOW_EXP)
- dp_send_flow_expired(dp, flow);
+ dp_send_flow_expired(dp, flow, reason);
}
}
tl->n_flows -= count;
}
}
tl->n_flows -= count;
/* The most significant bit being set in the version field indicates an
* experimental OpenFlow version.
*/
/* The most significant bit being set in the version field indicates an
* experimental OpenFlow version.
*/
-#define OFP_VERSION 0x85
+#define OFP_VERSION 0x86
#define OFP_MAX_TABLE_NAME_LEN 32
#define OFP_MAX_PORT_NAME_LEN 16
#define OFP_MAX_TABLE_NAME_LEN 32
#define OFP_MAX_PORT_NAME_LEN 16
OFP_ASSERT(sizeof(struct ofp_port_mod) == 44);
/* Why is this packet being sent to the controller? */
OFP_ASSERT(sizeof(struct ofp_port_mod) == 44);
/* Why is this packet being sent to the controller? */
+enum ofp_packet_in_reason {
OFPR_NO_MATCH, /* No matching flow. */
OFPR_ACTION /* Action explicitly output to controller. */
};
OFPR_NO_MATCH, /* No matching flow. */
OFPR_ACTION /* Action explicitly output to controller. */
};
};
OFP_ASSERT(sizeof(struct ofp_match) == 36);
};
OFP_ASSERT(sizeof(struct ofp_match) == 36);
-/* Value used in "max_idle" to indicate that the entry is permanent */
+/* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry
+ * is permanent. */
#define OFP_FLOW_PERMANENT 0
/* By default, choose a priority in the middle */
#define OFP_FLOW_PERMANENT 0
/* By default, choose a priority in the middle */
/* Flow actions. */
uint16_t command; /* One of OFPFC_*. */
/* Flow actions. */
uint16_t command; /* One of OFPFC_*. */
- uint16_t max_idle; /* Idle time before discarding (seconds). */
- uint32_t buffer_id; /* Buffered packet to apply to (or -1). */
+ uint16_t idle_timeout; /* Idle time before discarding (seconds). */
+ uint16_t hard_timeout; /* Max time before discarding (seconds). */
uint16_t priority; /* Priority level of flow entry. */
uint16_t priority; /* Priority level of flow entry. */
- uint8_t pad[2]; /* Align to 32-bits. */
+ uint32_t buffer_id; /* Buffered packet to apply to (or -1). */
uint32_t reserved; /* Reserved for future use. */
struct ofp_action actions[0]; /* The number of actions is inferred from
the length field in the header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 60);
uint32_t reserved; /* Reserved for future use. */
struct ofp_action actions[0]; /* The number of actions is inferred from
the length field in the header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 60);
+/* Why did this flow expire? */
+enum ofp_flow_expired_reason {
+ OFPER_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */
+ OFPER_HARD_TIMEOUT /* Time exceeded hard_timeout. */
+};
+
/* Flow expiration (datapath -> controller). */
struct ofp_flow_expired {
struct ofp_header header;
struct ofp_match match; /* Description of fields */
uint16_t priority; /* Priority level of flow entry. */
/* Flow expiration (datapath -> controller). */
struct ofp_flow_expired {
struct ofp_header header;
struct ofp_match match; /* Description of fields */
uint16_t priority; /* Priority level of flow entry. */
- uint8_t pad[2]; /* Align to 32-bits. */
+ uint8_t reason; /* One of OFPER_*. */
+ uint8_t pad[1]; /* Align to 32-bits. */
uint32_t duration; /* Time flow was alive in seconds. */
uint8_t pad2[4]; /* Align to 64-bits. */
uint32_t duration; /* Time flow was alive in seconds. */
uint8_t pad2[4]; /* Align to 64-bits. */
uint32_t duration; /* Time flow has been alive in seconds. */
uint16_t priority; /* Priority of the entry. Only meaningful
when this is not an exact-match entry. */
uint32_t duration; /* Time flow has been alive in seconds. */
uint16_t priority; /* Priority of the entry. Only meaningful
when this is not an exact-match entry. */
- uint16_t max_idle; /* Number of seconds idle before expiration. */
+ uint16_t idle_timeout; /* Number of seconds idle before expiration. */
+ uint16_t hard_timeout; /* Number of seconds before expiration. */
+ uint16_t pad2[3]; /* Pad to 64 bits. */
uint64_t packet_count; /* Number of packets in flow. */
uint64_t byte_count; /* Number of bytes in flow. */
struct ofp_action actions[0]; /* Actions. */
};
uint64_t packet_count; /* Number of packets in flow. */
uint64_t byte_count; /* Number of bytes in flow. */
struct ofp_action actions[0]; /* Actions. */
};
-OFP_ASSERT(sizeof(struct ofp_flow_stats) == 64);
+OFP_ASSERT(sizeof(struct ofp_flow_stats) == 72);
/* Body for ofp_stats_request of type OFPST_AGGREGATE. */
struct ofp_aggregate_stats_request {
/* Body for ofp_stats_request of type OFPST_AGGREGATE. */
struct ofp_aggregate_stats_request {
const struct ofp_flow_mod *ofm = oh;
ofp_print_match(string, &ofm->match, verbosity);
const struct ofp_flow_mod *ofm = oh;
ofp_print_match(string, &ofm->match, verbosity);
- ds_put_format(string, " cmd:%d idle:%d pri:%d buf:%#x",
- ntohs(ofm->command), ntohs(ofm->max_idle),
+ ds_put_format(string, " cmd:%d idle:%d hard:%d pri:%d buf:%#x",
+ ntohs(ofm->command), ntohs(ofm->idle_timeout),
+ ntohs(ofm->hard_timeout),
ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1,
ntohl(ofm->buffer_id));
ofp_print_actions(string, ofm->actions,
ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1,
ntohl(ofm->buffer_id));
ofp_print_actions(string, ofm->actions,
const struct ofp_flow_expired *ofe = oh;
ofp_print_match(string, &ofe->match, verbosity);
const struct ofp_flow_expired *ofe = oh;
ofp_print_match(string, &ofe->match, verbosity);
+ ds_put_cstr(string, " reason=");
+ switch (ofe->reason) {
+ case OFPER_IDLE_TIMEOUT:
+ ds_put_cstr(string, "idle");
+ break;
+ case OFPER_HARD_TIMEOUT:
+ ds_put_cstr(string, "hard");
+ break;
+ default:
+ ds_put_format(string, "**%"PRIu8"**", ofe->reason);
+ break;
+ }
ds_put_format(string,
" pri%"PRIu16" secs%"PRIu32" pkts%"PRIu64" bytes%"PRIu64"\n",
ofe->match.wildcards ? ntohs(ofe->priority) : (uint16_t)-1,
ds_put_format(string,
" pri%"PRIu16" secs%"PRIu32" pkts%"PRIu64" bytes%"PRIu64"\n",
ofe->match.wildcards ? ntohs(ofe->priority) : (uint16_t)-1,
ds_put_format(string, "n_packets=%"PRIu64", ",
ntohll(fs->packet_count));
ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count));
ds_put_format(string, "n_packets=%"PRIu64", ",
ntohll(fs->packet_count));
ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count));
- ds_put_format(string, "max_idle=%"PRIu16",", ntohs(fs->max_idle));
+ ds_put_format(string, "idle_timeout=%"PRIu16",",
+ ntohs(fs->idle_timeout));
+ ds_put_format(string, "hard_timeout=%"PRIu16",",
+ ntohs(fs->hard_timeout));
ofp_print_match(string, &fs->match, verbosity);
ofp_print_actions(string, fs->actions, length - sizeof *fs);
ds_put_char(string, '\n');
ofp_print_match(string, &fs->match, verbosity);
ofp_print_actions(string, fs->actions, length - sizeof *fs);
ds_put_char(string, '\n');
-make_add_flow(const struct flow *flow, uint32_t buffer_id, uint16_t max_idle,
- size_t n_actions)
+make_add_flow(const struct flow *flow, uint32_t buffer_id,
+ uint16_t idle_timeout, size_t n_actions)
{
struct ofp_flow_mod *ofm;
size_t size = sizeof *ofm + n_actions * sizeof ofm->actions[0];
{
struct ofp_flow_mod *ofm;
size_t size = sizeof *ofm + n_actions * sizeof ofm->actions[0];
ofm->match.tp_src = flow->tp_src;
ofm->match.tp_dst = flow->tp_dst;
ofm->command = htons(OFPFC_ADD);
ofm->match.tp_src = flow->tp_src;
ofm->match.tp_dst = flow->tp_dst;
ofm->command = htons(OFPFC_ADD);
- ofm->max_idle = htons(max_idle);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(OFP_FLOW_PERMANENT);
ofm->buffer_id = htonl(buffer_id);
return out;
}
struct buffer *
make_add_simple_flow(const struct flow *flow,
ofm->buffer_id = htonl(buffer_id);
return out;
}
struct buffer *
make_add_simple_flow(const struct flow *flow,
- uint32_t buffer_id, uint16_t out_port, uint16_t max_idle)
+ uint32_t buffer_id, uint16_t out_port,
+ uint16_t idle_timeout)
- struct buffer *buffer = make_add_flow(flow, buffer_id, max_idle, 1);
+ struct buffer *buffer = make_add_flow(flow, buffer_id, idle_timeout, 1);
struct ofp_flow_mod *ofm = buffer->data;
ofm->actions[0].type = htons(OFPAT_OUTPUT);
ofm->actions[0].arg.output.max_len = htons(0);
struct ofp_flow_mod *ofm = buffer->data;
ofm->actions[0].type = htons(OFPAT_OUTPUT);
ofm->actions[0].arg.output.max_len = htons(0);
void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
void dp_output_control(struct datapath *, struct buffer *, int in_port,
size_t max_len, int reason);
void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
void dp_output_control(struct datapath *, struct buffer *, int in_port,
size_t max_len, int reason);
-static void send_flow_expired(struct datapath *, struct sw_flow *);
+static void send_flow_expired(struct datapath *, struct sw_flow *,
+ enum ofp_flow_expired_reason);
static void send_port_status(struct sw_port *p, uint8_t status);
static void del_switch_port(struct sw_port *p);
static void execute_actions(struct datapath *, struct buffer *,
static void send_port_status(struct sw_port *p, uint8_t status);
static void del_switch_port(struct sw_port *p);
static void execute_actions(struct datapath *, struct buffer *,
chain_timeout(dp->chain, &deleted);
LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
chain_timeout(dp->chain, &deleted);
LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
- send_flow_expired(dp, f);
+ send_flow_expired(dp, f, f->reason);
list_remove(&f->node);
flow_free(f);
}
list_remove(&f->node);
flow_free(f);
}
-send_flow_expired(struct datapath *dp, struct sw_flow *flow)
+send_flow_expired(struct datapath *dp, struct sw_flow *flow,
+ enum ofp_flow_expired_reason reason)
{
struct buffer *buffer;
struct ofp_flow_expired *ofe;
ofe = make_openflow_xid(sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &buffer);
flow_fill_match(&ofe->match, &flow->key);
{
struct buffer *buffer;
struct ofp_flow_expired *ofe;
ofe = make_openflow_xid(sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &buffer);
flow_fill_match(&ofe->match, &flow->key);
- memset(ofe->pad, 0, sizeof ofe->pad);
ofe->priority = htons(flow->priority);
ofe->priority = htons(flow->priority);
+ ofe->reason = reason;
+ memset(ofe->pad, 0, sizeof ofe->pad);
- ofe->duration = htonl(flow->timeout - flow->max_idle - flow->created);
+ ofe->duration = htonl(time(0) - flow->created);
+ memset(ofe->pad2, 0, sizeof ofe->pad2);
ofe->packet_count = htonll(flow->packet_count);
ofe->byte_count = htonll(flow->byte_count);
send_openflow_buffer(dp, buffer, NULL);
ofe->packet_count = htonll(flow->packet_count);
ofe->byte_count = htonll(flow->byte_count);
send_openflow_buffer(dp, buffer, NULL);
ofs->match.tp_src = flow->key.flow.tp_src;
ofs->match.tp_dst = flow->key.flow.tp_dst;
ofs->duration = htonl(now - flow->created);
ofs->match.tp_src = flow->key.flow.tp_src;
ofs->match.tp_dst = flow->key.flow.tp_dst;
ofs->duration = htonl(now - flow->created);
+ ofs->priority = htons(flow->priority);
+ ofs->idle_timeout = htons(flow->idle_timeout);
+ ofs->hard_timeout = htons(flow->hard_timeout);
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
ofs->packet_count = htonll(flow->packet_count);
ofs->byte_count = htonll(flow->byte_count);
ofs->packet_count = htonll(flow->packet_count);
ofs->byte_count = htonll(flow->byte_count);
- ofs->priority = htons(flow->priority);
- ofs->max_idle = htons(flow->max_idle);
memcpy(ofs->actions, flow->actions,
sizeof *ofs->actions * flow->n_actions);
}
memcpy(ofs->actions, flow->actions,
sizeof *ofs->actions * flow->n_actions);
}
/* Fill out flow. */
flow_extract_match(&flow->key, &ofm->match);
/* Fill out flow. */
flow_extract_match(&flow->key, &ofm->match);
- flow->max_idle = ntohs(ofm->max_idle);
flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1;
flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1;
- flow->timeout = time(0) + flow->max_idle; /* FIXME */
+ flow->idle_timeout = ntohs(ofm->idle_timeout);
+ flow->hard_timeout = ntohs(ofm->hard_timeout);
+ flow->used = flow->created = time(0);
flow->n_actions = n_acts;
flow->n_actions = n_acts;
- flow->created = time(0); /* FIXME */
flow->byte_count = 0;
flow->packet_count = 0;
memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions);
flow->byte_count = 0;
flow->packet_count = 0;
memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions);
ntohs(f->tp_src), ntohs(f->tp_dst));
}
ntohs(f->tp_src), ntohs(f->tp_dst));
}
-int flow_timeout(struct sw_flow *flow)
+bool flow_timeout(struct sw_flow *flow)
- if (flow->max_idle == OFP_FLOW_PERMANENT)
- return 0;
-
- /* FIXME */
- return time(0) > flow->timeout;
+ time_t now = time(0);
+ if (flow->idle_timeout != OFP_FLOW_PERMANENT
+ && now > flow->used + flow->idle_timeout) {
+ flow->reason = OFPER_IDLE_TIMEOUT;
+ return true;
+ } else if (flow->hard_timeout != OFP_FLOW_PERMANENT
+ && now > flow->created + flow->hard_timeout) {
+ flow->reason = OFPER_HARD_TIMEOUT;
+ return true;
+ } else {
+ return false;
+ }
}
void flow_used(struct sw_flow *flow, struct buffer *buffer)
{
}
void flow_used(struct sw_flow *flow, struct buffer *buffer)
{
- if (flow->max_idle != OFP_FLOW_PERMANENT)
- flow->timeout = time(0) + flow->max_idle;
-
flow->packet_count++;
flow->byte_count += buffer->size;
}
flow->packet_count++;
flow->byte_count += buffer->size;
}
struct sw_flow {
struct sw_flow_key key;
struct sw_flow {
struct sw_flow_key key;
- uint16_t max_idle; /* Idle time before discarding (seconds). */
uint16_t priority; /* Only used on entries with wildcards. */
uint16_t priority; /* Only used on entries with wildcards. */
+ uint16_t idle_timeout; /* Idle time before discarding (seconds). */
+ uint16_t hard_timeout; /* Hard expiration time (seconds) */
+ time_t used; /* Last used time. */
time_t created; /* When the flow was created. */
time_t created; /* When the flow was created. */
- time_t timeout; /* When the flow expires (if idle). */
uint64_t packet_count; /* Number of packets seen. */
uint64_t byte_count; /* Number of bytes seen. */
uint64_t packet_count; /* Number of packets seen. */
uint64_t byte_count; /* Number of bytes seen. */
+ uint8_t reason; /* Reason flow expired (one of OFPER_*). */
/* Private to table implementations. */
struct list node;
/* Private to table implementations. */
struct list node;
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
void print_flow(const struct sw_flow_key *);
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
void print_flow(const struct sw_flow_key *);
-int flow_timeout(struct sw_flow *flow);
+bool flow_timeout(struct sw_flow *flow);
void flow_used(struct sw_flow *flow, struct buffer *buffer);
#endif /* switch-flow.h */
void flow_used(struct sw_flow *flow, struct buffer *buffer);
#endif /* switch-flow.h */
not yet expose to the user.)
.PP
not yet expose to the user.)
.PP
-The \fBadd-flows\fR and \fBdel-flows\fR commands support an additional
-optional field:
+The \fBadd-flow\fR, \fBadd-flows\fR, and \fBdel-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,
which should be a number between 0 and 65535, inclusive. If this
field is not specified, it defaults to 32768.
.IP \fBpriority=\fIvalue\fR
Sets the priority of the flow to be added or deleted to \fIvalue\fR,
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:
+
+.TP
+\fBidle_timeout=\fIseconds\fR
+Causes the flow to expire after the given number of seconds of
+inactivity. A value of 0 prevents a flow from expiring due to
+inactivity. The default is 60 seconds.
+
+.IP \fBhard_timeout=\fIseconds\fR
+Causes the flow to expire after the given number of seconds,
+regardless of activity. A value of 0 (the default) gives the flow no
+hard expiration deadline.
+
.PP
The \fBdump-flows\fR and \fBdump-aggregate\fR commands support an
additional optional field:
.PP
The \fBdump-flows\fR and \fBdump-aggregate\fR commands support an
additional optional field:
#include "vlog.h"
#define THIS_MODULE VLM_dpctl
#include "vlog.h"
#define THIS_MODULE VLM_dpctl
-#define DEFAULT_MAX_IDLE 60
+#define DEFAULT_IDLE_TIMEOUT 60
#define MAX_ADD_ACTS 5
static const char* ifconfigbin = "/sbin/ifconfig";
#define MAX_ADD_ACTS 5
static const char* ifconfigbin = "/sbin/ifconfig";
static void
str_to_flow(char *string, struct ofp_match *match,
struct ofp_action *action, int *n_actions, uint8_t *table_idx,
static void
str_to_flow(char *string, struct ofp_match *match,
struct ofp_action *action, int *n_actions, uint8_t *table_idx,
- uint16_t *priority, uint16_t *max_idle)
+ uint16_t *priority, uint16_t *idle_timeout, uint16_t *hard_timeout)
{
struct field {
const char *name;
{
struct field {
const char *name;
if (priority) {
*priority = OFP_DEFAULT_PRIORITY;
}
if (priority) {
*priority = OFP_DEFAULT_PRIORITY;
}
- if (max_idle) {
- *max_idle = DEFAULT_MAX_IDLE;
+ if (idle_timeout) {
+ *idle_timeout = DEFAULT_IDLE_TIMEOUT;
+ }
+ if (hard_timeout) {
+ *hard_timeout = OFP_FLOW_PERMANENT;
}
if (action) {
act_str = strstr(string, "action");
}
if (action) {
act_str = strstr(string, "action");
- if (max_idle && !strcmp(name, "max_idle")) {
- *max_idle = atoi(value);
+ if (idle_timeout && !strcmp(name, "idle_timeout")) {
+ *idle_timeout = atoi(value);
+ continue;
+ }
+
+ if (hard_timeout && !strcmp(name, "hard_timeout")) {
+ *hard_timeout = atoi(value);
req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
- &req->table_id, NULL, NULL);
+ &req->table_id, NULL, NULL, NULL);
memset(req->pad, 0, sizeof req->pad);
dump_stats_transaction(argv[1], request);
memset(req->pad, 0, sizeof req->pad);
dump_stats_transaction(argv[1], request);
req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
- &req->table_id, NULL, NULL);
+ &req->table_id, NULL, NULL, NULL);
memset(req->pad, 0, sizeof req->pad);
dump_stats_transaction(argv[1], request);
memset(req->pad, 0, sizeof req->pad);
dump_stats_transaction(argv[1], request);
struct vconn *vconn;
struct buffer *buffer;
struct ofp_flow_mod *ofm;
struct vconn *vconn;
struct buffer *buffer;
struct ofp_flow_mod *ofm;
- uint16_t priority, max_idle;
+ uint16_t priority, idle_timeout, hard_timeout;
size_t size;
int n_actions = MAX_ADD_ACTS;
size_t size;
int n_actions = MAX_ADD_ACTS;
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions,
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &n_actions,
- NULL, &priority, &max_idle);
+ NULL, &priority, &idle_timeout, &hard_timeout);
ofm->command = htons(OFPFC_ADD);
ofm->command = htons(OFPFC_ADD);
- ofm->max_idle = htons(max_idle);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(hard_timeout);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
while (fgets(line, sizeof line, file)) {
struct buffer *buffer;
struct ofp_flow_mod *ofm;
while (fgets(line, sizeof line, file)) {
struct buffer *buffer;
struct ofp_flow_mod *ofm;
- uint16_t priority, max_idle;
+ uint16_t priority, idle_timeout, hard_timeout;
size_t size;
int n_actions = MAX_ADD_ACTS;
size_t size;
int n_actions = MAX_ADD_ACTS;
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(line, &ofm->match, &ofm->actions[0], &n_actions,
size = sizeof *ofm + (sizeof ofm->actions[0] * MAX_ADD_ACTS);
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(line, &ofm->match, &ofm->actions[0], &n_actions,
- NULL, &priority, &max_idle);
+ NULL, &priority, &idle_timeout, &hard_timeout);
ofm->command = htons(OFPFC_ADD);
ofm->command = htons(OFPFC_ADD);
- ofm->max_idle = htons(max_idle);
+ ofm->idle_timeout = htons(idle_timeout);
+ ofm->hard_timeout = htons(hard_timeout);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
size = sizeof *ofm;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL,
size = sizeof *ofm;
ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL,
+ &priority, NULL, NULL);
ofm->command = htons(OFPFC_DELETE);
ofm->command = htons(OFPFC_DELETE);
- ofm->max_idle = htons(0);
+ ofm->idle_timeout = htons(0);
+ ofm->hard_timeout = htons(0);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);
ofm->buffer_id = htonl(UINT32_MAX);
ofm->priority = htons(priority);
ofm->reserved = htonl(0);