const struct ofputil_flow_mod *,
const struct ofp_header *);
static void delete_flow__(struct rule *, struct ofopgroup *);
-static bool handle_openflow(struct ofconn *, struct ofpbuf *);
+static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
const struct ofputil_flow_mod *,
const struct ofp_header *);
static size_t n_ofproto_classes;
static size_t allocated_ofproto_classes;
+unsigned flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT;
+
/* Map from datapath name to struct ofproto, for use by unixctl commands. */
static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+/* The default value of true waits for flow restore. */
+static bool flow_restore_wait = true;
+
/* Must be called to initialize the ofproto library.
*
* The caller may pass in 'iface_hints', which contains an shash of
hmap_insert(&all_ofprotos, &ofproto->hmap_node,
hash_string(ofproto->name, 0));
ofproto->datapath_id = 0;
- ofproto_set_flow_eviction_threshold(ofproto,
- OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT);
ofproto->forward_bpdu = false;
ofproto->fallback_dpid = pick_fallback_dpid();
ofproto->mfr_desc = NULL;
ofproto->max_ports = OFPP_MAX;
ofproto->tables = NULL;
ofproto->n_tables = 0;
+ hindex_init(&ofproto->cookies);
list_init(&ofproto->expirable);
ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
ofproto->state = S_OPENFLOW;
/* The "max_ports" member should have been set by ->construct(ofproto).
* Port 0 is not a valid OpenFlow port, so mark that as unavailable. */
- ofproto->ofp_port_ids = bitmap_allocate(ofproto->max_ports);
+ ofproto->ofp_port_ids = bitmap_allocate(ofp_to_u16(ofproto->max_ports));
bitmap_set1(ofproto->ofp_port_ids, 0);
/* Check that hidden tables, if any, are at the end. */
* Reserved ports numbered OFPP_MAX and higher are special and not subject to
* the 'max_ports' restriction. */
void
-ofproto_init_max_ports(struct ofproto *ofproto, uint16_t max_ports)
+ofproto_init_max_ports(struct ofproto *ofproto, ofp_port_t max_ports)
{
- ovs_assert(max_ports <= OFPP_MAX);
+ ovs_assert(ofp_to_u16(max_ports) <= ofp_to_u16(OFPP_MAX));
ofproto->max_ports = max_ports;
}
/* Sets the number of flows at which eviction from the kernel flow table
* will occur. */
void
-ofproto_set_flow_eviction_threshold(struct ofproto *ofproto, unsigned threshold)
+ofproto_set_flow_eviction_threshold(unsigned threshold)
{
- if (threshold < OFPROTO_FLOW_EVICTION_THRESHOLD_MIN) {
- ofproto->flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_MIN;
- } else {
- ofproto->flow_eviction_threshold = threshold;
- }
+ flow_eviction_threshold = MAX(OFPROTO_FLOW_EVICTION_THRESHOLD_MIN,
+ threshold);
}
/* If forward_bpdu is true, the NORMAL action will forward frames with
return (bo || fo) ? EOPNOTSUPP : 0;
}
}
+
+void
+ofproto_set_flow_restore_wait(bool flow_restore_wait_db)
+{
+ flow_restore_wait = flow_restore_wait_db;
+}
+
+bool
+ofproto_get_flow_restore_wait(void)
+{
+ return flow_restore_wait;
+}
+
\f
/* Spanning Tree Protocol (STP) configuration. */
*
* Returns 0 if successful, otherwise a positive errno value.*/
int
-ofproto_port_set_stp(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_stp(struct ofproto *ofproto, ofp_port_t ofp_port,
const struct ofproto_port_stp_settings *s)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
*
* Returns 0 if successful, otherwise a positive errno value.*/
int
-ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_get_stp_status(struct ofproto *ofproto, ofp_port_t ofp_port,
struct ofproto_port_stp_status *s)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
*
* Returns 0 if successful, otherwise a positive errno value. */
int
-ofproto_port_set_queues(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_queues(struct ofproto *ofproto, ofp_port_t ofp_port,
const struct ofproto_port_queue *queues,
size_t n_queues)
{
/* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */
void
-ofproto_port_clear_cfm(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_clear_cfm(struct ofproto *ofproto, ofp_port_t ofp_port)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
if (ofport && ofproto->ofproto_class->set_cfm) {
*
* This function has no effect if 'ofproto' does not have a port 'ofp_port'. */
void
-ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_cfm(struct ofproto *ofproto, ofp_port_t ofp_port,
const struct cfm_settings *s)
{
struct ofport *ofport;
/* Configures BFD on 'ofp_port' in 'ofproto'. This function has no effect if
* 'ofproto' does not have a port 'ofp_port'. */
void
-ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_set_bfd(struct ofproto *ofproto, ofp_port_t ofp_port,
const struct smap *cfg)
{
struct ofport *ofport;
* OVS database. Has no effect if 'ofp_port' is not na OpenFlow port in
* 'ofproto'. */
int
-ofproto_port_get_bfd_status(struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_get_bfd_status(struct ofproto *ofproto, ofp_port_t ofp_port,
struct smap *status)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
* 0 if LACP partner information is not current (generally indicating a
* connectivity problem), or -1 if LACP is not enabled on 'ofp_port'. */
int
-ofproto_port_is_lacp_current(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_is_lacp_current(struct ofproto *ofproto, ofp_port_t ofp_port)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
return (ofport && ofproto->ofproto_class->port_is_lacp_current
* 'ofp_portp' is non-null). */
int
ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
- uint16_t *ofp_portp)
+ ofp_port_t *ofp_portp)
{
- uint16_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
+ ofp_port_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
int error;
error = ofproto->ofproto_class->port_add(ofproto, netdev);
if (!error) {
const char *netdev_name = netdev_get_name(netdev);
- simap_put(&ofproto->ofp_requests, netdev_name, ofp_port);
+ simap_put(&ofproto->ofp_requests, netdev_name,
+ ofp_to_u16(ofp_port));
update_port(ofproto, netdev_name);
}
if (ofp_portp) {
/* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
* Returns 0 if successful, otherwise a positive errno. */
int
-ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_del(struct ofproto *ofproto, ofp_port_t ofp_port)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
sset_destroy(&devnames);
}
-static uint16_t
+static ofp_port_t
alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
{
- uint16_t ofp_port;
- uint16_t end_port_no = ofproto->alloc_port_no;
+ uint16_t max_ports = ofp_to_u16(ofproto->max_ports);
+ uint16_t port_idx;
- ofp_port = simap_get(&ofproto->ofp_requests, netdev_name);
- ofp_port = ofp_port ? ofp_port : OFPP_NONE;
+ port_idx = simap_get(&ofproto->ofp_requests, netdev_name);
+ if (!port_idx) {
+ port_idx = UINT16_MAX;
+ }
+
+ if (port_idx >= max_ports
+ || bitmap_is_set(ofproto->ofp_port_ids, port_idx)) {
+ uint16_t end_port_no = ofp_to_u16(ofproto->alloc_port_no);
+ uint16_t alloc_port_no = end_port_no;
- if (ofp_port >= ofproto->max_ports
- || bitmap_is_set(ofproto->ofp_port_ids, ofp_port)) {
/* Search for a free OpenFlow port number. We try not to
* immediately reuse them to prevent problems due to old
* flows. */
for (;;) {
- if (++ofproto->alloc_port_no >= ofproto->max_ports) {
- ofproto->alloc_port_no = 0;
+ if (++alloc_port_no >= max_ports) {
+ alloc_port_no = 0;
}
- if (!bitmap_is_set(ofproto->ofp_port_ids,
- ofproto->alloc_port_no)) {
- ofp_port = ofproto->alloc_port_no;
+ if (!bitmap_is_set(ofproto->ofp_port_ids, alloc_port_no)) {
+ port_idx = alloc_port_no;
+ ofproto->alloc_port_no = u16_to_ofp(alloc_port_no);
break;
}
- if (ofproto->alloc_port_no == end_port_no) {
+ if (alloc_port_no == end_port_no) {
return OFPP_NONE;
}
}
}
- bitmap_set1(ofproto->ofp_port_ids, ofp_port);
- return ofp_port;
+ bitmap_set1(ofproto->ofp_port_ids, port_idx);
+ return u16_to_ofp(port_idx);
}
static void
-dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
+dealloc_ofp_port(const struct ofproto *ofproto, ofp_port_t ofp_port)
{
- if (ofp_port < ofproto->max_ports) {
- bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+ if (ofp_to_u16(ofp_port) < ofp_to_u16(ofproto->max_ports)) {
+ bitmap_set0(ofproto->ofp_port_ids, ofp_to_u16(ofp_port));
}
}
ofport->created = time_msec();
/* Add port to 'p'. */
- hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
+ hmap_insert(&p->ports, &ofport->hmap_node,
+ hash_ofp_port(ofport->ofp_port));
shash_add(&p->port_by_name, netdev_name, ofport);
update_mtu(p, ofport);
}
void
-ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_port_unregister(struct ofproto *ofproto, ofp_port_t ofp_port)
{
struct ofport *port = ofproto_get_port(ofproto, ofp_port);
if (port) {
}
struct ofport *
-ofproto_get_port(const struct ofproto *ofproto, uint16_t ofp_port)
+ofproto_get_port(const struct ofproto *ofproto, ofp_port_t ofp_port)
{
struct ofport *port;
- HMAP_FOR_EACH_IN_BUCKET (port, hmap_node,
- hash_int(ofp_port, 0), &ofproto->ports) {
+ HMAP_FOR_EACH_IN_BUCKET (port, hmap_node, hash_ofp_port(ofp_port),
+ &ofproto->ports) {
if (port->ofp_port == ofp_port) {
return port;
}
netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
? ofport_open(ofproto, &ofproto_port, &pp)
: NULL);
+
if (netdev) {
port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
if (port && !strcmp(netdev_get_name(port->netdev), name)) {
node = shash_find(&init_ofp_ports, name);
if (node) {
const struct iface_hint *iface_hint = node->data;
- simap_put(&p->ofp_requests, name, iface_hint->ofp_port);
+ simap_put(&p->ofp_requests, name,
+ ofp_to_u16(iface_hint->ofp_port));
}
netdev = ofport_open(p, &ofproto_port, &pp);
/* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
* that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
bool
-ofproto_rule_has_out_port(const struct rule *rule, uint16_t port)
+ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port)
{
return (port == OFPP_ANY
|| ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
/* Returns true if a rule related to 'op' has an OpenFlow OFPAT_OUTPUT or
* OFPAT_ENQUEUE action that outputs to 'out_port'. */
bool
-ofoperation_has_out_port(const struct ofoperation *op, uint16_t out_port)
+ofoperation_has_out_port(const struct ofoperation *op, ofp_port_t out_port)
{
if (ofproto_rule_has_out_port(op->rule, out_port)) {
return true;
*
* Takes ownership of 'packet'. */
static int
-rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet)
+rule_execute(struct rule *rule, ofp_port_t in_port, struct ofpbuf *packet)
{
struct flow flow;
+ union flow_in_port in_port_;
ovs_assert(ofpbuf_headroom(packet) >= sizeof(struct ofp10_packet_in));
- flow_extract(packet, 0, 0, NULL, in_port, &flow);
+ in_port_.ofp_port = in_port;
+ flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
}
uint64_t ofpacts_stub[1024 / 8];
struct ofpbuf ofpacts;
struct flow flow;
+ union flow_in_port in_port_;
enum ofperr error;
COVERAGE_INC(ofproto_packet_out);
if (error) {
goto exit_free_ofpacts;
}
- if (po.in_port >= p->max_ports && po.in_port < OFPP_MAX) {
+ if (ofp_to_u16(po.in_port) >= ofp_to_u16(p->max_ports)
+ && ofp_to_u16(po.in_port) < ofp_to_u16(OFPP_MAX)) {
error = OFPERR_OFPBRC_BAD_PORT;
goto exit_free_ofpacts;
}
}
/* Verify actions against packet, then send packet if successful. */
- flow_extract(payload, 0, 0, NULL, po.in_port, &flow);
+ in_port_.ofp_port = po.in_port;
+ flow_extract(payload, 0, 0, NULL, &in_port_, &flow);
error = ofpacts_check(po.ofpacts, po.ofpacts_len, &flow, p->max_ports);
if (!error) {
error = p->ofproto_class->packet_out(p, payload, &flow,
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofport *port;
struct list replies;
- uint16_t port_no;
+ ofp_port_t port_no;
enum ofperr error;
error = ofputil_decode_port_stats_request(request, &port_no);
return 0;
}
+static uint32_t
+hash_cookie(ovs_be64 cookie)
+{
+ return hash_2words((OVS_FORCE uint64_t)cookie >> 32,
+ (OVS_FORCE uint64_t)cookie);
+}
+
+static void
+cookies_insert(struct ofproto *ofproto, struct rule *rule)
+{
+ hindex_insert(&ofproto->cookies, &rule->cookie_node,
+ hash_cookie(rule->flow_cookie));
+}
+
+static void
+cookies_remove(struct ofproto *ofproto, struct rule *rule)
+{
+ hindex_remove(&ofproto->cookies, &rule->cookie_node);
+}
+
+static void
+ofproto_rule_change_cookie(struct ofproto *ofproto, struct rule *rule,
+ ovs_be64 new_cookie)
+{
+ if (new_cookie != rule->flow_cookie) {
+ cookies_remove(ofproto, rule);
+
+ rule->flow_cookie = new_cookie;
+
+ cookies_insert(ofproto, rule);
+ }
+}
+
static void
calc_duration(long long int start, long long int now,
uint32_t *sec, uint32_t *nsec)
collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
const struct match *match,
ovs_be64 cookie, ovs_be64 cookie_mask,
- uint16_t out_port, struct list *rules)
+ ofp_port_t out_port, struct list *rules)
{
struct oftable *table;
struct cls_rule cr;
list_init(rules);
cls_rule_init(&cr, match, 0);
+
+ if (cookie_mask == htonll(UINT64_MAX)) {
+ struct rule *rule;
+
+ HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node, hash_cookie(cookie),
+ &ofproto->cookies) {
+ if (table_id != rule->table_id && table_id != 0xff) {
+ continue;
+ }
+ if (ofproto_rule_is_hidden(rule)) {
+ continue;
+ }
+ if (cls_rule_is_loose_match(&rule->cr, &cr.match)) {
+ if (rule->pending) {
+ error = OFPROTO_POSTPONE;
+ goto exit;
+ }
+ if (rule->flow_cookie == cookie /* Hash collisions possible. */
+ && ofproto_rule_has_out_port(rule, out_port)) {
+ list_push_back(rules, &rule->ofproto_node);
+ }
+ }
+ }
+ goto exit;
+ }
+
FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
struct cls_cursor cursor;
struct rule *rule;
collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
const struct match *match, unsigned int priority,
ovs_be64 cookie, ovs_be64 cookie_mask,
- uint16_t out_port, struct list *rules)
+ ofp_port_t out_port, struct list *rules)
{
struct oftable *table;
struct cls_rule cr;
list_init(rules);
cls_rule_init(&cr, match, priority);
+
+ if (cookie_mask == htonll(UINT64_MAX)) {
+ struct rule *rule;
+
+ HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node, hash_cookie(cookie),
+ &ofproto->cookies) {
+ if (table_id != rule->table_id && table_id != 0xff) {
+ continue;
+ }
+ if (ofproto_rule_is_hidden(rule)) {
+ continue;
+ }
+ if (cls_rule_equal(&rule->cr, &cr)) {
+ if (rule->pending) {
+ error = OFPROTO_POSTPONE;
+ goto exit;
+ }
+ if (rule->flow_cookie == cookie /* Hash collisions possible. */
+ && ofproto_rule_has_out_port(rule, out_port)) {
+ list_push_back(rules, &rule->ofproto_node);
+ }
+ }
+ }
+ goto exit;
+ }
+
FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
struct rule *rule;
* The caller must provide and owns '*status', but it does not own and must not
* modify or free the array returned in 'status->rmps'. */
bool
-ofproto_port_get_cfm_status(const struct ofproto *ofproto, uint16_t ofp_port,
+ofproto_port_get_cfm_status(const struct ofproto *ofproto, ofp_port_t ofp_port,
struct ofproto_cfm_status *status)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
struct oftable *table;
struct ofopgroup *group;
struct rule *victim;
- struct cls_rule cr;
struct rule *rule;
+ uint8_t table_id;
int error;
error = check_table_id(ofproto, fm->table_id);
/* Pick table. */
if (fm->table_id == 0xff) {
- uint8_t table_id;
if (ofproto->ofproto_class->rule_choose_table) {
error = ofproto->ofproto_class->rule_choose_table(ofproto,
&fm->match,
return error;
}
ovs_assert(table_id < ofproto->n_tables);
- table = &ofproto->tables[table_id];
} else {
- table = &ofproto->tables[0];
+ table_id = 0;
}
} else if (fm->table_id < ofproto->n_tables) {
- table = &ofproto->tables[fm->table_id];
+ table_id = fm->table_id;
} else {
return OFPERR_OFPBRC_BAD_TABLE_ID;
}
+ table = &ofproto->tables[table_id];
+
if (table->flags & OFTABLE_READONLY) {
return OFPERR_OFPBRC_EPERM;
}
cls_rule_init(&rule->cr, &fm->match, fm->priority);
/* Serialize against pending deletion. */
- if (is_flow_deletion_pending(ofproto, &cr, table - ofproto->tables)) {
+ if (is_flow_deletion_pending(ofproto, &rule->cr, table_id)) {
cls_rule_destroy(&rule->cr);
ofproto->ofproto_class->rule_dealloc(rule);
return OFPROTO_POSTPONE;
LIST_FOR_EACH (rule, ofproto_node, rules) {
struct ofoperation *op;
bool actions_changed;
- ovs_be64 new_cookie;
/* FIXME: Implement OFPFF12_RESET_COUNTS */
actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
rule->ofpacts, rule->ofpacts_len);
- new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
- ? fm->new_cookie
- : rule->flow_cookie);
op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
- rule->flow_cookie = new_cookie;
+
+ if (fm->new_cookie != htonll(UINT64_MAX)) {
+ ofproto_rule_change_cookie(ofproto, rule, fm->new_cookie);
+ }
if (actions_changed) {
op->ofpacts = rule->ofpacts;
op->ofpacts_len = rule->ofpacts_len;
}
static bool
-handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
+handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg)
{
int error = handle_openflow__(ofconn, ofp_msg);
if (error && error != OFPROTO_POSTPONE) {
LIST_FOR_EACH (op, group_node, &group->ops) {
if (op->type != OFOPERATION_DELETE) {
struct ofpbuf *packet;
- uint16_t in_port;
+ ofp_port_t in_port;
error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
&packet, &in_port);
if (!op->error) {
rule->modified = time_msec();
} else {
- rule->flow_cookie = op->flow_cookie;
+ ofproto_rule_change_cookie(ofproto, rule, op->flow_cookie);
if (op->ofpacts) {
free(rule->ofpacts);
rule->ofpacts = op->ofpacts;
struct oftable *table = &ofproto->tables[rule->table_id];
classifier_remove(&table->cls, &rule->cr);
+ cookies_remove(ofproto, rule);
eviction_group_remove_rule(rule);
if (!list_is_empty(&rule->expirable)) {
list_remove(&rule->expirable);
if (may_expire) {
list_insert(&ofproto->expirable, &rule->expirable);
}
+ cookies_insert(ofproto, rule);
victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
if (victim) {
+ cookies_remove(ofproto, victim);
+
if (!list_is_empty(&victim->expirable)) {
list_remove(&victim->expirable);
}
* device as a VLAN splinter for VLAN ID 'vid'. If 'realdev_ofp_port' is zero,
* then the VLAN device is un-enslaved. */
int
-ofproto_port_set_realdev(struct ofproto *ofproto, uint16_t vlandev_ofp_port,
- uint16_t realdev_ofp_port, int vid)
+ofproto_port_set_realdev(struct ofproto *ofproto, ofp_port_t vlandev_ofp_port,
+ ofp_port_t realdev_ofp_port, int vid)
{
struct ofport *ofport;
int error;