Skip to content

Commit c06bba0

Browse files
author
Justin Pettit
committed
mirroring: Don't require the "normal" action to perform mirroring.
Previously, mirrors only worked when using the "normal" action. This commit performs mirroring even when mirroring is not used. It also adds some unit tests.
1 parent 9d24de3 commit c06bba0

5 files changed

Lines changed: 360 additions & 53 deletions

File tree

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ post-v1.3.0
1212
- Added ability to modify ECN bits in IPv4.
1313
- Added ability to modify TTL in IPv4.
1414
- ovs-vswitchd:
15+
- Don't require the "normal" action to use mirrors. Traffic will
16+
now be properly mirrored for any flows, regardless of their
17+
actions.
1518
- Track packet and byte statistics sent on mirrors.
1619
- ovs-appctl:
1720
- New "fdb/flush" command to flush bridge's MAC learning table.

ofproto/ofproto-dpif.c

Lines changed: 75 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@ static void update_mirror_stats(struct ofproto_dpif *ofproto,
145145
mirror_mask_t mirrors,
146146
uint64_t packets, uint64_t bytes);
147147

148-
/* A group of one or more OpenFlow ports. */
149-
#define OFBUNDLE_FLOOD ((struct ofbundle *) 1)
150148
struct ofbundle {
151149
struct ofproto_dpif *ofproto; /* Owning ofproto. */
152150
struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
@@ -178,6 +176,8 @@ static void bundle_destroy(struct ofbundle *);
178176
static void bundle_del_port(struct ofport_dpif *);
179177
static void bundle_run(struct ofbundle *);
180178
static void bundle_wait(struct ofbundle *);
179+
static struct ofport_dpif *lookup_input_bundle(struct ofproto_dpif *,
180+
uint16_t in_port, bool warn);
181181

182182
static void stp_run(struct ofproto_dpif *ofproto);
183183
static void stp_wait(struct ofproto_dpif *ofproto);
@@ -559,6 +559,8 @@ static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet);
559559
static size_t
560560
compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions,
561561
const struct flow *, uint32_t odp_port);
562+
static void add_mirror_actions(struct action_xlate_ctx *ctx,
563+
const struct flow *flow);
562564
/* Global variables. */
563565
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
564566

@@ -4723,6 +4725,8 @@ static struct ofpbuf *
47234725
xlate_actions(struct action_xlate_ctx *ctx,
47244726
const union ofp_action *in, size_t n_in)
47254727
{
4728+
struct flow orig_flow = ctx->flow;
4729+
47264730
COVERAGE_INC(ofproto_dpif_xlate);
47274731

47284732
ctx->odp_actions = ofpbuf_new(512);
@@ -4775,6 +4779,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
47754779
compose_output_action(ctx, OFPP_LOCAL);
47764780
}
47774781
}
4782+
add_mirror_actions(ctx, &orig_flow);
47784783
fix_sflow_action(ctx);
47794784
}
47804785

@@ -4951,34 +4956,6 @@ ofbundle_get_a_port(const struct ofbundle *bundle)
49514956
struct ofport_dpif, bundle_node);
49524957
}
49534958

4954-
static mirror_mask_t
4955-
compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan,
4956-
const struct ofbundle *in_bundle,
4957-
const struct ofbundle *out_bundle)
4958-
{
4959-
mirror_mask_t dst_mirrors = 0;
4960-
4961-
if (out_bundle == OFBUNDLE_FLOOD) {
4962-
struct ofbundle *bundle;
4963-
4964-
HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
4965-
if (bundle != in_bundle
4966-
&& ofbundle_includes_vlan(bundle, vlan)
4967-
&& bundle->floodable
4968-
&& !bundle->mirror_out) {
4969-
output_normal(ctx, bundle, vlan);
4970-
dst_mirrors |= bundle->dst_mirrors;
4971-
}
4972-
}
4973-
ctx->nf_output_iface = NF_OUT_FLOOD;
4974-
} else if (out_bundle) {
4975-
output_normal(ctx, out_bundle, vlan);
4976-
dst_mirrors = out_bundle->dst_mirrors;
4977-
}
4978-
4979-
return dst_mirrors;
4980-
}
4981-
49824959
static bool
49834960
vlan_is_mirrored(const struct ofmirror *m, int vlan)
49844961
{
@@ -5027,18 +5004,68 @@ eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN])
50275004
}
50285005

50295006
static void
5030-
output_mirrors(struct action_xlate_ctx *ctx,
5031-
uint16_t vlan, const struct ofbundle *in_bundle,
5032-
mirror_mask_t dst_mirrors)
5007+
add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
50335008
{
50345009
struct ofproto_dpif *ofproto = ctx->ofproto;
50355010
mirror_mask_t mirrors;
5011+
struct ofport_dpif *in_port;
5012+
struct ofbundle *in_bundle;
5013+
uint16_t vlan;
5014+
uint16_t vid;
5015+
const struct nlattr *a;
5016+
size_t left;
5017+
5018+
/* Obtain in_port from orig_flow.in_port.
5019+
*
5020+
* lookup_input_bundle() also ensures that in_port belongs to a bundle. */
5021+
in_port = lookup_input_bundle(ctx->ofproto, orig_flow->in_port,
5022+
ctx->packet != NULL);
5023+
if (!in_port) {
5024+
return;
5025+
}
5026+
in_bundle = in_port->bundle;
5027+
mirrors = in_bundle->src_mirrors;
5028+
5029+
/* Drop frames on bundles reserved for mirroring. */
5030+
if (in_bundle->mirror_out) {
5031+
if (ctx->packet != NULL) {
5032+
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
5033+
VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
5034+
"%s, which is reserved exclusively for mirroring",
5035+
ctx->ofproto->up.name, in_bundle->name);
5036+
}
5037+
return;
5038+
}
5039+
5040+
/* Check VLAN. */
5041+
vid = vlan_tci_to_vid(orig_flow->vlan_tci);
5042+
if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
5043+
return;
5044+
}
5045+
vlan = input_vid_to_vlan(in_bundle, vid);
5046+
5047+
/* Look at the output ports to check for destination selections. */
5048+
5049+
NL_ATTR_FOR_EACH (a, left, ctx->odp_actions->data,
5050+
ctx->odp_actions->size) {
5051+
enum ovs_action_attr type = nl_attr_type(a);
5052+
struct ofport_dpif *ofport;
5053+
5054+
if (type != OVS_ACTION_ATTR_OUTPUT) {
5055+
continue;
5056+
}
5057+
5058+
ofport = get_odp_port(ofproto, nl_attr_get_u32(a));
5059+
mirrors |= ofport ? ofport->bundle->dst_mirrors : 0;
5060+
}
50365061

