#include "fail-open.h"
#include "hmapx.h"
#include "lacp.h"
+#include "learn.h"
#include "mac-learning.h"
#include "multipath.h"
#include "netdev.h"
* revalidating without a packet to refer to. */
const struct ofpbuf *packet;
+ /* Should OFPP_NORMAL MAC learning and NXAST_LEARN actions execute? We
+ * want to execute them if we are actually processing a packet, or if we
+ * are accounting for packets that the datapath has processed, but not if
+ * we are just revalidating. */
+ bool may_learn;
+
/* If nonnull, called just before executing a resubmit action.
*
* This is normally null so the client has to set it manually after
* to look at them after it returns. */
struct ofpbuf *odp_actions; /* Datapath actions. */
- tag_type tags; /* Tags associated with OFPP_NORMAL actions. */
+ tag_type tags; /* Tags associated with actions. */
bool may_set_up_flow; /* True ordinarily; false if the actions must
* be reassessed for every packet. */
+ bool has_learn; /* Actions include NXAST_LEARN? */
+ bool has_normal; /* Actions output to OFPP_NORMAL? */
uint16_t nf_output_iface; /* Output interface index for NetFlow. */
/* xlate_actions() initializes and uses these members, but the client has no
* 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. */
bool installed; /* Installed in datapath? */
bool may_install; /* True ordinarily; false if actions must
* be reassessed for every packet. */
+ bool has_learn; /* Actions include NXAST_LEARN? */
+ bool has_normal; /* Actions output to OFPP_NORMAL? */
size_t actions_len; /* Number of bytes in actions[]. */
struct nlattr *actions; /* Datapath actions. */
tag_type tags; /* Tags. */
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 *);
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");
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 "
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);
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.
/* Has 'rule' expired? */
now = time_msec();
if (rule->up.hard_timeout
- && now > rule->up.created + rule->up.hard_timeout * 1000) {
+ && now > rule->up.modified + rule->up.hard_timeout * 1000) {
reason = OFPRR_HARD_TIMEOUT;
} else if (rule->up.idle_timeout && list_is_empty(&rule->facets)
&& now > rule->used + rule->up.idle_timeout * 1000) {
struct ofpbuf *packet)
{
if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
- && odp_actions->nla_type == ODP_ACTION_ATTR_USERSPACE) {
+ && 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)
odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
facet->tags = ctx.tags;
facet->may_install = ctx.may_set_up_flow;
+ facet->has_learn = ctx.has_learn;
+ facet->has_normal = ctx.has_normal;
facet->nf_flow.output_iface = ctx.nf_output_iface;
if (facet->actions_len != odp_actions->size
}
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;
- struct ofbundle *in_bundle;
+ uint64_t n_bytes;
const struct nlattr *a;
- tag_type dummy = 0;
unsigned int left;
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.
- * This works because OFPP_NORMAL always sets a nonzero tag value.
- *
- * Feed information from the active flows back into the learning table to
+ /* Feed information from the active flows back into the learning table to
* ensure that table is always in sync with what is actually flowing
* through the datapath. */
- if (!facet->tags
- || !is_admissible(ofproto, &facet->flow, false, &dummy,
- &vlan, &in_bundle)) {
- return;
- }
+ if (facet->has_learn || facet->has_normal) {
+ struct action_xlate_ctx ctx;
- update_learning_table(ofproto, &facet->flow, vlan, in_bundle);
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL);
+ ctx.may_learn = true;
+ ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions,
+ facet->rule->up.n_actions));
+ }
- if (!ofproto->has_bonded_bundles) {
+ if (!facet->has_normal || !ofproto->has_bonded_bundles) {
return;
}
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);
}
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;
facet->tags = ctx.tags;
facet->nf_flow.output_iface = ctx.nf_output_iface;
facet->may_install = ctx.may_set_up_flow;
+ facet->has_learn = ctx.has_learn;
+ facet->has_normal = ctx.has_normal;
if (actions_changed) {
free(facet->actions);
facet->actions_len = odp_actions->size;
}
}
+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)
{
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 {
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);
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, ODP_ACTION_ATTR_SET_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;
}
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);
}
}
break;
case OFPP_CONTROLLER:
commit_odp_actions(ctx);
- nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_USERSPACE, 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);
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);
}
}
+static void
+xlate_learn_action(struct action_xlate_ctx *ctx,
+ const struct nx_action_learn *learn)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ struct ofputil_flow_mod fm;
+ int error;
+
+ learn_execute(learn, &ctx->flow, &fm);
+
+ error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
+ if (error && !VLOG_DROP_WARN(&rl)) {
+ char *msg = ofputil_error_to_string(error);
+ VLOG_WARN("learning action failed to modify flow table (%s)", msg);
+ free(msg);
+ }
+
+ free(fm.actions);
+}
+
static void
do_xlate_actions(const union ofp_action *in, size_t n_in,
struct action_xlate_ctx *ctx)
naor = (const struct nx_action_output_reg *) ia;
xlate_output_reg_action(ctx, naor);
break;
+
+ case OFPUTIL_NXAST_LEARN:
+ ctx->has_learn = true;
+ if (ctx->may_learn) {
+ xlate_learn_action(ctx, (const struct nx_action_learn *) ia);
+ }
+ break;
}
}
}
ctx->ofproto = ofproto;
ctx->flow = *flow;
ctx->packet = packet;
+ ctx->may_learn = packet != NULL;
ctx->resubmit_hook = NULL;
}
ctx->odp_actions = ofpbuf_new(512);
ctx->tags = 0;
ctx->may_set_up_flow = true;
+ ctx->has_learn = false;
+ ctx->has_normal = false;
ctx->nf_output_iface = NF_OUT_DROP;
ctx->recurse = 0;
ctx->priority = 0;
ctx->base_priority = 0;
ctx->base_flow = ctx->flow;
+ ctx->base_flow.tun_id = 0;
ctx->table_id = 0;
if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
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);
"port %"PRIu16,
ofproto->up.name, flow->in_port);
}
+ *vlanp = -1;
return false;
}
*vlanp = vlan = flow_get_vlan(ofproto, flow, in_bundle, have_packet);
struct mac_entry *mac;
int vlan;
+ ctx->has_normal = true;
+
/* Check whether we should drop packets in this flow. */
if (!is_admissible(ctx->ofproto, &ctx->flow, ctx->packet != NULL,
&ctx->tags, &vlan, &in_bundle)) {
goto done;
}
- /* Learn source MAC (but don't try to learn from revalidation). */
- if (ctx->packet) {
+ /* Learn source MAC. */
+ if (ctx->may_learn) {
update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
}
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_regs(result, ctx->recurse + 1, "Resubmitted regs", trace);
trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
}
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 */
+ if (dpname && arg1 && (!arg2 || !strcmp(arg2, "-generate")) && !arg3) {
+ /* ofproto/trace dpname flow [-generate] */
int error;
- /* Convert string to ODP key. */
+ /* 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, "Invalid flow");
goto exit;
}
+
+ /* Generate a packet, if requested. */
+ if (arg2) {
+ packet = ofpbuf_new(0);
+ flow_compose(packet, &flow);
+ }
} else if (dpname && arg1 && arg2 && arg3) {
/* ofproto/trace dpname tun_id in_port packet */
uint16_t in_port;
set_sflow,
set_cfm,
get_cfm_fault,
+ get_cfm_remote_mpids,
bundle_set,
bundle_remove,
mirror_set,