static int xlate_actions(const union ofp_action *in, size_t n_in,
const flow_t *flow, struct ofproto *ofproto,
- bool revalidating,
- struct odp_actions *out, tag_type *tags);
+ const struct ofpbuf *packet,
+ struct odp_actions *out, tag_type *tags,
+ bool *may_setup_flow);
#define UNKNOWN_SUPER ((struct rule *)-1)
struct rule {
* A super-rule with wildcard fields never has ODP actions (since the
* datapath only supports exact-match flows). */
bool installed; /* Installed in datapath? */
+ bool may_install; /* True ordinarily; false if actions must
+ * be reassessed for every packet. */
int n_odp_actions;
union odp_action *odp_actions;
};
static void rule_destroy(struct rule *);
static struct rule *rule_from_cls_rule(const struct cls_rule *);
static bool rule_make_actions(struct ofproto *, struct rule *,
- bool revalidating);
+ const struct ofpbuf *packet);
static void rule_install(struct ofproto *, struct rule *,
struct odp_flow_stats *);
static void rule_uninstall(struct ofproto *, struct rule *);
struct odp_actions odp_actions;
int error;
- error = xlate_actions(actions, n_actions, flow, p, false, &odp_actions,
- NULL);
+ error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
+ NULL, NULL);
if (error) {
return error;
}
}
if (!wildcards) {
- rule_make_actions(p, rule, false);
+ rule_make_actions(p, rule, packet);
if (packet
&& !dpif_execute(&p->dpif, flow->in_port,
rule->odp_actions, rule->n_odp_actions, packet)) {
/* Returns true if the actions changed, false otherwise. */
static bool
-rule_make_actions(struct ofproto *p, struct rule *rule, bool revalidating)
+rule_make_actions(struct ofproto *p, struct rule *rule,
+ const struct ofpbuf *packet)
{
const struct rule *super;
struct odp_actions a;
super = rule->super ? rule->super : rule;
rule->tags = 0;
xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
- revalidating, &a, &rule->tags);
+ packet, &a, &rule->tags, &rule->may_install);
actions_len = a.n_actions * sizeof *a.actions;
if (rule->n_odp_actions != a.n_actions
rule_install(struct ofproto *p, struct rule *rule,
struct odp_flow_stats *stats)
{
- struct odp_flow odp_flow;
-
assert(!rule->cr.wc.wildcards);
- odp_flow.key = rule->cr.flow;
- odp_flow.actions = rule->odp_actions;
- odp_flow.n_actions = rule->n_odp_actions;
- if (!dpif_flow_add(&p->dpif, &odp_flow)) {
- rule->installed = true;
- if (stats) {
- *stats = odp_flow.stats;
+ if (rule->may_install) {
+ struct odp_flow odp_flow;
+
+ odp_flow.key = rule->cr.flow;
+ odp_flow.actions = rule->odp_actions;
+ odp_flow.n_actions = rule->n_odp_actions;
+ if (!dpif_flow_add(&p->dpif, &odp_flow)) {
+ rule->installed = true;
+ if (stats) {
+ *stats = odp_flow.stats;
+ }
}
}
}
+static void
+rule_update(struct ofproto *p, struct rule *rule)
+{
+ if (rule->may_install) {
+ if (rule->installed) {
+ dpif_flow_set_actions(&p->dpif, &rule->cr.flow,
+ rule->odp_actions, rule->n_odp_actions);
+ } else {
+ rule_install(p, rule, NULL);
+ }
+ } else {
+ rule_uninstall(p, rule);
+ }
+}
+
static void
rule_uninstall(struct ofproto *p, struct rule *rule)
{
const flow_t *flow; /* Flow to which these actions correspond. */
int recurse; /* Recursion level, via xlate_table_action. */
struct ofproto *ofproto;
- bool revalidating;
+ const struct ofpbuf *packet; /* The packet corresponding to 'flow', or a
+ * null pointer if we are revalidating
+ * without a packet to refer to. */
/* Output. */
struct odp_actions *out; /* Datapath actions. */
tag_type *tags; /* Tags associated with OFPP_NORMAL actions. */
+ bool may_setup_flow; /* True ordinarily; false if the actions must
+ * be reassessed for every packet. */
};
static void do_xlate_actions(const union ofp_action *in, size_t n_in,
xlate_table_action(ctx, ctx->flow->in_port);
break;
case OFPP_NORMAL:
- ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->revalidating,
- ctx->out, ctx->tags,
- ctx->ofproto->aux);
+ if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
+ ctx->out, ctx->tags,
+ ctx->ofproto->aux)) {
+ ctx->may_setup_flow = false;
+ }
break;
case OFPP_FLOOD:
add_output_group_action(ctx->out, DP_GROUP_FLOOD);
static int
xlate_actions(const union ofp_action *in, size_t n_in,
- const flow_t *flow, struct ofproto *ofproto, bool revalidating,
- struct odp_actions *out, tag_type *tags)
+ const flow_t *flow, struct ofproto *ofproto,
+ const struct ofpbuf *packet,
+ struct odp_actions *out, tag_type *tags, bool *may_setup_flow)
{
tag_type no_tags = 0;
struct action_xlate_ctx ctx;
ctx.flow = flow;
ctx.recurse = 0;
ctx.ofproto = ofproto;
- ctx.revalidating = revalidating;
+ ctx.packet = packet;
ctx.out = out;
ctx.tags = tags ? tags : &no_tags;
+ ctx.may_setup_flow = true;
do_xlate_actions(in, n_in, &ctx);
+ if (may_setup_flow) {
+ *may_setup_flow = ctx.may_setup_flow;
+ }
if (odp_actions_overflow(out)) {
odp_actions_init(out);
return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_TOO_MANY);
flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
- &flow, p, false, &actions, NULL);
+ &flow, p, &payload, &actions, NULL, NULL);
if (error) {
return error;
}
static int
send_buffered(struct ofproto *p, struct ofconn *ofconn, uint32_t buffer_id,
- const struct rule *rule, int *byte_count)
+ const struct rule *rule,
+ struct ofpbuf **packetp, int *byte_count)
{
struct odp_actions actions;
struct ofpbuf *packet;
if (error) {
return error;
}
+ *packetp = packet;
flow_extract(packet, in_port, &flow);
- error = xlate_actions(rule->actions, rule->n_actions, &flow, p, false,
- &actions, NULL);
+ error = xlate_actions(rule->actions, rule->n_actions, &flow, p, packet,
+ &actions, NULL, NULL);
if (error) {
return error;
}
if (!error) {
*byte_count = packet->size;
}
- ofpbuf_delete(packet);
return 0;
}
struct ofp_flow_mod *ofm, size_t n_actions)
{
struct rule *rule, *displaced_rule;
+ struct ofpbuf *packet = NULL;
int error = 0;
rule = rule_create(NULL, (const union ofp_action *) ofm->actions,
if (ofm->buffer_id != htonl(UINT32_MAX)) {
int byte_count = 0;
error = send_buffered(p, ofconn, ntohl(ofm->buffer_id),
- rule, &byte_count);
+ rule, &packet, &byte_count);
rule->byte_count += byte_count;
rule->packet_count += byte_count > 0;
}
} else {
struct odp_flow_stats stats;
- rule_make_actions(p, rule, false);
+ rule_make_actions(p, rule, packet);
rule_install(p, rule, &stats);
if (displaced_rule) {
if (displaced_rule->super &&
rule_destroy(displaced_rule);
}
}
+ ofpbuf_delete(packet);
return error;
}
n_actions * sizeof *rule->actions);
rule->n_actions = n_actions;
if (!rule->cr.wc.wildcards) {
- if (rule_make_actions(p, rule, false)) {
- dpif_flow_set_actions(&p->dpif, &rule->cr.flow,
- rule->odp_actions, rule->n_odp_actions);
+ if (rule_make_actions(p, rule, NULL)) {
+ rule_update(p, rule);
+ } else if (rule->installed && !rule->may_install) {
+ rule_uninstall(p, rule);
}
}
}
}
/* Install flow entry into datapath. */
- rule_make_actions(p, subrule, false);
+ rule_make_actions(p, subrule, packet);
rule_install(p, subrule, NULL);
} else {
- /* A flow got dropped due to a hash collision, or the packet was
- * buffered before we processed another packet from the same flow. */
subrule = rule;
+ if (!rule->may_install) {
+ /* The rule is not installable, that is, we need to process every
+ * packet, so do that here. */
+ rule_make_actions(p, subrule, packet);
+ rule_install(p, subrule, NULL);
+ } else {
+ /* A flow got dropped due to a hash collision, or the packet was
+ * buffered before we processed another packet from the same
+ * flow. */
+ }
}
/* Execute subrule on packet. */
}
}
- if (rule_make_actions(p, rule, true)) {
- dpif_flow_set_actions(&p->dpif, flow, rule->odp_actions,
- rule->n_odp_actions);
+ if (rule_make_actions(p, rule, NULL)) {
+ rule_update(p, rule);
+ } else if (rule->installed && !rule->may_install) {
+ rule_uninstall(p, rule);
}
return true;
}
return eth_addr_to_uint64(ea);
}
\f
-static void
-default_normal_ofhook_cb(const flow_t *flow, bool revalidating,
+static bool
+default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
struct odp_actions *actions, tag_type *tags,
void *ofproto_)
{
/* Drop frames for reserved multicast addresses. */
if (eth_addr_is_reserved(flow->dl_dst)) {
- return;
+ return true;
}
/* Learn source MAC (but don't try to learn from revalidation). */
- if (!revalidating) {
+ if (packet != NULL) {
tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src,
0, flow->in_port);
if (rev_tag) {
} else {
/* Drop. */
}
+
+ return true;
}
static const struct ofhooks default_ofhooks = {
}
}
-static void
-process_flow(struct bridge *br, const flow_t *flow, bool revalidating,
- struct odp_actions *actions, tag_type *tags)
+/* 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
+process_flow(struct bridge *br, const flow_t *flow,
+ const struct ofpbuf *packet, struct odp_actions *actions,
+ tag_type *tags)
{
struct iface *in_iface;
struct port *in_port;
in_iface = iface_from_dp_ifidx(br, flow->in_port);
if (!in_iface) {
/* No interface? Something fishy... */
- if (!revalidating) {
+ if (packet != NULL) {
/* Odd. A few possible reasons here:
*
* - We deleted an interface but there are still a few packets
}
/* Return without adding any actions, to drop packets on this flow. */
- return;
+ return true;
}
in_port = in_iface->port;
if (in_port->vlan >= 0) {
if (vlan) {
/* XXX support double tagging? */
- if (!revalidating) {
+ if (packet != NULL) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
"packet received on port %s configured with "
int out_port_idx;
bool may_learn;
- if (revalidating) {
+ if (!packet) {
/* Don't try to learn from revalidation. */
may_learn = false;
} else if (in_port->n_ifaces > 1) {
out_port = NULL;
}
+done:
+ compose_actions(br, flow, vlan, in_port, out_port, tags, actions);
+
/*
- * Add a new flow.
- *
* We send out only a single packet, instead of setting up a flow, if:
*
* - Flows are disabled entirely; or
* handled differently, but OpenFlow unfortunately can't distinguish
* them.
*/
-done:
- compose_actions(br, flow, vlan, in_port, out_port, tags, actions);
+ return (br->flow_idle_time >= 0
+ && (in_port->n_ifaces < 2
+ || flow->dl_type != htons(ETH_TYPE_ARP)
+ || !eth_addr_is_broadcast(flow->dl_dst)));
}
/* Careful: 'opp' is in host byte order and opp->port_no is an OFP port
}
}
-static void
-bridge_normal_ofhook_cb(const flow_t *flow, bool revalidating,
+static bool
+bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
struct odp_actions *actions, tag_type *tags, void *br_)
{
struct bridge *br = br_;
}
#endif
- process_flow(br, flow, revalidating, actions, tags);
+ return process_flow(br, flow, packet, actions, tags);
}
static struct ofhooks bridge_ofhooks = {