static void ofopgroup_destroy(struct ofopgroup *);
static int add_flow(struct ofproto *, struct ofconn *,
- struct ofputil_flow_mod *, const struct ofp_header *);
-
-/* This return value tells handle_openflow() that processing of the current
- * OpenFlow message must be postponed until some ongoing operations have
- * completed.
- *
- * This particular value is a good choice because it is negative (so it won't
- * collide with any errno value or any value returned by ofp_mkerr()) and large
- * (so it won't accidentally collide with EOF or a negative errno value). */
-enum { OFPROTO_POSTPONE = -100000 };
+ const struct ofputil_flow_mod *,
+ const struct ofp_header *);
static bool handle_openflow(struct ofconn *, struct ofpbuf *);
+static int handle_flow_mod__(struct ofproto *, struct ofconn *,
+ const struct ofputil_flow_mod *,
+ const struct ofp_header *);
static void update_port(struct ofproto *, const char *devname);
static int init_ports(struct ofproto *);
static void reinit_ports(struct ofproto *);
+static void set_internal_devs_mtu(struct ofproto *);
static void ofproto_unixctl_init(void);
ofproto->sw_desc = xstrdup(DEFAULT_SW_DESC);
ofproto->serial_desc = xstrdup(DEFAULT_SERIAL_DESC);
ofproto->dp_desc = xstrdup(DEFAULT_DP_DESC);
+ ofproto->frag_handling = OFPC_FRAG_NORMAL;
hmap_init(&ofproto->ports);
shash_init(&ofproto->port_by_name);
ofproto->tables = NULL;
}
}
\f
+/* Spanning Tree Protocol (STP) configuration. */
+
+/* Configures STP on 'ofproto' using the settings defined in 's'. If
+ * 's' is NULL, disables STP.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+ofproto_set_stp(struct ofproto *ofproto,
+ const struct ofproto_stp_settings *s)
+{
+ return (ofproto->ofproto_class->set_stp
+ ? ofproto->ofproto_class->set_stp(ofproto, s)
+ : EOPNOTSUPP);
+}
+
+/* Retrieves STP status of 'ofproto' and stores it in 's'. If the
+ * 'enabled' member of 's' is false, then the other members are not
+ * meaningful.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+ofproto_get_stp_status(struct ofproto *ofproto,
+ struct ofproto_stp_status *s)
+{
+ return (ofproto->ofproto_class->get_stp_status
+ ? ofproto->ofproto_class->get_stp_status(ofproto, s)
+ : EOPNOTSUPP);
+}
+
+/* Configures STP on 'ofp_port' of 'ofproto' using the settings defined
+ * in 's'. The caller is responsible for assigning STP port numbers
+ * (using the 'port_num' member in the range of 1 through 255, inclusive)
+ * and ensuring there are no duplicates. If the 's' is NULL, then STP
+ * is disabled on the port.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.*/
+int
+ofproto_port_set_stp(struct ofproto *ofproto, uint16_t ofp_port,
+ const struct ofproto_port_stp_settings *s)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ if (!ofport) {
+ VLOG_WARN("%s: cannot configure STP on nonexistent port %"PRIu16,
+ ofproto->name, ofp_port);
+ return ENODEV;
+ }
+
+ return (ofproto->ofproto_class->set_stp_port
+ ? ofproto->ofproto_class->set_stp_port(ofport, s)
+ : EOPNOTSUPP);
+}
+
+/* Retrieves STP port status of 'ofp_port' on 'ofproto' and stores it in
+ * 's'. If the 'enabled' member in 's' is false, then the other members
+ * are not meaningful.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.*/
+int
+ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port,
+ struct ofproto_port_stp_status *s)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ if (!ofport) {
+ VLOG_WARN("%s: cannot get STP status on nonexistent port %"PRIu16,
+ ofproto->name, ofp_port);
+ return ENODEV;
+ }
+
+ return (ofproto->ofproto_class->get_stp_port_status
+ ? ofproto->ofproto_class->get_stp_port_status(ofport, s)
+ : EOPNOTSUPP);
+}
+\f
/* Connectivity Fault Management configuration. */
/* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */
/* Returns true if 'aux' is a registered bundle that is currently in use as the
* output for a mirror. */
bool
-ofproto_is_mirror_output_bundle(struct ofproto *ofproto, void *aux)
+ofproto_is_mirror_output_bundle(const struct ofproto *ofproto, void *aux)
{
return (ofproto->ofproto_class->is_mirror_output_bundle
? ofproto->ofproto_class->is_mirror_output_bundle(ofproto, aux)
}
}
+/* Executes the flow modification specified in 'fm'. Returns 0 on success, an
+ * OpenFlow error code as encoded by ofp_mkerr() on failure, or
+ * OFPROTO_POSTPONE if the operation cannot be initiated now but may be retried
+ * later.
+ *
+ * This is a helper function for in-band control and fail-open. */
+int
+ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm)
+{
+ return handle_flow_mod__(ofproto, NULL, fm, NULL);
+}
+
/* Searches for a rule with matching criteria exactly equal to 'target' in
* ofproto's table 0 and, if it finds one, deletes it.
*
{
const char *netdev_name = netdev_get_name(netdev);
struct ofport *ofport;
+ int dev_mtu;
int error;
/* Create ofport. */
hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
shash_add(&p->port_by_name, netdev_name, ofport);
+ if (!netdev_get_mtu(netdev, &dev_mtu)) {
+ set_internal_devs_mtu(p);
+ ofport->mtu = dev_mtu;
+ } else {
+ ofport->mtu = 0;
+ }
+
/* Let the ofproto_class initialize its private data. */
error = p->ofproto_class->port_construct(ofport);
if (error) {
}
}
-/* Updates 'port' within 'ofproto' with the new 'netdev' and 'opp'.
+/* Updates 'port' with new 'opp' description.
*
* Does not handle a name or port number change. The caller must implement
* such a change as a delete followed by an add. */
connmgr_send_port_status(port->ofproto->connmgr, &port->opp, OFPPR_MODIFY);
}
+/* Update OpenFlow 'state' in 'port' and notify controller. */
+void
+ofproto_port_set_state(struct ofport *port, ovs_be32 state)
+{
+ if (port->opp.state != state) {
+ port->opp.state = state;
+ connmgr_send_port_status(port->ofproto->connmgr, &port->opp,
+ OFPPR_MODIFY);
+ }
+}
+
void
ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port)
{
port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
if (port && !strcmp(netdev_get_name(port->netdev), name)) {
struct netdev *old_netdev = port->netdev;
+ int dev_mtu;
/* 'name' hasn't changed location. Any properties changed? */
if (!ofport_equal(&port->opp, &opp)) {
ofport_modified(port, &opp);
}
+ /* If this is a non-internal port and the MTU changed, check
+ * if the datapath's MTU needs to be updated. */
+ if (strcmp(netdev_get_type(netdev), "internal")
+ && !netdev_get_mtu(netdev, &dev_mtu)
+ && port->mtu != dev_mtu) {
+ set_internal_devs_mtu(ofproto);
+ port->mtu = dev_mtu;
+ }
+
/* Install the newly opened netdev in case it has changed.
* Don't close the old netdev yet in case port_modified has to
* remove a retained reference to it.*/
return 0;
}
+
+/* Find the minimum MTU of all non-datapath devices attached to 'p'.
+ * Returns ETH_PAYLOAD_MAX or the minimum of the ports. */
+static int
+find_min_mtu(struct ofproto *p)
+{
+ struct ofport *ofport;
+ int mtu = 0;
+
+ HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
+ struct netdev *netdev = ofport->netdev;
+ int dev_mtu;
+
+ /* Skip any internal ports, since that's what we're trying to
+ * set. */
+ if (!strcmp(netdev_get_type(netdev), "internal")) {
+ continue;
+ }
+
+ if (netdev_get_mtu(netdev, &dev_mtu)) {
+ continue;
+ }
+ if (!mtu || dev_mtu < mtu) {
+ mtu = dev_mtu;
+ }
+ }
+
+ return mtu ? mtu: ETH_PAYLOAD_MAX;
+}
+
+/* Set the MTU of all datapath devices on 'p' to the minimum of the
+ * non-datapath ports. */
+static void
+set_internal_devs_mtu(struct ofproto *p)
+{
+ struct ofport *ofport;
+ int mtu = find_min_mtu(p);
+
+ HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
+ struct netdev *netdev = ofport->netdev;
+
+ if (!strcmp(netdev_get_type(netdev), "internal")) {
+ netdev_set_mtu(netdev, mtu);
+ }
+ }
+}
\f
static void
ofproto_rule_destroy__(struct rule *rule)
assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
- flow_extract(packet, 0, in_port, &flow);
+ flow_extract(packet, 0, 0, in_port, &flow);
return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
}
osf->n_buffers = htonl(pktbuf_capacity());
osf->n_tables = ofproto->n_tables;
osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
- OFPC_PORT_STATS);
+ OFPC_PORT_STATS | OFPC_QUEUE_STATS);
if (arp_match_ip) {
osf->capabilities |= htonl(OFPC_ARP_MATCH_IP);
}
handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofpbuf *buf;
struct ofp_switch_config *osc;
- uint16_t flags;
- bool drop_frags;
-
- /* Figure out flags. */
- drop_frags = ofproto->ofproto_class->get_drop_frags(ofproto);
- flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
+ struct ofpbuf *buf;
/* Send reply. */
osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
- osc->flags = htons(flags);
+ osc->flags = htons(ofproto->frag_handling);
osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
ofconn_send_reply(ofconn, buf);
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
uint16_t flags = ntohs(osc->flags);
- if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
- && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
- switch (flags & OFPC_FRAG_MASK) {
- case OFPC_FRAG_NORMAL:
- ofproto->ofproto_class->set_drop_frags(ofproto, false);
- break;
- case OFPC_FRAG_DROP:
- ofproto->ofproto_class->set_drop_frags(ofproto, true);
- break;
- default:
- VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
- osc->flags);
- break;
+ if (ofconn_get_type(ofconn) != OFCONN_PRIMARY
+ || ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
+ enum ofp_config_flags cur = ofproto->frag_handling;
+ enum ofp_config_flags next = flags & OFPC_FRAG_MASK;
+
+ assert((cur & OFPC_FRAG_MASK) == cur);
+ if (cur != next) {
+ if (ofproto->ofproto_class->set_frag_handling(ofproto, next)) {
+ ofproto->frag_handling = next;
+ } else {
+ VLOG_WARN_RL(&rl, "%s: unsupported fragment handling mode %s",
+ ofproto->name,
+ ofputil_frag_handling_to_string(next));
+ }
}
}
/* Checks whether 'ofconn' is a slave controller. If so, returns an OpenFlow
* error message code (composed with ofp_mkerr()) for the caller to propagate
- * upward. Otherwise, returns 0.
- *
- * The log message mentions 'msg_type'. */
+ * upward. Otherwise, returns 0. */
static int
-reject_slave_controller(struct ofconn *ofconn, const char *msg_type)
+reject_slave_controller(const struct ofconn *ofconn)
{
if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
&& ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
- static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&perm_rl, "rejecting %s message from slave controller",
- msg_type);
-
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
} else {
return 0;
COVERAGE_INC(ofproto_packet_out);
- error = reject_slave_controller(ofconn, "OFPT_PACKET_OUT");
+ error = reject_slave_controller(ofconn);
if (error) {
return error;
}
/* Get payload. */
if (opo->buffer_id != htonl(UINT32_MAX)) {
error = ofconn_pktbuf_retrieve(ofconn, ntohl(opo->buffer_id),
- &buffer, &in_port);
+ &buffer, NULL);
if (error || !buffer) {
return error;
}
buffer = NULL;
}
+ /* Get in_port and partially validate it.
+ *
+ * We don't know what range of ports the ofproto actually implements, but
+ * we do know that only certain reserved ports (numbered OFPP_MAX and
+ * above) are valid. */
+ in_port = ntohs(opo->in_port);
+ if (in_port >= OFPP_MAX && in_port != OFPP_LOCAL && in_port != OFPP_NONE) {
+ return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT);
+ }
+
/* Send out packet. */
- flow_extract(&payload, 0, ntohs(opo->in_port), &flow);
+ flow_extract(&payload, 0, 0, in_port, &flow);
error = p->ofproto_class->packet_out(p, &payload, &flow,
ofp_actions, n_ofp_actions);
ofpbuf_delete(buffer);
struct ofport *port;
int error;
- error = reject_slave_controller(ofconn, "OFPT_PORT_MOD");
+ error = reject_slave_controller(ofconn);
if (error) {
return error;
}
*nsec = (msecs % 1000) * (1000 * 1000);
}
+/* Checks whether 'table_id' is 0xff or a valid table ID in 'ofproto'. Returns
+ * 0 if 'table_id' is OK, otherwise an OpenFlow error code. */
+static int
+check_table_id(const struct ofproto *ofproto, uint8_t table_id)
+{
+ return (table_id == 0xff || table_id < ofproto->n_tables
+ ? 0
+ : ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_TABLE_ID));
+
+}
+
static struct classifier *
first_matching_table(struct ofproto *ofproto, uint8_t table_id)
{
} else if (table_id < ofproto->n_tables) {
return &ofproto->tables[table_id];
} else {
- /* It would probably be better to reply with an error but there doesn't
- * seem to be any appropriate value, so that might just be
- * confusing. */
- VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8,
- table_id);
return NULL;
}
}
* - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates
* only once, for that table.
*
- * - Otherwise, TABLE_ID isn't valid for OFPROTO, so ofproto logs a warning
- * and does not enter the loop at all.
+ * - Otherwise, TABLE_ID isn't valid for OFPROTO, so the loop won't be
+ * entered at all. (Perhaps you should have validated TABLE_ID with
+ * check_table_id().)
*
* All parameters are evaluated multiple times.
*/
struct list *rules)
{
struct classifier *cls;
+ int error;
+
+ error = check_table_id(ofproto, table_id);
+ if (error) {
+ return error;
+ }
list_init(rules);
FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
struct list *rules)
{
struct classifier *cls;
+ int error;
+
+ error = check_table_id(ofproto, table_id);
+ if (error) {
+ return error;
+ }
list_init(rules);
FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
* in which no matching flow already exists in the flow table.
*
* Adds the flow specified by 'ofm', which is followed by 'n_actions'
- * ofp_actions, to the ofproto's flow table. Returns 0 on success or an
- * OpenFlow error code as encoded by ofp_mkerr() on failure.
+ * ofp_actions, to the ofproto's flow table. Returns 0 on success, an OpenFlow
+ * error code as encoded by ofp_mkerr() on failure, or OFPROTO_POSTPONE if the
+ * operation cannot be initiated now but may be retried later.
*
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
static int
add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
- struct ofputil_flow_mod *fm, const struct ofp_header *request)
+ const struct ofputil_flow_mod *fm, const struct ofp_header *request)
{
struct classifier *table;
struct ofopgroup *group;
struct rule *rule;
int error;
- /* Check for overlap, if requested. */
- if (fm->flags & OFPFF_CHECK_OVERLAP) {
- struct classifier *cls;
-
- FOR_EACH_MATCHING_TABLE (cls, fm->table_id, ofproto) {
- if (classifier_rule_overlaps(cls, &fm->cr)) {
- return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
- }
- }
+ error = check_table_id(ofproto, fm->table_id);
+ if (error) {
+ return error;
}
/* Pick table. */
return ofp_mkerr_nicira(OFPET_FLOW_MOD_FAILED, NXFMFC_BAD_TABLE_ID);
}
+ /* Check for overlap, if requested. */
+ if (fm->flags & OFPFF_CHECK_OVERLAP
+ && classifier_rule_overlaps(table, &fm->cr)) {
+ return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+ }
+
/* Serialize against pending deletion. */
if (is_flow_deletion_pending(ofproto, &fm->cr, table - ofproto->tables)) {
return OFPROTO_POSTPONE;
rule->cr = fm->cr;
rule->pending = NULL;
rule->flow_cookie = fm->cookie;
- rule->created = time_msec();
+ rule->created = rule->modified = time_msec();
rule->idle_timeout = fm->idle_timeout;
rule->hard_timeout = fm->hard_timeout;
rule->table_id = table - ofproto->tables;
rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
rule->n_actions = fm->n_actions;
rule->ofproto->ofproto_class->rule_modify_actions(rule);
+ } else {
+ rule->modified = time_msec();
}
rule->flow_cookie = fm->cookie;
}
* if any. */
static int
modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
- struct ofputil_flow_mod *fm,
+ const struct ofputil_flow_mod *fm,
const struct ofp_header *request)
{
struct list rules;
* if any. */
static int
modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
- struct ofputil_flow_mod *fm,
+ const struct ofputil_flow_mod *fm,
const struct ofp_header *request)
{
struct list rules;
/* Implements OFPFC_DELETE_STRICT. */
static int
delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
- struct ofputil_flow_mod *fm,
+ const struct ofputil_flow_mod *fm,
const struct ofp_header *request)
{
struct list rules;
static int
handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_mod fm;
int error;
- error = reject_slave_controller(ofconn, "flow_mod");
+ error = reject_slave_controller(ofconn);
if (error) {
return error;
}
- if (ofproto->n_pending >= 50) {
- assert(!list_is_empty(&ofproto->pending));
- return OFPROTO_POSTPONE;
- }
-
error = ofputil_decode_flow_mod(&fm, oh,
ofconn_get_flow_mod_table_id(ofconn));
if (error) {
return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
}
- switch (fm.command) {
+ return handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
+}
+
+static int
+handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
+ const struct ofputil_flow_mod *fm,
+ const struct ofp_header *oh)
+{
+ if (ofproto->n_pending >= 50) {
+ assert(!list_is_empty(&ofproto->pending));
+ return OFPROTO_POSTPONE;
+ }
+
+ switch (fm->command) {
case OFPFC_ADD:
- return add_flow(ofproto, ofconn, &fm, oh);
+ return add_flow(ofproto, ofconn, fm, oh);
case OFPFC_MODIFY:
- return modify_flows_loose(ofproto, ofconn, &fm, oh);
+ return modify_flows_loose(ofproto, ofconn, fm, oh);
case OFPFC_MODIFY_STRICT:
- return modify_flow_strict(ofproto, ofconn, &fm, oh);
+ return modify_flow_strict(ofproto, ofconn, fm, oh);
case OFPFC_DELETE:
- return delete_flows_loose(ofproto, ofconn, &fm, oh);
+ return delete_flows_loose(ofproto, ofconn, fm, oh);
case OFPFC_DELETE_STRICT:
- return delete_flow_strict(ofproto, ofconn, &fm, oh);
+ return delete_flow_strict(ofproto, ofconn, fm, oh);
default:
- if (fm.command > 0xff) {
+ if (fm->command > 0xff) {
VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
"flow_mod_table_id extension is not enabled");
}
uint32_t role;
if (ofconn_get_type(ofconn) != OFCONN_PRIMARY) {
- VLOG_WARN_RL(&rl, "ignoring role request on service connection");
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
}
role = ntohl(nrr->role);
if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
&& role != NX_ROLE_SLAVE) {
- VLOG_WARN_RL(&rl, "received request for unknown role %"PRIu32, role);
-
- /* There's no good error code for this. */
- return ofp_mkerr(OFPET_BAD_REQUEST, -1);
+ return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_ROLE);
}
if (ofconn_get_role(ofconn) != role
case OFPUTIL_NXST_FLOW_REPLY:
case OFPUTIL_NXST_AGGREGATE_REPLY:
default:
- if (VLOG_IS_WARN_ENABLED()) {
- char *s = ofp_to_string(oh, ntohs(oh->length), 2);
- VLOG_DBG_RL(&rl, "OpenFlow message ignored: %s", s);
- free(s);
- }
if (oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY) {
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT);
} else {
break;
case OFOPERATION_MODIFY:
- if (error) {
+ if (!error) {
+ rule->modified = time_msec();
+ } else {
free(rule->actions);
rule->actions = op->actions;
rule->n_actions = op->n_actions;
}
registered = true;
- unixctl_command_register("ofproto/list", ofproto_unixctl_list, NULL);
+ unixctl_command_register("ofproto/list", "", ofproto_unixctl_list, NULL);
}