/* Maximum depth of flow table recursion (due to resubmit actions) in a
* flow translation. */
-#define MAX_RESUBMIT_RECURSION 32
+#define MAX_RESUBMIT_RECURSION 64
/* Number of implemented OpenFlow tables. */
enum { N_TABLES = 255 };
static struct rule_dpif *rule_dpif_lookup__(struct ofproto_dpif *,
const struct flow *,
uint8_t table);
+static struct rule_dpif *rule_dpif_miss_rule(struct ofproto_dpif *ofproto,
+ const struct flow *flow);
static void rule_credit_stats(struct rule_dpif *,
const struct dpif_flow_stats *);
* calling action_xlate_ctx_init(). */
void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *rule);
+ /* If nonnull, flow translation calls this function to report some
+ * significant decision, e.g. to explain why OFPP_NORMAL translation
+ * dropped a packet. */
+ void (*report_hook)(struct action_xlate_ctx *, const char *s);
+
/* If nonnull, flow translation credits the specified statistics to each
* rule reached through a resubmit or OFPP_TABLE action.
*
const struct nlattr **actionsp,
size_t *actions_lenp);
+static void xlate_report(struct action_xlate_ctx *ctx, const char *s);
+
/* A subfacet (see "struct subfacet" below) has three possible installation
* states:
*
static struct subfacet *subfacet_create(struct facet *, enum odp_key_fitness,
const struct nlattr *key,
- size_t key_len, ovs_be16 initial_tci);
+ size_t key_len, ovs_be16 initial_tci,
+ long long int now);
static struct subfacet *subfacet_find(struct ofproto_dpif *,
const struct nlattr *key, size_t key_len);
static void subfacet_destroy(struct subfacet *);
}
static void
-get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots)
+get_tables(struct ofproto *ofproto_, struct ofp10_table_stats *ots)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct dpif_dp_stats s;
return ofport->cfm ? cfm_get_fault(ofport->cfm) : -1;
}
+static int
+get_cfm_opup(const struct ofport *ofport_)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ return ofport->cfm ? cfm_get_opup(ofport->cfm) : -1;
+}
+
static int
get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps,
size_t *n_rmps)
break;
case PORT_VLAN_TRUNK:
- trunks = (unsigned long *) s->trunks;
+ trunks = CONST_CAST(unsigned long *, s->trunks);
break;
case PORT_VLAN_NATIVE_UNTAGGED:
bitmap_set1(trunks, vlan);
bitmap_set0(trunks, 0);
} else {
- trunks = (unsigned long *) s->trunks;
+ trunks = CONST_CAST(unsigned long *, s->trunks);
}
break;
port_run_fast(ofport);
if (ofport->cfm) {
+ int cfm_opup = cfm_get_opup(ofport->cfm);
+
cfm_run(ofport->cfm);
- enable = enable && !cfm_get_fault(ofport->cfm)
- && cfm_get_opup(ofport->cfm);
+ enable = enable && !cfm_get_fault(ofport->cfm);
+
+ if (cfm_opup >= 0) {
+ enable = enable && cfm_opup;
+ }
}
if (ofport->bundle) {
port_add(struct ofproto *ofproto_, struct netdev *netdev, uint16_t *ofp_portp)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- uint16_t odp_port;
+ uint16_t odp_port = UINT16_MAX;
int error;
error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
/* ofproto->stats.rx_packets represents packets that were received on
* some port and we processed internally and dropped (e.g. STP).
- * Account fro them as if they had been forwarded to OFPP_LOCAL. */
+ * Account for them as if they had been forwarded to OFPP_LOCAL. */
if (stats->tx_packets != UINT64_MAX) {
stats->tx_packets += ofproto->stats.rx_packets;
flow_get_metadata(flow, &pin.fmd);
- /* Registers aren't meaningful on a miss. */
- memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks);
-
connmgr_send_packet_in(ofproto->up.connmgr, &pin);
}
struct flow_miss_op *ops, size_t *n_ops)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ long long int now = time_msec();
struct action_xlate_ctx ctx;
struct ofpbuf *packet;
ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
- dpif_flow_stats_extract(&miss->flow, packet, &stats);
+ dpif_flow_stats_extract(&miss->flow, packet, now, &stats);
rule_credit_stats(rule, &stats);
action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
}
/* Handles 'miss', which matches 'facet'. May add any required datapath
- * operations to 'ops', incrementing '*n_ops' for each new op. */
+ * operations to 'ops', incrementing '*n_ops' for each new op.
+ *
+ * All of the packets in 'miss' are considered to have arrived at time 'now'.
+ * This is really important only for new facets: if we just called time_msec()
+ * here, then the new subfacet or its packets could look (occasionally) as
+ * though it was used some time after the facet was used. That can make a
+ * one-packet flow look like it has a nonzero duration, which looks odd in
+ * e.g. NetFlow statistics. */
static void
handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
+ long long int now,
struct flow_miss_op *ops, size_t *n_ops)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
subfacet = subfacet_create(facet,
miss->key_fitness, miss->key, miss->key_len,
- miss->initial_tci);
+ miss->initial_tci, now);
LIST_FOR_EACH (packet, list_node, &miss->packets) {
struct flow_miss_op *op = &ops[*n_ops];
subfacet_make_actions(subfacet, packet, &odp_actions);
}
- dpif_flow_stats_extract(&facet->flow, packet, &stats);
+ dpif_flow_stats_extract(&facet->flow, packet, now, &stats);
subfacet_update_stats(subfacet, &stats);
if (subfacet->actions_len) {
struct flow_miss_op *ops, size_t *n_ops)
{
struct facet *facet;
+ long long int now;
uint32_t hash;
/* The caller must ensure that miss->hmap_node.hash contains
}
facet = facet_create(rule, &miss->flow, hash);
+ now = facet->used;
+ } else {
+ now = time_msec();
}
- handle_flow_miss_with_facet(miss, facet, ops, n_ops);
+ handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
}
/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
const struct ofpact *ofpacts = rule->ofpacts;
size_t ofpacts_len = rule->ofpacts_len;
- if (ofpacts->type == OFPACT_CONTROLLER &&
+ if (ofpacts_len > 0 &&
+ ofpacts->type == OFPACT_CONTROLLER &&
ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len)) {
return true;
}
* subfacet_make_actions(). */
static struct subfacet *
subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
- const struct nlattr *key, size_t key_len, ovs_be16 initial_tci)
+ const struct nlattr *key, size_t key_len,
+ ovs_be16 initial_tci, long long int now)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
uint32_t key_hash = odp_flow_key_hash(key, key_len);
struct subfacet *subfacet;
- subfacet = subfacet_find__(ofproto, key, key_len, key_hash, &facet->flow);
- if (subfacet) {
- if (subfacet->facet == facet) {
- return subfacet;
+ if (list_is_empty(&facet->subfacets)) {
+ subfacet = &facet->one_subfacet;
+ } else {
+ subfacet = subfacet_find__(ofproto, key, key_len, key_hash,
+ &facet->flow);
+ if (subfacet) {
+ if (subfacet->facet == facet) {
+ return subfacet;
+ }
+
+ /* This shouldn't happen. */
+ VLOG_ERR_RL(&rl, "subfacet with wrong facet");
+ subfacet_destroy(subfacet);
}
- /* This shouldn't happen. */
- VLOG_ERR_RL(&rl, "subfacet with wrong facet");
- subfacet_destroy(subfacet);
+ subfacet = xmalloc(sizeof *subfacet);
}
- subfacet = (list_is_empty(&facet->subfacets)
- ? &facet->one_subfacet
- : xmalloc(sizeof *subfacet));
hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
list_push_back(&facet->subfacets, &subfacet->list_node);
subfacet->facet = facet;
subfacet->key = NULL;
subfacet->key_len = 0;
}
- subfacet->used = time_msec();
+ subfacet->used = now;
subfacet->dp_packet_count = 0;
subfacet->dp_byte_count = 0;
subfacet->actions_len = 0;
static struct rule_dpif *
rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
{
- struct ofport_dpif *port;
struct rule_dpif *rule;
rule = rule_dpif_lookup__(ofproto, flow, 0);
return rule;
}
- port = get_ofp_port(ofproto, flow->in_port);
- if (!port) {
- VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, flow->in_port);
- return ofproto->miss_rule;
- }
-
- if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
- return ofproto->no_packet_in_rule;
- }
- return ofproto->miss_rule;
+ return rule_dpif_miss_rule(ofproto, flow);
}
static struct rule_dpif *
return rule_dpif_cast(rule_from_cls_rule(cls_rule));
}
+static struct rule_dpif *
+rule_dpif_miss_rule(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct ofport_dpif *port;
+
+ port = get_ofp_port(ofproto, flow->in_port);
+ if (!port) {
+ VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, flow->in_port);
+ return ofproto->miss_rule;
+ }
+
+ if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
+ return ofproto->no_packet_in_rule;
+ }
+ return ofproto->miss_rule;
+}
+
static void
complete_operation(struct rule_dpif *rule)
{
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
- dpif_flow_stats_extract(flow, packet, &stats);
+ dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
rule_credit_stats(rule, &stats);
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
struct flow flow;
int error;
- flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow);
+ flow_extract(packet, 0, 0, 0, &flow);
odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
flow.vlan_tci);
if (odp_port != ofport->odp_port) {
static void
xlate_table_action(struct action_xlate_ctx *ctx,
- uint16_t in_port, uint8_t table_id)
+ uint16_t in_port, uint8_t table_id, bool may_packet_in)
{
if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
struct ofproto_dpif *ofproto = ctx->ofproto;
ctx->resubmit_hook(ctx, rule);
}
+ if (rule == NULL && may_packet_in) {
+ /* TODO:XXX
+ * check if table configuration flags
+ * OFPTC_TABLE_MISS_CONTROLLER, default.
+ * OFPTC_TABLE_MISS_CONTINUE,
+ * OFPTC_TABLE_MISS_DROP
+ * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
+ */
+ rule = rule_dpif_miss_rule(ofproto, &ctx->flow);
+ }
+
if (rule) {
struct rule_dpif *old_rule = ctx->rule;
table_id = ctx->table_id;
}
- xlate_table_action(ctx, in_port, table_id);
+ xlate_table_action(ctx, in_port, table_id, false);
}
static void
}
static bool
-compose_dec_ttl(struct action_xlate_ctx *ctx)
+compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
{
if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
ctx->flow.nw_ttl--;
return false;
} else {
- execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+ size_t i;
+
+ for (i = 0; i < ids->n_controllers; i++) {
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
+ ids->cnt_ids[i]);
+ }
/* Stop processing for current table. */
return true;
static void
xlate_output_action(struct action_xlate_ctx *ctx,
- uint16_t port, uint16_t max_len)
+ uint16_t port, uint16_t max_len, bool may_packet_in)
{
uint16_t prev_nf_output_iface = ctx->nf_output_iface;
compose_output_action(ctx, ctx->flow.in_port);
break;
case OFPP_TABLE:
- xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id);
+ xlate_table_action(ctx, ctx->flow.in_port, 0, may_packet_in);
break;
case OFPP_NORMAL:
xlate_normal(ctx);
{
uint64_t port = mf_get_subfield(&or->src, &ctx->flow);
if (port <= UINT16_MAX) {
- xlate_output_action(ctx, port, or->max_len);
+ xlate_output_action(ctx, port, or->max_len, false);
}
}
error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
if (error) {
/* Fall back to ordinary output action. */
- xlate_output_action(ctx, enqueue->port, 0);
+ xlate_output_action(ctx, enqueue->port, 0, false);
return;
}
if (bundle->dst.field) {
nxm_reg_load(&bundle->dst, port, &ctx->flow);
} else {
- xlate_output_action(ctx, port, 0);
+ xlate_output_action(ctx, port, 0, false);
}
}
switch (a->type) {
case OFPACT_OUTPUT:
xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
- ofpact_get_OUTPUT(a)->max_len);
+ ofpact_get_OUTPUT(a)->max_len, true);
break;
case OFPACT_CONTROLLER:
break;
case OFPACT_DEC_TTL:
- if (compose_dec_ttl(ctx)) {
+ if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
goto out;
}
break;
ctx->may_learn = packet != NULL;
ctx->tcp_flags = tcp_flags;
ctx->resubmit_hook = NULL;
+ ctx->report_hook = NULL;
ctx->resubmit_stats = NULL;
}
xlate_actions(ctx, ofpacts, ofpacts_len, &odp_actions);
ofpbuf_uninit(&odp_actions);
}
+
+static void
+xlate_report(struct action_xlate_ctx *ctx, const char *s)
+{
+ if (ctx->report_hook) {
+ ctx->report_hook(ctx, s);
+ }
+}
\f
/* OFPP_NORMAL implementation. */
* so in one special case.
*/
static bool
-is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
- struct ofport_dpif *in_port, uint16_t vlan, tag_type *tags)
+is_admissible(struct action_xlate_ctx *ctx, struct ofport_dpif *in_port,
+ uint16_t vlan)
{
+ struct ofproto_dpif *ofproto = ctx->ofproto;
+ struct flow *flow = &ctx->flow;
struct ofbundle *in_bundle = in_port->bundle;
/* Drop frames for reserved multicast addresses
* only if forward_bpdu option is absent. */
if (!ofproto->up.forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
+ xlate_report(ctx, "packet has reserved destination MAC, dropping");
return false;
}
struct mac_entry *mac;
switch (bond_check_admissibility(in_bundle->bond, in_port,
- flow->dl_dst, tags)) {
+ flow->dl_dst, &ctx->tags)) {
case BV_ACCEPT:
break;
case BV_DROP:
+ xlate_report(ctx, "bonding refused admissibility, dropping");
return false;
case BV_DROP_IF_MOVED:
if (mac && mac->port.p != in_bundle &&
(!is_gratuitous_arp(flow)
|| mac_entry_is_grat_arp_locked(mac))) {
+ xlate_report(ctx, "SLB bond thinks this packet looped back, "
+ "dropping");
return false;
}
break;
in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port,
ctx->packet != NULL, &in_port);
if (!in_bundle) {
+ xlate_report(ctx, "no input bundle, dropping");
return;
}
"VLAN tag received on port %s",
ctx->ofproto->up.name, in_bundle->name);
}
+ xlate_report(ctx, "partial VLAN tag, dropping");
return;
}
"%s, which is reserved exclusively for mirroring",
ctx->ofproto->up.name, in_bundle->name);
}
+ xlate_report(ctx, "input port is mirror output port, dropping");
return;
}
/* Check VLAN. */
vid = vlan_tci_to_vid(ctx->flow.vlan_tci);
if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
+ xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
return;
}
vlan = input_vid_to_vlan(in_bundle, vid);
/* Check other admissibility requirements. */
- if (in_port &&
- !is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
+ if (in_port && !is_admissible(ctx, in_port, vlan)) {
return;
}
&ctx->tags);
if (mac) {
if (mac->port.p != in_bundle) {
+ xlate_report(ctx, "forwarding to learned port");
output_normal(ctx, mac->port.p, vlan);
+ } else {
+ xlate_report(ctx, "learned port is input port, dropping");
}
} else {
struct ofbundle *bundle;
+ xlate_report(ctx, "no learned MAC for destination, flooding");
HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
if (bundle != in_bundle
&& ofbundle_includes_vlan(bundle, vlan)
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&key, flow);
- dpif_flow_stats_extract(flow, packet, &stats);
+ dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
packet_get_tcp_flags(packet, flow), packet);
trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
}
+static void
+trace_report(struct action_xlate_ctx *ctx, const char *s)
+{
+ struct trace_ctx *trace = CONTAINER_OF(ctx, struct trace_ctx, ctx);
+ struct ds *result = trace->result;
+
+ ds_put_char_multiple(result, '\t', ctx->recurse);
+ ds_put_cstr(result, s);
+ ds_put_char(result, '\n');
+}
+
static void
ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
void *aux OVS_UNUSED)
action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci,
rule, tcp_flags, packet);
trace.ctx.resubmit_hook = trace_resubmit;
+ trace.ctx.report_hook = trace_report;
xlate_actions(&trace.ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
set_sflow,
set_cfm,
get_cfm_fault,
+ get_cfm_opup,
get_cfm_remote_mpids,
get_cfm_health,
set_stp,