#include "shash.h"
#include "status.h"
#include "svec.h"
+#include "tag.h"
#include "timeval.h"
#include "vconn.h"
#include "vconn-ssl.h"
static void xlate_actions(const union ofp_action *in, size_t n_in,
const flow_t *flow, struct ofproto *ofproto,
- struct odp_actions *out);
+ bool revalidating,
+ struct odp_actions *out, tag_type *tags);
#define UNKNOWN_SUPER ((struct rule *)-1)
struct rule {
uint64_t byte_count; /* Bytes from *expired* subrules. */
uint8_t tcp_flags; /* Bitwise-OR of all TCP flags seen. */
uint8_t ip_tos; /* Last-seen IP type-of-service. */
+ tag_type tags; /* Tags (set only by hooks). */
struct rule *super;
struct list list;
static void rule_free(struct rule *);
static void rule_destroy(struct rule *);
static struct rule *rule_from_cls_rule(const struct cls_rule *);
-static void rule_make_actions(struct ofproto *,
- const struct rule *, struct odp_actions *);
+static void rule_make_actions(struct ofproto *, struct rule *,
+ bool revalidating, struct odp_actions *);
struct ofconn {
struct list node;
static void send_packet_in_action(struct ofpbuf *, void *ofproto);
static void update_used(struct ofproto *);
static void expire_rule(struct cls_rule *, void *ofproto);
-static bool revalidate_subrule(struct ofproto *p, struct rule *subrule);
-static void revalidate_subrule_cb(struct cls_rule *sub_, void *p_);
+static bool revalidate_rule(struct ofproto *p, struct rule *rule);
+static void revalidate_cb(struct cls_rule *rule_, void *p_);
static void handle_odp_msg(struct ofproto *, struct ofpbuf *);
int
ofproto_run(struct ofproto *p)
+{
+ int error = ofproto_run1(p);
+ if (!error) {
+ error = ofproto_run2(p, false, NULL);
+ }
+ return error;
+}
+
+int
+ofproto_run1(struct ofproto *p)
{
struct ofconn *ofconn, *next_ofconn;
char *devname;
classifier_for_each(&p->cls, CLS_INC_EXACT, expire_rule, p);
}
- if (p->need_revalidate) {
- classifier_for_each(&p->cls, CLS_INC_EXACT, revalidate_subrule_cb, p);
+ return 0;
+}
+
+struct revalidate_cbdata {
+ struct ofproto *ofproto;
+ bool revalidate_all; /* Revalidate all exact-match rules? */
+ bool revalidate_subrules; /* Revalidate all exact-match subrules? */
+ struct tag_set revalidate_set; /* Set of tags to revalidate. */
+};
+
+int
+ofproto_run2(struct ofproto *p,
+ bool revalidate_all, const struct tag_set *revalidate_set)
+{
+ if (p->need_revalidate || revalidate_all
+ || (revalidate_set && !tag_set_is_empty(revalidate_set))) {
+ struct revalidate_cbdata cbdata;
+ cbdata.ofproto = p;
+ cbdata.revalidate_all = revalidate_all;
+ cbdata.revalidate_subrules = p->need_revalidate;
+ if (revalidate_set) {
+ cbdata.revalidate_set = *revalidate_set;
+ } else {
+ tag_set_init(&cbdata.revalidate_set);
+ }
+ classifier_for_each(&p->cls, CLS_INC_EXACT, revalidate_cb, &cbdata);
p->need_revalidate = false;
}
struct odp_actions odp_actions;
int error;
- xlate_actions(actions, n_actions, flow, p, &odp_actions);
+ xlate_actions(actions, n_actions, flow, p, false, &odp_actions, NULL);
error = dpif_execute(&p->dpif, flow->in_port, odp_actions.actions,
odp_actions.n_actions, packet);
odp_actions_free(&odp_actions);
rule->byte_count = 0;
rule->tcp_flags = 0;
rule->ip_tos = 0;
+ rule->tags = 0;
rule->super = NULL; /* XXX */
list_init(&rule->list);
rule->n_actions = n_actions;
}
if (!wildcards) {
- rule_make_actions(p, rule, &odp_actions);
+ rule_make_actions(p, rule, false, &odp_actions);
if (packet) {
if (!ofproto_send_packet(p, flow, actions, n_actions, packet)) {
rule->byte_count = packet->size;
rule->n_actions = n_actions;
rule->actions = xmemdup(actions, n_actions * sizeof *rule->actions);
- rule_make_actions(ofproto, rule, &odp_actions);
+ rule->tags = 0;
+ rule_make_actions(ofproto, rule, false, &odp_actions);
dpif_flow_set_actions(&ofproto->dpif, flow, odp_actions.actions,
odp_actions.n_actions);
odp_actions_free(&odp_actions);
}
static void
-rule_make_actions(struct ofproto *p,
- const struct rule *rule, struct odp_actions *actions)
+rule_make_actions(struct ofproto *p, struct rule *rule, bool revalidating,
+ struct odp_actions *actions)
{
const struct rule *super = rule->super ? rule->super : rule;
assert(!rule->cr.wc.wildcards);
+ rule->tags = 0;
xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
- actions);
+ revalidating, actions, &rule->tags);
}
\f
static void
struct action_xlate_ctx {
/* Input. */
- const union ofp_action *in; /* OpenFlow actions. */
- size_t n_in; /* Number of elements in 'in' array. */
const flow_t *flow; /* Flow to which these actions correspond. */
int recurse; /* Recursion level, via xlate_table_action. */
struct ofproto *ofproto;
+ bool revalidating;
/* Output. */
struct odp_actions *out; /* Datapath actions. */
+ tag_type *tags; /* Tags associated with OFPP_NORMAL actions. */
};
-static void do_xlate_actions(struct action_xlate_ctx *ctx);
+static void do_xlate_actions(const union ofp_action *in, size_t n_in,
+ struct action_xlate_ctx *ctx);
static void
-xlate_table_action(const struct action_xlate_ctx *ctx, uint16_t in_port)
+xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
{
struct ofproto *p = ctx->ofproto;
struct action_xlate_ctx nested_ctx;
return;
} else if (rule->super && p->need_revalidate) {
/* This might be a subrule that is now invalid. Revalidate it. */
- if (!revalidate_subrule(p, rule)) {
+ if (!revalidate_rule(p, rule)) {
/* The subrule got deleted so we can optimize slightly by only
* looking through the wildcarded rules. */
rule = rule_from_cls_rule(classifier_lookup_wild(&p->cls, &flow));
rule = rule->super;
}
- nested_ctx.in = rule->actions;
- nested_ctx.n_in = rule->n_actions;
- nested_ctx.flow = ctx->flow;
- nested_ctx.recurse = ctx->recurse + 1;
- nested_ctx.ofproto = ctx->ofproto;
- nested_ctx.out = ctx->out;
- do_xlate_actions(&nested_ctx);
+ ctx->recurse++;
+ do_xlate_actions(rule->actions, rule->n_actions, &nested_ctx);
+ ctx->recurse--;
}
static void
-xlate_output_action(const struct action_xlate_ctx *ctx,
+xlate_output_action(struct action_xlate_ctx *ctx,
const struct ofp_action_output *oao)
{
uint16_t odp_port;
xlate_table_action(ctx, ctx->flow->in_port);
break;
case OFPP_NORMAL:
- add_output_group_action(ctx->out, DP_GROUP_FLOOD); /* XXX */
+ if (ctx->ofproto->ofhooks->normal_cb) {
+ ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->revalidating,
+ ctx->out, ctx->tags,
+ ctx->ofproto->aux);
+ } else {
+ add_output_group_action(ctx->out, DP_GROUP_FLOOD);
+ }
break;
case OFPP_FLOOD:
add_output_group_action(ctx->out, DP_GROUP_FLOOD);
}
static void
-xlate_nicira_action(const struct action_xlate_ctx *ctx,
+xlate_nicira_action(struct action_xlate_ctx *ctx,
const struct nx_action_header *nah)
{
const struct nx_action_snat *nas;
}
static void
-do_xlate_actions(struct action_xlate_ctx *ctx)
+do_xlate_actions(const union ofp_action *in, size_t n_in,
+ struct action_xlate_ctx *ctx)
{
struct actions_iterator iter;
const union ofp_action *ia;
- for (ia = actions_first(&iter, ctx->in, ctx->n_in); ia;
- ia = actions_next(&iter))
- {
+ for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
uint16_t type = ntohs(ia->type);
union odp_action *oa;
static void
xlate_actions(const union ofp_action *in, size_t n_in,
- const flow_t *flow, struct ofproto *ofproto,
- struct odp_actions *out)
+ const flow_t *flow, struct ofproto *ofproto, bool revalidating,
+ struct odp_actions *out, tag_type *tags)
{
+ tag_type no_tags = 0;
struct action_xlate_ctx ctx;
odp_actions_init(out);
- ctx.in = in;
- ctx.n_in = n_in;
ctx.flow = flow;
ctx.recurse = 0;
ctx.ofproto = ofproto;
+ ctx.revalidating = revalidating;
ctx.out = out;
- do_xlate_actions(&ctx);
+ ctx.tags = tags ? tags : &no_tags;
+ do_xlate_actions(in, n_in, &ctx);
}
static int
flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
xlate_actions((const union ofp_action *) opo->actions, n_actions, &flow,
- p, &actions);
+ p, false, &actions, NULL);
dpif_execute(&p->dpif, flow.in_port, actions.actions, actions.n_actions,
&payload);
odp_actions_free(&actions);
}
flow_extract(packet, in_port, &flow);
- xlate_actions(rule->actions, rule->n_actions, &flow, p, &actions);
+ xlate_actions(rule->actions, rule->n_actions, &flow, p, false,
+ &actions, NULL);
error = dpif_execute(&p->dpif, in_port,
actions.actions, actions.n_actions, packet);
if (!error) {
rule->byte_count = 0;
rule->tcp_flags = 0;
rule->ip_tos = 0;
+ rule->tags = 0;
rule->super = NULL;
list_init(&rule->list);
rule->n_actions = n_actions;
struct odp_actions actions;
xlate_actions((const union ofp_action *) ofm->actions, n_actions,
- &rule->cr.flow, p, &actions);
+ &rule->cr.flow, p, false, &actions, NULL);
odp_flow.key = rule->cr.flow;
odp_flow.actions = actions.actions;
struct odp_actions actions;
xlate_actions((const union ofp_action *) ofm->actions, n_actions,
- &rule->cr.flow, p, &actions);
+ &rule->cr.flow, p, false, &actions, NULL);
odp_flow.key = rule->cr.flow;
odp_flow.actions = actions.actions;
odp_flow.n_actions = actions.n_actions;
subrule->packet_count = subrule->byte_count = 0;
subrule->tcp_flags = 0;
subrule->ip_tos = 0;
+ subrule->tags = 0;
subrule->super = rule;
subrule->n_actions = 0;
subrule->actions = NULL;
free(subrule);
/* Execute old_sr on packet. */
- rule_make_actions(p, old_sr, &actions);
+ rule_make_actions(p, old_sr, false, &actions);
dpif_execute(&p->dpif, msg->port,
actions.actions, actions.n_actions, &payload);
odp_actions_free(&actions);
rule->used = time_msec();
/* Install flow entry into datapath. */
- rule_make_actions(p, subrule, &actions);
+ rule_make_actions(p, subrule, false, &actions);
odp_flow.key = flow;
odp_flow.actions = actions.actions;
odp_flow.n_actions = actions.n_actions;
dpif_flow_add(&p->dpif, &odp_flow);
} else {
- /* XXX This should happen only if a flow got dropped--perhaps a hash
- * collision? Oh, it could also indicate that the packet was buffered
- * before we processed another packet from the same flow. */
+ /* 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;
- rule_make_actions(p, subrule, &actions);
+ rule_make_actions(p, subrule, false, &actions);
}
/* Execute subrule on packet. */
}
\f
static void
-revalidate_subrule_cb(struct cls_rule *sub_, void *p_)
+revalidate_cb(struct cls_rule *sub_, void *cbdata_)
{
struct rule *sub = rule_from_cls_rule(sub_);
- struct ofproto *p = p_;
+ struct revalidate_cbdata *cbdata = cbdata_;
- if (sub->super) {
- revalidate_subrule(p, sub);
+ if (cbdata->revalidate_all
+ || (cbdata->revalidate_subrules && sub->super)
+ || (tag_set_intersects(&cbdata->revalidate_set, sub->tags))) {
+ revalidate_rule(cbdata->ofproto, sub);
}
}
static bool
-revalidate_subrule(struct ofproto *p, struct rule *sub)
+revalidate_rule(struct ofproto *p, struct rule *rule)
{
- const flow_t *flow = &sub->cr.flow;
- struct rule *super;
+ const flow_t *flow = &rule->cr.flow;
+ struct odp_actions actions;
- super = rule_from_cls_rule(classifier_lookup_wild(&p->cls, flow));
- if (super != sub->super) {
+ if (rule->super) {
+ struct rule *super;
+ super = rule_from_cls_rule(classifier_lookup_wild(&p->cls, flow));
if (!super) {
struct odp_flow odp_flow;
memset(&odp_flow.stats, 0, sizeof odp_flow.stats);
- odp_flow.key = sub->cr.flow;
+ odp_flow.key = rule->cr.flow;
odp_flow.actions = NULL;
odp_flow.n_actions = 0;
dpif_flow_del(&p->dpif, &odp_flow);
- classifier_remove(&p->cls, &sub->cr);
- rule_destroy(sub);
+ classifier_remove(&p->cls, &rule->cr);
+ rule_destroy(rule);
return false;
} else {
- struct odp_actions actions;
-
- sub->super = super;
- sub->hard_timeout = super->hard_timeout;
- sub->idle_timeout = super->idle_timeout;
- sub->created = super->created;
- sub->used = 0;
-
- rule_make_actions(p, sub, &actions);
- dpif_flow_set_actions(&p->dpif, flow, actions.actions,
- actions.n_actions);
- odp_actions_free(&actions);
+ rule->super = super;
+ rule->hard_timeout = super->hard_timeout;
+ rule->idle_timeout = super->idle_timeout;
+ rule->created = super->created;
+ rule->used = 0;
}
}
+
+ rule_make_actions(p, rule, true, &actions);
+ dpif_flow_set_actions(&p->dpif, flow, actions.actions,
+ actions.n_actions);
+ odp_actions_free(&actions);
return true;
}
}
update_time(rule, &f->stats);
- /* XXX update p->next_expiration */
}
free(flows);
}
#include "dirs.h"
#include "dpif.h"
#include "flow.h"
-#include "flowtrack.h"
#include "hash.h"
#include "list.h"
#include "mac-learning.h"
#define THIS_MODULE VLM_bridge
#include "vlog.h"
+struct dst {
+ uint16_t vlan;
+ uint16_t dp_ifidx;
+};
+
struct iface {
struct port *port; /* Containing port. */
size_t port_ifidx; /* Index within containing port. */
size_t n_ports, allocated_ports;
/* Flow tracking. */
- struct ft *ft;
struct tag_set revalidate_set;
bool flush;
static void bridge_pick_local_hw_addr(struct bridge *,
struct iface *local_iface);
-static void revalidate_flow(struct bridge *, struct ft_flow *);
-
-static void flowstats_run(struct bridge *);
-static void flowstats_wait(struct bridge *);
-
static void bond_run(struct bridge *);
static void bond_wait(struct bridge *);
static void brstp_adjust_timers(struct bridge *);
static void brstp_run(struct bridge *);
static void brstp_wait(struct bridge *);
-static void brstp_receive(struct bridge *, const flow_t *,
- const struct ofpbuf *);
static void iface_create(struct port *, const char *name);
static void iface_destroy(struct iface *);
if (br->ml) {
mac_learning_wait(br->ml);
}
- flowstats_wait(br);
bond_wait(br);
brstp_wait(br);
if (!tag_set_is_empty(&br->revalidate_set)) {
port_array_init(&br->ifaces);
- br->ft = ft_create();
tag_set_init(&br->revalidate_set);
br->flush = false;
dpif_close(&br->dpif);
ofproto_destroy(br->ofproto);
free(br->controller);
- ft_destroy(br->ft);
mac_learning_destroy(br->ml);
port_array_destroy(&br->ifaces);
free(br->ports);
{
int error;
- if (br->controller) {
- return ofproto_run(br->ofproto);
+ error = ofproto_run1(br->ofproto);
+ if (error) {
+ return error;
}
- /* Now do the things that may want to revalidate flows. */
- error = ofproto_run(br->ofproto);
- hmap_shrink(&br->ft->flows);
if (br->ml) {
mac_learning_run(br->ml, &br->revalidate_set);
}
- flowstats_run(br);
bond_run(br);
brstp_run(br);
- /* Now revalidate any flows that need it. */
- if (br->flush || !tag_set_is_empty(&br->revalidate_set)) {
- struct ft_flow *f, *next;
-
- HMAP_FOR_EACH_SAFE (f, next, struct ft_flow, node, &br->ft->flows) {
- if (br->flush
- || tag_set_intersects(&br->revalidate_set, f->tags)) {
- revalidate_flow(br, f);
- }
- }
- }
+ error = ofproto_run2(br->ofproto, br->flush, &br->revalidate_set);
tag_set_init(&br->revalidate_set);
br->flush = false;
} else {
ofproto_set_remote_execution(br->ofproto, NULL, NULL);
}
- } else {
+
if (br->controller) {
- /* There was a controller configured, so we want to disconnect
- * from it so that connect_ofproto() will set up a "loopback"
- * connection to us over a socketpair. */
- ofproto_set_controller(br->ofproto, NULL);
- } else {
- /* No controller configured before either, so we're already doing
- * the right thing. */
+ /* Get rid of our match-everything flow, and all the rest while
+ * we're at it (which should mostly be subflows set up by that
+ * one). */
+ ofproto_flush_flows(br->ofproto);
}
+ } else {
+ union ofp_action action;
+ flow_t flow;
+
+ /* Set up a flow that matches every packet and directs them to
+ * OFPP_NORMAL (which goes to us). */
+ memset(&action, 0, sizeof action);
+ action.type = htons(OFPAT_OUTPUT);
+ action.output.len = htons(sizeof action);
+ action.output.port = htons(OFPP_NORMAL);
+ memset(&flow, 0, sizeof flow);
+ ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0,
+ &action, 1, NULL, 0);
+
+ ofproto_set_controller(br->ofproto, NULL);
ofproto_set_in_band(br->ofproto, false);
ofproto_set_max_backoff(br->ofproto, 1);
ofproto_set_probe_interval(br->ofproto, 5);
}
free(dpif_ports);
}
-
-/* Returns the idle time that the bridge is currently using. We reduce the
- * idle time as the flow table grows, so as to act as a brake on further flow
- * table growth. */
-static int
-bridge_idle_time(const struct bridge *br)
-{
- int idle_time = br->flow_idle_time;
- if (idle_time) {
- size_t n_flows = hmap_count(&br->ft->flows);
- int step = MAX(1, br->flow_idle_time / 5);
- while (n_flows > 1000 && idle_time > 0) {
- idle_time -= step;
- n_flows /= 2;
- }
- if (idle_time < 1) {
- idle_time = 1;
- }
- }
- return idle_time;
-}
\f
/* Bridge packet processing functions. */
return true;
}
-static void
-bond_account_flow(struct bridge *br, const struct ft_flow *f)
-{
- const struct ft_dst *dst;
-
- if (f->byte_count <= f->last_byte_count) {
- /* No bytes to add, so don't waste our time. */
- return;
- }
- /* XXX return immediately if no bonded interfaces. */
-
- for (dst = &f->dsts[0]; dst < &f->dsts[f->n_dsts]; dst++) {
- struct port *port = port_from_dp_ifidx(br, dst->dp_ifidx);
- if (port && port->n_ifaces >= 2) {
- struct bond_entry *e = lookup_bond_entry(port, f->flow.dl_src);
- e->tx_bytes += f->byte_count - f->last_byte_count;
- }
- }
-}
-
static void
bond_link_status_update(struct iface *iface, bool carrier)
{
}
static bool
-set_dst(struct ft_dst *p, const flow_t *flow,
+set_dst(struct dst *p, const flow_t *flow,
const struct port *in_port, const struct port *out_port,
tag_type *tags)
{
}
static void
-swap_dst(struct ft_dst *p, struct ft_dst *q)
+swap_dst(struct dst *p, struct dst *q)
{
- struct ft_dst tmp = *p;
+ struct dst tmp = *p;
*p = *q;
*q = tmp;
}
-/* Moves all the ft_dsts with vlan == 'vlan' to the front of the 'n_dsts' in
+/* Moves all the dsts with vlan == 'vlan' to the front of the 'n_dsts' in
* 'dsts'. (This may help performance by reducing the number of VLAN changes
* that we push over OpenFlow. We could in fact fully sort the array by vlan,
* but in most cases there are at most two different vlan tags so that's
* possibly overkill.) */
static void
-partition_dsts(struct ft_dst *dsts, size_t n_dsts, int vlan)
+partition_dsts(struct dst *dsts, size_t n_dsts, int vlan)
{
- struct ft_dst *first = dsts;
- struct ft_dst *last = dsts + n_dsts;
+ struct dst *first = dsts;
+ struct dst *last = dsts + n_dsts;
while (first != last) {
/* Invariants:
- * - All ft_dsts < first have vlan == 'vlan'.
- * - All ft_dsts >= last have vlan != 'vlan'.
+ * - All dsts < first have vlan == 'vlan'.
+ * - All dsts >= last have vlan != 'vlan'.
* - first < last. */
while (first->vlan == vlan) {
if (++first == last) {
}
}
-static void *
-add_action_header(struct ofpbuf *buf, uint16_t type)
-{
- struct ofp_action_header *oah = ofpbuf_put_zeros(buf, sizeof *oah);
- oah->type = htons(type);
- oah->len = htons(sizeof *oah);
- return oah;
-}
-
-static void
-add_output_action(struct ofpbuf *buf, uint16_t dp_ifidx)
-{
- struct ofp_action_output *oao = add_action_header(buf, OFPAT_OUTPUT);
- oao->port = htons(dp_ifidx);
- oao->max_len = htons(0);
-}
-
-static void
-add_vlan_action(struct ofpbuf *buf, uint16_t old_vlan, uint16_t new_vlan)
-{
- assert(old_vlan != new_vlan);
- if (new_vlan == htons(OFP_VLAN_NONE)) {
- add_action_header(buf, OFPAT_STRIP_VLAN);
- } else {
- struct ofp_action_vlan_vid *oavv
- = add_action_header(buf, OFPAT_SET_VLAN_VID);
- oavv->vlan_vid = htons(new_vlan);
- }
-}
-
static int
mirror_mask_ffs(mirror_mask_t mask)
{
}
static bool
-dst_is_duplicate(const struct ft_dst *dsts, size_t n_dsts,
- const struct ft_dst *test)
+dst_is_duplicate(const struct dst *dsts, size_t n_dsts,
+ const struct dst *test)
{
size_t i;
for (i = 0; i < n_dsts; i++) {
static size_t
compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
const struct port *in_port, const struct port *out_port,
- struct ft_dst dsts[], tag_type *tags)
+ struct dst dsts[], tag_type *tags)
{
mirror_mask_t mirrors = in_port->src_mirrors;
- struct ft_dst *dst = dsts;
+ struct dst *dst = dsts;
size_t i;
*tags |= in_port->stp_state_tag;
if (out_port == FLOOD_PORT) {
- /* XXX use OFPP_FLOOD if no vlans or bonding. */
+ /* XXX use ODP_FLOOD if no vlans or bonding. */
/* XXX even better, define each VLAN as a datapath port group */
for (i = 0; i < br->n_ports; i++) {
struct port *port = br->ports[i];
if (port->vlan < 0) {
dst->vlan = m->out_vlan;
}
- if (dst->dp_ifidx == flow->in_port) {
- if (dst->vlan == vlan) {
- /* Don't send out input port on same VLAN. */
- continue;
- }
- dst->dp_ifidx = OFPP_IN_PORT;
+ if (dst->dp_ifidx == flow->in_port
+ && dst->vlan == vlan) {
+ /* Don't send out input port on same VLAN. */
+ continue;
}
dst++;
}
return dst - dsts;
}
-static void
-put_actions(const struct ft_dst dsts[], size_t n_dsts, uint16_t flow_vlan,
- struct ofpbuf *buf)
+static void UNUSED
+print_dsts(const struct dst *dsts, size_t n)
{
- const struct ft_dst *p;
- uint16_t vlan = flow_vlan;
- for (p = dsts; p < &dsts[n_dsts]; p++) {
- if (p->vlan != vlan) {
- add_vlan_action(buf, vlan, p->vlan);
- vlan = p->vlan;
+ for (; n--; dsts++) {
+ printf(">p%"PRIu16, dsts->dp_ifidx);
+ if (dsts->vlan != OFP_VLAN_NONE) {
+ printf("v%"PRIu16, dsts->vlan);
}
- add_output_action(buf, p->dp_ifidx);
}
}
static void
-send_packets(struct bridge *br, const flow_t *flow,
- const struct ofpbuf *pkt, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- tag_type tags, bool setup_flow)
+compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
+ const struct port *in_port, const struct port *out_port,
+ tag_type *tags, struct odp_actions *actions)
{
- struct ft_dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
- size_t actions_len; /* Estimated length of actions, in bytes. */
+ struct dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
size_t n_dsts;
+ const struct dst *p;
+ uint16_t cur_vlan;
- n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, &tags);
- actions_len = (sizeof(struct ofp_action_header) + 2) * n_dsts;
-
- if (setup_flow) {
- enum { NO_OP, ADD_FLOW, SET_ACTIONS } command;
- struct ft_flow *f;
-
- f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
- if (f) {
- if (!ftd_equal(dsts, n_dsts, f->dsts, f->n_dsts)) {
- /* Update the flow. Use OFPFC_MODIFY_STRICT instead of
- * OFPFC_ADD so that we don't reset the idle-timer
- * countdown for this flow. */
- ftf_set_dsts(f, dsts, n_dsts);
- command = SET_ACTIONS;
- } else {
- /* Correct flow is already in the flow table, nothing to do.
- * This should only happen on revalidate, since
- * process_packet_in() will delete any flow entry for a
- * no-match packet-in message. (If it could happen in other
- * circumstances then we'd want to arrange to send a packet-out
- * below, but there's no need.) */
- command = NO_OP;
- }
- f->tags = tags;
- } else {
- f = ftf_create(flow, dsts, n_dsts, tags);
- ft_insert(br->ft, f);
- command = ADD_FLOW;
- }
- f->need_drop = false;
-
- if (command != NO_OP) {
- struct ofpbuf abuf;
- size_t n_actions;
-
- ofpbuf_init(&abuf, actions_len);
- put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), &abuf);
- n_actions = abuf.size / sizeof(union ofp_action);
-
- if (command == ADD_FLOW) {
- ofproto_add_flow(br->ofproto, flow, 0, UINT16_MAX,
- abuf.data, n_actions,
- pkt, bridge_idle_time(br));
- pkt = NULL; /* Already sent. */
+ n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags);
- /* ofproto_add_flow() will reset the byte counters. */
- f->last_byte_count = f->byte_count = 0;
- } else {
- ofproto_set_actions(br->ofproto, flow, abuf.data, n_actions);
- }
- ofpbuf_uninit(&abuf);
- }
- } else {
- struct ft_flow *f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
- if (f) {
- /* XXX delete flow from ft, queue delete-flow openflow message */
+ cur_vlan = ntohs(flow->dl_vlan);
+ for (p = dsts; p < &dsts[n_dsts]; p++) {
+ union odp_action *a;
+ if (p->vlan != cur_vlan) {
+ a = odp_actions_add(actions, ODPAT_SET_VLAN_VID);
+ a->vlan_vid.vlan_vid = htons(p->vlan);
+ cur_vlan = p->vlan;
}
+ a = odp_actions_add(actions, ODPAT_OUTPUT);
+ a->output.port = p->dp_ifidx;
}
-
- /* XXX we should send the packet *before* we set up the flow, so as to not
- * reorder packets in the flow. */
- if (pkt) {
- struct ofpbuf abuf;
-
- ofpbuf_init(&abuf, actions_len);
- put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), &abuf);
- ofproto_send_packet(br->ofproto, flow,
- abuf.data, abuf.size / sizeof(union ofp_action),
- pkt);
- ofpbuf_uninit(&abuf);
- }
-}
-
-static bool
-is_bcast_arp_reply(const flow_t *flow, const struct ofpbuf *pkt)
-{
- return (flow->dl_type == htons(ETH_TYPE_ARP)
- && eth_addr_is_broadcast(flow->dl_dst)
- && pkt->size >= sizeof(struct arp_eth_header)
- && ((struct arp_eth_header *) pkt->data)->ar_op == ARP_OP_REQUEST);
}
static void
-process_flow(struct bridge *br, const flow_t *flow, const struct ofpbuf *pkt)
+process_flow(struct bridge *br, const flow_t *flow, bool revalidating,
+ struct odp_actions *actions, tag_type *tags)
{
struct iface *in_iface;
struct port *in_port;
struct port *out_port = NULL; /* By default, drop the packet/flow. */
- tag_type tags = 0;
int vlan;
/* Find the interface and port structure for the received packet. */
in_iface = iface_from_dp_ifidx(br, flow->in_port);
if (!in_iface) {
/* No interface? Something fishy... */
- struct ft_flow *f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
- if (pkt || (f && f->need_drop)) {
+ if (!revalidating) {
/* Odd. A few possible reasons here:
*
* - We deleted an interface but there are still a few packets
VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
"interface %"PRIu16, br->name, flow->in_port);
-
- ofproto_add_flow(br->ofproto, flow, 0, UINT16_MAX,
- NULL, 0, NULL, bridge_idle_time(br));
- if (f) {
- ftf_set_dsts(f, NULL, 0);
- f->tags = tags;
- } else {
- f = ftf_create(flow, NULL, 0, tags);
- ft_insert(br->ft, f);
- }
-
- /* Mark the flow as being needed to drop packets. (If we don't do
- * this, then the flow will be deleted as soon as we revalidate
- * it.) */
- f->need_drop = true;
- } else {
- /* We're revalidating, not receiving a fresh packet. Most likely,
- * this interface existed and has now been deleted from the
- * datapath. Drop the flow. */
- assert(f);
-
- ofproto_delete_flow(br->ofproto, flow, 0, UINT16_MAX);
-
- /* ofproto_delete_flow() better not call back to
- * bridge_flow_expired_ofhook_cb(). */
- ft_remove(br->ft, f);
- ftf_destroy(f);
- /* 'flow' pointed to &f->flow, so don't access it anymore. */
}
+
+ /* Return without adding any actions, to drop packets on this flow. */
return;
}
in_port = in_iface->port;
if (in_port->vlan >= 0) {
if (vlan) {
/* XXX support double tagging? */
- if (pkt) {
+ if (!revalidating) {
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 "
/* Drop multicast and broadcast packets on inactive bonded interfaces, to
* avoid receiving duplicates. */
if (in_port->n_ifaces > 1 && eth_addr_is_multicast(flow->dl_dst)) {
- tags |= in_port->active_iface_tag;
+ *tags |= in_port->active_iface_tag;
if (in_port->active_iface != in_iface->port_ifidx) {
goto done;
}
int out_port_idx;
bool may_learn;
- if (!pkt) {
+ if (revalidating) {
/* Don't try to learn from revalidation. */
may_learn = false;
} else if (in_port->n_ifaces > 1) {
int src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
may_learn = src_idx < 0 || src_idx == in_port->port_idx;
- /* Broadcast ARP replies are an exception to this rule: the host
- * has moved to another switch. */
- if (!may_learn && is_bcast_arp_reply(flow, pkt)) {
- may_learn = true;
- }
+ /* XXX Need to make an exception for broadcast ARP replies here:
+ * the host has moved to another switch. */
} else {
may_learn = true;
}
/* Determine output port. */
out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan,
- &tags);
+ tags);
if (out_port_idx >= 0 && out_port_idx < br->n_ports) {
out_port = br->ports[out_port_idx];
}
* them.
*/
done:
- send_packets(br, flow, pkt, vlan, in_port, out_port, tags,
- (br->flow_idle_time >= 0
- && (in_port->n_ifaces < 2
- || flow->dl_type != htons(ETH_TYPE_ARP)
- || !eth_addr_is_broadcast(flow->dl_dst))));
-}
-
-static void
-revalidate_flow(struct bridge *br, struct ft_flow *f)
-{
- process_flow(br, &f->flow, NULL);
+ compose_actions(br, flow, vlan, in_port, out_port, tags, actions);
}
/* Careful: 'opp' is in host byte order and opp->port_no is an OFP port
}
}
-static bool
-bridge_packet_in_ofhook_cb(const flow_t *flow, const struct ofpbuf *payload,
- void *br_)
+static void
+bridge_normal_ofhook_cb(const flow_t *flow, bool revalidating,
+ struct odp_actions *actions, tag_type *tags, void *br_)
{
struct bridge *br = br_;
- struct ft_flow *f;
-
- /* Delete any existing flow from the flow tracker. The flow cannot really
- * be in the flow table (otherwise we wouldn't be getting called). */
- f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
- if (f) {
- ft_remove(br->ft, f);
- ftf_destroy(f);
- }
+#if 0
if (flow->dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
&& eth_addr_equals(flow->dl_dst, stp_eth_addr)) {
brstp_receive(br, flow, payload);
return true;
}
+#endif
- process_flow(br, flow, payload);
-
- return true;
-}
-
-static void
-bridge_flow_expired_ofhook_cb(const struct ofexpired *expired, void *br_)
-{
- struct bridge *br = br_;
- struct ft_flow *f;
-
- f = ft_lookup(br->ft, &expired->flow, flow_hash(&expired->flow, 0));
- if (f) {
- /* Update statistics. */
- f->last_byte_count = f->byte_count;
- f->byte_count = expired->byte_count;
- bond_account_flow(br, f);
-
- ft_remove(br->ft, f);
- ftf_destroy(f);
- } else if (VLOG_IS_DBG_ENABLED()) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_DBG_RL(&rl, "received flow expiration for flow not in table");
- }
+ process_flow(br, flow, revalidating, actions, tags);
}
static struct ofhooks bridge_ofhooks = {
bridge_port_changed_ofhook_cb,
- bridge_packet_in_ofhook_cb,
- bridge_flow_expired_ofhook_cb,
+ NULL,
+ NULL,
+ bridge_normal_ofhook_cb,
};
\f
-/* Flow statistics collection. */
-
-struct slave_balance {
- struct iface *iface;
- uint64_t tx_bytes;
- struct bond_entry **hashes;
- size_t n_hashes;
-};
-
-/* Sorts pointers to pointers to bond_entries in ascending order by the
- * interface to which they are assigned, and within a single interface in
- * ascending order of bytes transmitted. */
-static int
-compare_bond_entries(const void *a_, const void *b_)
-{
- const struct bond_entry *const *ap = a_;
- const struct bond_entry *const *bp = b_;
- const struct bond_entry *a = *ap;
- const struct bond_entry *b = *bp;
- if (a->iface_idx != b->iface_idx) {
- return a->iface_idx > b->iface_idx ? 1 : -1;
- } else if (a->tx_bytes != b->tx_bytes) {
- return a->tx_bytes > b->tx_bytes ? 1 : -1;
- } else {
- return 0;
- }
-}
-
-/* Sorts slave_balances in *descending* order by number of bytes
- * transmitted. */
-static int
-compare_slave_balance(const void *a_, const void *b_)
-{
- const struct slave_balance *a = a_;
- const struct slave_balance *b = b_;
- return a->tx_bytes > b->tx_bytes ? -1 : a->tx_bytes < b->tx_bytes;
-}
-
-static void
-swap_bals(struct slave_balance *a, struct slave_balance *b)
-{
- struct slave_balance tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-static void
-resort_bals(struct slave_balance *p, struct slave_balance *bals, size_t n_bals)
-{
- if (n_bals > 1) {
- for (; p > bals && p->tx_bytes > p[-1].tx_bytes; p--) {
- swap_bals(p, p - 1);
- }
- for (; p < &bals[n_bals - 1] && p->tx_bytes < p[1].tx_bytes; p++) {
- swap_bals(p, p + 1);
- }
- }
-}
-
-static void UNUSED
-log_bals(const char *title, const struct slave_balance *bals, size_t n_bals,
- struct port *port)
-{
- const struct slave_balance *b;
-
- if (VLOG_IS_DBG_ENABLED()) {
- VLOG_DBG("slave balance %s:", title);
- for (b = bals; b < bals + n_bals; b++) {
- size_t i;
-
- VLOG_DBG("\t%s: %"PRIu64" bytes over:",
- b->iface->name, b->tx_bytes);
- for (i = 0; i < b->n_hashes; i++) {
- const struct bond_entry *e = b->hashes[i];
- VLOG_DBG("\t\thash %td: %"PRIu64" bytes",
- e - port->bond_hash, e->tx_bytes);
- }
- }
- }
-}
-
-static void
-bond_rebalance_port(struct port *port)
-{
- struct slave_balance bals[DP_MAX_PORTS];
- struct bond_entry *hashes[BOND_MASK + 1];
- struct slave_balance *b, *least_loaded;
- struct bond_entry *e;
- size_t i;
-
- for (b = bals; b < &bals[port->n_ifaces]; b++) {
- b->iface = port->ifaces[b - bals];
- b->tx_bytes = 0;
- b->hashes = NULL;
- b->n_hashes = 0;
- }
- for (i = 0; i <= BOND_MASK; i++) {
- hashes[i] = &port->bond_hash[i];
- }
- qsort(hashes, BOND_MASK + 1, sizeof *hashes, compare_bond_entries);
- for (i = 0; i <= BOND_MASK; i++) {
- e = hashes[i];
- if (e->iface_idx >= 0 && e->iface_idx < port->n_ifaces) {
- b = &bals[e->iface_idx];
- b->tx_bytes += e->tx_bytes;
- if (!b->hashes) {
- b->hashes = &hashes[i];
- }
- b->n_hashes++;
- }
- }
-
- /* Sort in decreasing order of tx_bytes. */
- qsort(bals, port->n_ifaces, sizeof *bals, compare_slave_balance);
-
-
- /* Shift load from the most-heavily-loaded slaves to the
- * least-heavily-loaded slaves. */
- least_loaded = &bals[port->n_ifaces - 1];
- //log_bals("before", bals, port->n_ifaces, port);
- for (b = bals; b < least_loaded; ) {
- uint64_t overload = b->tx_bytes - least_loaded->tx_bytes;
- if (overload < least_loaded->tx_bytes >> 5 || overload < 100000) {
- /* The extra load on this slave (and all less-loaded slaves),
- * compared to that of the least-loaded slave, is less than ~3%, or
- * it is less than ~1Mbps. No point in rebalancing. */
- break;
- }
-
- if (b->n_hashes == 1) {
- /* This slave only carries a single MAC hash, so we can't shift any
- * load away from it, even though we want to. */
- b++;
- continue;
- }
-
- for (i = 0; i < b->n_hashes; i++) {
- e = b->hashes[i];
- if (least_loaded->tx_bytes + e->tx_bytes
- < b->tx_bytes - e->tx_bytes) {
- /* Delete element from e->hashes. */
- if (!i) {
- b->hashes++;
- } else {
- memmove(b->hashes + i, b->hashes + i + 1,
- (b->n_hashes - (i + 1)) * sizeof *b->hashes);
- }
- b->n_hashes--;
-
- /* Shift load away from 'b'. */
- b->tx_bytes -= e->tx_bytes;
- resort_bals(b, bals, port->n_ifaces);
-
- /* Shift load to 'least_loaded'. */
- tag_set_add(&port->bridge->revalidate_set, e->iface_tag);
- e->iface_idx = least_loaded->iface->port_ifidx;
- e->iface_tag = tag_create_random();
- least_loaded->tx_bytes += e->tx_bytes;
- resort_bals(least_loaded, bals, port->n_ifaces);
- goto again;
- }
- }
- b++;
-
- again: ;
- }
- //log_bals("after", bals, port->n_ifaces, port);
-
- /* Implement exponentially weighted moving average. A weight of 1/2 causes
- * historical data to decay to <1% in 7 rebalancing runs. */
- for (e = &port->bond_hash[0]; e <= &port->bond_hash[BOND_MASK]; e++) {
- e->tx_bytes /= 2;
- }
-}
-
-static void
-bond_rebalance(struct bridge *br)
-{
- size_t i;
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- if (port->n_ifaces < 2) {
- continue;
- }
-
- bond_rebalance_port(port);
- }
-}
-
-static void
-request_flow_stats(struct bridge *br)
-{
- /* This ft_dst will never appear as a valid flow's action. Thus, we can
- * set it as a flow's action to force revalidation to update that flow. */
- static const struct ft_dst invalid_dst = {0xffff, 0xffff};
-
- struct odp_flow *flows, *odp_flow;
- size_t n_flows;
- int error;
-
- tag_type flowstats_tag;
- struct ft *new_ft;
- size_t n_tagged;
-
- error = dpif_flow_list_all(&br->dpif, &flows, &n_flows);
- if (error) {
- return;
- }
-
- new_ft = ft_create();
- flowstats_tag = tag_create_random();
- n_tagged = 0;
- for (odp_flow = flows; odp_flow < &flows[n_flows]; odp_flow++) {
- const flow_t *flow = &odp_flow->key;
- size_t hash = flow_hash(flow, 0);
- struct ft_flow *f = ft_lookup(br->ft, flow, hash);
- if (f) {
- /* Move from br->ft to new_ft. */
- ft_remove(br->ft, f);
- hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash);
-
- /* Update statistics.
- * XXX is this still accurate with the new datapath? */
- f->last_byte_count = f->byte_count;
- f->byte_count = odp_flow->stats.n_bytes;
-
- bond_account_flow(br, f);
-
- /* XXX Should we verify that the flow's actions are what we
- * expect? (This used to be easy; now it's harder.) */
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- f = ft_lookup(new_ft, flow, hash);
- if (!f) {
- if (!VLOG_DROP_WARN(&rl)) {
- char *flow_string = flow_to_string(flow);
- VLOG_WARN("unexpected flow in flow table: %s",
- flow_string);
- free(flow_string);
- }
- f = ftf_create(flow, &invalid_dst, 1, flowstats_tag);
- hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash);
- f->tags |= flowstats_tag;
- n_tagged++;
- f->last_byte_count = f->byte_count = odp_flow->stats.n_bytes;
- } else {
- VLOG_WARN_RL(&rl, "duplicate flow in flow stats reply");
- }
- }
- }
-
- /* Delete all the flows that remain in br->ft, replacing them by the ones
- * from new_ft. */
- hmap_expand(&new_ft->flows);
- hmap_swap(&br->ft->flows, &new_ft->flows);
- ft_destroy(new_ft);
-
- /* If we tagged anything for revalidation, add that tag to the revalidation
- * set. */
- if (n_tagged) {
- tag_set_add(&br->revalidate_set, flowstats_tag);
- }
-
- /* Rebalance any bonded ports. */
- bond_rebalance(br);
-}
-
-static void
-flowstats_run(struct bridge *br)
-{
- if (time_now() >= br->next_stats_request) {
- request_flow_stats(br);
- br->next_stats_request = time_now() + 10;
- }
-}
-
-static void
-flowstats_wait(struct bridge *br)
-{
- poll_timer_wait((br->next_stats_request - time_now()) * 1000);
-}
-\f
/* Port functions. */
static void
action.output.len = htons(sizeof action);
action.output.port = htons(port_no);
- flow_extract(pkt, htons(OFPP_NONE), &flow);
+ flow_extract(pkt, ODPP_NONE, &flow);
ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
}
ofpbuf_delete(pkt);
poll_timer_wait(1000);
}
}
-
-static void
-brstp_receive(struct bridge *br, const flow_t *flow, const struct ofpbuf *pkt)
-{
- struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- struct ofpbuf payload = *pkt;
- struct eth_header *eth;
- struct llc_header *llc;
- struct stp_port *sp;
-
- /* Find the interface and port structure for the received packet. */
- if (flow->in_port >= STP_MAX_PORTS) {
- return;
- }
- sp = stp_get_port(br->stp, flow->in_port);
- if (stp_port_get_state(sp) == STP_DISABLED) {
- return;
- }
-
- /* Check LLC DSAP. (Caller already verified dl_type and dl_dst.) */
- eth = payload.data;
- llc = ofpbuf_at_assert(&payload, sizeof(struct eth_header), sizeof *llc);
- if (llc->llc_dsap != STP_LLC_DSAP) {
- VLOG_DBG_RL(&rl, "bad DSAP 0x%02"PRIx8" received on STP multicast "
- "address", llc->llc_dsap);
- return;
- }
-
- /* Trim off padding on payload. */
- if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
- payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN;
- }
- if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
- struct stp_port *p = stp_get_port(br->stp, flow->in_port);
- stp_received_bpdu(p, payload.data, payload.size);
- }
-}