5037-
mirrors = in_bundle->src_mirrors | dst_mirrors;
50385062
if (!mirrors) {
50395063
return;
50405064
}
50415065

5066+
/* Restore the original packet before adding the mirror actions. */
5067+
ctx->flow = *orig_flow;
5068+
50425069
while (mirrors) {
50435070
struct ofmirror *m;
50445071

@@ -5053,7 +5080,7 @@ output_mirrors(struct action_xlate_ctx *ctx,
50535080
ctx->mirrors |= m->dup_mirrors;
50545081
if (m->out) {
50555082
output_normal(ctx, m->out, vlan);
5056-
} else if (eth_dst_may_rspan(ctx->flow.dl_dst)
5083+
} else if (eth_dst_may_rspan(orig_flow->dl_dst)
50575084
&& vlan != m->out_vlan) {
50585085
struct ofbundle *bundle;
50595086

@@ -5229,10 +5256,8 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
52295256
static void
52305257
xlate_normal(struct action_xlate_ctx *ctx)
52315258
{
5232-
mirror_mask_t dst_mirrors = 0;
52335259
struct ofport_dpif *in_port;
52345260
struct ofbundle *in_bundle;
5235-
struct ofbundle *out_bundle;
52365261
struct mac_entry *mac;
52375262
uint16_t vlan;
52385263
uint16_t vid;
@@ -5281,7 +5306,6 @@ xlate_normal(struct action_xlate_ctx *ctx)
52815306

52825307
/* Check other admissibility requirements. */
52835308
if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
5284-
output_mirrors(ctx, vlan, in_bundle, 0);
52855309
return;
52865310
}
52875311

@@ -5294,7 +5318,9 @@ xlate_normal(struct action_xlate_ctx *ctx)
52945318
mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
52955319
&ctx->tags);
52965320
if (mac) {
5297-
out_bundle = mac->port.p;
5321+
if (mac->port.p != in_bundle) {
5322+
output_normal(ctx, mac->port.p, vlan);
5323+
}
52985324
} else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) {
52995325
/* If we are revalidating but don't have a learning entry then eject
53005326
* the flow. Installing a flow that floods packets opens up a window
@@ -5304,14 +5330,18 @@ xlate_normal(struct action_xlate_ctx *ctx)
53045330
ctx->may_set_up_flow = false;
53055331
return;
53065332
} else {
5307-
out_bundle = OFBUNDLE_FLOOD;
5308-
}
5333+
struct ofbundle *bundle;
53095334

5310-
/* Don't send packets out their input bundles. */
5311-
if (in_bundle != out_bundle) {
5312-
dst_mirrors = compose_dsts(ctx, vlan, in_bundle, out_bundle);
5335+
HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
5336+
if (bundle != in_bundle
5337+
&& ofbundle_includes_vlan(bundle, vlan)
5338+
&& bundle->floodable
5339+
&& !bundle->mirror_out) {
5340+
output_normal(ctx, bundle, vlan);
5341+
}
5342+
}
5343+
ctx->nf_output_iface = NF_OUT_FLOOD;
53135344
}
5314-
output_mirrors(ctx, vlan, in_bundle, dst_mirrors);
53155345
}
53165346

53175347
/* Optimized flow revalidation.

ofproto/ofproto-provider.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,10 +1020,10 @@ struct ofproto_class {
10201020
* 'ofproto' associated with client data pointer 'aux'. If no such mirror
10211021
* has been registered, this has no effect.
10221022
*
1023-
* This function affects only the behavior of the OFPP_NORMAL action. An
1024-
* implementation that does not support it at all may set it to NULL or
1025-
* return EOPNOTSUPP. An implementation that supports only a subset of the
1026-
* functionality should implement what it can and return 0. */
1023+
* An implementation that does not support mirroring at all may set
1024+
* it to NULL or return EOPNOTSUPP. An implementation that supports
1025+
* only a subset of the functionality should implement what it can
1026+
* and return 0. */
10271027
int (*mirror_set)(struct ofproto *ofproto, void *aux,
10281028
const struct ofproto_mirror_settings *s);
10291029

ofproto/ofproto.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -724,10 +724,7 @@ ofproto_bundle_unregister(struct ofproto *ofproto, void *aux)
724724

725725
/* Registers a mirror associated with client data pointer 'aux' in 'ofproto'.
726726
* If 'aux' is already registered then this function updates its configuration
727-
* to 's'. Otherwise, this function registers a new mirror.
728-
*
729-
* Mirrors affect only the treatment of packets output to the OFPP_NORMAL
730-
* port. */
727+
* to 's'. Otherwise, this function registers a new mirror. */
731728
int
732729
ofproto_mirror_register(struct ofproto *ofproto, void *aux,
733730
const struct ofproto_mirror_settings *s)

0 commit comments

Comments
 (0)