[ovs-dev] [PATCH 2/6] ofproto: Translate NXAST_SAMPLE actions into SAMPLE "flow_sample" dp actions
Romain Lenglet
rlenglet at vmware.com
Thu Apr 18 02:19:22 UTC 2013
The "flow_sample" upcall is is used to sample packets on a per-flow
basis.
Make the userspace action's userdata size variable depending on the
union member used.
Signed-off-by: Romain Lenglet <rlenglet at vmware.com>
---
FAQ | 7 ++-
lib/odp-util.c | 107 ++++++++++++++++++++++++---------------
lib/odp-util.h | 13 ++++-
ofproto/ofproto-dpif.c | 132 +++++++++++++++++++++++++++++++++++++------------
tests/odp.at | 2 +-
5 files changed, 184 insertions(+), 77 deletions(-)
diff --git a/FAQ b/FAQ
index 767f2f7..38c6346 100644
--- a/FAQ
+++ b/FAQ
@@ -154,6 +154,11 @@ A: The following table lists the Linux kernel versions against which the
It should build against almost any kernel, certainly against 2.6.18
and later.
+Q: What Linux kernel versions does IPFIX flow monitoring work with?
+
+A: IPFIX flow monitoring requires the Linux kernel module from Open
+ vSwitch version 1.10.90 or later.
+
Q: Should userspace or kernel be upgraded first to minimize downtime?
In general, the Open vSwitch userspace should be used with the
@@ -187,7 +192,7 @@ Q: What features are not available when using the userspace datapath?
A: Tunnel virtual ports are not supported, as described in the
previous answer. It is also not possible to use queue-related
actions. On Linux kernels before 2.6.39, maximum-sized VLAN packets
- may not be transmitted.
+ may not be transmitted. IPFIX is not supported.
Terminology
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 751c1c9..52923fc 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -262,43 +262,57 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA];
- if (userdata_attr && nl_attr_get_size(userdata_attr) == sizeof(uint64_t)) {
- uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
- union user_action_cookie cookie;
-
- memcpy(&cookie, &userdata, sizeof cookie);
- switch (cookie.type) {
- case USER_ACTION_COOKIE_SFLOW:
- ds_put_format(ds, ",sFlow("
- "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
- vlan_tci_to_vid(cookie.sflow.vlan_tci),
- vlan_tci_to_pcp(cookie.sflow.vlan_tci),
- cookie.sflow.output);
- break;
-
- case USER_ACTION_COOKIE_SLOW_PATH:
- ds_put_cstr(ds, ",slow_path(");
- format_flags(ds, slow_path_reason_to_string,
- cookie.slow_path.reason, ',');
- ds_put_format(ds, ")");
- break;
+ if (userdata_attr) {
+ const uint8_t *userdata = nl_attr_get(userdata_attr);
+ size_t userdata_len = nl_attr_get_size(userdata_attr);
+ bool userdata_unspec = true;
+ union user_action_cookie cookie;
- case USER_ACTION_COOKIE_UNSPEC:
- default:
- ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
- break;
+ if (userdata_len >= sizeof cookie.type
+ && userdata_len <= sizeof cookie) {
+
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, userdata, userdata_len);
+
+ userdata_unspec = false;
+
+ if (userdata_len == sizeof cookie.sflow
+ && cookie.type == USER_ACTION_COOKIE_SFLOW) {
+ ds_put_format(ds, ",sFlow("
+ "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
+ vlan_tci_to_vid(cookie.sflow.vlan_tci),
+ vlan_tci_to_pcp(cookie.sflow.vlan_tci),
+ cookie.sflow.output);
+ } else if (userdata_len == sizeof cookie.slow_path
+ && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
+ ds_put_cstr(ds, ",slow_path(");
+ format_flags(ds, slow_path_reason_to_string,
+ cookie.slow_path.reason, ',');
+ ds_put_format(ds, ")");
+ } else if (userdata_len == sizeof cookie.flow_sample
+ && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
+ ds_put_format(ds, ",flow_sample(probability=%"PRIu16
+ ",collector_set_id=%"PRIu32
+ ",obs_domain_id=%"PRIu32
+ ",obs_point_id=%"PRIu32")",
+ cookie.flow_sample.probability,
+ cookie.flow_sample.collector_set_id,
+ cookie.flow_sample.obs_domain_id,
+ cookie.flow_sample.obs_point_id);
+ } else {
+ userdata_unspec = true;
+ }
}
- } else if (userdata_attr) {
- const uint8_t *userdata = nl_attr_get(userdata_attr);
- size_t len = nl_attr_get_size(userdata_attr);
- size_t i;
- ds_put_format(ds, ",userdata(");
- for (i = 0; i < len; i++) {
- ds_put_format(ds, "%02x", userdata[i]);
+ if (userdata_unspec) {
+ size_t i;
+ ds_put_format(ds, ",userdata(");
+ for (i = 0; i < userdata_len; i++) {
+ ds_put_format(ds, "%02x", userdata[i]);
+ }
+ ds_put_char(ds, ')');
}
- ds_put_char(ds, ')');
}
ds_put_char(ds, ')');
@@ -456,7 +470,10 @@ parse_odp_action(const char *s, const struct simap *port_names,
{
unsigned long long int pid;
unsigned long long int output;
- char userdata_s[32];
+ unsigned long long int probability;
+ unsigned long long int collector_set_id;
+ unsigned long long int obs_domain_id;
+ unsigned long long int obs_point_id;
int vid, pcp;
int n = -1;
@@ -477,7 +494,8 @@ parse_odp_action(const char *s, const struct simap *port_names,
cookie.type = USER_ACTION_COOKIE_SFLOW;
cookie.sflow.vlan_tci = htons(tci);
cookie.sflow.output = output;
- odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie.sflow,
+ actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
&& n > 0) {
@@ -499,15 +517,22 @@ parse_odp_action(const char *s, const struct simap *port_names,
}
n++;
- odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path,
+ actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,userdata="
- "%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
- &n) > 0 && n > 0) {
- uint64_t userdata;
+ } else if (sscanf(s, "userspace(pid=%lli,flow_sample(probability=%lli,"
+ "collector_set_id=%lli,obs_domain_id=%lli,"
+ "obs_point_id=%lli))%n",
+ &pid, &probability, &collector_set_id,
+ &obs_domain_id, &obs_point_id, &n) > 0 && n > 0) {
+ union user_action_cookie cookie;
- userdata = strtoull(userdata_s, NULL, 0);
- odp_put_userspace_action(pid, &userdata, sizeof(userdata),
+ cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE;
+ cookie.flow_sample.probability = probability;
+ cookie.flow_sample.collector_set_id = collector_set_id;
+ cookie.flow_sample.obs_domain_id = obs_domain_id;
+ cookie.flow_sample.obs_point_id = obs_point_id;
+ odp_put_userspace_action(pid, &cookie, sizeof cookie.flow_sample,
actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,userdata(%n", &pid, &n) > 0
diff --git a/lib/odp-util.h b/lib/odp-util.h
index ad0fb30..0198a54 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -128,7 +128,8 @@ void commit_odp_actions(const struct flow *, struct flow *base,
enum user_action_cookie_type {
USER_ACTION_COOKIE_UNSPEC,
USER_ACTION_COOKIE_SFLOW, /* Packet for sFlow sampling. */
- USER_ACTION_COOKIE_SLOW_PATH /* Userspace must process this flow. */
+ USER_ACTION_COOKIE_SLOW_PATH, /* Userspace must process this flow. */
+ USER_ACTION_COOKIE_FLOW_SAMPLE, /* Packet for per-flow sampling. */
};
/* user_action_cookie is passed as argument to OVS_ACTION_ATTR_USERSPACE.
@@ -147,8 +148,16 @@ union user_action_cookie {
uint16_t unused;
uint32_t reason; /* enum slow_path_reason. */
} slow_path;
+
+ struct {
+ uint16_t type; /* USER_ACTION_COOKIE_FLOW_SAMPLE. */
+ uint16_t probability; /* Sampling probability. */
+ uint32_t collector_set_id; /* ID of IPFIX collector set. */
+ uint32_t obs_domain_id; /* Observation Domain ID. */
+ uint32_t obs_point_id; /* Observation Point ID. */
+ } flow_sample;
};
-BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 8);
+BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 16);
size_t odp_put_userspace_action(uint32_t pid,
const void *userdata, size_t userdata_size,
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index d0ce338..8708d0f 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -325,7 +325,8 @@ static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
static size_t put_userspace_action(const struct ofproto_dpif *,
struct ofpbuf *odp_actions,
const struct flow *,
- const union user_action_cookie *);
+ const union user_action_cookie *,
+ const size_t);
static void compose_slow_path(const struct ofproto_dpif *, const struct flow *,
enum slow_path_reason,
@@ -4006,9 +4007,10 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
hmap_destroy(&todo);
}
-static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL }
+static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL, FLOW_SAMPLE_UPCALL }
classify_upcall(const struct dpif_upcall *upcall)
{
+ size_t userdata_len;
union user_action_cookie cookie;
/* First look at the upcall type. */
@@ -4030,23 +4032,27 @@ classify_upcall(const struct dpif_upcall *upcall)
VLOG_WARN_RL(&rl, "action upcall missing cookie");
return BAD_UPCALL;
}
- if (nl_attr_get_size(upcall->userdata) != sizeof(cookie)) {
+ userdata_len = nl_attr_get_size(upcall->userdata);
+ if (userdata_len < sizeof cookie.type
+ || userdata_len > sizeof cookie) {
VLOG_WARN_RL(&rl, "action upcall cookie has unexpected size %zu",
- nl_attr_get_size(upcall->userdata));
+ userdata_len);
return BAD_UPCALL;
}
- memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
- switch (cookie.type) {
- case USER_ACTION_COOKIE_SFLOW:
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(upcall->userdata), userdata_len);
+ if (userdata_len == sizeof cookie.sflow
+ && cookie.type == USER_ACTION_COOKIE_SFLOW) {
return SFLOW_UPCALL;
-
- case USER_ACTION_COOKIE_SLOW_PATH:
+ } else if (userdata_len == sizeof cookie.slow_path
+ && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
return MISS_UPCALL;
-
- case USER_ACTION_COOKIE_UNSPEC:
- default:
- VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64,
- nl_attr_get_u64(upcall->userdata));
+ } else if (userdata_len == sizeof cookie.flow_sample
+ && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
+ return FLOW_SAMPLE_UPCALL;
+ } else {
+ VLOG_WARN_RL(&rl, "invalid user cookie of type %"PRIu16
+ " and size %zu", cookie.type, userdata_len);
return BAD_UPCALL;
}
}
@@ -4066,11 +4072,19 @@ handle_sflow_upcall(struct dpif_backer *backer,
return;
}
- memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.sflow);
dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
odp_in_port, &cookie);
}
+static void
+handle_flow_sample_upcall(struct dpif_backer *backer OVS_UNUSED,
+ const struct dpif_upcall *upcall OVS_UNUSED)
+{
+ /* TODO: Send a IPFIX flow record to each IPFIX collector. */
+}
+
static int
handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
{
@@ -4108,6 +4122,11 @@ handle_upcalls(struct dpif_backer *backer, unsigned int max_batch)
ofpbuf_uninit(buf);
break;
+ case FLOW_SAMPLE_UPCALL:
+ handle_flow_sample_upcall(backer, upcall);
+ ofpbuf_uninit(buf);
+ break;
+
case BAD_UPCALL:
ofpbuf_uninit(buf);
break;
@@ -5796,9 +5815,10 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
ofpbuf_use_stack(&buf, stub, stub_size);
if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
- odp_put_userspace_action(pid, &cookie, sizeof cookie, &buf);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, &buf);
} else {
- put_userspace_action(ofproto, &buf, flow, &cookie);
+ put_userspace_action(ofproto, &buf, flow, &cookie,
+ sizeof cookie.slow_path);
}
*actionsp = buf.data;
*actions_lenp = buf.size;
@@ -5808,14 +5828,43 @@ static size_t
put_userspace_action(const struct ofproto_dpif *ofproto,
struct ofpbuf *odp_actions,
const struct flow *flow,
- const union user_action_cookie *cookie)
+ const union user_action_cookie *cookie,
+ const size_t cookie_size)
{
uint32_t pid;
pid = dpif_port_get_pid(ofproto->backer->dpif,
ofp_port_to_odp_port(ofproto, flow->in_port));
- return odp_put_userspace_action(pid, cookie, sizeof *cookie, odp_actions);
+ return odp_put_userspace_action(pid, cookie, cookie_size, odp_actions);
+}
+
+/* Compose SAMPLE action for sFlow or IPFIX. The given probability is
+ * the number of packets out of UINT32_MAX to sample. The given
+ * cookie is passed back in the callback for each sampled packet.
+ */
+static size_t
+compose_sample_action(const struct ofproto_dpif *ofproto,
+ struct ofpbuf *odp_actions,
+ const struct flow *flow,
+ const uint32_t probability,
+ const union user_action_cookie *cookie,
+ const size_t cookie_size)
+{
+ size_t sample_offset, actions_offset;
+ int cookie_offset;
+
+ sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
+
+ nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
+
+ actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
+ cookie_offset = put_userspace_action(ofproto, odp_actions, flow, cookie,
+ cookie_size);
+
+ nl_msg_end_nested(odp_actions, actions_offset);
+ nl_msg_end_nested(odp_actions, sample_offset);
+ return cookie_offset;
}
static void
@@ -5859,27 +5908,29 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
{
uint32_t probability;
union user_action_cookie cookie;
- size_t sample_offset, actions_offset;
- int cookie_offset;
if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
return 0;
}
- sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
-
- /* Number of packets out of UINT_MAX to sample. */
probability = dpif_sflow_get_probability(ofproto->sflow);
- nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
-
- actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
compose_sflow_cookie(ofproto, htons(0), odp_port,
odp_port == OVSP_NONE ? 0 : 1, &cookie);
- cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie);
- nl_msg_end_nested(odp_actions, actions_offset);
- nl_msg_end_nested(odp_actions, sample_offset);
- return cookie_offset;
+ return compose_sample_action(ofproto, odp_actions, flow, probability,
+ &cookie, sizeof cookie.sflow);
+}
+
+static void
+compose_flow_sample_cookie(uint16_t probability, uint32_t collector_set_id,
+ uint32_t obs_domain_id, uint32_t obs_point_id,
+ union user_action_cookie *cookie)
+{
+ cookie->type = USER_ACTION_COOKIE_FLOW_SAMPLE;
+ cookie->flow_sample.probability = probability;
+ cookie->flow_sample.collector_set_id = collector_set_id;
+ cookie->flow_sample.obs_domain_id = obs_domain_id;
+ cookie->flow_sample.obs_point_id = obs_point_id;
}
/* SAMPLE action must be first action in any given list of actions.
@@ -6527,6 +6578,23 @@ xlate_fin_timeout(struct action_xlate_ctx *ctx,
}
}
+static void
+xlate_sample_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_sample *os)
+{
+ union user_action_cookie cookie;
+ /* Scale the probability from 16-bit to 32-bit while representing
+ * the same percentage. */
+ uint32_t probability = (os->probability << 16) | os->probability;
+
+ commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
+
+ compose_flow_sample_cookie(os->probability, os->collector_set_id,
+ os->obs_domain_id, os->obs_point_id, &cookie);
+ compose_sample_action(ctx->ofproto, ctx->odp_actions, &ctx->flow,
+ probability, &cookie, sizeof cookie.flow_sample);
+}
+
static bool
may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
{
@@ -6809,7 +6877,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
}
case OFPACT_SAMPLE:
- /* TODO: Actually implement the translation here. */
+ xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
break;
}
}
diff --git a/tests/odp.at b/tests/odp.at
index 95cfba8..ec99ec5 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -88,8 +88,8 @@ userspace(pid=6633,sFlow(vid=9,pcp=7,output=10))
userspace(pid=9765,slow_path())
userspace(pid=9765,slow_path(cfm))
userspace(pid=9765,slow_path(cfm,match))
-userspace(pid=9123,userdata=0x815309)
userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f))
+userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456))
set(in_port(2))
set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))
set(eth_type(0x1234))
--
1.8.1.3
More information about the dev
mailing list