-/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "cfm.h"
#include "connmgr.h"
#include "coverage.h"
+#include "dp-packet.h"
#include "dpif.h"
#include "dynamic-string.h"
#include "in-band.h"
#include "ofproto/ofproto-dpif-sflow.h"
#include "ofproto/ofproto-dpif.h"
#include "ofproto/ofproto-provider.h"
-#include "packet-dpif.h"
#include "ovs-router.h"
#include "tnl-ports.h"
#include "tunnel.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
COVERAGE_DEFINE(xlate_actions);
COVERAGE_DEFINE(xlate_actions_oversize);
COVERAGE_DEFINE(xlate_actions_too_many_output);
-COVERAGE_DEFINE(xlate_actions_mpls_overflow);
VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
int recurse; /* Current resubmit nesting depth. */
int resubmits; /* Total number of resubmits. */
bool in_group; /* Currently translating ofgroup, if true. */
+ bool in_action_set; /* Currently translating action_set, if true. */
uint32_t orig_skb_priority; /* Priority when packet arrived. */
uint8_t table_id; /* OpenFlow table ID where flow was found. */
xlate_lookup_ofproto_(const struct dpif_backer *backer, const struct flow *flow,
ofp_port_t *ofp_in_port, const struct xport **xportp)
{
+ struct ofproto_dpif *recv_ofproto = NULL;
+ struct ofproto_dpif *recirc_ofproto = NULL;
const struct xport *xport;
+ ofp_port_t in_port = OFPP_NONE;
*xportp = xport = xlate_lookup_xport(backer, flow);
if (xport) {
- if (ofp_in_port) {
- *ofp_in_port = xport->ofp_port;
+ recv_ofproto = xport->xbridge->ofproto;
+ in_port = xport->ofp_port;
+ }
+
+ /* When recirc_id is set in 'flow', checks whether the ofproto_dpif that
+ * corresponds to the recirc_id is same as the receiving bridge. If they
+ * are the same, uses the 'recv_ofproto' and keeps the 'ofp_in_port' as
+ * assigned. Otherwise, uses the 'recirc_ofproto' that owns recirc_id and
+ * assigns OFPP_NONE to 'ofp_in_port'. Doing this is in that, the
+ * recirculated flow must be processced by the ofproto which originates
+ * the recirculation, and as bridges can only see their own ports, the
+ * in_port of the 'recv_ofproto' should not be passed to the
+ * 'recirc_ofproto'.
+ *
+ * Admittedly, setting the 'ofp_in_port' to OFPP_NONE limits the
+ * 'recirc_ofproto' from meaningfully matching on in_port of recirculated
+ * flow, and should be fixed in the near future.
+ *
+ * TODO: Restore the original patch port.
+ */
+ if (recv_ofproto && flow->recirc_id) {
+ recirc_ofproto = ofproto_dpif_recirc_get_ofproto(backer,
+ flow->recirc_id);
+ if (recv_ofproto != recirc_ofproto) {
+ *xportp = xport = NULL;
+ in_port = OFPP_NONE;
}
- return xport->xbridge->ofproto;
}
- return NULL;
+ if (ofp_in_port) {
+ *ofp_in_port = in_port;
+ }
+
+ return xport ? recv_ofproto : recirc_ofproto;
}
/* Given a datapath and flow metadata ('backer', and 'flow' respectively)
* pointers until quiescing, for longer term use additional references must
* be taken.
*
- * '*ofp_in_port' is set to OFPP_NONE if 'flow''s in_port does not exist.
- *
- * Returns 0 if successful, ENODEV if the parsed flow has no associated ofport.
+ * Returns 0 if successful, ENODEV if the parsed flow has no associated ofproto.
*/
int
xlate_lookup(const struct dpif_backer *backer, const struct flow *flow,
ofproto = xlate_lookup_ofproto_(backer, flow, ofp_in_port, &xport);
- if (ofp_in_port && !xport) {
- *ofp_in_port = OFPP_NONE;
- }
-
- if (!xport) {
+ if (!ofproto) {
return ENODEV;
}
}
if (ipfix) {
- *ipfix = xport->xbridge->ipfix;
+ *ipfix = xport ? xport->xbridge->ipfix : NULL;
}
if (sflow) {
- *sflow = xport->xbridge->sflow;
+ *sflow = xport ? xport->xbridge->sflow : NULL;
}
if (netflow) {
- *netflow = xport->xbridge->netflow;
+ *netflow = xport ? xport->xbridge->netflow : NULL;
}
+
return 0;
}
}
static void
-stp_process_packet(const struct xport *xport, const struct ofpbuf *packet)
+stp_process_packet(const struct xport *xport, const struct dp_packet *packet)
{
struct stp_port *sp = xport_get_stp_port(xport);
- struct ofpbuf payload = *packet;
- struct eth_header *eth = ofpbuf_data(&payload);
+ struct dp_packet payload = *packet;
+ struct eth_header *eth = dp_packet_data(&payload);
/* Sink packets on ports that have STP disabled when the bridge has
* STP enabled. */
}
/* Trim off padding on payload. */
- if (ofpbuf_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
- ofpbuf_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN);
+ if (dp_packet_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
+ dp_packet_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN);
}
- if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
- stp_received_bpdu(sp, ofpbuf_data(&payload), ofpbuf_size(&payload));
+ if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
+ stp_received_bpdu(sp, dp_packet_data(&payload), dp_packet_size(&payload));
}
}
}
static void
-rstp_process_packet(const struct xport *xport, const struct ofpbuf *packet)
+rstp_process_packet(const struct xport *xport, const struct dp_packet *packet)
{
- struct ofpbuf payload = *packet;
- struct eth_header *eth = ofpbuf_data(&payload);
+ struct dp_packet payload = *packet;
+ struct eth_header *eth = dp_packet_data(&payload);
/* Sink packets on ports that have no RSTP. */
if (!xport->rstp_port) {
}
/* Trim off padding on payload. */
- if (ofpbuf_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
- ofpbuf_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN);
+ if (dp_packet_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
+ dp_packet_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN);
}
- if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
- rstp_port_received_bpdu(xport->rstp_port, ofpbuf_data(&payload),
- ofpbuf_size(&payload));
+ if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
+ rstp_port_received_bpdu(xport->rstp_port, dp_packet_data(&payload),
+ dp_packet_size(&payload));
}
}
case BV_DROP_IF_MOVED:
ovs_rwlock_rdlock(&xbridge->ml->rwlock);
mac = mac_learning_lookup(xbridge->ml, flow->dl_src, vlan);
- if (mac && mac->port.p != in_xbundle->ofbundle &&
- (!is_gratuitous_arp(flow, &ctx->xout->wc)
- || mac_entry_is_grat_arp_locked(mac))) {
+ if (mac
+ && mac_entry_get_port(xbridge->ml, mac) != in_xbundle->ofbundle
+ && (!is_gratuitous_arp(flow, &ctx->xout->wc)
+ || mac_entry_is_grat_arp_locked(mac))) {
ovs_rwlock_unlock(&xbridge->ml->rwlock);
xlate_report(ctx, "SLB bond thinks this packet looped back, "
"dropping");
}
}
- return mac->port.p != in_xbundle->ofbundle;
+ return mac_entry_get_port(ml, mac) != in_xbundle->ofbundle;
}
}
}
- if (mac->port.p != in_xbundle->ofbundle) {
+ if (mac_entry_get_port(xbridge->ml, mac) != in_xbundle->ofbundle) {
/* 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);
xbridge->name, ETH_ADDR_ARGS(flow->dl_src),
in_xbundle->name, vlan);
- mac->port.p = in_xbundle->ofbundle;
- mac_learning_changed(xbridge->ml);
+ mac_entry_set_port(xbridge->ml, mac, in_xbundle->ofbundle);
}
}
struct mcast_snooping *ms = xbridge->ms;
struct xlate_cfg *xcfg;
struct xbundle *mcast_xbundle;
- struct mcast_fport_bundle *fport;
+ struct mcast_port_bundle *fport;
/* Don't learn the OFPP_NONE port. */
if (in_xbundle == &ofpp_none_bundle) {
mcast_xbundle = NULL;
ovs_rwlock_wrlock(&ms->rwlock);
xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
- LIST_FOR_EACH(fport, fport_node, &ms->fport_list) {
+ LIST_FOR_EACH(fport, node, &ms->fport_list) {
mcast_xbundle = xbundle_lookup(xcfg, fport->port);
if (mcast_xbundle == in_xbundle) {
break;
OVS_REQ_RDLOCK(ms->rwlock)
{
struct xlate_cfg *xcfg;
- struct mcast_fport_bundle *fport;
+ struct mcast_port_bundle *fport;
struct xbundle *mcast_xbundle;
xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
- LIST_FOR_EACH(fport, fport_node, &ms->fport_list) {
+ LIST_FOR_EACH(fport, node, &ms->fport_list) {
mcast_xbundle = xbundle_lookup(xcfg, fport->port);
if (mcast_xbundle && mcast_xbundle != in_xbundle) {
xlate_report(ctx, "forwarding to mcast flood port");
}
}
+/* forward the Reports to configured ports */
+static void
+xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
+ struct mcast_snooping *ms,
+ struct xbundle *in_xbundle, uint16_t vlan)
+ OVS_REQ_RDLOCK(ms->rwlock)
+{
+ struct xlate_cfg *xcfg;
+ struct mcast_port_bundle *rport;
+ struct xbundle *mcast_xbundle;
+
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ LIST_FOR_EACH(rport, node, &ms->rport_list) {
+ mcast_xbundle = xbundle_lookup(xcfg, rport->port);
+ if (mcast_xbundle && mcast_xbundle != in_xbundle) {
+ xlate_report(ctx, "forwarding Report to mcast flagged port");
+ output_normal(ctx, mcast_xbundle, vlan);
+ } else if (!mcast_xbundle) {
+ xlate_report(ctx, "mcast port is unknown, dropping the Report");
+ } else {
+ xlate_report(ctx, "mcast port is input port, dropping the Report");
+ }
+ }
+}
+
static void
xlate_normal_flood(struct xlate_ctx *ctx, struct xbundle *in_xbundle,
uint16_t vlan)
if (mcast_snooping_is_membership(flow->tp_src)) {
ovs_rwlock_rdlock(&ms->rwlock);
xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
+ /* RFC4541: section 2.1.1, item 1: A snooping switch should
+ * forward IGMP Membership Reports only to those ports where
+ * multicast routers are attached. Alternatively stated: a
+ * snooping switch should not forward IGMP Membership Reports
+ * to ports on which only hosts are attached.
+ * An administrative control may be provided to override this
+ * restriction, allowing the report messages to be flooded to
+ * other ports. */
+ xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan);
ovs_rwlock_unlock(&ms->rwlock);
} else {
xlate_report(ctx, "multicast traffic, flooding");
} else {
ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock);
mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);
- mac_port = mac ? mac->port.p : NULL;
+ mac_port = mac ? mac_entry_get_port(ctx->xbridge->ml, mac) : NULL;
ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
if (mac_port) {
static enum slow_path_reason
process_special(struct xlate_ctx *ctx, const struct flow *flow,
- const struct xport *xport, const struct ofpbuf *packet)
+ const struct xport *xport, const struct dp_packet *packet)
{
struct flow_wildcards *wc = &ctx->xout->wc;
const struct xbridge *xbridge = ctx->xbridge;
}
static int
-xlate_flood_packet(struct xbridge *xbridge, struct ofpbuf *packet)
+xlate_flood_packet(struct xbridge *xbridge, struct dp_packet *packet)
{
struct ofpact_output output;
struct flow flow;
ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output);
/* Use OFPP_NONE as the in_port to avoid special packet processing. */
- flow_extract(packet, NULL, &flow);
+ flow_extract(packet, &flow);
flow.in_port.ofp_port = OFPP_NONE;
output.port = OFPP_FLOOD;
output.max_len = 0;
ovs_be32 ip_src, ovs_be32 ip_dst)
{
struct xbridge *xbridge = out_dev->xbridge;
- struct ofpbuf packet;
+ struct dp_packet packet;
- ofpbuf_init(&packet, 0);
+ dp_packet_init(&packet, 0);
compose_arp(&packet, eth_src, ip_src, ip_dst);
xlate_flood_packet(xbridge, &packet);
- ofpbuf_uninit(&packet);
+ dp_packet_uninit(&packet);
}
static int
struct xc_entry *entry;
entry = xlate_cache_add_entry(ctx->xin->xcache, XC_TNL_ARP);
- strncpy(entry->u.tnl_arp_cache.br_name, out_dev->xbridge->name, IFNAMSIZ);
+ ovs_strlcpy(entry->u.tnl_arp_cache.br_name, out_dev->xbridge->name,
+ sizeof entry->u.tnl_arp_cache.br_name);
entry->u.tnl_arp_cache.d_ip = d_ip;
}
err = tnl_port_build_header(xport->ofport, flow,
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 31);
memset(&flow_tnl, 0, sizeof flow_tnl);
if (!xport) {
uint16_t controller_id)
{
struct ofproto_packet_in *pin;
- struct dpif_packet *packet;
+ struct dp_packet *packet;
ctx->xout->slow |= SLOW_CONTROLLER;
if (!ctx->xin->packet) {
return;
}
- packet = dpif_packet_clone_from_ofpbuf(ctx->xin->packet);
+ packet = dp_packet_clone(ctx->xin->packet);
ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
ctx->xout->odp_actions,
ofpbuf_size(ctx->xout->odp_actions), NULL);
pin = xmalloc(sizeof *pin);
- pin->up.packet_len = ofpbuf_size(&packet->ofpbuf);
- pin->up.packet = ofpbuf_steal_data(&packet->ofpbuf);
+ pin->up.packet_len = dp_packet_size(packet);
+ pin->up.packet = dp_packet_steal_data(packet);
pin->up.reason = reason;
pin->up.table_id = ctx->table_id;
pin->up.cookie = (ctx->rule
}
}
ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin);
- dpif_packet_delete(packet);
+ dp_packet_delete(packet);
}
static void
}
ctx->exit = true;
return;
- } else if (n >= ctx->xbridge->max_mpls_depth) {
- COVERAGE_INC(xlate_actions_mpls_overflow);
- ctx->xout->slow |= SLOW_ACTION;
}
flow_push_mpls(flow, n, mpls->ethertype, wc);
int n = flow_count_mpls_labels(flow, wc);
if (flow_pop_mpls(flow, n, eth_type, wc)) {
- if (ctx->xbridge->enable_recirc && !eth_type_mpls(eth_type)) {
+ if (ctx->xbridge->enable_recirc) {
ctx->was_mpls = true;
}
} else if (n >= FLOW_MAX_MPLS_LABELS) {
break;
case OFPP_CONTROLLER:
execute_controller_action(ctx, max_len,
- ctx->in_group ? OFPR_GROUP : OFPR_ACTION, 0);
+ (ctx->in_group ? OFPR_GROUP
+ : ctx->in_action_set ? OFPR_ACTION_SET
+ : OFPR_ACTION),
+ 0);
break;
case OFPP_NONE:
break;
uint64_t action_list_stub[1024 / 64];
struct ofpbuf action_list;
+ ctx->in_action_set = true;
ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub);
ofpacts_execute_action_set(&action_list, &ctx->action_set);
do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx);
+ ctx->in_action_set = false;
ofpbuf_uninit(&action_list);
}
static bool
-ofpact_needs_recirculation_after_mpls(const struct xlate_ctx *ctx,
- const struct ofpact *a)
+ofpact_needs_recirculation_after_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
{
struct flow_wildcards *wc = &ctx->xout->wc;
struct flow *flow = &ctx->xin->flow;
+ if (!ctx->was_mpls) {
+ return false;
+ }
+
switch (a->type) {
case OFPACT_OUTPUT:
case OFPACT_GROUP:
case OFPACT_SET_TUNNEL:
case OFPACT_SET_QUEUE:
case OFPACT_POP_QUEUE:
- case OFPACT_POP_MPLS:
- case OFPACT_DEC_MPLS_TTL:
- case OFPACT_SET_MPLS_TTL:
- case OFPACT_SET_MPLS_TC:
- case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_CONJUNCTION:
case OFPACT_NOTE:
case OFPACT_OUTPUT_REG:
case OFPACT_EXIT:
case OFPACT_SAMPLE:
return false;
+ case OFPACT_POP_MPLS:
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_SET_MPLS_TC:
+ case OFPACT_SET_MPLS_LABEL:
case OFPACT_SET_IPV4_SRC:
case OFPACT_SET_IPV4_DST:
case OFPACT_SET_IP_DSCP:
break;
}
- if (ctx->was_mpls && ofpact_needs_recirculation_after_mpls(ctx, a)) {
+ if (ofpact_needs_recirculation_after_mpls(a, ctx)) {
compose_recirculate_action(ctx, ofpacts, a, ofpacts_len);
return;
}
xlate_learn_action(ctx, ofpact_get_LEARN(a));
break;
+ case OFPACT_CONJUNCTION: {
+ /* A flow with a "conjunction" action represents part of a special
+ * kind of "set membership match". Such a flow should not actually
+ * get executed, but it could via, say, a "packet-out", even though
+ * that wouldn't be useful. Log it to help debugging. */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_INFO_RL(&rl, "executing no-op conjunction action");
+ break;
+ }
+
case OFPACT_EXIT:
ctx->exit = true;
break;
xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto,
const struct flow *flow, ofp_port_t in_port,
struct rule_dpif *rule, uint16_t tcp_flags,
- const struct ofpbuf *packet)
+ const struct dp_packet *packet)
{
xin->ofproto = ofproto;
xin->flow = *flow;
ctx.recurse = 0;
ctx.resubmits = 0;
ctx.in_group = false;
+ ctx.in_action_set = false;
ctx.orig_skb_priority = flow->skb_priority;
ctx.table_id = 0;
ctx.exit = false;
* May modify 'packet'.
* Returns 0 if successful, otherwise a positive errno value. */
int
-xlate_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
+xlate_send_packet(const struct ofport_dpif *ofport, struct dp_packet *packet)
{
struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
struct xport *xport;
ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output);
/* Use OFPP_NONE as the in_port to avoid special packet processing. */
- flow_extract(packet, NULL, &flow);
+ flow_extract(packet, &flow);
flow.in_port.ofp_port = OFPP_NONE;
xport = xport_lookup(xcfg, ofport);