Message ID | 20240712170618.1903024-14-amorenoz@redhat.com |
---|---|
State | Superseded |
Headers | show |
Series | Introduce local sampling with NXAST_SAMPLE action. | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
ovsrobot/intel-ovs-compilation | fail | test: fail |
On 7/12/24 19:06, Adrian Moreno wrote: > When sample action gets used as a way of sampling traffic with > controller-generated metadata (i.e: obs_domain_id and obs_point_id), > the controller will have to increase the number of flows to ensure each > part of the pipeline contains the right metadata. > > As an example, if the controller decides to sample stateful traffic, it > could store the computed metadata for each connection in the conntrack > label. However, for established connections, a flow must be created for > each different ct_label value with a sample action that contains a > different hardcoded obs_domain and obs_point id. > > This patch adds a new version of the NXAST_RAW_SAMPLE* action (number 4) > that supports specifying the observation point and domain using an > OpenFlow field reference, so now the controller can express: > > sample(... > obs_domain_id=NXM_NX_CT_LABEL[0..31], > obs_point_id=NXM_NX_CT_LABEL[32..63] > ... > ) > > Signed-off-by: Adrian Moreno <amorenoz@redhat.com> > --- > Documentation/ref/ovs-actions.7.rst | 12 +- > NEWS | 2 + > include/openvswitch/ofp-actions.h | 8 +- > lib/ofp-actions.c | 245 +++++++++++++++++++++++++--- > ofproto/ofproto-dpif-xlate.c | 44 ++++- > python/ovs/flow/ofp.py | 8 +- > python/ovs/flow/ofp_act.py | 6 +- > tests/ofp-actions.at | 8 + > tests/ofproto-dpif.at | 44 +++++ > tests/ovs-ofctl.at | 14 ++ > tests/system-traffic.at | 92 +++++++++++ > 11 files changed, 442 insertions(+), 41 deletions(-) > > diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst > index 80acd9070..01021dc0a 100644 > --- a/Documentation/ref/ovs-actions.7.rst > +++ b/Documentation/ref/ovs-actions.7.rst > @@ -2201,13 +2201,17 @@ The following *argument* forms are accepted: > The unsigned 32-bit integer identifier of the set of sample collectors to > send sampled packets to. Defaults to 0. > > - ``obs_domain_id=``\ *id* > + ``obs_domain_id=``\ *value* > When sending samples to IPFIX collectors, the unsigned 32-bit integer > - Observation Domain ID sent in every IPFIX flow record. Defaults to 0. > + Observation Domain ID sent in every IPFIX flow record. The *value* may > + be specified as a 32-bit integer or a field or subfield in the syntax > + described under `Field Specifications`_ above. Defaults to 0. > > - ``obs_point_id=``\ *id* > + ``obs_point_id=``\ *value* > When sending samples to IPFIX collectors, the unsigned 32-bit integer > - Observation Point ID sent in every IPFIX flow record. Defaults to 0. > + Observation Point ID sent in every IPFIX flow record. The *value* may > + be specified as a 32-bit integer or a field or subfield in the syntax > + described under `Field Specifications`_ above. Defaults to 0. > Add a note to the Conformance section below that support ofr fields and subfields in observation ids was added in 3.4. > ``sampling_port=``\ *port* > Sample packets on *port*, which should be the ingress or egress port. This > diff --git a/NEWS b/NEWS > index 4eb73aada..cac844063 100644 > --- a/NEWS > +++ b/NEWS > @@ -45,6 +45,8 @@ Post-v3.3.0 > Open_vSwitch table controls whether an explicit drop action shall be > added at the end of datapath flows whose last action is an > observability-driven sample action. > + - OpenFlow: a new version of the "sample" action is introduced that allows > + the use of subfields in obs_point_id and obs_domain_id. make it: + - OpenFlow: + * A new version of the "sample" action (NXAST_SAMPLE4) is introduced + that allows use of subfields in 'obs_point_id' and 'obs_domain_id'. > > > v3.3.0 - 16 Feb 2024 > diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h > index 7b57e49ad..56dc2c147 100644 > --- a/include/openvswitch/ofp-actions.h > +++ b/include/openvswitch/ofp-actions.h > @@ -1015,14 +1015,16 @@ enum nx_action_sample_direction { > > /* OFPACT_SAMPLE. > * > - * Used for NXAST_SAMPLE, NXAST_SAMPLE2, and NXAST_SAMPLE3. */ > + * Used for NXAST_SAMPLE, NXAST_SAMPLE2, NXAST_SAMPLE3 and NXAST_SAMPLE4. */ > struct ofpact_sample { > OFPACT_PADDED_MEMBERS( > struct ofpact ofpact; > uint16_t probability; /* Always positive. */ > uint32_t collector_set_id; > - uint32_t obs_domain_id; > - uint32_t obs_point_id; > + uint32_t obs_domain_imm; > + struct mf_subfield obs_domain_src; > + uint32_t obs_point_imm; > + struct mf_subfield obs_point_src; > ofp_port_t sampling_port; > enum nx_action_sample_direction direction; > ); > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c > index da7b1dd31..6b030bf29 100644 > --- a/lib/ofp-actions.c > +++ b/lib/ofp-actions.c > @@ -330,6 +330,8 @@ enum ofp_raw_action_type { > NXAST_RAW_SAMPLE2, > /* NX1.0+(41): struct nx_action_sample2. */ > NXAST_RAW_SAMPLE3, > + /* NX1.0+(51): struct nx_action_sample4. VLMFF */ > + NXAST_RAW_SAMPLE4, > > /* NX1.0+(34): struct nx_action_conjunction. */ > NXAST_RAW_CONJUNCTION, > @@ -6188,6 +6190,34 @@ struct nx_action_sample2 { > }; > OFP_ASSERT(sizeof(struct nx_action_sample2) == 32); > > +/* Action structure for NXAST_SAMPLE4 > + * > + * NXAST_SAMPLE4 was added in Open vSwitch 3.4.0. Compared to NXAST_SAMPLE3, > + * it adds support for using field specifiers for observation_domain_id and > + * observation_point_id. */ > +struct nx_action_sample4 { > + ovs_be16 type; /* OFPAT_VENDOR. */ > + ovs_be16 len; /* Length is 40. */ > + ovs_be32 vendor; /* NX_VENDOR_ID. */ > + ovs_be16 subtype; /* NXAST_SAMPLE4. */ > + ovs_be16 probability; /* Fraction of packets to sample. */ > + ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ > + ovs_be32 obs_domain_src; /* The observation_domain_id source. */ > + union { > + ovs_be16 obs_domain_ofs_nbits; /* Range to use from source field. */ > + ovs_be32 obs_domain_imm; /* Immediate value for domain id. */ > + }; > + ovs_be32 obs_point_src; /* The observation_point_id source. */ > + union { > + ovs_be16 obs_point_ofs_nbits; /* Range to use from source field. */ > + ovs_be32 obs_point_imm; /* Immediate value for point id. */ > + }; > + ovs_be16 sampling_port; /* Sampling port. */ > + uint8_t direction; /* Sampling direction. */ > + uint8_t zeros[5]; /* Pad to a multiple of 8 bytes */ > + }; > + OFP_ASSERT(sizeof(struct nx_action_sample4) == 40); > + > static enum ofperr > decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, > enum ofp_version ofp_version OVS_UNUSED, > @@ -6199,11 +6229,14 @@ decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, > sample->ofpact.raw = NXAST_RAW_SAMPLE; > sample->probability = ntohs(nas->probability); > sample->collector_set_id = ntohl(nas->collector_set_id); > - sample->obs_domain_id = ntohl(nas->obs_domain_id); > - sample->obs_point_id = ntohl(nas->obs_point_id); > + sample->obs_domain_imm = ntohl(nas->obs_domain_id); > + sample->obs_domain_src.field = NULL; > + sample->obs_point_imm = ntohl(nas->obs_point_id); > + sample->obs_point_src.field = NULL; > sample->sampling_port = OFPP_NONE; > sample->direction = NX_ACTION_SAMPLE_DEFAULT; > - > + sample->obs_domain_src.field = NULL; > + sample->obs_point_src.field = NULL; > if (sample->probability == 0) { > return OFPERR_OFPBAC_BAD_ARGUMENT; > } > @@ -6220,8 +6253,10 @@ decode_SAMPLE2(const struct nx_action_sample2 *nas, > sample->ofpact.raw = raw; > sample->probability = ntohs(nas->probability); > sample->collector_set_id = ntohl(nas->collector_set_id); > - sample->obs_domain_id = ntohl(nas->obs_domain_id); > - sample->obs_point_id = ntohl(nas->obs_point_id); > + sample->obs_domain_imm = ntohl(nas->obs_domain_id); > + sample->obs_domain_src.field = NULL; > + sample->obs_point_imm = ntohl(nas->obs_point_id); > + sample->obs_point_src.field = NULL; > sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port)); > sample->direction = direction; > > @@ -6241,41 +6276,170 @@ decode_NXAST_RAW_SAMPLE2(const struct nx_action_sample2 *nas, > ofpact_put_SAMPLE(out)); > } > > +static int > +check_sample_direction(enum nx_action_sample_direction direction) > +{ > + if (direction != NX_ACTION_SAMPLE_DEFAULT && > + direction != NX_ACTION_SAMPLE_INGRESS && > + direction != NX_ACTION_SAMPLE_EGRESS) { > + VLOG_WARN_RL(&rl, "invalid sample direction %"PRIu8, direction); > + return OFPERR_OFPBAC_BAD_ARGUMENT; > + } > + return 0; > +} > + > static enum ofperr > decode_NXAST_RAW_SAMPLE3(const struct nx_action_sample2 *nas, > enum ofp_version ofp_version OVS_UNUSED, > struct ofpbuf *out) > { > struct ofpact_sample *sample = ofpact_put_SAMPLE(out); > + int err; > + > if (!is_all_zeros(nas->zeros, sizeof nas->zeros)) { > return OFPERR_NXBRC_MUST_BE_ZERO; > } > - if (nas->direction != NX_ACTION_SAMPLE_DEFAULT && > - nas->direction != NX_ACTION_SAMPLE_INGRESS && > - nas->direction != NX_ACTION_SAMPLE_EGRESS) { > - VLOG_WARN_RL(&rl, "invalid sample direction %"PRIu8, nas->direction); > - return OFPERR_OFPBAC_BAD_ARGUMENT; > + err = check_sample_direction(nas->direction); > + if (err) { > + return err; > } > return decode_SAMPLE2(nas, NXAST_RAW_SAMPLE3, nas->direction, sample); > } > > +static int > +decode_sample_obs_id(ovs_be32 src, ovs_be16 ofs_nbits, ovs_be32 imm, > + const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap, > + struct mf_subfield *src_out, uint32_t *imm_out) > +{ > + if (src) { > + enum ofperr error; > + > + src_out->ofs = nxm_decode_ofs(ofs_nbits); > + src_out->n_bits = nxm_decode_n_bits(ofs_nbits); > + error = mf_vl_mff_mf_from_nxm_header(ntohl(src), > + vl_mff_map, &src_out->field, > + tlv_bitmap); > + if (error) { > + return error; > + } > + > + error = mf_check_src(src_out, NULL); > + if (error) { > + return error; > + } > + > + if (src_out->n_bits > 32) { > + VLOG_WARN_RL(&rl, "size of field used in observation_id (%d) " > + "exceeds maximum (32)", src_out->n_bits); > + return OFPERR_OFPBAC_BAD_ARGUMENT; > + } > + } else { > + src_out->field = NULL; > + *imm_out = ntohl(imm); > + } > + > + return 0; > +} > + > +static enum ofperr > +decode_NXAST_RAW_SAMPLE4(const struct nx_action_sample4 *nas, > + enum ofp_version ofp_version OVS_UNUSED, > + const struct vl_mff_map *vl_mff_map, > + uint64_t *tlv_bitmap, > + struct ofpbuf *out) > +{ > + struct ofpact_sample *sample = ofpact_put_SAMPLE(out); > + int err; > + > + if (!is_all_zeros(nas->zeros, sizeof nas->zeros)) { > + return OFPERR_NXBRC_MUST_BE_ZERO; > + } > + > + err = check_sample_direction(nas->direction); > + if (err) { > + return err; > + } > + > + sample->ofpact.raw = NXAST_RAW_SAMPLE4; > + sample->probability = ntohs(nas->probability); > + sample->collector_set_id = ntohl(nas->collector_set_id); > + sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port)); > + sample->direction = nas->direction; > + > + if (sample->probability == 0) { > + return OFPERR_OFPBAC_BAD_ARGUMENT; > + } > + > + err = decode_sample_obs_id(nas->obs_domain_src, > + nas->obs_domain_ofs_nbits, > + nas->obs_domain_imm, > + vl_mff_map, tlv_bitmap, > + &sample->obs_domain_src, > + &sample->obs_domain_imm); > + if (err) { > + return err; > + } > + > + return decode_sample_obs_id(nas->obs_point_src, > + nas->obs_point_ofs_nbits, > + nas->obs_point_imm, > + vl_mff_map, tlv_bitmap, > + &sample->obs_point_src, > + &sample->obs_point_imm); > +} > + > static void > encode_SAMPLE2(const struct ofpact_sample *sample, > struct nx_action_sample2 *nas) > { > nas->probability = htons(sample->probability); > nas->collector_set_id = htonl(sample->collector_set_id); > - nas->obs_domain_id = htonl(sample->obs_domain_id); > - nas->obs_point_id = htonl(sample->obs_point_id); > + nas->obs_domain_id = htonl(sample->obs_domain_imm); > + nas->obs_point_id = htonl(sample->obs_point_imm); > + nas->sampling_port = htons(ofp_to_u16(sample->sampling_port)); > + nas->direction = sample->direction; > +} > + > +static void > +encode_SAMPLE4(const struct ofpact_sample *sample, > + struct nx_action_sample4 *nas) > +{ > + nas->probability = htons(sample->probability); > + nas->collector_set_id = htonl(sample->collector_set_id); > nas->sampling_port = htons(ofp_to_u16(sample->sampling_port)); > nas->direction = sample->direction; > + > + if (sample->obs_domain_src.field) { > + nas->obs_domain_src = > + htonl(nxm_header_from_mff(sample->obs_domain_src.field)); > + nas->obs_domain_ofs_nbits = > + nxm_encode_ofs_nbits(sample->obs_domain_src.ofs, > + sample->obs_domain_src.n_bits); > + } else { > + nas->obs_domain_src = htonl(0); > + nas->obs_domain_imm = htonl(sample->obs_domain_imm); > + } > + if (sample->obs_point_src.field) { > + nas->obs_point_src = > + htonl(nxm_header_from_mff(sample->obs_point_src.field)); > + nas->obs_point_ofs_nbits = > + nxm_encode_ofs_nbits(sample->obs_point_src.ofs, > + sample->obs_point_src.n_bits); > + } else { > + nas->obs_point_src = htonl(0); > + nas->obs_point_imm = htonl(sample->obs_point_imm); > + } > } > > static void > encode_SAMPLE(const struct ofpact_sample *sample, > enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) > { > - if (sample->ofpact.raw == NXAST_RAW_SAMPLE3 > + if (sample->ofpact.raw == NXAST_RAW_SAMPLE4 || > + sample->obs_domain_src.field || > + sample->obs_point_src.field) { > + encode_SAMPLE4(sample, put_NXAST_SAMPLE4(out)); > + } else if (sample->ofpact.raw == NXAST_RAW_SAMPLE3 > || sample->direction != NX_ACTION_SAMPLE_DEFAULT) { > encode_SAMPLE2(sample, put_NXAST_SAMPLE3(out)); > } else if (sample->ofpact.raw == NXAST_RAW_SAMPLE2 > @@ -6285,8 +6449,8 @@ encode_SAMPLE(const struct ofpact_sample *sample, > struct nx_action_sample *nas = put_NXAST_SAMPLE(out); > nas->probability = htons(sample->probability); > nas->collector_set_id = htonl(sample->collector_set_id); > - nas->obs_domain_id = htonl(sample->obs_domain_id); > - nas->obs_point_id = htonl(sample->obs_point_id); > + nas->obs_domain_id = htonl(sample->obs_domain_imm); > + nas->obs_point_id = htonl(sample->obs_point_imm); > } > } > > @@ -6314,9 +6478,35 @@ parse_SAMPLE(char *arg, const struct ofpact_parse_params *pp) > } else if (!strcmp(key, "collector_set_id")) { > error = str_to_u32(value, &os->collector_set_id); > } else if (!strcmp(key, "obs_domain_id")) { > - error = str_to_u32(value, &os->obs_domain_id); > + error = str_to_u32(value, &os->obs_domain_imm); > + > + if (error) { > + free(error); > + error = mf_parse_subfield(&os->obs_domain_src, value); > + if (error) { > + return error; > + } > + if (os->obs_domain_src.n_bits > 32) { > + return xasprintf("size of obs_domain_id field (%d) " > + "exceeds maximum (32)", > + os->obs_point_src.n_bits); > + } > + } > } else if (!strcmp(key, "obs_point_id")) { > - error = str_to_u32(value, &os->obs_point_id); > + error = str_to_u32(value, &os->obs_point_imm); > + > + if (error) { > + free(error); > + error = mf_parse_subfield(&os->obs_point_src, value); > + if (error) { > + return error; > + } > + if (os->obs_point_src.n_bits != 32) { Should be > 32 ? > + return xasprintf("size of obs_point_id field (%d) " > + "exceeds maximum (32)", > + os->obs_point_src.n_bits); > + } > + } > } else if (!strcmp(key, "sampling_port")) { > if (!ofputil_port_from_string(value, pp->port_map, > &os->sampling_port)) { > @@ -6346,14 +6536,23 @@ format_SAMPLE(const struct ofpact_sample *a, > const struct ofpact_format_params *fp) > { > ds_put_format(fp->s, "%ssample(%s%sprobability=%s%"PRIu16 > - ",%scollector_set_id=%s%"PRIu32 > - ",%sobs_domain_id=%s%"PRIu32 > - ",%sobs_point_id=%s%"PRIu32, > + ",%scollector_set_id=%s%"PRIu32, > colors.paren, colors.end, > colors.param, colors.end, a->probability, > - colors.param, colors.end, a->collector_set_id, > - colors.param, colors.end, a->obs_domain_id, > - colors.param, colors.end, a->obs_point_id); > + colors.param, colors.end, a->collector_set_id); > + > + ds_put_format(fp->s, ",%sobs_domain_id=%s", colors.param, colors.end); > + if (a->obs_domain_src.field) { > + mf_format_subfield(&a->obs_domain_src, fp->s); > + } else { > + ds_put_format(fp->s, "%"PRIu32, a->obs_domain_imm); > + } > + ds_put_format(fp->s, ",%sobs_point_id=%s", colors.param, colors.end); > + if (a->obs_point_src.field) { > + mf_format_subfield(&a->obs_point_src, fp->s); > + } else { > + ds_put_format(fp->s, "%"PRIu32, a->obs_point_imm); > + } > if (a->sampling_port != OFPP_NONE) { > ds_put_format(fp->s, ",%ssampling_port=%s", colors.param, colors.end); > ofputil_format_port(a->sampling_port, fp->port_map, fp->s); > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index abf855c3e..e8ec343a6 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -5902,6 +5902,40 @@ xlate_fin_timeout(struct xlate_ctx *ctx, > } > } > > +static uint32_t > +ofpact_sample_get_domain(struct xlate_ctx *ctx, > + const struct ofpact_sample *os) > +{ > + if (os->obs_domain_src.field) { > + uint32_t obs_domain_id; > + > + obs_domain_id = mf_get_subfield(&os->obs_domain_src, &ctx->xin->flow); > + mf_write_subfield_flow(&os->obs_domain_src, &exact_sub_match_mask, > + &ctx->wc->masks); > + > + return obs_domain_id; > + } else { > + return os->obs_domain_imm; > + } > +} > + > +static uint32_t > +ofpact_sample_get_point(struct xlate_ctx *ctx, > + const struct ofpact_sample *os) > +{ > + if (os->obs_point_src.field) { > + uint32_t obs_point_id; > + > + obs_point_id = mf_get_subfield(&os->obs_point_src, &ctx->xin->flow); > + mf_write_subfield_flow(&os->obs_point_src, &exact_sub_match_mask, > + &ctx->wc->masks); > + > + return obs_point_id; > + } else { > + return os->obs_point_imm; > + } > +} > + > static void > xlate_fill_ipfix_sample(struct xlate_ctx *ctx, > const struct ofpact_sample *os, > @@ -5968,8 +6002,10 @@ xlate_fill_ipfix_sample(struct xlate_ctx *ctx, > userspace->cookie.ofproto_uuid = ctx->xbridge->ofproto->uuid; > userspace->cookie.flow_sample.probability = os->probability; > userspace->cookie.flow_sample.collector_set_id = os->collector_set_id; > - userspace->cookie.flow_sample.obs_domain_id = os->obs_domain_id; > - userspace->cookie.flow_sample.obs_point_id = os->obs_point_id; > + userspace->cookie.flow_sample.obs_domain_id = > + ofpact_sample_get_domain(ctx, os); > + userspace->cookie.flow_sample.obs_point_id = > + ofpact_sample_get_point(ctx, os); > userspace->cookie.flow_sample.output_odp_port = output_odp_port; > userspace->cookie.flow_sample.direction = os->direction; > userspace->include_actions = false; > @@ -6003,8 +6039,8 @@ xlate_sample_action(struct xlate_ctx *ctx, > dpif_lsample_get_group_id(lsample, > os->collector_set_id, > &psample.group_id)) { > - psample.cookie.hi = htonl(os->obs_domain_id); > - psample.cookie.lo = htonl(os->obs_point_id); > + psample.cookie.hi = htonl(ofpact_sample_get_domain(ctx, os)); > + psample.cookie.lo = htonl(ofpact_sample_get_point(ctx,os)); Missing space. > > compose_args.psample = &psample; > > diff --git a/python/ovs/flow/ofp.py b/python/ovs/flow/ofp.py > index 3d3226c91..f011b0460 100644 > --- a/python/ovs/flow/ofp.py > +++ b/python/ovs/flow/ofp.py > @@ -30,7 +30,7 @@ from ovs.flow.ofp_act import ( > decode_move_field, > decode_dec_ttl, > decode_chk_pkt_larger, > - decode_zone, > + decode_field_or_int, > decode_learn, > ) > > @@ -330,7 +330,7 @@ class OFPFlow(Flow): > KVDecoders( > { > "commit": decode_flag, > - "zone": decode_zone, > + "zone": decode_field_or_int, > "table": decode_int, > "nat": decode_nat, > "force": decode_flag, > @@ -426,8 +426,8 @@ class OFPFlow(Flow): > { > "probability": decode_int, > "collector_set_id": decode_int, > - "obs_domain_id": decode_int, > - "obs_point_id": decode_int, > + "obs_domain_id": decode_field_or_int, > + "obs_point_id": decode_field_or_int, > "sampling_port": decode_default, > "ingress": decode_flag, > "egress": decode_flag, > diff --git a/python/ovs/flow/ofp_act.py b/python/ovs/flow/ofp_act.py > index 2c85076a3..73727428a 100644 > --- a/python/ovs/flow/ofp_act.py > +++ b/python/ovs/flow/ofp_act.py > @@ -246,9 +246,9 @@ def decode_chk_pkt_larger(value): > return {"pkt_len": pkt_len, "dst": dst} > > > -# CT decoders > -def decode_zone(value): > - """Decodes the value of the 'zone' keyword (part of the ct action).""" > +def decode_field_or_int(value): > + """Decodes a value that can be either a subfield specification or an > + integer.""" > try: > return int(value, 0) > except ValueError: > diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at > index 40a23bb15..86aec12e8 100644 > --- a/tests/ofp-actions.at > +++ b/tests/ofp-actions.at > @@ -136,6 +136,9 @@ ffff 0020 00002320 0026 3039 00005BA0 00008707 0000B26E DDD50000 00000000 > # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) > ffff 0020 00002320 0029 3039 00005BA0 00008707 0000B26E DDD50200 00000000 > > +# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) > +ffff 0028 00002320 0033 3039 00005ba0 00000002 000f0000 0001d810 081f0000 0000 000000000000 > + > # bad OpenFlow10 actions: OFPBAC_BAD_LEN > & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 240 exceeds action buffer length 8 > & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_LEN): > @@ -489,6 +492,9 @@ ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000 > # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E > > +# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) > +ffff 0028 00002320 0033 3039 00005ba0 00000002 000f0000 0001d810 081f0000 0000 000000000000 > + > # bad OpenFlow11 actions: OFPBAC_BAD_OUT_PORT > & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_OUT_PORT): > & 00000000 00 00 00 10 ff ff ff ff-00 00 00 00 00 00 00 00 > @@ -1121,6 +1127,8 @@ bad_action 'unroll_xlate' "UNROLL is an internal action that shouldn't be used v > # sample > bad_action 'sample(probability=0)' 'invalid probability value "0"' > bad_action 'sample(sampling_port=asdf)' 'asdf: unknown port' > +bad_action 'sample(probability=12345,obs_point_id=NXM_NX_CT_LABEL[[0..32]])' \ > + 'size of obs_point_id field (33) exceeds maximum (32)' > bad_action 'sample(foo=bar)' 'invalid key "foo" in "sample" argument' > bad_action 'sample' 'non-zero "probability" must be specified on sample' > > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > index f65ace850..8bbcfa2eb 100644 > --- a/tests/ofproto-dpif.at > +++ b/tests/ofproto-dpif.at > @@ -8304,6 +8304,50 @@ AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore]) > OVS_VSWITCHD_STOP > AT_CLEANUP > > +AT_SETUP([ofproto-dpif - Flow IPFIX sanity check - from field]) > +OVS_VSWITCHD_START > +add_of_ports br0 1 2 > + > +AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ > + -- --id=@ipfix create IPFIX targets=\"127.0.0.1:5500\" \ > + -- --id=@cs create Flow_Sample_Collector_Set id=0 \ > + bridge=@br0 ipfix=@ipfix], > + [0], [ignore]) > + > +m4_define([SAMPLE_ACTION], > + [sample(probability=65535,collector_set_id=1,obs_domain_id=NXM_OF_IN_PORT,obs_point_id=$1)]dnl > +) > + > +dnl Store in_port in obs_domain_id and dp_hash in the obs_point_id. > +AT_DATA([flows.txt], [dnl > +priority=100,arp,action=normal > +priority=10,in_port=1,ip actions=SAMPLE_ACTION(NXM_NX_DP_HASH),2 > +priority=10,in_port=2,ip actions=SAMPLE_ACTION(NXM_NX_CT_LABEL[[[0..31]]]),1 Could we also check something like 5 bits in the middle? > +]) > +AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) > + > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ > + "in_port(1),dp_hash(45),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ > + ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) > + > +AT_CHECK([tail -2 stdout], [0], [dnl > +Megaflow: recirc_id=0,dp_hash=0x2d,eth,ip,in_port=1,nw_frag=no > +Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=1,obs_point_id=45,output_port=4294967295)),2 > +]) > + > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ > + "in_port(2),ct_label(0x1234567890abcdef1234567890abcdef),\ > + eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ > + ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) > + > +AT_CHECK([tail -2 stdout], [0], [dnl > +Megaflow: recirc_id=0,ct_label=0x90abcdef/0xffffffff,eth,ip,in_port=2,nw_frag=no > +Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=2,obs_point_id=2427178479,output_port=4294967295)),1 > +]) > + > +OVS_VSWITCHD_STOP > +AT_CLEANUP > + > AT_SETUP([ofproto-dpif - clone action]) > OVS_VSWITCHD_START > add_of_ports br0 1 2 3 4 > diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at > index d03d36500..e2f4429ae 100644 > --- a/tests/ovs-ofctl.at > +++ b/tests/ovs-ofctl.at > @@ -198,6 +198,8 @@ actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_ > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,ingress) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) > +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) > +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63]) > ip,actions=ct(nat) > ip,actions=ct(commit,nat(dst)) > ip,actions=ct(commit,nat(src)) > @@ -233,6 +235,8 @@ OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_d > OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,ingress) > OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) > +OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) > +OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63]) > OFPT_FLOW_MOD: ADD ip actions=ct(nat) > OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(dst)) > OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(src)) > @@ -265,6 +269,7 @@ sctp actions=drop > in_port=0 actions=resubmit:0 > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) > ]]) > > AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt > @@ -286,6 +291,7 @@ OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop > OFPT_FLOW_MOD (OF1.1): ADD in_port=0 actions=resubmit:0 > OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) > ]]) > AT_CLEANUP > > @@ -312,6 +318,7 @@ in_port=0 actions=mod_dl_src:11:22:33:44:55:66,mod_dl_dst:10:20:30:40:50:60 > in_port=0 actions=resubmit:0 > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) > ]]) > > AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt > @@ -339,6 +346,7 @@ OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=set_field:11:22:33:44:55:66->eth_sr > OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=resubmit:0 > OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) > ]]) > AT_CLEANUP > > @@ -441,6 +449,7 @@ tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15) > actions=controller(max_len=123,reason=invalid_ttl,id=555) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789) > mpls,mpls_label=5,mpls_tc=1,mpls_ttl=1,mpls_bos=0,actions=drop > ip,actions=ct(commit,zone=5) > ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) > @@ -508,6 +517,7 @@ NXT_FLOW_MOD: ADD table:255 tcp actions=fin_timeout(idle_timeout=5,hard_timeout= > NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555) > NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789) > NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=5,mpls_tc=1,mpls_ttl=1,mpls_bos=0 actions=drop > NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5) > NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) > @@ -567,6 +577,7 @@ dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff,actions=drop > dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[[]],obs_point_id=NXM_NX_CT_LABEL[[32..63]],sampling_port=56789,egress) > ip,actions=ct(commit,zone=5) > ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[[]]))) > ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[[]]))) > @@ -608,6 +619,7 @@ NXT_FLOW_MOD: ADD dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff actions=drop > NXT_FLOW_MOD: ADD actions=drop > NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +NXT_FLOW_MOD: ADD actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[[]],obs_point_id=NXM_NX_CT_LABEL[[32..63]],sampling_port=56789,egress) > NXT_FLOW_MOD: ADD ip actions=ct(commit,zone=5) > NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[[]])) > NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[[0..63]],load:0->NXM_NX_CT_LABEL[[64..127]])) > @@ -648,6 +660,7 @@ actions=push:reg0[0..31],pop:reg0 > vlan_tci=0x1123/0x1fff,actions=drop > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) > ip,actions=ct(commit,zone=5) > ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) > ip,actions=ct(commit,exec(load(1->NXM_NX_CT_LABEL[]))) > @@ -688,6 +701,7 @@ NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] > NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop > NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) > NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) > +NXT_FLOW_MOD: ADD <any> actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) > NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,zone=5) > NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) > NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > index cab811ad0..2d3f7ab39 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -9461,3 +9461,95 @@ dnl OVS will fail to send IPFIX packets because the target is localhost > dnl and the port is closed. Ignore the message it generates. > OVS_TRAFFIC_VSWITCHD_STOP(["/sending to collector failed/d"]) > AT_CLEANUP > + > +AT_SETUP([psample - from ct label]) > +CHECK_CONNTRACK() > +OVS_TRAFFIC_VSWITCHD_START() > +OVS_CHECK_PSAMPLE() > + > +ADD_NAMESPACES(at_ns0, at_ns1) > +NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) > +NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "e4:11:22:33:44:55") > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "e4:11:22:33:44:66") > + > +AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ > + -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ > + -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ > + ipfix=@ipfix, local_group_id=10 \ > + -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ > + ipfix=@ipfix, local_group_id=12], local-group-id > + [0], [ignore]) > + > +m4_define([CT_STORE_ACT], > + [ct(zone=5,commit,exec(load:0x0bb102030->NXM_NX_CT_LABEL[[0..31]],load:0xbb405060->NXM_NX_CT_LABEL[[32..63]]))]) > + > +AT_DATA([flows.txt], [dnl > +priority=100,ip actions=ct(zone=5, table=10) > +priority=0 actions=NORMAL > +table=10,priority=100,ip,ct_state=+trk+new action=SAMPLE_ACTION(1, 2853183536, 2856341600),CT_STORE_ACT,NORMAL > +table=10,priority=100,ip,ct_state=+trk-new action=SAMPLE_ACTION(2, NXM_NX_CT_LABEL[[[0..31]]], NXM_NX_CT_LABEL[[[32..63]]]),NORMAL > +table=10, priority=50, ip, actions=DROP > +]) > + > +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) > + > +OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample1.pid]) > +OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) > + > +NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl > +1 packets transmitted, 1 received, 0% packet loss, time 0ms > +]) > + > +m4_define([SAMPLE1], [m4_join([ ], > + [group_id=0xa,prob=4294967295], > + [obs_domain=0xaa102030,obs_point=0xaa405060], > + [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) > + > +m4_define([SAMPLE2], [m4_join([ ], > + [group_id=0xc,prob=4294967295], > + [obs_domain=0xbb102030,obs_point=0xbb405060], > + [.*icmp.*nw_src=10.1.1.2,nw_dst=10.1.1.1])]) > +AT_CHECK([grep -qE 'SAMPLE1' psample.out]) > +AT_CHECK([grep -qE 'SAMPLE2' psample.out]) > + > +m4_define([FLOW_MATCH], [m4_join([], > + [ct_label(0xbb405060bb102030/0xffffffffffffffff).*actions:], > + [actions:psample(group=12,cookie=0xbb102030bb405060),], > + [userspace(pid=[[0-9]]+,flow_sample(.*obs_domain_id=3138396208,obs_point_id=3141554272.*))] > +)]) > + > +AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p1 \ > + | grep -qE 'FLOW_MATCH' ], [0], []) > + > +dnl Check IPFIX samples have been received. > +dnl Entries can be unsorted and IFPIX packets might not have been sent (or > +dnl at least tried to be sent) yet. > +OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-ipfix-flow br0 | \ > + sed 's/tx pkts=[[0-9]]*/tx pkts=24/' | \ > + sed 's/tx errs=[[0-9]]*/tx errs=0/' | \ > + sed 's/id [[1-2]]:/id ?:/'], [dnl > +NXST_IPFIX_FLOW reply (xid=0x2): 2 ids > + id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 > + pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0 > + id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 > + pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0]) > + > +AT_CHECK([ovs-appctl lsample/show br0], [0], [dnl > +Local sample statistics for bridge "br0": > +Collector Set ID: 1: > + Group ID : 10 > + Total packets: 1 > + Total bytes : 98 > + > +Collector Set ID: 2: > + Group ID : 12 > + Total packets: 1 > + Total bytes : 98 > +]) Drop this check alson with unixctl ? > + > +dnl OVS will fail to send IPFIX packets because the target is localhost > +dnl and the port is closed. Ignore the message it generates. > +OVS_TRAFFIC_VSWITCHD_STOP(["/sending to collector failed/d"]) > +AT_CLEANUP
diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst index 80acd9070..01021dc0a 100644 --- a/Documentation/ref/ovs-actions.7.rst +++ b/Documentation/ref/ovs-actions.7.rst @@ -2201,13 +2201,17 @@ The following *argument* forms are accepted: The unsigned 32-bit integer identifier of the set of sample collectors to send sampled packets to. Defaults to 0. - ``obs_domain_id=``\ *id* + ``obs_domain_id=``\ *value* When sending samples to IPFIX collectors, the unsigned 32-bit integer - Observation Domain ID sent in every IPFIX flow record. Defaults to 0. + Observation Domain ID sent in every IPFIX flow record. The *value* may + be specified as a 32-bit integer or a field or subfield in the syntax + described under `Field Specifications`_ above. Defaults to 0. - ``obs_point_id=``\ *id* + ``obs_point_id=``\ *value* When sending samples to IPFIX collectors, the unsigned 32-bit integer - Observation Point ID sent in every IPFIX flow record. Defaults to 0. + Observation Point ID sent in every IPFIX flow record. The *value* may + be specified as a 32-bit integer or a field or subfield in the syntax + described under `Field Specifications`_ above. Defaults to 0. ``sampling_port=``\ *port* Sample packets on *port*, which should be the ingress or egress port. This diff --git a/NEWS b/NEWS index 4eb73aada..cac844063 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,8 @@ Post-v3.3.0 Open_vSwitch table controls whether an explicit drop action shall be added at the end of datapath flows whose last action is an observability-driven sample action. + - OpenFlow: a new version of the "sample" action is introduced that allows + the use of subfields in obs_point_id and obs_domain_id. v3.3.0 - 16 Feb 2024 diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 7b57e49ad..56dc2c147 100644 --- a/include/openvswitch/ofp-actions.h +++ b/include/openvswitch/ofp-actions.h @@ -1015,14 +1015,16 @@ enum nx_action_sample_direction { /* OFPACT_SAMPLE. * - * Used for NXAST_SAMPLE, NXAST_SAMPLE2, and NXAST_SAMPLE3. */ + * Used for NXAST_SAMPLE, NXAST_SAMPLE2, NXAST_SAMPLE3 and NXAST_SAMPLE4. */ struct ofpact_sample { OFPACT_PADDED_MEMBERS( struct ofpact ofpact; uint16_t probability; /* Always positive. */ uint32_t collector_set_id; - uint32_t obs_domain_id; - uint32_t obs_point_id; + uint32_t obs_domain_imm; + struct mf_subfield obs_domain_src; + uint32_t obs_point_imm; + struct mf_subfield obs_point_src; ofp_port_t sampling_port; enum nx_action_sample_direction direction; ); diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index da7b1dd31..6b030bf29 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -330,6 +330,8 @@ enum ofp_raw_action_type { NXAST_RAW_SAMPLE2, /* NX1.0+(41): struct nx_action_sample2. */ NXAST_RAW_SAMPLE3, + /* NX1.0+(51): struct nx_action_sample4. VLMFF */ + NXAST_RAW_SAMPLE4, /* NX1.0+(34): struct nx_action_conjunction. */ NXAST_RAW_CONJUNCTION, @@ -6188,6 +6190,34 @@ struct nx_action_sample2 { }; OFP_ASSERT(sizeof(struct nx_action_sample2) == 32); +/* Action structure for NXAST_SAMPLE4 + * + * NXAST_SAMPLE4 was added in Open vSwitch 3.4.0. Compared to NXAST_SAMPLE3, + * it adds support for using field specifiers for observation_domain_id and + * observation_point_id. */ +struct nx_action_sample4 { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 40. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_SAMPLE4. */ + ovs_be16 probability; /* Fraction of packets to sample. */ + ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ + ovs_be32 obs_domain_src; /* The observation_domain_id source. */ + union { + ovs_be16 obs_domain_ofs_nbits; /* Range to use from source field. */ + ovs_be32 obs_domain_imm; /* Immediate value for domain id. */ + }; + ovs_be32 obs_point_src; /* The observation_point_id source. */ + union { + ovs_be16 obs_point_ofs_nbits; /* Range to use from source field. */ + ovs_be32 obs_point_imm; /* Immediate value for point id. */ + }; + ovs_be16 sampling_port; /* Sampling port. */ + uint8_t direction; /* Sampling direction. */ + uint8_t zeros[5]; /* Pad to a multiple of 8 bytes */ + }; + OFP_ASSERT(sizeof(struct nx_action_sample4) == 40); + static enum ofperr decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, enum ofp_version ofp_version OVS_UNUSED, @@ -6199,11 +6229,14 @@ decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, sample->ofpact.raw = NXAST_RAW_SAMPLE; sample->probability = ntohs(nas->probability); sample->collector_set_id = ntohl(nas->collector_set_id); - sample->obs_domain_id = ntohl(nas->obs_domain_id); - sample->obs_point_id = ntohl(nas->obs_point_id); + sample->obs_domain_imm = ntohl(nas->obs_domain_id); + sample->obs_domain_src.field = NULL; + sample->obs_point_imm = ntohl(nas->obs_point_id); + sample->obs_point_src.field = NULL; sample->sampling_port = OFPP_NONE; sample->direction = NX_ACTION_SAMPLE_DEFAULT; - + sample->obs_domain_src.field = NULL; + sample->obs_point_src.field = NULL; if (sample->probability == 0) { return OFPERR_OFPBAC_BAD_ARGUMENT; } @@ -6220,8 +6253,10 @@ decode_SAMPLE2(const struct nx_action_sample2 *nas, sample->ofpact.raw = raw; sample->probability = ntohs(nas->probability); sample->collector_set_id = ntohl(nas->collector_set_id); - sample->obs_domain_id = ntohl(nas->obs_domain_id); - sample->obs_point_id = ntohl(nas->obs_point_id); + sample->obs_domain_imm = ntohl(nas->obs_domain_id); + sample->obs_domain_src.field = NULL; + sample->obs_point_imm = ntohl(nas->obs_point_id); + sample->obs_point_src.field = NULL; sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port)); sample->direction = direction; @@ -6241,41 +6276,170 @@ decode_NXAST_RAW_SAMPLE2(const struct nx_action_sample2 *nas, ofpact_put_SAMPLE(out)); } +static int +check_sample_direction(enum nx_action_sample_direction direction) +{ + if (direction != NX_ACTION_SAMPLE_DEFAULT && + direction != NX_ACTION_SAMPLE_INGRESS && + direction != NX_ACTION_SAMPLE_EGRESS) { + VLOG_WARN_RL(&rl, "invalid sample direction %"PRIu8, direction); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + return 0; +} + static enum ofperr decode_NXAST_RAW_SAMPLE3(const struct nx_action_sample2 *nas, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_sample *sample = ofpact_put_SAMPLE(out); + int err; + if (!is_all_zeros(nas->zeros, sizeof nas->zeros)) { return OFPERR_NXBRC_MUST_BE_ZERO; } - if (nas->direction != NX_ACTION_SAMPLE_DEFAULT && - nas->direction != NX_ACTION_SAMPLE_INGRESS && - nas->direction != NX_ACTION_SAMPLE_EGRESS) { - VLOG_WARN_RL(&rl, "invalid sample direction %"PRIu8, nas->direction); - return OFPERR_OFPBAC_BAD_ARGUMENT; + err = check_sample_direction(nas->direction); + if (err) { + return err; } return decode_SAMPLE2(nas, NXAST_RAW_SAMPLE3, nas->direction, sample); } +static int +decode_sample_obs_id(ovs_be32 src, ovs_be16 ofs_nbits, ovs_be32 imm, + const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap, + struct mf_subfield *src_out, uint32_t *imm_out) +{ + if (src) { + enum ofperr error; + + src_out->ofs = nxm_decode_ofs(ofs_nbits); + src_out->n_bits = nxm_decode_n_bits(ofs_nbits); + error = mf_vl_mff_mf_from_nxm_header(ntohl(src), + vl_mff_map, &src_out->field, + tlv_bitmap); + if (error) { + return error; + } + + error = mf_check_src(src_out, NULL); + if (error) { + return error; + } + + if (src_out->n_bits > 32) { + VLOG_WARN_RL(&rl, "size of field used in observation_id (%d) " + "exceeds maximum (32)", src_out->n_bits); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + } else { + src_out->field = NULL; + *imm_out = ntohl(imm); + } + + return 0; +} + +static enum ofperr +decode_NXAST_RAW_SAMPLE4(const struct nx_action_sample4 *nas, + enum ofp_version ofp_version OVS_UNUSED, + const struct vl_mff_map *vl_mff_map, + uint64_t *tlv_bitmap, + struct ofpbuf *out) +{ + struct ofpact_sample *sample = ofpact_put_SAMPLE(out); + int err; + + if (!is_all_zeros(nas->zeros, sizeof nas->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + err = check_sample_direction(nas->direction); + if (err) { + return err; + } + + sample->ofpact.raw = NXAST_RAW_SAMPLE4; + sample->probability = ntohs(nas->probability); + sample->collector_set_id = ntohl(nas->collector_set_id); + sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port)); + sample->direction = nas->direction; + + if (sample->probability == 0) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + err = decode_sample_obs_id(nas->obs_domain_src, + nas->obs_domain_ofs_nbits, + nas->obs_domain_imm, + vl_mff_map, tlv_bitmap, + &sample->obs_domain_src, + &sample->obs_domain_imm); + if (err) { + return err; + } + + return decode_sample_obs_id(nas->obs_point_src, + nas->obs_point_ofs_nbits, + nas->obs_point_imm, + vl_mff_map, tlv_bitmap, + &sample->obs_point_src, + &sample->obs_point_imm); +} + static void encode_SAMPLE2(const struct ofpact_sample *sample, struct nx_action_sample2 *nas) { nas->probability = htons(sample->probability); nas->collector_set_id = htonl(sample->collector_set_id); - nas->obs_domain_id = htonl(sample->obs_domain_id); - nas->obs_point_id = htonl(sample->obs_point_id); + nas->obs_domain_id = htonl(sample->obs_domain_imm); + nas->obs_point_id = htonl(sample->obs_point_imm); + nas->sampling_port = htons(ofp_to_u16(sample->sampling_port)); + nas->direction = sample->direction; +} + +static void +encode_SAMPLE4(const struct ofpact_sample *sample, + struct nx_action_sample4 *nas) +{ + nas->probability = htons(sample->probability); + nas->collector_set_id = htonl(sample->collector_set_id); nas->sampling_port = htons(ofp_to_u16(sample->sampling_port)); nas->direction = sample->direction; + + if (sample->obs_domain_src.field) { + nas->obs_domain_src = + htonl(nxm_header_from_mff(sample->obs_domain_src.field)); + nas->obs_domain_ofs_nbits = + nxm_encode_ofs_nbits(sample->obs_domain_src.ofs, + sample->obs_domain_src.n_bits); + } else { + nas->obs_domain_src = htonl(0); + nas->obs_domain_imm = htonl(sample->obs_domain_imm); + } + if (sample->obs_point_src.field) { + nas->obs_point_src = + htonl(nxm_header_from_mff(sample->obs_point_src.field)); + nas->obs_point_ofs_nbits = + nxm_encode_ofs_nbits(sample->obs_point_src.ofs, + sample->obs_point_src.n_bits); + } else { + nas->obs_point_src = htonl(0); + nas->obs_point_imm = htonl(sample->obs_point_imm); + } } static void encode_SAMPLE(const struct ofpact_sample *sample, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - if (sample->ofpact.raw == NXAST_RAW_SAMPLE3 + if (sample->ofpact.raw == NXAST_RAW_SAMPLE4 || + sample->obs_domain_src.field || + sample->obs_point_src.field) { + encode_SAMPLE4(sample, put_NXAST_SAMPLE4(out)); + } else if (sample->ofpact.raw == NXAST_RAW_SAMPLE3 || sample->direction != NX_ACTION_SAMPLE_DEFAULT) { encode_SAMPLE2(sample, put_NXAST_SAMPLE3(out)); } else if (sample->ofpact.raw == NXAST_RAW_SAMPLE2 @@ -6285,8 +6449,8 @@ encode_SAMPLE(const struct ofpact_sample *sample, struct nx_action_sample *nas = put_NXAST_SAMPLE(out); nas->probability = htons(sample->probability); nas->collector_set_id = htonl(sample->collector_set_id); - nas->obs_domain_id = htonl(sample->obs_domain_id); - nas->obs_point_id = htonl(sample->obs_point_id); + nas->obs_domain_id = htonl(sample->obs_domain_imm); + nas->obs_point_id = htonl(sample->obs_point_imm); } } @@ -6314,9 +6478,35 @@ parse_SAMPLE(char *arg, const struct ofpact_parse_params *pp) } else if (!strcmp(key, "collector_set_id")) { error = str_to_u32(value, &os->collector_set_id); } else if (!strcmp(key, "obs_domain_id")) { - error = str_to_u32(value, &os->obs_domain_id); + error = str_to_u32(value, &os->obs_domain_imm); + + if (error) { + free(error); + error = mf_parse_subfield(&os->obs_domain_src, value); + if (error) { + return error; + } + if (os->obs_domain_src.n_bits > 32) { + return xasprintf("size of obs_domain_id field (%d) " + "exceeds maximum (32)", + os->obs_point_src.n_bits); + } + } } else if (!strcmp(key, "obs_point_id")) { - error = str_to_u32(value, &os->obs_point_id); + error = str_to_u32(value, &os->obs_point_imm); + + if (error) { + free(error); + error = mf_parse_subfield(&os->obs_point_src, value); + if (error) { + return error; + } + if (os->obs_point_src.n_bits != 32) { + return xasprintf("size of obs_point_id field (%d) " + "exceeds maximum (32)", + os->obs_point_src.n_bits); + } + } } else if (!strcmp(key, "sampling_port")) { if (!ofputil_port_from_string(value, pp->port_map, &os->sampling_port)) { @@ -6346,14 +6536,23 @@ format_SAMPLE(const struct ofpact_sample *a, const struct ofpact_format_params *fp) { ds_put_format(fp->s, "%ssample(%s%sprobability=%s%"PRIu16 - ",%scollector_set_id=%s%"PRIu32 - ",%sobs_domain_id=%s%"PRIu32 - ",%sobs_point_id=%s%"PRIu32, + ",%scollector_set_id=%s%"PRIu32, colors.paren, colors.end, colors.param, colors.end, a->probability, - colors.param, colors.end, a->collector_set_id, - colors.param, colors.end, a->obs_domain_id, - colors.param, colors.end, a->obs_point_id); + colors.param, colors.end, a->collector_set_id); + + ds_put_format(fp->s, ",%sobs_domain_id=%s", colors.param, colors.end); + if (a->obs_domain_src.field) { + mf_format_subfield(&a->obs_domain_src, fp->s); + } else { + ds_put_format(fp->s, "%"PRIu32, a->obs_domain_imm); + } + ds_put_format(fp->s, ",%sobs_point_id=%s", colors.param, colors.end); + if (a->obs_point_src.field) { + mf_format_subfield(&a->obs_point_src, fp->s); + } else { + ds_put_format(fp->s, "%"PRIu32, a->obs_point_imm); + } if (a->sampling_port != OFPP_NONE) { ds_put_format(fp->s, ",%ssampling_port=%s", colors.param, colors.end); ofputil_format_port(a->sampling_port, fp->port_map, fp->s); diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index abf855c3e..e8ec343a6 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -5902,6 +5902,40 @@ xlate_fin_timeout(struct xlate_ctx *ctx, } } +static uint32_t +ofpact_sample_get_domain(struct xlate_ctx *ctx, + const struct ofpact_sample *os) +{ + if (os->obs_domain_src.field) { + uint32_t obs_domain_id; + + obs_domain_id = mf_get_subfield(&os->obs_domain_src, &ctx->xin->flow); + mf_write_subfield_flow(&os->obs_domain_src, &exact_sub_match_mask, + &ctx->wc->masks); + + return obs_domain_id; + } else { + return os->obs_domain_imm; + } +} + +static uint32_t +ofpact_sample_get_point(struct xlate_ctx *ctx, + const struct ofpact_sample *os) +{ + if (os->obs_point_src.field) { + uint32_t obs_point_id; + + obs_point_id = mf_get_subfield(&os->obs_point_src, &ctx->xin->flow); + mf_write_subfield_flow(&os->obs_point_src, &exact_sub_match_mask, + &ctx->wc->masks); + + return obs_point_id; + } else { + return os->obs_point_imm; + } +} + static void xlate_fill_ipfix_sample(struct xlate_ctx *ctx, const struct ofpact_sample *os, @@ -5968,8 +6002,10 @@ xlate_fill_ipfix_sample(struct xlate_ctx *ctx, userspace->cookie.ofproto_uuid = ctx->xbridge->ofproto->uuid; userspace->cookie.flow_sample.probability = os->probability; userspace->cookie.flow_sample.collector_set_id = os->collector_set_id; - userspace->cookie.flow_sample.obs_domain_id = os->obs_domain_id; - userspace->cookie.flow_sample.obs_point_id = os->obs_point_id; + userspace->cookie.flow_sample.obs_domain_id = + ofpact_sample_get_domain(ctx, os); + userspace->cookie.flow_sample.obs_point_id = + ofpact_sample_get_point(ctx, os); userspace->cookie.flow_sample.output_odp_port = output_odp_port; userspace->cookie.flow_sample.direction = os->direction; userspace->include_actions = false; @@ -6003,8 +6039,8 @@ xlate_sample_action(struct xlate_ctx *ctx, dpif_lsample_get_group_id(lsample, os->collector_set_id, &psample.group_id)) { - psample.cookie.hi = htonl(os->obs_domain_id); - psample.cookie.lo = htonl(os->obs_point_id); + psample.cookie.hi = htonl(ofpact_sample_get_domain(ctx, os)); + psample.cookie.lo = htonl(ofpact_sample_get_point(ctx,os)); compose_args.psample = &psample; diff --git a/python/ovs/flow/ofp.py b/python/ovs/flow/ofp.py index 3d3226c91..f011b0460 100644 --- a/python/ovs/flow/ofp.py +++ b/python/ovs/flow/ofp.py @@ -30,7 +30,7 @@ from ovs.flow.ofp_act import ( decode_move_field, decode_dec_ttl, decode_chk_pkt_larger, - decode_zone, + decode_field_or_int, decode_learn, ) @@ -330,7 +330,7 @@ class OFPFlow(Flow): KVDecoders( { "commit": decode_flag, - "zone": decode_zone, + "zone": decode_field_or_int, "table": decode_int, "nat": decode_nat, "force": decode_flag, @@ -426,8 +426,8 @@ class OFPFlow(Flow): { "probability": decode_int, "collector_set_id": decode_int, - "obs_domain_id": decode_int, - "obs_point_id": decode_int, + "obs_domain_id": decode_field_or_int, + "obs_point_id": decode_field_or_int, "sampling_port": decode_default, "ingress": decode_flag, "egress": decode_flag, diff --git a/python/ovs/flow/ofp_act.py b/python/ovs/flow/ofp_act.py index 2c85076a3..73727428a 100644 --- a/python/ovs/flow/ofp_act.py +++ b/python/ovs/flow/ofp_act.py @@ -246,9 +246,9 @@ def decode_chk_pkt_larger(value): return {"pkt_len": pkt_len, "dst": dst} -# CT decoders -def decode_zone(value): - """Decodes the value of the 'zone' keyword (part of the ct action).""" +def decode_field_or_int(value): + """Decodes a value that can be either a subfield specification or an + integer.""" try: return int(value, 0) except ValueError: diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 40a23bb15..86aec12e8 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -136,6 +136,9 @@ ffff 0020 00002320 0026 3039 00005BA0 00008707 0000B26E DDD50000 00000000 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) ffff 0020 00002320 0029 3039 00005BA0 00008707 0000B26E DDD50200 00000000 +# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) +ffff 0028 00002320 0033 3039 00005ba0 00000002 000f0000 0001d810 081f0000 0000 000000000000 + # bad OpenFlow10 actions: OFPBAC_BAD_LEN & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 240 exceeds action buffer length 8 & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_LEN): @@ -489,6 +492,9 @@ ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E +# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) +ffff 0028 00002320 0033 3039 00005ba0 00000002 000f0000 0001d810 081f0000 0000 000000000000 + # bad OpenFlow11 actions: OFPBAC_BAD_OUT_PORT & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_OUT_PORT): & 00000000 00 00 00 10 ff ff ff ff-00 00 00 00 00 00 00 00 @@ -1121,6 +1127,8 @@ bad_action 'unroll_xlate' "UNROLL is an internal action that shouldn't be used v # sample bad_action 'sample(probability=0)' 'invalid probability value "0"' bad_action 'sample(sampling_port=asdf)' 'asdf: unknown port' +bad_action 'sample(probability=12345,obs_point_id=NXM_NX_CT_LABEL[[0..32]])' \ + 'size of obs_point_id field (33) exceeds maximum (32)' bad_action 'sample(foo=bar)' 'invalid key "foo" in "sample" argument' bad_action 'sample' 'non-zero "probability" must be specified on sample' diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index f65ace850..8bbcfa2eb 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -8304,6 +8304,50 @@ AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto-dpif - Flow IPFIX sanity check - from field]) +OVS_VSWITCHD_START +add_of_ports br0 1 2 + +AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ + -- --id=@ipfix create IPFIX targets=\"127.0.0.1:5500\" \ + -- --id=@cs create Flow_Sample_Collector_Set id=0 \ + bridge=@br0 ipfix=@ipfix], + [0], [ignore]) + +m4_define([SAMPLE_ACTION], + [sample(probability=65535,collector_set_id=1,obs_domain_id=NXM_OF_IN_PORT,obs_point_id=$1)]dnl +) + +dnl Store in_port in obs_domain_id and dp_hash in the obs_point_id. +AT_DATA([flows.txt], [dnl +priority=100,arp,action=normal +priority=10,in_port=1,ip actions=SAMPLE_ACTION(NXM_NX_DP_HASH),2 +priority=10,in_port=2,ip actions=SAMPLE_ACTION(NXM_NX_CT_LABEL[[[0..31]]]),1 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) + +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ + "in_port(1),dp_hash(45),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ + ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) + +AT_CHECK([tail -2 stdout], [0], [dnl +Megaflow: recirc_id=0,dp_hash=0x2d,eth,ip,in_port=1,nw_frag=no +Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=1,obs_point_id=45,output_port=4294967295)),2 +]) + +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ + "in_port(2),ct_label(0x1234567890abcdef1234567890abcdef),\ + eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ + ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) + +AT_CHECK([tail -2 stdout], [0], [dnl +Megaflow: recirc_id=0,ct_label=0x90abcdef/0xffffffff,eth,ip,in_port=2,nw_frag=no +Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=2,obs_point_id=2427178479,output_port=4294967295)),1 +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto-dpif - clone action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index d03d36500..e2f4429ae 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -198,6 +198,8 @@ actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_ actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,ingress) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63]) ip,actions=ct(nat) ip,actions=ct(commit,nat(dst)) ip,actions=ct(commit,nat(src)) @@ -233,6 +235,8 @@ OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_d OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,ingress) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) +OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) +OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63]) OFPT_FLOW_MOD: ADD ip actions=ct(nat) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(dst)) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(src)) @@ -265,6 +269,7 @@ sctp actions=drop in_port=0 actions=resubmit:0 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt @@ -286,6 +291,7 @@ OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop OFPT_FLOW_MOD (OF1.1): ADD in_port=0 actions=resubmit:0 OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CLEANUP @@ -312,6 +318,7 @@ in_port=0 actions=mod_dl_src:11:22:33:44:55:66,mod_dl_dst:10:20:30:40:50:60 in_port=0 actions=resubmit:0 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt @@ -339,6 +346,7 @@ OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=set_field:11:22:33:44:55:66->eth_sr OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=resubmit:0 OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CLEANUP @@ -441,6 +449,7 @@ tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15) actions=controller(max_len=123,reason=invalid_ttl,id=555) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789) mpls,mpls_label=5,mpls_tc=1,mpls_ttl=1,mpls_bos=0,actions=drop ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) @@ -508,6 +517,7 @@ NXT_FLOW_MOD: ADD table:255 tcp actions=fin_timeout(idle_timeout=5,hard_timeout= NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555) NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789) NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=5,mpls_tc=1,mpls_ttl=1,mpls_bos=0 actions=drop NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) @@ -567,6 +577,7 @@ dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff,actions=drop dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[[]],obs_point_id=NXM_NX_CT_LABEL[[32..63]],sampling_port=56789,egress) ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[[]]))) ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[[]]))) @@ -608,6 +619,7 @@ NXT_FLOW_MOD: ADD dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff actions=drop NXT_FLOW_MOD: ADD actions=drop NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +NXT_FLOW_MOD: ADD actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[[]],obs_point_id=NXM_NX_CT_LABEL[[32..63]],sampling_port=56789,egress) NXT_FLOW_MOD: ADD ip actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[[]])) NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[[0..63]],load:0->NXM_NX_CT_LABEL[[64..127]])) @@ -648,6 +660,7 @@ actions=push:reg0[0..31],pop:reg0 vlan_tci=0x1123/0x1fff,actions=drop actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_LABEL[]))) @@ -688,6 +701,7 @@ NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) +NXT_FLOW_MOD: ADD <any> actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) diff --git a/tests/system-traffic.at b/tests/system-traffic.at index cab811ad0..2d3f7ab39 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -9461,3 +9461,95 @@ dnl OVS will fail to send IPFIX packets because the target is localhost dnl and the port is closed. Ignore the message it generates. OVS_TRAFFIC_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP + +AT_SETUP([psample - from ct label]) +CHECK_CONNTRACK() +OVS_TRAFFIC_VSWITCHD_START() +OVS_CHECK_PSAMPLE() + +ADD_NAMESPACES(at_ns0, at_ns1) +NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) +NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) + +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "e4:11:22:33:44:55") +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "e4:11:22:33:44:66") + +AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ + -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ + -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ + ipfix=@ipfix, local_group_id=10 \ + -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ + ipfix=@ipfix, local_group_id=12], + [0], [ignore]) + +m4_define([CT_STORE_ACT], + [ct(zone=5,commit,exec(load:0x0bb102030->NXM_NX_CT_LABEL[[0..31]],load:0xbb405060->NXM_NX_CT_LABEL[[32..63]]))]) + +AT_DATA([flows.txt], [dnl +priority=100,ip actions=ct(zone=5, table=10) +priority=0 actions=NORMAL +table=10,priority=100,ip,ct_state=+trk+new action=SAMPLE_ACTION(1, 2853183536, 2856341600),CT_STORE_ACT,NORMAL +table=10,priority=100,ip,ct_state=+trk-new action=SAMPLE_ACTION(2, NXM_NX_CT_LABEL[[[0..31]]], NXM_NX_CT_LABEL[[[32..63]]]),NORMAL +table=10, priority=50, ip, actions=DROP +]) + +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample1.pid]) +OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl +1 packets transmitted, 1 received, 0% packet loss, time 0ms +]) + +m4_define([SAMPLE1], [m4_join([ ], + [group_id=0xa,prob=4294967295], + [obs_domain=0xaa102030,obs_point=0xaa405060], + [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) + +m4_define([SAMPLE2], [m4_join([ ], + [group_id=0xc,prob=4294967295], + [obs_domain=0xbb102030,obs_point=0xbb405060], + [.*icmp.*nw_src=10.1.1.2,nw_dst=10.1.1.1])]) +AT_CHECK([grep -qE 'SAMPLE1' psample.out]) +AT_CHECK([grep -qE 'SAMPLE2' psample.out]) + +m4_define([FLOW_MATCH], [m4_join([], + [ct_label(0xbb405060bb102030/0xffffffffffffffff).*actions:], + [actions:psample(group=12,cookie=0xbb102030bb405060),], + [userspace(pid=[[0-9]]+,flow_sample(.*obs_domain_id=3138396208,obs_point_id=3141554272.*))] +)]) + +AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p1 \ + | grep -qE 'FLOW_MATCH' ], [0], []) + +dnl Check IPFIX samples have been received. +dnl Entries can be unsorted and IFPIX packets might not have been sent (or +dnl at least tried to be sent) yet. +OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-ipfix-flow br0 | \ + sed 's/tx pkts=[[0-9]]*/tx pkts=24/' | \ + sed 's/tx errs=[[0-9]]*/tx errs=0/' | \ + sed 's/id [[1-2]]:/id ?:/'], [dnl +NXST_IPFIX_FLOW reply (xid=0x2): 2 ids + id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 + pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0 + id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 + pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0]) + +AT_CHECK([ovs-appctl lsample/show br0], [0], [dnl +Local sample statistics for bridge "br0": +Collector Set ID: 1: + Group ID : 10 + Total packets: 1 + Total bytes : 98 + +Collector Set ID: 2: + Group ID : 12 + Total packets: 1 + Total bytes : 98 +]) + +dnl OVS will fail to send IPFIX packets because the target is localhost +dnl and the port is closed. Ignore the message it generates. +OVS_TRAFFIC_VSWITCHD_STOP(["/sending to collector failed/d"]) +AT_CLEANUP
When sample action gets used as a way of sampling traffic with controller-generated metadata (i.e: obs_domain_id and obs_point_id), the controller will have to increase the number of flows to ensure each part of the pipeline contains the right metadata. As an example, if the controller decides to sample stateful traffic, it could store the computed metadata for each connection in the conntrack label. However, for established connections, a flow must be created for each different ct_label value with a sample action that contains a different hardcoded obs_domain and obs_point id. This patch adds a new version of the NXAST_RAW_SAMPLE* action (number 4) that supports specifying the observation point and domain using an OpenFlow field reference, so now the controller can express: sample(... obs_domain_id=NXM_NX_CT_LABEL[0..31], obs_point_id=NXM_NX_CT_LABEL[32..63] ... ) Signed-off-by: Adrian Moreno <amorenoz@redhat.com> --- Documentation/ref/ovs-actions.7.rst | 12 +- NEWS | 2 + include/openvswitch/ofp-actions.h | 8 +- lib/ofp-actions.c | 245 +++++++++++++++++++++++++--- ofproto/ofproto-dpif-xlate.c | 44 ++++- python/ovs/flow/ofp.py | 8 +- python/ovs/flow/ofp_act.py | 6 +- tests/ofp-actions.at | 8 + tests/ofproto-dpif.at | 44 +++++ tests/ovs-ofctl.at | 14 ++ tests/system-traffic.at | 92 +++++++++++ 11 files changed, 442 insertions(+), 41 deletions(-)