#include <config.h>
-#include "ofproto/private.h"
+#include "ofproto/ofproto-provider.h"
#include <errno.h>
#include "autopath.h"
#include "bond.h"
+#include "bundle.h"
#include "byte-order.h"
#include "connmgr.h"
#include "coverage.h"
COVERAGE_DEFINE(facet_revalidate);
COVERAGE_DEFINE(facet_unexpected);
-/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
+/* Maximum depth of flow table recursion (due to resubmit actions) in a
* flow translation. */
#define MAX_RESUBMIT_RECURSION 16
return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
}
-static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *ofproto,
- const struct flow *flow);
+static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
+ const struct flow *, uint8_t table);
#define MAX_MIRRORS 32
typedef uint32_t mirror_mask_t;
uint32_t priority; /* Current flow priority. 0 if none. */
struct flow base_flow; /* Flow at the last commit. */
uint32_t base_priority; /* Priority at the last commit. */
+ uint8_t table_id; /* OpenFlow table ID where flow was found. */
};
static void action_xlate_ctx_init(struct action_xlate_ctx *,
* dpif_execute().
*
* - Do include packets and bytes that were obtained from the datapath
- * when a flow was deleted (e.g. dpif_flow_del()) or when its
- * statistics were reset (e.g. dpif_flow_put() with
+ * when its statistics were reset (e.g. dpif_flow_put() with
* DPIF_FP_ZERO_STATS).
- *
- * - Do not include any packets or bytes that can currently be obtained
- * from the datapath by, e.g., dpif_flow_get().
*/
uint64_t packet_count; /* Number of packets received. */
uint64_t byte_count; /* Number of bytes received. */
uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */
long long int rs_used; /* Used time pushed to resubmit children. */
- /* Number of bytes passed to account_cb. This may include bytes that can
- * currently obtained from the datapath (thus, it can be greater than
- * byte_count). */
- uint64_t accounted_bytes;
+ uint64_t accounted_bytes; /* Bytes processed by facet_account(). */
struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */
struct list list_node; /* In owning rule's 'facets' list. */
long long int used);
static void facet_update_stats(struct ofproto_dpif *, struct facet *,
const struct dpif_flow_stats *);
+static void facet_reset_counters(struct facet *);
static void facet_reset_dp_stats(struct facet *, struct dpif_flow_stats *);
static void facet_push_stats(struct facet *);
-static void facet_account(struct ofproto_dpif *, struct facet *,
- uint64_t extra_bytes);
+static void facet_account(struct ofproto_dpif *, struct facet *);
static bool facet_is_controller_flow(struct facet *);
struct cfm *cfm; /* Connectivity Fault Management, if any. */
tag_type tag; /* Tag associated with this port. */
uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */
+ bool may_enable; /* May be enabled in bonds. */
};
static struct ofport_dpif *
/* Support for debugging async flow mods. */
struct list completions;
+
+ bool has_bundle_action; /* True when the first bundle action appears. */
};
/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
}
static int
-construct(struct ofproto *ofproto_)
+construct(struct ofproto *ofproto_, int *n_tablesp)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
const char *name = ofproto->up.name;
list_init(&ofproto->completions);
- ofproto->up.tables = xmalloc(sizeof *ofproto->up.tables);
- classifier_init(&ofproto->up.tables[0]);
- ofproto->up.n_tables = 1;
-
ofproto_dpif_unixctl_init();
+ ofproto->has_bundle_action = false;
+
+ *n_tablesp = 255;
return 0;
}
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct rule_dpif *rule, *next_rule;
- struct cls_cursor cursor;
+ struct classifier *table;
int i;
complete_operations(ofproto);
- cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
- ofproto_rule_destroy(&rule->up);
+ OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
+ struct cls_cursor cursor;
+
+ cls_cursor_init(&cursor, table, NULL);
+ CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
+ ofproto_rule_destroy(&rule->up);
+ }
}
for (i = 0; i < MAX_MIRRORS; i++) {
bundle_run(bundle);
}
+ mac_learning_run(ofproto->ml, &ofproto->revalidate_set);
+
/* Now revalidate if there's anything to do. */
if (ofproto->need_revalidate
|| !tag_set_is_empty(&ofproto->revalidate_set)) {
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
bundle_wait(bundle);
}
+ mac_learning_wait(ofproto->ml);
if (ofproto->need_revalidate) {
/* Shouldn't happen, but if it does just go around again. */
VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- struct odp_stats s;
+ struct ovs_dp_stats s;
strcpy(ots->name, "classifier");
port->bundle = NULL;
port->cfm = NULL;
port->tag = tag_create_random();
+ port->may_enable = true;
if (ofproto->sflow) {
dpif_sflow_add_port(ofproto->sflow, port->odp_port,
return ofport->cfm ? cfm_get_fault(ofport->cfm) : -1;
}
+
+static int
+get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps,
+ size_t *n_rmps)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ if (ofport->cfm) {
+ cfm_get_remote_mpids(ofport->cfm, rmps, n_rmps);
+ return 0;
+ } else {
+ return -1;
+ }
+}
\f
/* Bundles. */
}
static void
-send_pdu_cb(void *port_, const struct lacp_pdu *pdu)
+send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
struct ofport_dpif *port = port_;
error = netdev_get_etheraddr(port->up.netdev, ea);
if (!error) {
- struct lacp_pdu *packet_pdu;
struct ofpbuf packet;
+ void *packet_pdu;
ofpbuf_init(&packet, 0);
packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
- sizeof *packet_pdu);
- *packet_pdu = *pdu;
+ pdu_size);
+ memcpy(packet_pdu, pdu, pdu_size);
+
error = netdev_send(port->up.netdev, &packet);
if (error) {
VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
struct ofport_dpif *port;
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
- bool may_enable = lacp_slave_may_enable(bundle->lacp, port);
-
- if (may_enable && port->cfm) {
- may_enable = !cfm_get_fault(port->cfm);
- }
- bond_slave_set_may_enable(bundle->bond, port, may_enable);
+ bond_slave_set_may_enable(bundle->bond, port, port->may_enable);
}
bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
mirror = ofproto->mirrors[idx] = xzalloc(sizeof *mirror);
mirror->ofproto = ofproto;
mirror->idx = idx;
+ mirror->aux = aux;
mirror->out_vlan = -1;
mirror->name = NULL;
}
struct ofbundle *bundle = bundle_lookup(ofproto, aux);
return bundle && bundle->mirror_out != 0;
}
+
+static void
+forward_bpdu_changed(struct ofproto *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ /* Revalidate cached flows whenever forward_bpdu option changes. */
+ ofproto->need_revalidate = true;
+}
\f
/* Ports. */
static void
port_run(struct ofport_dpif *ofport)
{
+ bool enable = netdev_get_carrier(ofport->up.netdev);
+
if (ofport->cfm) {
cfm_run(ofport->cfm);
ofport->odp_port, &packet);
ofpbuf_uninit(&packet);
}
+
+ enable = enable && !cfm_get_fault(ofport->cfm);
}
+
+ if (ofport->bundle) {
+ enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport);
+ }
+
+ if (ofport->may_enable != enable) {
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+
+ if (ofproto->has_bundle_action) {
+ ofproto->need_revalidate = true;
+ }
+ }
+
+ ofport->may_enable = enable;
}
static void
process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
const struct ofpbuf *packet)
{
- if (cfm_should_process_flow(flow)) {
- struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
- if (packet && ofport && ofport->cfm) {
+ struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
+
+ if (!ofport) {
+ return false;
+ }
+
+ if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow)) {
+ if (packet) {
cfm_process_heartbeat(ofport->cfm, packet);
}
return true;
- } else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
- struct ofport_dpif *port = get_ofp_port(ofproto, flow->in_port);
- if (packet && port && port->bundle && port->bundle->lacp) {
- const struct lacp_pdu *pdu = parse_lacp_packet(packet);
- if (pdu) {
- lacp_process_pdu(port->bundle->lacp, port, pdu);
- }
+ } else if (ofport->bundle && ofport->bundle->lacp
+ && flow->dl_type == htons(ETH_TYPE_LACP)) {
+ if (packet) {
+ lacp_process_packet(ofport->bundle->lacp, ofport, packet);
}
return true;
}
/* Check with in-band control to see if this packet should be sent
* to the local port regardless of the flow table. */
if (connmgr_msg_in_hook(ofproto->up.connmgr, &flow, upcall->packet)) {
- send_packet(ofproto, ODPP_LOCAL, upcall->packet);
+ send_packet(ofproto, OVSP_LOCAL, upcall->packet);
}
facet = facet_lookup_valid(ofproto, &flow);
if (!facet) {
- struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow);
+ struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow, 0);
if (!rule) {
/* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
struct ofport_dpif *port = get_ofp_port(ofproto, flow.in_port);
expire(struct ofproto_dpif *ofproto)
{
struct rule_dpif *rule, *next_rule;
- struct cls_cursor cursor;
+ struct classifier *table;
int dp_max_idle;
/* Update stats for each flow in the datapath. */
expire_facets(ofproto, dp_max_idle);
/* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */
- cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL);
- CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
- rule_expire(rule);
+ OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
+ struct cls_cursor cursor;
+
+ cls_cursor_init(&cursor, table, NULL);
+ CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
+ rule_expire(rule);
+ }
}
/* All outstanding data in existing flows has been accounted, so it's a
ds_init(&s);
odp_flow_key_format(key, key_len, &s);
- VLOG_WARN_RL(&rl, "failed to convert ODP flow key to flow: %s",
+ VLOG_WARN_RL(&rl, "failed to convert datapath flow key to flow: %s",
ds_cstr(&s));
ds_destroy(&s);
facet->dp_byte_count = stats->n_bytes;
facet_update_time(p, facet, stats->used);
- facet_account(p, facet, stats->n_bytes);
+ facet_account(p, facet);
facet_push_stats(facet);
} else {
/* There's a flow in the datapath that we know nothing about.
* N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each facet
* that is installed in the kernel gets dropped in the appropriate bucket.
* After the histogram has been built, we compute the cutoff so that only
- * the most-recently-used 1% of facets (but at least 1000 flows) are kept
- * cached. At least the most-recently-used bucket of facets is kept, so
- * actually an arbitrary number of facets can be kept in any given
- * expiration run (though the next run will delete most of those unless
- * they receive additional data).
+ * the most-recently-used 1% of facets (but at least
+ * ofproto->up.flow_eviction_threshold flows) are kept cached. At least
+ * the most-recently-used bucket of facets is kept, so actually an
+ * arbitrary number of facets can be kept in any given expiration run
+ * (though the next run will delete most of those unless they receive
+ * additional data).
*
* This requires a second pass through the facets, in addition to the pass
* made by update_stats(), because the former function never looks
int i;
total = hmap_count(&ofproto->facets);
- if (total <= 1000) {
+ if (total <= ofproto->up.flow_eviction_threshold) {
return N_BUCKETS * BUCKET_WIDTH;
}
subtotal = bucket = 0;
do {
subtotal += buckets[bucket++];
- } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100));
+ } while (bucket < N_BUCKETS &&
+ subtotal < MAX(ofproto->up.flow_eviction_threshold, total / 100));
if (VLOG_IS_DBG_ENABLED()) {
struct ds s;
struct ofpbuf *packet)
{
if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
- && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) {
+ && odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE) {
/* As an optimization, avoid a round-trip from userspace to kernel to
* userspace. This also avoids possibly filling up kernel packet
* buffers along the way. */
* applying flow_extract() to 'packet' would yield the same flow as
* 'facet->flow'.
*
- * 'facet' must have accurately composed ODP actions; that is, it must not be
- * in need of revalidation.
+ * 'facet' must have accurately composed datapath actions; that is, it must
+ * not be in need of revalidation.
*
* Takes ownership of 'packet'. */
static void
facet_free(facet);
}
-/* Composes the ODP actions for 'facet' based on its rule's actions. */
+/* Composes the datapath actions for 'facet' based on its rule's actions. */
static void
facet_make_actions(struct ofproto_dpif *p, struct facet *facet,
const struct ofpbuf *packet)
}
static void
-facet_account(struct ofproto_dpif *ofproto,
- struct facet *facet, uint64_t extra_bytes)
+facet_account(struct ofproto_dpif *ofproto, struct facet *facet)
{
- uint64_t total_bytes, n_bytes;
+ uint64_t n_bytes;
struct ofbundle *in_bundle;
const struct nlattr *a;
tag_type dummy = 0;
ovs_be16 vlan_tci;
int vlan;
- total_bytes = facet->byte_count + extra_bytes;
- if (total_bytes <= facet->accounted_bytes) {
+ if (facet->byte_count <= facet->accounted_bytes) {
return;
}
- n_bytes = total_bytes - facet->accounted_bytes;
- facet->accounted_bytes = total_bytes;
+ n_bytes = facet->byte_count - facet->accounted_bytes;
+ facet->accounted_bytes = facet->byte_count;
/* Test that 'tags' is nonzero to ensure that only flows that include an
* OFPP_NORMAL action are used for learning and bond slave rebalancing.
struct ofport_dpif *port;
switch (nl_attr_type(a)) {
- case ODP_ACTION_ATTR_OUTPUT:
+ case OVS_ACTION_ATTR_OUTPUT:
port = get_odp_port(ofproto, nl_attr_get_u32(a));
if (port && port->bundle && port->bundle->bond) {
bond_account(port->bundle->bond, &facet->flow,
}
break;
- case ODP_ACTION_ATTR_STRIP_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
vlan_tci = htons(0);
break;
- case ODP_ACTION_ATTR_SET_DL_TCI:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
vlan_tci = nl_attr_get_be16(a);
break;
}
assert(!facet->dp_packet_count);
facet_push_stats(facet);
- facet_account(ofproto, facet, 0);
+ facet_account(ofproto, facet);
if (ofproto->netflow && !facet_is_controller_flow(facet)) {
struct ofexpired expired;
/* Reset counters to prevent double counting if 'facet' ever gets
* reinstalled. */
- facet->packet_count = 0;
- facet->byte_count = 0;
- facet->rs_packet_count = 0;
- facet->rs_byte_count = 0;
- facet->accounted_bytes = 0;
+ facet_reset_counters(facet);
netflow_flow_clear(&facet->nf_flow);
}
COVERAGE_INC(facet_revalidate);
/* Determine the new rule. */
- new_rule = rule_dpif_lookup(ofproto, &facet->flow);
+ new_rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
if (!new_rule) {
/* No new rule, so delete the facet. */
facet_remove(ofproto, facet);
return false;
}
- /* Calculate new ODP actions.
+ /* Calculate new datapath actions.
*
* We do not modify any 'facet' state yet, because we might need to, e.g.,
* emit a NetFlow expiration and, if so, we need to have the old state
|| memcmp(facet->actions, odp_actions->data,
facet->actions_len));
- /* If the ODP actions changed or the installability changed, then we need
- * to talk to the datapath. */
+ /* If the datapath actions changed or the installability changed,
+ * then we need to talk to the datapath. */
if (actions_changed || ctx.may_set_up_flow != facet->installed) {
if (ctx.may_set_up_flow) {
struct dpif_flow_stats stats;
}
}
+static void
+facet_reset_counters(struct facet *facet)
+{
+ facet->packet_count = 0;
+ facet->byte_count = 0;
+ facet->rs_packet_count = 0;
+ facet->rs_byte_count = 0;
+ facet->accounted_bytes = 0;
+}
+
static void
facet_push_stats(struct facet *facet)
{
/* Rules. */
static struct rule_dpif *
-rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
+rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
+ uint8_t table_id)
{
return rule_dpif_cast(rule_from_cls_rule(
- classifier_lookup(&ofproto->up.tables[0],
+ classifier_lookup(&ofproto->up.tables[table_id],
flow)));
}
rule->facets = victim->facets;
list_moved(&rule->facets);
LIST_FOR_EACH (facet, list_node, &rule->facets) {
+ /* XXX: We're only clearing our local counters here. It's possible
+ * that quite a few packets are unaccounted for in the datapath
+ * statistics. These will be accounted to the new rule instead of
+ * cleared as required. This could be fixed by clearing out the
+ * datapath statistics for this facet, but currently it doesn't
+ * seem worth it. */
+ facet_reset_counters(facet);
facet->rule = rule;
}
} else {
/* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
* create a new facet for it and use that. */
- if (rule_dpif_lookup(ofproto, flow) == rule) {
+ if (rule_dpif_lookup(ofproto, flow, 0) == rule) {
facet = facet_create(rule, flow, packet);
facet_execute(ofproto, facet, packet);
facet_install(ofproto, facet, true);
odp_flow_key_from_flow(&key, &flow);
ofpbuf_init(&odp_actions, 32);
- nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
+ nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
error = dpif_execute(ofproto->dpif,
key.data, key.size,
odp_actions.data, odp_actions.size,
return error;
}
\f
-/* OpenFlow to ODP action translation. */
+/* OpenFlow to datapath action translation. */
static void do_xlate_actions(const union ofp_action *in, size_t n_in,
struct action_xlate_ctx *ctx);
-static bool xlate_normal(struct action_xlate_ctx *);
+static void xlate_normal(struct action_xlate_ctx *);
static void
commit_odp_actions(struct action_xlate_ctx *ctx)
struct ofpbuf *odp_actions = ctx->odp_actions;
if (base->tun_id != flow->tun_id) {
- nl_msg_put_be64(odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
+ nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
base->tun_id = flow->tun_id;
}
if (base->nw_src != flow->nw_src) {
- nl_msg_put_be32(odp_actions, ODP_ACTION_ATTR_SET_NW_SRC, flow->nw_src);
+ nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_SRC, flow->nw_src);
base->nw_src = flow->nw_src;
}
if (base->nw_dst != flow->nw_dst) {
- nl_msg_put_be32(odp_actions, ODP_ACTION_ATTR_SET_NW_DST, flow->nw_dst);
+ nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_DST, flow->nw_dst);
base->nw_dst = flow->nw_dst;
}
+ if (base->nw_tos != flow->nw_tos) {
+ nl_msg_put_u8(odp_actions, OVS_ACTION_ATTR_SET_NW_TOS, flow->nw_tos);
+ base->nw_tos = flow->nw_tos;
+ }
+
if (base->vlan_tci != flow->vlan_tci) {
if (!(flow->vlan_tci & htons(VLAN_CFI))) {
- nl_msg_put_flag(odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
} else {
- nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
+ if (base->vlan_tci != htons(0)) {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+ }
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
flow->vlan_tci & ~htons(VLAN_CFI));
}
base->vlan_tci = flow->vlan_tci;
}
if (base->tp_src != flow->tp_src) {
- nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_TP_SRC, flow->tp_src);
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src);
base->tp_src = flow->tp_src;
}
if (base->tp_dst != flow->tp_dst) {
- nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_TP_DST, flow->tp_dst);
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_DST, flow->tp_dst);
base->tp_dst = flow->tp_dst;
}
if (!eth_addr_equals(base->dl_src, flow->dl_src)) {
- nl_msg_put_unspec(odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_SRC,
flow->dl_src, ETH_ADDR_LEN);
memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
}
if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) {
- nl_msg_put_unspec(odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_DST,
flow->dl_dst, ETH_ADDR_LEN);
memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
}
if (ctx->base_priority != ctx->priority) {
if (ctx->priority) {
- nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_SET_PRIORITY,
+ nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY,
ctx->priority);
} else {
- nl_msg_put_flag(odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY);
}
ctx->base_priority = ctx->priority;
}
}
commit_odp_actions(ctx);
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
+ nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
ctx->nf_output_iface = ofp_port;
}
static void
-xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
+xlate_table_action(struct action_xlate_ctx *ctx,
+ uint16_t in_port, uint8_t table_id)
{
if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
struct rule_dpif *rule;
uint16_t old_in_port;
+ uint8_t old_table_id;
+
+ old_table_id = ctx->table_id;
+ ctx->table_id = table_id;
/* Look up a flow with 'in_port' as the input port. Then restore the
* original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
* have surprising behavior). */
old_in_port = ctx->flow.in_port;
ctx->flow.in_port = in_port;
- rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow);
+ rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow, table_id);
ctx->flow.in_port = old_in_port;
if (ctx->resubmit_hook) {
do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
ctx->recurse--;
}
+
+ ctx->table_id = old_table_id;
} else {
static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
+ VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times",
MAX_RESUBMIT_RECURSION);
}
}
+static void
+xlate_resubmit_table(struct action_xlate_ctx *ctx,
+ const struct nx_action_resubmit *nar)
+{
+ uint16_t in_port;
+ uint8_t table_id;
+
+ in_port = (nar->in_port == htons(OFPP_IN_PORT)
+ ? ctx->flow.in_port
+ : ntohs(nar->in_port));
+ table_id = nar->table == 255 ? ctx->table_id : nar->table;
+
+ xlate_table_action(ctx, in_port, table_id);
+}
+
static void
flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask)
{
HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
uint16_t ofp_port = ofport->up.ofp_port;
if (ofp_port != ctx->flow.in_port && !(ofport->up.opp.config & mask)) {
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT,
+ nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT,
ofport->odp_port);
}
}
add_output_action(ctx, ctx->flow.in_port);
break;
case OFPP_TABLE:
- xlate_table_action(ctx, ctx->flow.in_port);
+ xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id);
break;
case OFPP_NORMAL:
xlate_normal(ctx);
break;
case OFPP_CONTROLLER:
commit_odp_actions(ctx);
- nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len);
+ nl_msg_put_u64(ctx->odp_actions, OVS_ACTION_ATTR_USERSPACE, max_len);
break;
case OFPP_LOCAL:
add_output_action(ctx, OFPP_LOCAL);
break;
+ case OFPP_NONE:
+ break;
default:
if (port != ctx->flow.in_port) {
add_output_action(ctx, port);
}
}
+static void
+xlate_output_reg_action(struct action_xlate_ctx *ctx,
+ const struct nx_action_output_reg *naor)
+{
+ uint64_t ofp_port;
+
+ ofp_port = nxm_read_field_bits(naor->src, naor->ofs_nbits, &ctx->flow);
+
+ if (ofp_port <= UINT16_MAX) {
+ xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len));
+ }
+}
+
static void
xlate_output_action(struct action_xlate_ctx *ctx,
const struct ofp_action_output *oao)
return;
}
- /* Figure out ODP output port. */
+ /* Figure out datapath output port. */
ofp_port = ntohs(oae->port);
if (ofp_port == OFPP_IN_PORT) {
ofp_port = ctx->flow.in_port;
}
odp_port = ofp_port_to_odp_port(ofp_port);
- /* Add ODP actions. */
+ /* Add datapath actions. */
ctx_priority = ctx->priority;
ctx->priority = priority;
add_output_action(ctx, odp_port);
autopath_execute(naa, &ctx->flow, ofp_port);
}
-static void
-xlate_nicira_action(struct action_xlate_ctx *ctx,
- const struct nx_action_header *nah)
+static bool
+slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
{
- const struct nx_action_resubmit *nar;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- enum nx_action_subtype subtype = ntohs(nah->subtype);
- ovs_be64 tun_id;
-
- assert(nah->vendor == htonl(NX_VENDOR_ID));
- switch (subtype) {
- case NXAST_RESUBMIT:
- nar = (const struct nx_action_resubmit *) nah;
- xlate_table_action(ctx, ntohs(nar->in_port));
- break;
-
- case NXAST_SET_TUNNEL:
- nast = (const struct nx_action_set_tunnel *) nah;
- tun_id = htonll(ntohl(nast->tun_id));
- ctx->flow.tun_id = tun_id;
- break;
-
- case NXAST_SET_QUEUE:
- nasq = (const struct nx_action_set_queue *) nah;
- xlate_set_queue_action(ctx, nasq);
- break;
-
- case NXAST_POP_QUEUE:
- ctx->priority = 0;
- break;
-
- case NXAST_REG_MOVE:
- nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
- &ctx->flow);
- break;
-
- case NXAST_REG_LOAD:
- nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
- &ctx->flow);
- break;
-
- case NXAST_NOTE:
- /* Nothing to do. */
- break;
-
- case NXAST_SET_TUNNEL64:
- tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
- ctx->flow.tun_id = tun_id;
- break;
-
- case NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) nah;
- multipath_execute(nam, &ctx->flow);
- break;
-
- case NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *) nah;
- xlate_autopath(ctx, naa);
- break;
+ struct ofproto_dpif *ofproto = ofproto_;
+ struct ofport_dpif *port;
- case NXAST_SNAT__OBSOLETE:
- case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
+ switch (ofp_port) {
+ case OFPP_IN_PORT:
+ case OFPP_TABLE:
+ case OFPP_NORMAL:
+ case OFPP_FLOOD:
+ case OFPP_ALL:
+ case OFPP_LOCAL:
+ return true;
+ case OFPP_CONTROLLER: /* Not supported by the bundle action. */
+ return false;
default:
- VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) subtype);
- break;
+ port = get_ofp_port(ofproto, ofp_port);
+ return port ? port->may_enable : false;
}
}
OFPUTIL_ACTION_FOR_EACH_UNSAFE (ia, left, in, n_in) {
const struct ofp_action_dl_addr *oada;
- enum ofp_action_type type = ntohs(ia->type);
-
- switch (type) {
- case OFPAT_OUTPUT:
+ const struct nx_action_resubmit *nar;
+ const struct nx_action_set_tunnel *nast;
+ const struct nx_action_set_queue *nasq;
+ const struct nx_action_multipath *nam;
+ const struct nx_action_autopath *naa;
+ const struct nx_action_bundle *nab;
+ const struct nx_action_output_reg *naor;
+ enum ofputil_action_code code;
+ ovs_be64 tun_id;
+
+ code = ofputil_decode_action_unsafe(ia);
+ switch (code) {
+ case OFPUTIL_OFPAT_OUTPUT:
xlate_output_action(ctx, &ia->output);
break;
- case OFPAT_SET_VLAN_VID:
+ case OFPUTIL_OFPAT_SET_VLAN_VID:
ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
break;
- case OFPAT_SET_VLAN_PCP:
+ case OFPUTIL_OFPAT_SET_VLAN_PCP:
ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
ctx->flow.vlan_tci |= htons(
(ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
break;
- case OFPAT_STRIP_VLAN:
+ case OFPUTIL_OFPAT_STRIP_VLAN:
ctx->flow.vlan_tci = htons(0);
break;
- case OFPAT_SET_DL_SRC:
+ case OFPUTIL_OFPAT_SET_DL_SRC:
oada = ((struct ofp_action_dl_addr *) ia);
memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
break;
- case OFPAT_SET_DL_DST:
+ case OFPUTIL_OFPAT_SET_DL_DST:
oada = ((struct ofp_action_dl_addr *) ia);
memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
break;
- case OFPAT_SET_NW_SRC:
+ case OFPUTIL_OFPAT_SET_NW_SRC:
ctx->flow.nw_src = ia->nw_addr.nw_addr;
break;
- case OFPAT_SET_NW_DST:
+ case OFPUTIL_OFPAT_SET_NW_DST:
ctx->flow.nw_dst = ia->nw_addr.nw_addr;
break;
- case OFPAT_SET_NW_TOS:
- ctx->flow.nw_tos = ia->nw_tos.nw_tos;
+ case OFPUTIL_OFPAT_SET_NW_TOS:
+ ctx->flow.nw_tos = ia->nw_tos.nw_tos & IP_DSCP_MASK;
break;
- case OFPAT_SET_TP_SRC:
+ case OFPUTIL_OFPAT_SET_TP_SRC:
ctx->flow.tp_src = ia->tp_port.tp_port;
break;
- case OFPAT_SET_TP_DST:
+ case OFPUTIL_OFPAT_SET_TP_DST:
ctx->flow.tp_dst = ia->tp_port.tp_port;
break;
- case OFPAT_VENDOR:
- xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
+ case OFPUTIL_OFPAT_ENQUEUE:
+ xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
break;
- case OFPAT_ENQUEUE:
- xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+ case OFPUTIL_NXAST_RESUBMIT:
+ nar = (const struct nx_action_resubmit *) ia;
+ xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id);
+ break;
+
+ case OFPUTIL_NXAST_RESUBMIT_TABLE:
+ xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia);
+ break;
+
+ case OFPUTIL_NXAST_SET_TUNNEL:
+ nast = (const struct nx_action_set_tunnel *) ia;
+ tun_id = htonll(ntohl(nast->tun_id));
+ ctx->flow.tun_id = tun_id;
+ break;
+
+ case OFPUTIL_NXAST_SET_QUEUE:
+ nasq = (const struct nx_action_set_queue *) ia;
+ xlate_set_queue_action(ctx, nasq);
+ break;
+
+ case OFPUTIL_NXAST_POP_QUEUE:
+ ctx->priority = 0;
break;
- default:
- VLOG_DBG_RL(&rl, "unknown action type %d", (int) type);
+ case OFPUTIL_NXAST_REG_MOVE:
+ nxm_execute_reg_move((const struct nx_action_reg_move *) ia,
+ &ctx->flow);
+ break;
+
+ case OFPUTIL_NXAST_REG_LOAD:
+ nxm_execute_reg_load((const struct nx_action_reg_load *) ia,
+ &ctx->flow);
+ break;
+
+ case OFPUTIL_NXAST_NOTE:
+ /* Nothing to do. */
+ break;
+
+ case OFPUTIL_NXAST_SET_TUNNEL64:
+ tun_id = ((const struct nx_action_set_tunnel64 *) ia)->tun_id;
+ ctx->flow.tun_id = tun_id;
+ break;
+
+ case OFPUTIL_NXAST_MULTIPATH:
+ nam = (const struct nx_action_multipath *) ia;
+ multipath_execute(nam, &ctx->flow);
+ break;
+
+ case OFPUTIL_NXAST_AUTOPATH:
+ naa = (const struct nx_action_autopath *) ia;
+ xlate_autopath(ctx, naa);
+ break;
+
+ case OFPUTIL_NXAST_BUNDLE:
+ ctx->ofproto->has_bundle_action = true;
+ nab = (const struct nx_action_bundle *) ia;
+ xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow,
+ slave_enabled_cb,
+ ctx->ofproto), 0);
+ break;
+
+ case OFPUTIL_NXAST_BUNDLE_LOAD:
+ ctx->ofproto->has_bundle_action = true;
+ nab = (const struct nx_action_bundle *) ia;
+ bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
+ ctx->ofproto);
+ break;
+
+ case OFPUTIL_NXAST_OUTPUT_REG:
+ naor = (const struct nx_action_output_reg *) ia;
+ xlate_output_reg_action(ctx, naor);
break;
}
}
ctx->priority = 0;
ctx->base_priority = 0;
ctx->base_flow = ctx->flow;
+ ctx->table_id = 0;
if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
ctx->may_set_up_flow = false;
static bool
ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan)
{
- return bundle->vlan < 0 && vlan_bitmap_contains(bundle->trunks, vlan);
+ return (bundle->vlan < 0
+ && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan)));
}
static bool
static bool
vlan_is_mirrored(const struct ofmirror *m, int vlan)
{
- return vlan_bitmap_contains(m->vlans, vlan);
+ return !m->vlans || bitmap_is_set(m->vlans, vlan);
+}
+
+/* Returns true if a packet with Ethernet destination MAC 'dst' may be mirrored
+ * to a VLAN. In general most packets may be mirrored but we want to drop
+ * protocols that may confuse switches. */
+static bool
+eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN])
+{
+ /* If you change this function's behavior, please update corresponding
+ * documentation in vswitch.xml at the same time. */
+ if (dst[0] != 0x01) {
+ /* All the currently banned MACs happen to start with 01 currently, so
+ * this is a quick way to eliminate most of the good ones. */
+ } else {
+ if (eth_addr_is_reserved(dst)) {
+ /* Drop STP, IEEE pause frames, and other reserved protocols
+ * (01-80-c2-00-00-0x). */
+ return false;
+ }
+
+ if (dst[0] == 0x01 && dst[1] == 0x00 && dst[2] == 0x0c) {
+ /* Cisco OUI. */
+ if ((dst[3] & 0xfe) == 0xcc &&
+ (dst[4] & 0xfe) == 0xcc &&
+ (dst[5] & 0xfe) == 0xcc) {
+ /* Drop the following protocols plus others following the same
+ pattern:
+
+ CDP, VTP, DTP, PAgP (01-00-0c-cc-cc-cc)
+ Spanning Tree PVSTP+ (01-00-0c-cc-cc-cd)
+ STP Uplink Fast (01-00-0c-cd-cd-cd) */
+ return false;
+ }
+
+ if (!(dst[3] | dst[4] | dst[5])) {
+ /* Drop Inter Switch Link packets (01-00-0c-00-00-00). */
+ return false;
+ }
+ }
+ }
+ return true;
}
static void
&& !dst_is_duplicate(set, &dst)) {
dst_set_add(set, &dst);
}
- } else {
+ } else if (eth_dst_may_rspan(ctx->flow.dl_dst)) {
struct ofbundle *bundle;
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
continue;
}
nl_msg_put_u32(ctx->odp_actions,
- ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port);
+ OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port);
}
/* Then output the rest. */
}
if (dst->vlan != cur_vlan) {
if (dst->vlan == OFP_VLAN_NONE) {
- nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
+ nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
} else {
ovs_be16 tci;
+
+ if (cur_vlan != OFP_VLAN_NONE) {
+ nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+ }
tci = htons(dst->vlan & VLAN_VID_MASK);
tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK);
nl_msg_put_be16(ctx->odp_actions,
- ODP_ACTION_ATTR_SET_DL_TCI, tci);
+ OVS_ACTION_ATTR_PUSH_VLAN, tci);
}
cur_vlan = dst->vlan;
}
nl_msg_put_u32(ctx->odp_actions,
- ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port);
+ OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port);
}
dst_set_free(&set);
return false;
}
- /* Drop frames for reserved multicast addresses. */
- if (eth_addr_is_reserved(flow->dl_dst)) {
+ /* Drop frames for reserved multicast addresses
+ * only if forward_bpdu option is absent. */
+ if (eth_addr_is_reserved(flow->dl_dst) &&
+ !ofproto->up.forward_bpdu) {
return false;
}
return true;
}
-/* If the composed actions may be applied to any packet in the given 'flow',
- * returns true. Otherwise, the actions should only be applied to 'packet', or
- * not at all, if 'packet' was NULL. */
-static bool
+static void
xlate_normal(struct action_xlate_ctx *ctx)
{
struct ofbundle *in_bundle;
* of time where we could learn from a packet reflected on a bond and
* blackhole packets before the learning table is updated to reflect
* the correct port. */
- return false;
+ ctx->may_set_up_flow = false;
+ return;
} else {
out_bundle = OFBUNDLE_FLOOD;
}
if (in_bundle) {
compose_actions(ctx, vlan, in_bundle, out_bundle);
}
-
- return true;
}
\f
static bool
};
static void
-trace_format_rule(struct ds *result, int level, const struct rule *rule)
+trace_format_rule(struct ds *result, uint8_t table_id, int level,
+ const struct rule_dpif *rule)
{
ds_put_char_multiple(result, '\t', level);
if (!rule) {
return;
}
- ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
- ntohll(rule->flow_cookie));
- cls_rule_format(&rule->cr, result);
+ ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
+ table_id, ntohll(rule->up.flow_cookie));
+ cls_rule_format(&rule->up.cr, result);
ds_put_char(result, '\n');
ds_put_char_multiple(result, '\t', level);
ds_put_cstr(result, "OpenFlow ");
- ofp_print_actions(result, rule->actions, rule->n_actions);
+ ofp_print_actions(result, rule->up.actions, rule->up.n_actions);
ds_put_char(result, '\n');
}
ds_put_char(result, '\n');
}
+static void
+trace_format_regs(struct ds *result, int level, const char *title,
+ struct ofproto_trace *trace)
+{
+ size_t i;
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s:", title);
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ ds_put_format(result, " reg%zu=0x%"PRIx32, i, trace->flow.regs[i]);
+ }
+ ds_put_char(result, '\n');
+}
+
static void
trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
{
ds_put_char(result, '\n');
trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
- trace_format_rule(result, ctx->recurse + 1, &rule->up);
+ trace_format_regs(result, ctx->recurse + 1, "Resubmitted regs", trace);
+ trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
}
static void
ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
void *aux OVS_UNUSED)
{
- char *dpname, *in_port_s, *tun_id_s, *packet_s;
+ char *dpname, *arg1, *arg2, *arg3;
char *args = xstrdup(args_);
char *save_ptr = NULL;
struct ofproto_dpif *ofproto;
- struct ofpbuf packet;
+ struct ofpbuf odp_key;
+ struct ofpbuf *packet;
struct rule_dpif *rule;
struct ds result;
struct flow flow;
- uint16_t in_port;
- ovs_be64 tun_id;
char *s;
- ofpbuf_init(&packet, strlen(args) / 2);
+ packet = NULL;
+ ofpbuf_init(&odp_key, 0);
ds_init(&result);
dpname = strtok_r(args, " ", &save_ptr);
- tun_id_s = strtok_r(NULL, " ", &save_ptr);
- in_port_s = strtok_r(NULL, " ", &save_ptr);
- packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
- if (!dpname || !in_port_s || !packet_s) {
+ arg1 = strtok_r(NULL, " ", &save_ptr);
+ arg2 = strtok_r(NULL, " ", &save_ptr);
+ arg3 = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
+ if (dpname && arg1 && !arg2 && !arg3) {
+ /* ofproto/trace dpname flow */
+ int error;
+
+ /* Convert string to datapath key. */
+ ofpbuf_init(&odp_key, 0);
+ error = odp_flow_key_from_string(arg1, &odp_key);
+ if (error) {
+ unixctl_command_reply(conn, 501, "Bad flow syntax");
+ goto exit;
+ }
+
+ /* Convert odp_key to flow. */
+ error = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow);
+ if (error) {
+ unixctl_command_reply(conn, 501, "Invalid flow");
+ goto exit;
+ }
+ } else if (dpname && arg1 && arg2 && arg3) {
+ /* ofproto/trace dpname tun_id in_port packet */
+ uint16_t in_port;
+ ovs_be64 tun_id;
+
+ tun_id = htonll(strtoull(arg1, NULL, 0));
+ in_port = ofp_port_to_odp_port(atoi(arg2));
+
+ packet = ofpbuf_new(strlen(args) / 2);
+ arg3 = ofpbuf_put_hex(packet, arg3, NULL);
+ arg3 += strspn(arg3, " ");
+ if (*arg3 != '\0') {
+ unixctl_command_reply(conn, 501, "Trailing garbage in command");
+ goto exit;
+ }
+ if (packet->size < ETH_HEADER_LEN) {
+ unixctl_command_reply(conn, 501,
+ "Packet data too short for Ethernet");
+ goto exit;
+ }
+
+ ds_put_cstr(&result, "Packet: ");
+ s = ofp_packet_to_string(packet->data, packet->size, packet->size);
+ ds_put_cstr(&result, s);
+ free(s);
+
+ flow_extract(packet, tun_id, in_port, &flow);
+ } else {
unixctl_command_reply(conn, 501, "Bad command syntax");
goto exit;
}
goto exit;
}
- tun_id = htonll(strtoull(tun_id_s, NULL, 0));
- in_port = ofp_port_to_odp_port(atoi(in_port_s));
-
- packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
- packet_s += strspn(packet_s, " ");
- if (*packet_s != '\0') {
- unixctl_command_reply(conn, 501, "Trailing garbage in command");
- goto exit;
- }
- if (packet.size < ETH_HEADER_LEN) {
- unixctl_command_reply(conn, 501, "Packet data too short for Ethernet");
- goto exit;
- }
-
- ds_put_cstr(&result, "Packet: ");
- s = ofp_packet_to_string(packet.data, packet.size, packet.size);
- ds_put_cstr(&result, s);
- free(s);
-
- flow_extract(&packet, tun_id, in_port, &flow);
ds_put_cstr(&result, "Flow: ");
flow_format(&result, &flow);
ds_put_char(&result, '\n');
- rule = rule_dpif_lookup(ofproto, &flow);
- trace_format_rule(&result, 0, &rule->up);
+ rule = rule_dpif_lookup(ofproto, &flow, 0);
+ trace_format_rule(&result, 0, 0, rule);
if (rule) {
struct ofproto_trace trace;
struct ofpbuf *odp_actions;
trace.result = &result;
trace.flow = flow;
- action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet);
+ action_xlate_ctx_init(&trace.ctx, ofproto, &flow, packet);
trace.ctx.resubmit_hook = trace_resubmit;
odp_actions = xlate_actions(&trace.ctx,
rule->up.actions, rule->up.n_actions);
ds_put_cstr(&result, "Datapath actions: ");
format_odp_actions(&result, odp_actions->data, odp_actions->size);
ofpbuf_delete(odp_actions);
+
+ if (!trace.ctx.may_set_up_flow) {
+ if (packet) {
+ ds_put_cstr(&result, "\nThis flow is not cachable.");
+ } else {
+ ds_put_cstr(&result, "\nThe datapath actions are incomplete--"
+ "for complete actions, please supply a packet.");
+ }
+ }
}
unixctl_command_reply(conn, 200, ds_cstr(&result));
exit:
ds_destroy(&result);
- ofpbuf_uninit(&packet);
+ ofpbuf_delete(packet);
+ ofpbuf_uninit(&odp_key);
free(args);
}
set_sflow,
set_cfm,
get_cfm_fault,
+ get_cfm_remote_mpids,
bundle_set,
bundle_remove,
mirror_set,
set_flood_vlans,
is_mirror_output_bundle,
+ forward_bpdu_changed,
};