#include "coverage.h"
#include "dhcp.h"
+#include "mac-learning.h"
#include "netdev.h"
#include "netflow.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "ofproto.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
#include "packets.h"
struct port_array ports; /* Index is xflow port nr;
* wdp_port->opp.port_no is OFP port nr. */
struct shash port_by_name;
- bool need_revalidate;
long long int next_expiration;
+
+ /* Rules that might need to be revalidated. */
+ bool need_revalidate; /* Revalidate all subrules? */
+ bool revalidate_all; /* Revalidate all subrules and other rules? */
+ struct tag_set revalidate_set; /* Tag set of (sub)rules to revalidate. */
+
+ /* Hooks for ovs-vswitchd. */
+ const struct ofhooks *ofhooks;
+ void *aux;
+
+ /* Used by default ofhooks. */
+ struct mac_learning *ml;
};
+static const struct ofhooks default_ofhooks;
+
static struct list all_wx = LIST_INITIALIZER(&all_wx);
static int wx_port_init(struct wx *);
static int
wx_xlate_actions(struct wx *, const union ofp_action *, size_t n,
const flow_t *flow, const struct ofpbuf *packet,
- struct xflow_actions *out, bool *may_set_up_flow);
+ tag_type *tags, struct xflow_actions *out,
+ bool *may_set_up_flow);
\f
struct wx_rule {
struct wdp_rule wr;
uint64_t byte_count; /* Number of bytes received. */
uint64_t accounted_bytes; /* Number of bytes passed to account_cb. */
long long int used; /* Last-used time (0 if never used). */
+ tag_type tags; /* Tags (set only by hooks). */
/* If 'super' is non-NULL, this rule is a subrule, that is, it is an
* exact-match rule (having cr.wc.wildcards of 0) generated from the
{
struct wx_rule *super = rule->super ? rule->super : rule;
if (wx_xlate_actions(wx, super->wr.actions, super->wr.n_actions, flow,
- packet, &a, NULL)) {
+ packet, NULL, &a, NULL)) {
return;
}
actions = a.actions;
super = rule->super ? rule->super : rule;
wx_xlate_actions(wx, super->wr.actions, super->wr.n_actions,
- &rule->wr.cr.flow, packet, &a, &rule->may_install);
+ &rule->wr.cr.flow, packet,
+ &rule->tags, &a, &rule->may_install);
actions_len = a.n_actions * sizeof *a.actions;
if (rule->n_xflow_actions != a.n_actions
/* Output. */
struct xflow_actions *out; /* Datapath actions. */
- //tag_type *tags; /* Tags associated with OFPP_NORMAL actions. */
+ tag_type *tags; /* Tags associated with OFPP_NORMAL actions. */
bool may_set_up_flow; /* True ordinarily; false if the actions must
* be reassessed for every packet. */
uint16_t nf_output_iface; /* Output interface index for NetFlow. */
xlate_table_action(ctx, ctx->flow.in_port);
break;
case OFPP_NORMAL:
-#if 0
- if (!ctx->wx->ofhooks->normal_cb(ctx->flow, ctx->packet,
+ if (!ctx->wx->ofhooks->normal_cb(&ctx->flow, ctx->packet,
ctx->out, ctx->tags,
&ctx->nf_output_iface,
ctx->wx->aux)) {
ctx->may_set_up_flow = false;
}
break;
-#else
- /* fall through to flood for now */
-#endif
+
case OFPP_FLOOD:
add_output_group_action(ctx->out, WX_GROUP_FLOOD,
&ctx->nf_output_iface);
static int
wx_xlate_actions(struct wx *wx, const union ofp_action *in, size_t n_in,
const flow_t *flow, const struct ofpbuf *packet,
- struct xflow_actions *out, bool *may_set_up_flow)
+ tag_type *tags, struct xflow_actions *out,
+ bool *may_set_up_flow)
{
- //tag_type no_tags = 0;
+ tag_type no_tags = 0;
struct wx_xlate_ctx ctx;
COVERAGE_INC(wx_ofp2xflow);
xflow_actions_init(out);
ctx.wx = wx;
ctx.packet = packet;
ctx.out = out;
- //ctx.tags = tags ? tags : &no_tags;
+ ctx.tags = tags ? tags : &no_tags;
ctx.may_set_up_flow = true;
ctx.nf_output_iface = NF_OUT_DROP;
do_xlate_actions(in, n_in, &ctx);
struct wx *wx;
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. */
+ struct tag_set revalidate_set; /* Set of tags to revalidate. */
};
static bool
if (cbdata->revalidate_all
|| (cbdata->revalidate_subrules && sub->super)
- /*|| (tag_set_intersects(&cbdata->revalidate_set, sub->tags))*/) {
+ || tag_set_intersects(&cbdata->revalidate_set, sub->tags)) {
revalidate_rule(cbdata->wx, sub);
}
}
/* XXX account_checkpoint_cb */
}
- if (wx->need_revalidate /*|| !tag_set_is_empty(&p->revalidate_set)*/) {
+ if (wx->need_revalidate || !tag_set_is_empty(&wx->revalidate_set)) {
struct revalidate_cbdata cbdata;
cbdata.wx = wx;
- cbdata.revalidate_all = false;
+ cbdata.revalidate_all = wx->revalidate_all;
cbdata.revalidate_subrules = wx->need_revalidate;
- //cbdata.revalidate_set = wx->revalidate_set;
- //tag_set_init(&wx->revalidate_set);
+ cbdata.revalidate_set = wx->revalidate_set;
+ tag_set_init(&wx->revalidate_set);
COVERAGE_INC(wx_revalidate);
classifier_for_each(&wx->cls, CLS_INC_EXACT, revalidate_cb, &cbdata);
wx->need_revalidate = false;
static void
wx_wait_one(struct wx *wx)
{
- if (wx->need_revalidate /*|| !tag_set_is_empty(&p->revalidate_set)*/) {
+ if (wx->need_revalidate || !tag_set_is_empty(&wx->revalidate_set)) {
poll_immediate_wake();
} else if (wx->next_expiration != LLONG_MAX) {
poll_timer_wait_until(wx->next_expiration);
port_array_init(&wx->ports);
shash_init(&wx->port_by_name);
wx->next_expiration = time_msec() + 1000;
+ tag_set_init(&wx->revalidate_set);
wx_port_init(wx);
+ wx->ofhooks = &default_ofhooks;
+ wx->aux = wx;
+ wx->ml = mac_learning_create();
+
*wdpp = &wx->wdp;
}
classifier_destroy(&wx->cls);
netdev_monitor_destroy(wx->netdev_monitor);
list_remove(&wx->list_node);
+ mac_learning_destroy(wx->ml);
free(wx);
}
flow_extract((struct ofpbuf *) packet, 0, in_port, &flow);
error = wx_xlate_actions(wx, actions, n_actions, &flow, packet,
- &xflow_actions, NULL);
+ NULL, &xflow_actions, NULL);
if (error) {
return error;
}
xfif_recv_wait(wx->xfif);
}
+
+static int
+wx_set_ofhooks(struct wdp *wdp, const struct ofhooks *ofhooks, void *aux)
+{
+ struct wx *wx = wx_cast(wdp);
+
+ if (wx->ofhooks == &default_ofhooks) {
+ mac_learning_destroy(wx->ml);
+ wx->ml = NULL;
+ }
+
+ wx->ofhooks = ofhooks;
+ wx->aux = aux;
+ return 0;
+}
+
+static void
+wx_revalidate(struct wdp *wdp, tag_type tag)
+{
+ struct wx *wx = wx_cast(wdp);
+
+ tag_set_add(&wx->revalidate_set, tag);
+}
+
+static void
+wx_revalidate_all(struct wdp *wdp)
+{
+ struct wx *wx = wx_cast(wdp);
+
+ wx->revalidate_all = true;
+}
\f
static void wx_port_update(struct wx *, const char *devname,
wdp_port_poll_cb_func *cb, void *aux);
wx_recv,
wx_recv_purge,
wx_recv_wait,
+ wx_set_ofhooks,
+ wx_revalidate,
+ wx_revalidate_all,
};
static bool inited = false;
svec_destroy(&types);
}
+\f
+static bool
+default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
+ struct xflow_actions *actions, tag_type *tags,
+ uint16_t *nf_output_iface, void *wx_)
+{
+ struct wx *wx = wx_;
+ int out_port;
+
+ /* Drop frames for reserved multicast addresses. */
+ if (eth_addr_is_reserved(flow->dl_dst)) {
+ return true;
+ }
+
+ /* Learn source MAC (but don't try to learn from revalidation). */
+ if (packet != NULL) {
+ tag_type rev_tag = mac_learning_learn(wx->ml, flow->dl_src,
+ 0, flow->in_port,
+ GRAT_ARP_LOCK_NONE);
+ if (rev_tag) {
+ /* The log messages here could actually be useful in debugging,
+ * so keep the rate limit relatively high. */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
+ VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
+ ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
+ tag_set_add(&wx->revalidate_set, rev_tag);
+ }
+ }
+
+ /* Determine output port. */
+ out_port = mac_learning_lookup_tag(wx->ml, flow->dl_dst, 0, tags,
+ NULL);
+ if (out_port < 0) {
+ add_output_group_action(actions, WX_GROUP_FLOOD, nf_output_iface);
+ } else if (out_port != flow->in_port) {
+ xflow_actions_add(actions, XFLOWAT_OUTPUT)->output.port = out_port;
+ *nf_output_iface = out_port;
+ } else {
+ /* Drop. */
+ }
+
+ return true;
+}
+
+static const struct ofhooks default_ofhooks = {
+ NULL,
+ default_normal_ofhook_cb,
+ NULL,
+ NULL
+};
*engine_id = wdp->netflow_engine_id;
}
\f
+/* ovs-vswitchd interface.
+ *
+ * This needs to be redesigned, because it only makes sense for wdp-xflow. The
+ * ofhooks are currently the key to implementing the OFPP_NORMAL feature of
+ * ovs-vswitchd. */
+
+/* Sets the ofhooks for 'wdp' to 'ofhooks' with the accompanying 'aux' value.
+ * Only the xflow implementation of wdp is expected to implement this function;
+ * other implementations should just set it to NULL.
+ *
+ * The ofhooks are currently the key to implementing the OFPP_NORMAL feature of
+ * ovs-vswitchd. This design is not adequate for the long term; it needs to be
+ * redone.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+wdp_set_ofhooks(struct wdp *wdp, const struct ofhooks *ofhooks, void *aux)
+{
+ int error;
+ error = (wdp->wdp_class->set_ofhooks
+ ? wdp->wdp_class->set_ofhooks(wdp, ofhooks, aux)
+ : EOPNOTSUPP);
+ log_operation(wdp, "set_ofhooks", error);
+ return error;
+}
+
+/* Tell 'wdp' to revalidate all the flows that match 'tag'.
+ *
+ * This needs to be redesigned, because it only makes sense for wdp-xflow.
+ * Other implementations cannot practically use this interface and should just
+ * set this to NULL. */
+void
+wdp_revalidate(struct wdp *wdp, tag_type tag)
+{
+ if (wdp->wdp_class->revalidate) {
+ wdp->wdp_class->revalidate(wdp, tag);
+ }
+}
+
+/* Tell 'wdp' to revalidate every flow. (This is not the same as calling
+ * 'revalidate' with all-1-bits for 'tag' because it also revalidates flows
+ * that do not have any tag at all.)
+ *
+ * This needs to be redesigned, because it only makes sense for wdp-xflow.
+ * Other implementations cannot practically use this interface and should just
+ * set this to NULL. */
+void
+wdp_revalidate_all(struct wdp *wdp)
+{
+ if (wdp->wdp_class->revalidate_all) {
+ wdp->wdp_class->revalidate_all(wdp);
+ }
+}
+\f
/* Returns a copy of 'old'. The packet's payload, if any, is copied as well,
* but if it is longer than 'trim' bytes it is truncated to that length. */
struct wdp_packet *