#include "ofproto/ofproto-dpif-mirror.h"
#include "ofproto/ofproto-dpif-sflow.h"
#include "ofproto/ofproto-dpif.h"
+#include "ofproto/ofproto-provider.h"
#include "tunnel.h"
#include "vlog.h"
* flow translation. */
#define MAX_RESUBMIT_RECURSION 64
+struct ovs_rwlock xlate_rwlock = OVS_RWLOCK_INITIALIZER;
+
struct xbridge {
struct hmap_node hmap_node; /* Node in global 'xbridges' map. */
struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
struct dpif_ipfix *ipfix; /* Ipfix handle, or null. */
struct stp *stp; /* STP or null if disabled. */
+ /* Special rules installed by ofproto-dpif. */
+ struct rule_dpif *miss_rule;
+ struct rule_dpif *no_packet_in_rule;
+
enum ofp_config_flags frag; /* Fragmentation handling. */
bool has_netflow; /* Bridge runs netflow? */
bool has_in_band; /* Bridge has in band control? */
struct xport *peer; /* Patch port peer or null. */
enum ofputil_port_config config; /* OpenFlow port configuration. */
- int stp_port_no; /* STP port number or 0 if not in use. */
+ int stp_port_no; /* STP port number or -1 if not in use. */
struct hmap skb_priorities; /* Map of 'skb_priority_to_dscp's. */
uint16_t vlan);
static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port);
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
static struct xbridge *xbridge_lookup(const struct ofproto_dpif *);
static struct xbundle *xbundle_lookup(const struct ofbundle *);
static struct xport *xport_lookup(const struct ofport_dpif *);
void
xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
- struct dpif *dpif, const struct mac_learning *ml,
- struct stp *stp, const struct mbridge *mbridge,
+ struct dpif *dpif, struct rule_dpif *miss_rule,
+ struct rule_dpif *no_packet_in_rule,
+ const struct mac_learning *ml, struct stp *stp,
+ const struct mbridge *mbridge,
const struct dpif_sflow *sflow,
const struct dpif_ipfix *ipfix, enum ofp_config_flags frag,
bool forward_bpdu, bool has_in_band, bool has_netflow)
xbridge->has_in_band = has_in_band;
xbridge->has_netflow = has_netflow;
xbridge->frag = frag;
+ xbridge->miss_rule = miss_rule;
+ xbridge->no_packet_in_rule = no_packet_in_rule;
}
void
}
hmap_remove(&xbridges, &xbridge->hmap_node);
+ mac_learning_unref(xbridge->ml);
+ mbridge_unref(xbridge->mbridge);
+ dpif_sflow_unref(xbridge->sflow);
+ dpif_ipfix_unref(xbridge->ipfix);
+ stp_unref(xbridge->stp);
+ hmap_destroy(&xbridge->xports);
free(xbridge->name);
free(xbridge);
}
const struct xport *xport;
int error = ENODEV;
+ ovs_rwlock_rdlock(&xlate_rwlock);
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
error = EINVAL;
if (fitnessp) {
*fitnessp = fitness;
}
+ ovs_rwlock_unlock(&xlate_rwlock);
return error;
}
static struct stp_port *
xport_get_stp_port(const struct xport *xport)
{
- return xport->xbridge->stp && xport->stp_port_no
+ return xport->xbridge->stp && xport->stp_port_no != -1
? stp_get_port(xport->xbridge->stp, xport->stp_port_no)
: NULL;
}
}
}
-static void
-update_learning_table(const struct xbridge *xbridge,
- const struct flow *flow, struct flow_wildcards *wc,
- int vlan, struct xbundle *in_xbundle)
+/* Checks whether a MAC learning update is necessary for MAC learning table
+ * 'ml' given that a packet matching 'flow' was received on 'in_xbundle' in
+ * 'vlan'.
+ *
+ * Most packets processed through the MAC learning table do not actually
+ * change it in any way. This function requires only a read lock on the MAC
+ * learning table, so it is much cheaper in this common case.
+ *
+ * Keep the code here synchronized with that in update_learning_table__()
+ * below. */
+static bool
+is_mac_learning_update_needed(const struct mac_learning *ml,
+ const struct flow *flow,
+ struct flow_wildcards *wc,
+ int vlan, struct xbundle *in_xbundle)
+ OVS_REQ_RDLOCK(ml->rwlock)
{
struct mac_entry *mac;
- /* Don't learn the OFPP_NONE port. */
- if (in_xbundle == &ofpp_none_bundle) {
- return;
+ if (!mac_learning_may_learn(ml, flow->dl_src, vlan)) {
+ return false;
+ }
+
+ mac = mac_learning_lookup(ml, flow->dl_src, vlan);
+ if (!mac || mac_entry_age(ml, mac)) {
+ return true;
+ }
+
+ if (is_gratuitous_arp(flow, wc)) {
+ /* We don't want to learn from gratuitous ARP packets that are
+ * reflected back over bond slaves so we lock the learning table. */
+ if (!in_xbundle->bond) {
+ return true;
+ } else if (mac_entry_is_grat_arp_locked(mac)) {
+ return false;
+ }
}
- ovs_rwlock_wrlock(&xbridge->ml->rwlock);
+ return mac->port.p != in_xbundle->ofbundle;
+}
+
+
+/* Updates MAC learning table 'ml' given that a packet matching 'flow' was
+ * received on 'in_xbundle' in 'vlan'.
+ *
+ * This code repeats all the checks in is_mac_learning_update_needed() because
+ * the lock was released between there and here and thus the MAC learning state
+ * could have changed.
+ *
+ * Keep the code here synchronized with that in is_mac_learning_update_needed()
+ * above. */
+static void
+update_learning_table__(const struct xbridge *xbridge,
+ const struct flow *flow, struct flow_wildcards *wc,
+ int vlan, struct xbundle *in_xbundle)
+ OVS_REQ_WRLOCK(xbridge->ml->rwlock)
+{
+ struct mac_entry *mac;
+
if (!mac_learning_may_learn(xbridge->ml, flow->dl_src, vlan)) {
- goto out;
+ return;
}
mac = mac_learning_insert(xbridge->ml, flow->dl_src, vlan);
if (!in_xbundle->bond) {
mac_entry_set_grat_arp_lock(mac);
} else if (mac_entry_is_grat_arp_locked(mac)) {
- goto out;
+ return;
}
}
/* 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, "bridge %s: learned that "ETH_ADDR_FMT" is "
"on port %s in VLAN %d",
xbridge->name, ETH_ADDR_ARGS(flow->dl_src),
mac->port.p = in_xbundle->ofbundle;
mac_learning_changed(xbridge->ml);
}
-out:
+}
+
+static void
+update_learning_table(const struct xbridge *xbridge,
+ const struct flow *flow, struct flow_wildcards *wc,
+ int vlan, struct xbundle *in_xbundle)
+{
+ bool need_update;
+
+ /* Don't learn the OFPP_NONE port. */
+ if (in_xbundle == &ofpp_none_bundle) {
+ return;
+ }
+
+ /* First try the common case: no change to MAC learning table. */
+ ovs_rwlock_rdlock(&xbridge->ml->rwlock);
+ need_update = is_mac_learning_update_needed(xbridge->ml, flow, wc, vlan,
+ in_xbundle);
ovs_rwlock_unlock(&xbridge->ml->rwlock);
+
+ if (need_update) {
+ /* Slow path: MAC learning table might need an update. */
+ ovs_rwlock_wrlock(&xbridge->ml->rwlock);
+ update_learning_table__(xbridge, flow, wc, vlan, in_xbundle);
+ ovs_rwlock_unlock(&xbridge->ml->rwlock);
+ }
}
/* Determines whether packets in 'flow' within 'xbridge' should be forwarded or
struct xbundle *in_xbundle;
struct xport *in_port;
struct mac_entry *mac;
+ void *mac_port;
uint16_t vlan;
uint16_t vid;
/* Determine output bundle. */
ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock);
mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);
- if (mac) {
- struct xbundle *mac_xbundle = xbundle_lookup(mac->port.p);
+ mac_port = mac ? mac->port.p : NULL;
+ ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
+
+ if (mac_port) {
+ struct xbundle *mac_xbundle = xbundle_lookup(mac_port);
if (mac_xbundle && mac_xbundle != in_xbundle) {
xlate_report(ctx, "forwarding to learned port");
output_normal(ctx, mac_xbundle, vlan);
}
ctx->xout->nf_output_iface = NF_OUT_FLOOD;
}
- ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
}
/* Compose SAMPLE action for sFlow or IPFIX. The given probability is
struct flow_wildcards *wc = &ctx->xout->wc;
struct flow *flow = &ctx->xin->flow;
ovs_be16 flow_vlan_tci;
- uint32_t flow_skb_mark;
+ uint32_t flow_pkt_mark;
uint8_t flow_nw_tos;
odp_port_t out_port, odp_port;
uint8_t dscp;
}
flow_vlan_tci = flow->vlan_tci;
- flow_skb_mark = flow->skb_mark;
+ flow_pkt_mark = flow->pkt_mark;
flow_nw_tos = flow->nw_tos;
if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) {
out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port);
flow->vlan_tci = htons(0);
}
- flow->skb_mark &= ~IPSEC_MARK;
}
if (out_port != ODPP_NONE) {
out:
/* Restore flow */
flow->vlan_tci = flow_vlan_tci;
- flow->skb_mark = flow_skb_mark;
+ flow->pkt_mark = flow_pkt_mark;
flow->nw_tos = flow_nw_tos;
}
compose_output_action__(ctx, ofp_port, true);
}
-/* Common rule processing in one place to avoid duplicating code. */
-static struct rule_dpif *
-ctx_rule_hooks(struct xlate_ctx *ctx, struct rule_dpif *rule,
- bool may_packet_in)
+static void
+xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
{
- if (ctx->xin->resubmit_hook) {
- ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
- }
- if (rule == NULL && may_packet_in) {
- /* 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(ctx->xbridge->ofproto, &ctx->xin->flow);
- }
- if (rule && ctx->xin->resubmit_stats) {
- rule_credit_stats(rule, ctx->xin->resubmit_stats);
+ struct rule_dpif *old_rule = ctx->rule;
+ struct rule_actions *actions;
+
+ if (ctx->xin->resubmit_stats) {
+ rule_dpif_credit_stats(rule, ctx->xin->resubmit_stats);
}
- return rule;
+
+ ctx->recurse++;
+ ctx->rule = rule;
+ actions = rule_dpif_get_actions(rule);
+ do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx);
+ rule_actions_unref(actions);
+ ctx->rule = old_rule;
+ ctx->recurse--;
}
static void
ctx->table_id = table_id;
- /* Look up a flow with 'in_port' as the input port. */
+ /* Look up a flow with 'in_port' as the input port. Then restore the
+ * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
+ * have surprising behavior). */
ctx->xin->flow.in_port.ofp_port = in_port;
- rule = rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
- &ctx->xin->flow, &ctx->xout->wc,
- table_id);
-
- /* Restore the original input port. Otherwise OFPP_NORMAL and
- * OFPP_IN_PORT will have surprising behavior. */
+ rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
+ &ctx->xin->flow, &ctx->xout->wc,
+ table_id, &rule);
ctx->xin->flow.in_port.ofp_port = old_in_port;
- rule = ctx_rule_hooks(ctx, rule, may_packet_in);
+ if (ctx->xin->resubmit_hook) {
+ ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
+ }
- if (rule) {
- struct rule_dpif *old_rule = ctx->rule;
+ if (!rule && may_packet_in) {
+ struct xport *xport;
- ctx->recurse++;
- ctx->rule = rule;
- do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
- ctx->rule = old_rule;
- ctx->recurse--;
+ /* 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? */
+ xport = get_ofp_port(ctx->xbridge, ctx->xin->flow.in_port.ofp_port);
+ choose_miss_rule(xport ? xport->config : 0,
+ ctx->xbridge->miss_rule,
+ ctx->xbridge->no_packet_in_rule, &rule);
+ }
+ if (rule) {
+ xlate_recursively(ctx, rule);
+ rule_dpif_unref(rule);
}
ctx->table_id = old_table_id;
packet = ofpbuf_clone(ctx->xin->packet);
key.skb_priority = 0;
- key.skb_mark = 0;
+ key.pkt_mark = 0;
memset(&key.tunnel, 0, sizeof key.tunnel);
commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
pin->reason = reason;
pin->controller_id = controller_id;
pin->table_id = ctx->table_id;
- pin->cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
+ pin->cookie = ctx->rule ? rule_dpif_get_flow_cookie(ctx->rule) : 0;
pin->send_len = len;
flow_get_metadata(&ctx->xin->flow, &pin->fmd);
xlate_learn_action(struct xlate_ctx *ctx,
const struct ofpact_learn *learn)
{
- struct ofputil_flow_mod *fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofputil_flow_mod fm;
struct ofpbuf ofpacts;
ctx->xout->has_learn = true;
return;
}
- fm = xmalloc(sizeof *fm);
- ofpbuf_init(&ofpacts, 0);
- learn_execute(learn, &ctx->xin->flow, fm, &ofpacts);
-
- ofproto_dpif_flow_mod(ctx->xbridge->ofproto, fm);
-}
-
-/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
- * means "infinite". */
-static void
-reduce_timeout(uint16_t max, uint16_t *timeout)
-{
- if (max && (!*timeout || *timeout > max)) {
- *timeout = max;
- }
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ learn_execute(learn, &ctx->xin->flow, &fm, &ofpacts);
+ ofproto_dpif_flow_mod(ctx->xbridge->ofproto, &fm);
+ ofpbuf_uninit(&ofpacts);
}
static void
const struct ofpact_fin_timeout *oft)
{
if (ctx->xin->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
- struct rule_dpif *rule = ctx->rule;
-
- if (list_is_empty(&rule->up.expirable)) {
- list_insert(&rule->up.ofproto->expirable, &rule->up.expirable);
- }
-
- reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
- reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
+ rule_dpif_reduce_timeouts(ctx->rule, oft->fin_idle_timeout,
+ oft->fin_hard_timeout);
}
}
return true;
}
-static bool
-tunnel_ecn_ok(struct xlate_ctx *ctx)
-{
- if (is_ip_any(&ctx->base_flow)
- && (ctx->xin->flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
- if ((ctx->base_flow.nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
- VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
- " but is not ECN capable");
- return false;
- } else {
- /* Set the ECN CE value in the tunneled packet. */
- ctx->xin->flow.nw_tos |= IP_ECN_CE;
- }
- }
-
- return true;
-}
-
static void
do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct xlate_ctx *ctx)
{
struct flow_wildcards *wc = &ctx->xout->wc;
struct flow *flow = &ctx->xin->flow;
- bool was_evictable = true;
const struct ofpact *a;
- if (ctx->rule) {
- /* Don't let the rule we're working on get evicted underneath us. */
- was_evictable = ctx->rule->up.evictable;
- ctx->rule->up.evictable = false;
- }
-
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
struct ofpact_controller *controller;
const struct ofpact_metadata *metadata;
ofpact_get_OUTPUT(a)->max_len, true);
break;
+ case OFPACT_GROUP:
+ /* XXX not yet implemented */
+ break;
+
case OFPACT_CONTROLLER:
controller = ofpact_get_CONTROLLER(a);
execute_controller_action(ctx, controller->max_len,
case OFPACT_SET_MPLS_TTL:
if (compose_set_mpls_ttl_action(ctx,
ofpact_get_SET_MPLS_TTL(a)->ttl)) {
- goto out;
+ return;
}
break;
case OFPACT_DEC_MPLS_TTL:
if (compose_dec_mpls_ttl_action(ctx)) {
- goto out;
+ return;
}
break;
case OFPACT_DEC_TTL:
wc->masks.nw_ttl = 0xff;
if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
- goto out;
+ return;
}
break;
break;
}
}
-
-out:
- if (ctx->rule) {
- ctx->rule->up.evictable = was_evictable;
- }
}
void
}
/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
- * into datapath actions in 'odp_actions', using 'ctx'. */
+ * into datapath actions in 'odp_actions', using 'ctx'.
+ *
+ * The caller must take responsibility for eventually freeing 'xout', with
+ * xlate_out_uninit(). */
void
xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
{
struct flow_wildcards *wc = &xout->wc;
struct flow *flow = &xin->flow;
+ struct rule_actions *actions = NULL;
enum slow_path_reason special;
const struct ofpact *ofpacts;
struct xport *in_port;
struct flow orig_flow;
struct xlate_ctx ctx;
size_t ofpacts_len;
+ bool tnl_may_send;
COVERAGE_INC(xlate_actions);
+ ovs_rwlock_rdlock(&xlate_rwlock);
+
/* Flow initialization rules:
* - 'base_flow' must match the kernel's view of the packet at the
* time that action processing starts. 'flow' represents any
ctx.xbridge = xbridge_lookup(xin->ofproto);
if (!ctx.xbridge) {
- return;
+ goto out;
}
ctx.rule = xin->rule;
memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type);
wc->masks.nw_frag |= FLOW_NW_FRAG_MASK;
- if (tnl_port_should_receive(&ctx.xin->flow)) {
- memset(&wc->masks.tunnel, 0xff, sizeof wc->masks.tunnel);
- /* skb_mark is currently used only by tunnels but that will likely
- * change in the future. */
- memset(&wc->masks.skb_mark, 0xff, sizeof wc->masks.skb_mark);
- }
+ tnl_may_send = tnl_xlate_init(&ctx.base_flow, flow, wc);
if (ctx.xbridge->has_netflow) {
netflow_mask_wc(flow, wc);
}
ofpacts = xin->ofpacts;
ofpacts_len = xin->ofpacts_len;
} else if (xin->rule) {
- ofpacts = xin->rule->up.ofpacts;
- ofpacts_len = xin->rule->up.ofpacts_len;
+ actions = rule_dpif_get_actions(xin->rule);
+ ofpacts = actions->ofpacts;
+ ofpacts_len = actions->ofpacts_len;
} else {
NOT_REACHED();
}
break;
case OFPC_FRAG_DROP:
- return;
+ goto out;
case OFPC_FRAG_REASM:
NOT_REACHED();
add_ipfix_action(&ctx);
sample_actions_len = ctx.xout->odp_actions.size;
- if (tunnel_ecn_ok(&ctx) && (!in_port || may_receive(in_port, &ctx))) {
+ if (tnl_may_send && (!in_port || may_receive(in_port, &ctx))) {
do_xlate_actions(ofpacts, ofpacts_len, &ctx);
/* We've let OFPP_NORMAL and the learning action look at the
* use non-header fields as part of the cache. */
memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+
+out:
+ ovs_rwlock_unlock(&xlate_rwlock);
+
+ rule_actions_unref(actions);
}