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 <[email protected]> --- 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 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
