/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Don't report on the datapath's device. */
if (!strcmp(devname, dpif_base_name(backer->dpif))) {
- continue;
+ goto next;
}
ofproto = lookup_ofproto_dpif_by_port_name(devname);
}
dpif_port_destroy(&port);
+ next:
free(devname);
}
ofproto->port_poll_errno = 0;
SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) {
- const struct iface_hint *iface_hint = node->data;
+ struct iface_hint *iface_hint = node->data;
if (!strcmp(iface_hint->br_name, ofproto->up.name)) {
/* Check if the datapath already has this port. */
free(iface_hint->br_name);
free(iface_hint->br_type);
+ free(iface_hint);
shash_delete(&init_ofp_ports, node);
}
}
static struct ofport_dpif *
get_odp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
{
- return get_ofp_port(ofproto, odp_port_to_ofp_port(ofproto, odp_port));
+ struct ofport_dpif *port = odp_port_to_ofport(ofproto->backer, odp_port);
+ return port && &ofproto->up == port->up.ofproto ? port : NULL;
}
static void
error = netdev_get_stats(ofport->up.netdev, stats);
- if (!error && ofport->odp_port == OVSP_LOCAL) {
+ if (!error && ofport_->ofp_port == OFPP_LOCAL) {
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
/* ofproto->stats.tx_packets represents packets that we created
handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
}
-/* This function does post-processing on data returned from
- * odp_flow_key_to_flow() to help make VLAN splinters transparent to the
- * rest of the upcall processing logic. In particular, if the extracted
- * in_port is a VLAN splinter port, it replaces flow->in_port by the "real"
- * port, sets flow->vlan_tci correctly for the VLAN of the VLAN splinter
- * port, and pushes a VLAN header onto 'packet' (if it is nonnull). The
- * caller must have called odp_flow_key_to_flow() and supply 'fitness' and
- * 'flow' from its output. The 'flow' argument must have had the "in_port"
- * member converted to the OpenFlow number.
+/* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
+ * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
+ * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
+ * returned by odp_flow_key_to_flow(). Also, optionally populates 'ofproto'
+ * with the ofproto_dpif, and 'odp_in_port' with the datapath in_port, that
+ * 'packet' ingressed.
+ *
+ * If 'ofproto' is nonnull, requires 'flow''s in_port to exist. Otherwise sets
+ * 'flow''s in_port to OFPP_NONE.
+ *
+ * This function does post-processing on data returned from
+ * odp_flow_key_to_flow() to help make VLAN splinters transparent to the rest
+ * of the upcall processing logic. In particular, if the extracted in_port is
+ * a VLAN splinter port, it replaces flow->in_port by the "real" port, sets
+ * flow->vlan_tci correctly for the VLAN of the VLAN splinter port, and pushes
+ * a VLAN header onto 'packet' (if it is nonnull).
+ *
+ * Optionally, if nonnull, sets '*initial_tci' to the VLAN TCI with which the
+ * packet was really received, that is, the actual VLAN TCI extracted by
+ * odp_flow_key_to_flow(). (This differs from the value returned in
+ * flow->vlan_tci only for packets received on VLAN splinters.)
*
- * Sets '*initial_tci' to the VLAN TCI with which the packet was really
- * received, that is, the actual VLAN TCI extracted by odp_flow_key_to_flow().
- * (This differs from the value returned in flow->vlan_tci only for packets
- * received on VLAN splinters.) */
-static enum odp_key_fitness
-ofproto_dpif_vsp_adjust(const struct ofproto_dpif *ofproto,
- enum odp_key_fitness fitness,
- struct flow *flow, ovs_be16 *initial_tci,
- struct ofpbuf *packet)
+ * Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
+ * or some other positive errno if there are other problems. */
+static int
+ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
+ const struct nlattr *key, size_t key_len,
+ struct flow *flow, enum odp_key_fitness *fitnessp,
+ struct ofproto_dpif **ofproto, uint32_t *odp_in_port,
+ ovs_be16 *initial_tci)
{
+ const struct ofport_dpif *port;
+ enum odp_key_fitness fitness;
+ int error;
+
+ fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
- return fitness;
+ error = EINVAL;
+ goto exit;
+ }
+
+ if (initial_tci) {
+ *initial_tci = flow->vlan_tci;
+ }
+
+ if (odp_in_port) {
+ *odp_in_port = flow->in_port;
}
- *initial_tci = flow->vlan_tci;
- if (vsp_adjust_flow(ofproto, flow)) {
+ port = odp_port_to_ofport(backer, flow->in_port);
+ if (!port) {
+ flow->in_port = OFPP_NONE;
+ error = ofproto ? ENODEV : 0;
+ goto exit;
+ }
+
+ if (ofproto) {
+ *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ }
+
+ flow->in_port = port->up.ofp_port;
+ if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
if (packet) {
/* Make the packet resemble the flow, so that it gets sent to an
* OpenFlow controller properly, so that it looks correct for
fitness = ODP_FIT_TOO_MUCH;
}
}
+ error = 0;
- return fitness;
+exit:
+ if (fitnessp) {
+ *fitnessp = fitness;
+ }
+ return error;
}
static void
for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) {
struct flow_miss *miss = &misses[n_misses];
struct flow_miss *existing_miss;
- enum odp_key_fitness fitness;
struct ofproto_dpif *ofproto;
- struct ofport_dpif *port;
uint32_t odp_in_port;
struct flow flow;
uint32_t hash;
+ int error;
- fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
- port = odp_port_to_ofport(backer, flow.in_port);
- if (!port) {
+ error = ofproto_receive(backer, upcall->packet, upcall->key,
+ upcall->key_len, &flow, &miss->key_fitness,
+ &ofproto, &odp_in_port, &miss->initial_tci);
+ if (error == ENODEV) {
/* Received packet on port for which we couldn't associate
* an ofproto. This can happen if a port is removed while
* traffic is being received. Print a rate-limited message
* in case it happens frequently. */
VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
flow.in_port);
- continue;
}
- ofproto = ofproto_dpif_cast(port->up.ofproto);
- odp_in_port = flow.in_port;
- flow.in_port = port->up.ofp_port;
-
- /* Obtain metadata and check userspace/kernel agreement on flow match,
- * then set 'flow''s header pointers. */
- miss->key_fitness = ofproto_dpif_vsp_adjust(ofproto, fitness,
- &flow, &miss->initial_tci, upcall->packet);
- if (miss->key_fitness == ODP_FIT_ERROR) {
+ if (error) {
continue;
}
flow_extract(upcall->packet, flow.skb_priority, flow.skb_mark,
{
struct ofproto_dpif *ofproto;
union user_action_cookie cookie;
- enum odp_key_fitness fitness;
- struct ofport_dpif *port;
- ovs_be16 initial_tci;
struct flow flow;
uint32_t odp_in_port;
- fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
-
- port = odp_port_to_ofport(backer, flow.in_port);
- if (!port) {
- return;
- }
-
- ofproto = ofproto_dpif_cast(port->up.ofproto);
- if (!ofproto->sflow) {
- return;
- }
-
- odp_in_port = flow.in_port;
- flow.in_port = port->up.ofp_port;
- fitness = ofproto_dpif_vsp_adjust(ofproto, fitness, &flow,
- &initial_tci, upcall->packet);
- if (fitness == ODP_FIT_ERROR) {
+ if (ofproto_receive(backer, upcall->packet, upcall->key, upcall->key_len,
+ &flow, NULL, &ofproto, &odp_in_port, NULL)
+ || !ofproto->sflow) {
return;
}
ofpbuf_use_stack(&buf, stub, stub_size);
if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
- uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT16_MAX);
+ uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
odp_put_userspace_action(pid, &cookie, &buf);
} else {
put_userspace_action(ofproto, &buf, flow, &cookie);
uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
uint8_t flow_nw_tos = ctx->flow.nw_tos;
- uint16_t out_port;
-
- if (ofport) {
- struct priority_to_dscp *pdscp;
+ struct priority_to_dscp *pdscp;
+ uint32_t out_port;
- if (ofport->up.pp.config & OFPUTIL_PC_NO_FWD) {
- xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
- return;
- } else if (check_stp && !stp_forward_in_state(ofport->stp_state)) {
- xlate_report(ctx, "STP not in forwarding state, skipping output");
- return;
- }
+ if (!ofport) {
+ xlate_report(ctx, "Nonexistent output port");
+ return;
+ } else if (ofport->up.pp.config & OFPUTIL_PC_NO_FWD) {
+ xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
+ return;
+ } else if (check_stp && !stp_forward_in_state(ofport->stp_state)) {
+ xlate_report(ctx, "STP not in forwarding state, skipping output");
+ return;
+ }
- pdscp = get_priority(ofport, ctx->flow.skb_priority);
- if (pdscp) {
- ctx->flow.nw_tos &= ~IP_DSCP_MASK;
- ctx->flow.nw_tos |= pdscp->dscp;
- }
- } else {
- /* We may not have an ofport record for this port, but it doesn't hurt
- * to allow forwarding to it anyhow. Maybe such a port will appear
- * later and we're pre-populating the flow table. */
+ pdscp = get_priority(ofport, ctx->flow.skb_priority);
+ if (pdscp) {
+ ctx->flow.nw_tos &= ~IP_DSCP_MASK;
+ ctx->flow.nw_tos |= pdscp->dscp;
}
out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
}
if (rule == NULL && may_packet_in) {
- /* TODO:XXX
+ /* XXX
* check if table configuration flags
* OFPTC_TABLE_MISS_CONTROLLER, default.
* OFPTC_TABLE_MISS_CONTINUE,
break;
case OFPACT_PUSH_VLAN:
- /* TODO:XXX 802.1AD(QinQ) */
+ /* XXX 802.1AD(QinQ) */
ctx->flow.vlan_tci = htons(VLAN_CFI);
break;
break;
case OFPACT_CLEAR_ACTIONS:
- /* TODO:XXX
+ /* XXX
* Nothing to do because writa-actions is not supported for now.
* When writa-actions is supported, clear-actions also must
* be supported at the same time.
break;
case OFPACT_GOTO_TABLE: {
- /* TODO:XXX remove recursion */
+ /* XXX remove recursion */
/* It is assumed that goto-table is last action */
struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
assert(ctx->table_id < ogt->table_id);
ovs_be16 initial_tci, struct rule_dpif *rule,
uint8_t tcp_flags, const struct ofpbuf *packet)
{
+ ovs_be64 initial_tun_id = flow->tunnel.tun_id;
+
+ /* Flow initialization rules:
+ * - 'base_flow' must match the kernel's view of the packet at the
+ * time that action processing starts. 'flow' represents any
+ * transformations we wish to make through actions.
+ * - By default 'base_flow' and 'flow' are the same since the input
+ * packet matches the output before any actions are applied.
+ * - When using VLAN splinters, 'base_flow''s VLAN is set to the value
+ * of the received packet as seen by the kernel. If we later output
+ * to another device without any modifications this will cause us to
+ * insert a new tag since the original one was stripped off by the
+ * VLAN device.
+ * - Tunnel 'flow' is largely cleared when transitioning between
+ * the input and output stages since it does not make sense to output
+ * a packet with the exact headers that it was received with (i.e.
+ * the destination IP is us). The one exception is the tun_id, which
+ * is preserved to allow use in later resubmit lookups and loads into
+ * registers.
+ * - Tunnel 'base_flow' is completely cleared since that is what the
+ * kernel does. If we wish to maintain the original values an action
+ * needs to be generated. */
+
ctx->ofproto = ofproto;
ctx->flow = *flow;
+ memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
ctx->base_flow = ctx->flow;
- memset(&ctx->base_flow.tunnel, 0, sizeof ctx->base_flow.tunnel);
ctx->base_flow.vlan_tci = initial_tci;
+ ctx->flow.tunnel.tun_id = initial_tun_id;
ctx->rule = rule;
ctx->packet = packet;
ctx->may_learn = packet != NULL;
} else {
static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
+ uint32_t local_odp_port;
add_sflow_action(ctx);
do_xlate_actions(ofpacts, ofpacts_len, ctx);
}
}
+ local_odp_port = ofp_port_to_odp_port(ctx->ofproto, OFPP_LOCAL);
if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
+ local_odp_port,
ctx->odp_actions->data,
ctx->odp_actions->size)) {
ctx->slow |= SLOW_IN_BAND;
* you just say "syntax error" or do you present both error messages?
* Both choices seem lousy. */
if (strchr(flow_s, '(')) {
- enum odp_key_fitness fitness;
int error;
/* Convert string to datapath key. */
goto exit;
}
- fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow);
- flow.in_port = odp_port_to_ofp_port(ofproto, flow.in_port);
-
- /* Convert odp_key to flow. */
- error = ofproto_dpif_vsp_adjust(ofproto, fitness, &flow,
- &initial_tci, NULL);
- if (error == ODP_FIT_ERROR) {
+ /* XXX: Since we allow the user to specify an ofproto, it's
+ * possible they will specify a different ofproto than the one the
+ * port actually belongs too. Ideally we should simply remove the
+ * ability to specify the ofproto. */
+ if (ofproto_receive(ofproto->backer, NULL, odp_key.data,
+ odp_key.size, &flow, NULL, NULL, NULL,
+ &initial_tci)) {
unixctl_command_reply_error(conn, "Invalid flow");
goto exit;
}
}
initial_tci = flow.vlan_tci;
- vsp_adjust_flow(ofproto, &flow);
}
/* Generate a packet, if requested. */
struct ofport_dpif *port;
port = odp_port_to_ofport(ofproto->backer, odp_port);
- if (port && ofproto == ofproto_dpif_cast(port->up.ofproto)) {
+ if (port && &ofproto->up == port->up.ofproto) {
return port->up.ofp_port;
} else {
return OFPP_NONE;