[I did not receive this email either.] With the comments below,
Acked-by: Jarno Rajahalme <jrajaha...@nicira.com> > This patch adds a new 128-bit metadata field to the connection tracking > interface. When a label is specified as part of the ct action and the > connection is committed, the value is saved with the current connection. Saving seems to happen also with non-commit ct actions. > Subsequent ct lookups with the table specified will expose this metadata > as the "ct_label" field in the flow. > > For example, to allow new connections from port 1->2 and only allow > established connections from port 2->1, and to associate a label with > those connections: > > priority=1,action=drop > priority=10,arp,action=normal > priority=10,icmp,action=normal > in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2 > in_port=2,ct_state=-trk,tcp,action=ct(table=1) > table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1 > > Signed-off-by: Joe Stringer <joestrin...@nicira.com> > --- > NEWS | 4 +- > build-aux/extract-ofp-fields | 2 + > datapath/flow_netlink.c | 2 +- > datapath/linux/compat/include/linux/openvswitch.h | 10 ++ > lib/dpif-netdev.c | 3 +- > lib/flow.c | 16 +++ > lib/flow.h | 4 +- > lib/match.c | 37 +++++++ > lib/match.h | 2 + > lib/meta-flow.c | 71 ++++++++++++++ > lib/meta-flow.h | 19 ++++ > lib/nx-match.c | 13 +++ > lib/odp-execute.c | 2 + > lib/odp-util.c | 114 > +++++++++++++++++++++- > lib/odp-util.h | 4 +- > lib/ofp-actions.c | 11 ++- > lib/packets.h | 1 + > ofproto/ofproto-dpif-sflow.c | 1 + > ofproto/ofproto-dpif-xlate.c | 31 ++++++ > ofproto/ofproto-dpif.c | 7 +- > ofproto/ofproto-unixctl.man | 2 + > tests/dpif-netdev.at | 2 +- > tests/odp.at | 6 +- > tests/ofproto-dpif.at | 4 +- > tests/ofproto.at | 3 +- > tests/system-traffic.at | 39 ++++++++ > tests/test-odp.c | 1 + > utilities/ovs-ofctl.8.in | 8 ++ > 28 files changed, 401 insertions(+), 18 deletions(-) > > diff --git a/NEWS b/NEWS > index 6eeccdc..7045d72 100644 > --- a/NEWS > +++ b/NEWS > @@ -22,8 +22,8 @@ Post-v2.4.0 > a Vagrant box. See INSTALL.md for details > - Dropped support for GRE64 tunnel. > - Add support for connection tracking through the new "ct" action > - and "ct_state"/"ct_zone"/"ct_mark" match fields. Only available on > - Linux kernels with the connection tracking module loaded. > + and "ct_state"/"ct_zone"/"ct_mark"/"ct_label" match fields. Only > + available on Linux kernels with the connection tracking module loaded. > > > v2.4.0 - 20 Aug 2015 > diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields > index b0d9681..ecd535e 100755 > --- a/build-aux/extract-ofp-fields > +++ b/build-aux/extract-ofp-fields > @@ -20,11 +20,13 @@ TYPES = {"u8": (1, False), > "MAC": (6, False), > "be64": (8, False), > "IPv6": (16, False), > + "u128": (16, False), > "tunnelMD": (124, True)} > > FORMATTING = {"decimal": ("MFS_DECIMAL", 1, 8), > "hexadecimal": ("MFS_HEXADECIMAL", 1, 127), > "conn state": ("MFS_CT_STATE", 1, 1), > + "conn label": ("MFS_CT_LABEL", 16, 16), > "Ethernet": ("MFS_ETHERNET", 6, 6), > "IPv4": ("MFS_IPV4", 4, 4), > "IPv6": ("MFS_IPV6", 16, 16), > diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c > index 352fc87..780dda4 100644 > --- a/datapath/flow_netlink.c > +++ b/datapath/flow_netlink.c > @@ -281,7 +281,7 @@ size_t ovs_key_attr_size(void) > /* Whenever adding new OVS_KEY_ FIELDS, we should consider > * updating this function. > */ > - BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 25); > + BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 26); > > return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ > + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ > diff --git a/datapath/linux/compat/include/linux/openvswitch.h > b/datapath/linux/compat/include/linux/openvswitch.h > index 5c7b2af..39a321e 100644 > --- a/datapath/linux/compat/include/linux/openvswitch.h > +++ b/datapath/linux/compat/include/linux/openvswitch.h > @@ -346,6 +346,7 @@ enum ovs_key_attr { > OVS_KEY_ATTR_CT_STATE, /* u8 bitmask of OVS_CS_F_* */ > OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */ > OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */ > + OVS_KEY_ATTR_CT_LABEL, /* 16-octet connection tracking label */ > > #ifdef __KERNEL__ > /* Only used within kernel data path. */ > @@ -459,6 +460,11 @@ struct ovs_key_nd { > __u8 nd_tll[ETH_ALEN]; > }; > > +#define OVS_CT_LABEL_LEN 16 > +struct ovs_key_ct_label { > + __u8 ct_label[OVS_CT_LABEL_LEN]; > +}; > + This would be more efficient if defined as __u32 ct_label[4]. > /* OVS_KEY_ATTR_CT_STATE flags */ > #define OVS_CS_F_NEW 0x01 /* Beginning of a new connection. */ > #define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */ > @@ -660,12 +666,16 @@ struct ovs_action_push_tnl { > * @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the > * mask, the corresponding bit in the value is copied to the connection > * tracking mark field in the connection. > + * @OVS_CT_ATTR_LABEL: %OVS_CT_LABEL_LEN value followed by %OVS_CT_LABEL_LEN > + * mask. For each bit set in the mask, the corresponding bit in the value is > + * copied to the connection tracking label field in the connection. > */ > enum ovs_ct_attr { > OVS_CT_ATTR_UNSPEC, > OVS_CT_ATTR_FLAGS, /* u32 bitmask of OVS_CT_F_*. */ > OVS_CT_ATTR_ZONE, /* u16 zone id. */ > OVS_CT_ATTR_MARK, /* mark to associate with this connection. */ > + OVS_CT_ATTR_LABEL, /* label to associate with this connection. */ Nit about the terminology: conntrack treats each bit as a label, so the collection of them would be “labels”? > __OVS_CT_ATTR_MAX > }; > > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c > index 8a183c0..666bdb8 100644 > --- a/lib/dpif-netdev.c > +++ b/lib/dpif-netdev.c > @@ -1921,7 +1921,8 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, > uint32_t key_len, > } > > /* Userspace datapath doesn't support conntrack. */ > - if (flow->ct_state || flow->ct_zone || flow->ct_mark) { > + if (flow->ct_state || flow->ct_zone || flow->ct_mark > + || !is_all_zeros(&flow->ct_label, sizeof(flow->ct_label))) { Could this use ovs_u128_is_zero() function introduced in the previous patch? > return EINVAL; > } > > diff --git a/lib/flow.c b/lib/flow.c > index 3ae1432..fbd7c60 100644 > --- a/lib/flow.c > +++ b/lib/flow.c > @@ -267,6 +267,9 @@ BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() > will have runtime " > MF.data += 1; /* First word only. */ \ > } > > +#define miniflow_push_uint64(MF, FIELD, VALUE) \ > + miniflow_push_uint64_(MF, offsetof(struct flow, FIELD), VALUE) > + Apparently this macro is not used, so it could be removed? > #define miniflow_push_uint32(MF, FIELD, VALUE) \ > miniflow_push_uint32_(MF, offsetof(struct flow, FIELD), VALUE) > > @@ -507,6 +510,11 @@ miniflow_extract(struct dp_packet *packet, struct > miniflow *dst) > miniflow_push_uint16(mf, ct_zone, md->ct_zone); > miniflow_push_uint8(mf, ct_state, md->ct_state); > miniflow_pad_to_64(mf, pad1); > + > + if (!ovs_u128_is_zero(&md->ct_label)) { > + miniflow_push_words(mf, ct_label, &md->ct_label, > + sizeof md->ct_label / 8); Would be nice to use sizeof(uint64_t) instead of “8”. > + } > } > > /* Initialize packet's layer pointer and offsets. */ > @@ -868,6 +876,9 @@ flow_get_metadata(const struct flow *flow, struct match > *flow_metadata) > if (flow->ct_mark != 0) { > match_set_ct_mark(flow_metadata, flow->ct_mark); > } > + if (!is_all_zeros(&flow->ct_label, sizeof(flow->ct_label))) { > + match_set_ct_label(flow_metadata, flow->ct_label); > + } I think we should use the u128 in struct flow, so all the is_all_zeros() calls introduced here could be replaced with u128 equivalent. > } > > char * > @@ -1152,6 +1163,9 @@ flow_format(struct ds *ds, const struct flow *flow) > if (!flow->ct_mark) { > WC_UNMASK_FIELD(wc, ct_mark); > } > + if (is_all_zeros(&flow->ct_label, sizeof(flow->ct_label))) { > + WC_UNMASK_FIELD(wc, ct_label); > + } > for (int i = 0; i < FLOW_N_REGS; i++) { > if (!flow->regs[i]) { > WC_UNMASK_FIELD(wc, regs[i]); > @@ -1229,6 +1243,7 @@ void flow_wildcards_init_for_packet(struct > flow_wildcards *wc, > WC_MASK_FIELD(wc, ct_state); > WC_MASK_FIELD(wc, ct_zone); > WC_MASK_FIELD(wc, ct_mark); > + WC_MASK_FIELD(wc, ct_label); > WC_MASK_FIELD(wc, recirc_id); > WC_MASK_FIELD(wc, dp_hash); > WC_MASK_FIELD(wc, in_port); > @@ -1335,6 +1350,7 @@ flow_wc_map(const struct flow *flow, struct flowmap > *map) > FLOWMAP_SET(map, ct_state); > FLOWMAP_SET(map, ct_zone); > FLOWMAP_SET(map, ct_mark); > + FLOWMAP_SET(map, ct_label); > > /* Ethertype-dependent fields. */ > if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) { > diff --git a/lib/flow.h b/lib/flow.h > index b5edaa0..92d1ee7 100644 > --- a/lib/flow.h > +++ b/lib/flow.h > @@ -107,6 +107,7 @@ struct flow { > uint16_t ct_zone; /* Connection Zone. */ > uint8_t ct_state; /* Connection state. */ > uint8_t pad1[1]; /* Pad to 64 bits. */ > + ovs_u128 ct_label; /* Connection label. */ Oh, we do already :-) > ofp_port_t actset_output; /* Output port in action set. */ > uint8_t pad2[6]; /* Pad to 64 bits. */ > > @@ -157,7 +158,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % > sizeof(uint64_t) == 0); > > /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ > BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) > - == sizeof(struct flow_tnl) + 200 > + == sizeof(struct flow_tnl) + 216 > && FLOW_WC_SEQ == 33); > > /* Incremental points at which flow classification may be performed in > @@ -987,6 +988,7 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const > struct flow *flow) > md->ct_state = flow->ct_state; > md->ct_zone = flow->ct_zone; > md->ct_mark = flow->ct_mark; > + md->ct_label = flow->ct_label; > } > > static inline bool is_ip_any(const struct flow *flow) > diff --git a/lib/match.c b/lib/match.c > index 7e0d7dd..9ca82ba 100644 > --- a/lib/match.c > +++ b/lib/match.c > @@ -319,6 +319,25 @@ match_set_ct_mark_masked(struct match *match, uint32_t > ct_mark, > } > > void > +match_set_ct_label(struct match *match, ovs_u128 ct_label) > +{ > + ovs_u128 mask; > + > + mask.u64.lo = UINT64_MAX; > + mask.u64.hi = UINT64_MAX; > + match_set_ct_label_masked(match, ct_label, mask); > +} > + > +void > +match_set_ct_label_masked(struct match *match, ovs_u128 value, ovs_u128 mask) > +{ > + match->flow.ct_label.u64.lo = value.u64.lo & mask.u64.lo; > + match->flow.ct_label.u64.hi = value.u64.hi & mask.u64.hi; > + match->wc.masks.ct_label.u64.lo = mask.u64.lo; > + match->wc.masks.ct_label.u64.hi = mask.u64.hi; > +} > + > +void > match_set_dl_type(struct match *match, ovs_be16 dl_type) > { > match->wc.masks.dl_type = OVS_BE16_MAX; > @@ -957,6 +976,20 @@ format_flow_tunnel(struct ds *s, const struct match > *match) > tun_metadata_match_format(s, match); > } > > +static void > +format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 > *mask) > +{ > + if (!is_all_zeros(mask, sizeof(*mask))) { > + ds_put_format(s, "ct_label="); > + ds_put_hex(s, key, sizeof(*key)); > + if (!is_all_ones(mask, sizeof(*mask))) { Here as well. > + ds_put_char(s, '/'); > + ds_put_hex(s, mask, sizeof(*mask)); > + } > + ds_put_char(s, ','); > + } > +} > + > /* Appends a string representation of 'match' to 's'. If 'priority' is > * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */ > void > @@ -1026,6 +1059,10 @@ match_format(const struct match *match, struct ds *s, > int priority) > format_uint32_masked(s, "ct_mark", f->ct_mark, wc->masks.ct_mark); > } > > + if (!is_all_zeros(&wc->masks.ct_label, sizeof(wc->masks.ct_label))) { Ditto. > + format_ct_label_masked(s, &f->ct_label, &wc->masks.ct_label); > + } > + > if (wc->masks.dl_type) { > skip_type = true; > if (f->dl_type == htons(ETH_TYPE_IP)) { > diff --git a/lib/match.h b/lib/match.h > index 5d606ae..cf23e65 100644 > --- a/lib/match.h > +++ b/lib/match.h > @@ -88,6 +88,8 @@ void match_set_ct_state_masked(struct match *, uint8_t > ct_state, uint8_t mask); > void match_set_ct_zone(struct match *, uint16_t ct_zone); > void match_set_ct_mark(struct match *, uint32_t ct_mark); > void match_set_ct_mark_masked(struct match *, uint32_t ct_mark, uint32_t > mask); > +void match_set_ct_label(struct match *, ovs_u128 ct_label); > +void match_set_ct_label_masked(struct match *, ovs_u128 ct_label, ovs_u128 > mask); > void match_set_skb_priority(struct match *, uint32_t skb_priority); > void match_set_dl_type(struct match *, ovs_be16); > void match_set_dl_src(struct match *, const struct eth_addr ); > diff --git a/lib/meta-flow.c b/lib/meta-flow.c > index 444c2e4..27d2060 100644 > --- a/lib/meta-flow.c > +++ b/lib/meta-flow.c > @@ -220,6 +220,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct > flow_wildcards *wc) > return !wc->masks.ct_zone; > case MFF_CT_MARK: > return !wc->masks.ct_mark; > + case MFF_CT_LABEL: > + return is_all_zeros(&wc->masks.ct_label, sizeof(wc->masks.ct_label)); Ditto. > CASE_MFF_REGS: > return !wc->masks.regs[mf->id - MFF_REG0]; > CASE_MFF_XREGS: > @@ -506,6 +508,7 @@ mf_is_value_valid(const struct mf_field *mf, const union > mf_value *value) > case MFF_CT_STATE: > case MFF_CT_ZONE: > case MFF_CT_MARK: > + case MFF_CT_LABEL: > CASE_MFF_REGS: > CASE_MFF_XREGS: > case MFF_ETH_SRC: > @@ -665,6 +668,10 @@ mf_get_value(const struct mf_field *mf, const struct > flow *flow, > value->be32 = htonl(flow->ct_mark); > break; > > + case MFF_CT_LABEL: > + memcpy(&value->u128, &flow->ct_label, sizeof(flow->ct_label)); Would an assignment work here? > + break; > + > CASE_MFF_REGS: > value->be32 = htonl(flow->regs[mf->id - MFF_REG0]); > break; > @@ -909,6 +916,10 @@ mf_set_value(const struct mf_field *mf, > match_set_ct_mark(match, ntohl(value->be32)); > break; > > + case MFF_CT_LABEL: > + match_set_ct_label(match, value->u128); > + break; > + > CASE_MFF_REGS: > match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32)); > break; > @@ -1205,6 +1216,10 @@ mf_set_flow_value(const struct mf_field *mf, > flow->ct_mark = ntohl(value->be32); > break; > > + case MFF_CT_LABEL: > + memcpy(&flow->ct_label, &value->u128, sizeof(flow->ct_label)); And here? > + break; > + > CASE_MFF_REGS: > flow->regs[mf->id - MFF_REG0] = ntohl(value->be32); > break; > @@ -1509,6 +1524,11 @@ mf_set_wild(const struct mf_field *mf, struct match > *match, char **err_str) > match->wc.masks.ct_mark = 0; > break; > > + case MFF_CT_LABEL: > + memset(&match->flow.ct_label, 0, sizeof(match->flow.ct_label)); > + memset(&match->wc.masks.ct_label, 0, > sizeof(match->wc.masks.ct_label)); > + break; > + > CASE_MFF_REGS: > match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0); > break; > @@ -1780,6 +1800,10 @@ mf_set(const struct mf_field *mf, > match_set_ct_mark_masked(match, ntohl(value->be32), > ntohl(mask->be32)); > break; > > + case MFF_CT_LABEL: > + match_set_ct_label_masked(match, value->u128, mask->u128); > + break; > + > case MFF_ETH_DST: > match_set_dl_dst_masked(match, value->mac, mask->mac); > break; > @@ -1971,6 +1995,32 @@ mf_from_integer_string(const struct mf_field *mf, > const char *s, > } > > static char * > +mf_from_u128_string(const struct mf_field *mf, const char *s_, > + ovs_u128 *valuep, ovs_u128 *maskp) > +{ > + char *s = CONST_CAST(char *, s_); > + > + ovs_assert(mf->n_bytes == sizeof(*valuep)); > + > + if (!parse_int_string(s, (uint8_t *)valuep, sizeof(*valuep), &s)) { > + if (strlen(s)) { > + if (*s == '/' > + && !parse_int_string(s + 1, (uint8_t *)maskp, sizeof(*maskp), > + &s)) { > + return NULL; > + } else { > + /* parse error */ > + } > + } else { > + memset(maskp, 0xff, sizeof(*maskp)); > + return NULL; > + } > + } > + > + return xasprintf("%s: invalid u128 for %s", s, mf->name); > +} > + > +static char * > mf_from_ethernet_string(const struct mf_field *mf, const char *s, > struct eth_addr *mac, struct eth_addr *mask) > { > @@ -2214,6 +2264,11 @@ mf_parse(const struct mf_field *mf, const char *s, > error = mf_from_ct_state_string(s, &value->u8, &mask->u8); > break; > > + case MFS_CT_LABEL: > + ovs_assert(mf->n_bytes == sizeof(ovs_u128)); > + error = mf_from_u128_string(mf, s, &value->u128, &mask->u128); > + break; > + > case MFS_ETHERNET: > error = mf_from_ethernet_string(mf, s, &value->mac, &mask->mac); > break; > @@ -2341,6 +2396,18 @@ mf_format_ct_state_string(uint8_t value, uint8_t mask, > struct ds *s) > UINT8_MAX); > } > > +static void > +mf_format_ct_label_string(const ovs_u128 *value, const ovs_u128 *mask, > + struct ds *s) > +{ > + ds_put_format(s, "ct_label="); > + ds_put_hex(s, value, sizeof(*value)); > + if (mask) { > + ds_put_char(s, '/'); > + ds_put_hex(s, mask, sizeof(*mask)); > + } > +} > + > /* Appends to 's' a string representation of field 'mf' whose value is in > * 'value' and 'mask'. 'mask' may be NULL to indicate an exact match. */ > void > @@ -2381,6 +2448,10 @@ mf_format(const struct mf_field *mf, > mf_format_ct_state_string(value->u8, mask ? mask->u8 : UINT8_MAX, s); > break; > > + case MFS_CT_LABEL: > + mf_format_ct_label_string(&value->u128, (ovs_u128 *)mask, s); > + break; > + > case MFS_ETHERNET: > eth_format_masked(value->mac, mask ? &mask->mac : NULL, s); > break; > diff --git a/lib/meta-flow.h b/lib/meta-flow.h > index 576f4d7..2f22337 100644 > --- a/lib/meta-flow.h > +++ b/lib/meta-flow.h > @@ -765,6 +765,23 @@ enum OVS_PACKED_ENUM mf_field_id { > */ > MFF_CT_MARK, > > + /* "ct_label". > + * > + * Connection tracking label. The label is carried with the > + * connection tracking state. On Linux this is held in the > + * conntrack label extension but the exact implementation is > + * platform-dependent. > + * > + * Type: u128. > + * Maskable: bitwise. > + * Formatting: conn label. > + * Prerequisites: none. > + * Access: read/write. Mention writability restrictions in the comment above? > + * NXM: NXM_NX_CT_LABEL(108) since v2.5. > + * OXM: none. > + */ > + MFF_CT_LABEL, > + > #if FLOW_N_REGS == 8 > /* "reg<N>". > * > @@ -1742,6 +1759,7 @@ enum OVS_PACKED_ENUM mf_string { > > /* Other formats. */ > MFS_CT_STATE, /* Connection tracking state */ > + MFS_CT_LABEL, /* Connection tracking label */ > MFS_ETHERNET, > MFS_IPV4, > MFS_IPV6, > @@ -1809,6 +1827,7 @@ union mf_value { > ovs_be32 be32; > ovs_be16 be16; > uint8_t u8; > + ovs_u128 u128; > }; > BUILD_ASSERT_DECL(sizeof(union mf_value) == 128); > BUILD_ASSERT_DECL(sizeof(union mf_value) >= GENEVE_MAX_OPT_SIZE); > diff --git a/lib/nx-match.c b/lib/nx-match.c > index c298a6f..1d34d6a 100644 > --- a/lib/nx-match.c > +++ b/lib/nx-match.c > @@ -781,6 +781,14 @@ nxm_put_frag(struct ofpbuf *b, const struct match *match, > nw_frag_mask == FLOW_NW_FRAG_MASK ? UINT8_MAX : nw_frag_mask); > } > > +static void > +nxm_put_ct_label(struct ofpbuf *b, > + enum mf_field_id field, enum ofp_version version, > + const ovs_u128 value, const ovs_u128 mask) > +{ > + nxm_put(b, field, version, &value, &mask, sizeof(value)); > +} > + > /* Appends to 'b' a set of OXM or NXM matches for the IPv4 or IPv6 fields in > * 'match'. */ > static void > @@ -1049,6 +1057,11 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, > const struct match *match, > nxm_put_32m(b, MFF_CT_MARK, oxm, htonl(flow->ct_mark), > htonl(match->wc.masks.ct_mark)); > } > + if (!is_all_zeros(&match->wc.masks.ct_label, Ditto. > + sizeof(match->wc.masks.ct_label))) { > + nxm_put_ct_label(b, MFF_CT_LABEL, oxm, flow->ct_label, > + match->wc.masks.ct_label); > + } > > /* OpenFlow 1.1+ Metadata. */ > nxm_put_64m(b, MFF_METADATA, oxm, > diff --git a/lib/odp-execute.c b/lib/odp-execute.c > index 4d6384e..209512b 100644 > --- a/lib/odp-execute.c > +++ b/lib/odp-execute.c > @@ -329,6 +329,7 @@ odp_execute_set_action(struct dp_packet *packet, const > struct nlattr *a) > case OVS_KEY_ATTR_CT_STATE: > case OVS_KEY_ATTR_CT_ZONE: > case OVS_KEY_ATTR_CT_MARK: > + case OVS_KEY_ATTR_CT_LABEL: > case __OVS_KEY_ATTR_MAX: > default: > OVS_NOT_REACHED(); > @@ -420,6 +421,7 @@ odp_execute_masked_set_action(struct dp_packet *packet, > case OVS_KEY_ATTR_CT_STATE: > case OVS_KEY_ATTR_CT_ZONE: > case OVS_KEY_ATTR_CT_MARK: > + case OVS_KEY_ATTR_CT_LABEL: > case OVS_KEY_ATTR_ENCAP: > case OVS_KEY_ATTR_ETHERTYPE: > case OVS_KEY_ATTR_IN_PORT: > diff --git a/lib/odp-util.c b/lib/odp-util.c > index 3784b0f..fa5bc86 100644 > --- a/lib/odp-util.c > +++ b/lib/odp-util.c > @@ -138,6 +138,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char > *namebuf, size_t bufsize) > case OVS_KEY_ATTR_CT_STATE: return "ct_state"; > case OVS_KEY_ATTR_CT_ZONE: return "ct_zone"; > case OVS_KEY_ATTR_CT_MARK: return "ct_mark"; > + case OVS_KEY_ATTR_CT_LABEL: return "ct_label"; > case OVS_KEY_ATTR_TUNNEL: return "tunnel"; > case OVS_KEY_ATTR_IN_PORT: return "in_port"; > case OVS_KEY_ATTR_ETHERNET: return "eth"; > @@ -543,12 +544,15 @@ static const struct nl_policy ovs_conntrack_policy[] = { > .min_len = sizeof(uint16_t)}, > [OVS_CT_ATTR_MARK] = { .type = NL_A_UNSPEC, .optional = true, > .min_len = sizeof(uint32_t) * 2 }, > + [OVS_CT_ATTR_LABEL] = { .type = NL_A_UNSPEC, .optional = true, > + .min_len = sizeof(ovs_u128) * 2 }, > }; > > static void > format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr) > { > struct nlattr *a[ARRAY_SIZE(ovs_conntrack_policy)]; > + const ovs_u128 *label; > const uint32_t *mark; > uint32_t flags; > uint16_t zone; > @@ -561,9 +565,10 @@ format_odp_conntrack_action(struct ds *ds, const struct > nlattr *attr) > flags = a[OVS_CT_ATTR_FLAGS] ? nl_attr_get_u32(a[OVS_CT_ATTR_FLAGS]) : 0; > zone = a[OVS_CT_ATTR_ZONE] ? nl_attr_get_u16(a[OVS_CT_ATTR_ZONE]) : 0; > mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL; > + label = a[OVS_CT_ATTR_LABEL] ? nl_attr_get(a[OVS_CT_ATTR_LABEL]): NULL; > > ds_put_format(ds, "ct"); > - if (flags || zone || mark) { > + if (flags || zone || mark || label) { > ds_put_cstr(ds, "("); > if (flags & OVS_CT_F_COMMIT) { > ds_put_format(ds, "commit"); > @@ -581,6 +586,15 @@ format_odp_conntrack_action(struct ds *ds, const struct > nlattr *attr) > ds_put_format(ds, "mark=%"PRIx32"/%"PRIx32, *mark, > *(mark + 1)); > } > + if (label) { > + if (ds_last(ds) != '(') { > + ds_put_char(ds, ','); > + } > + ds_put_format(ds, "label="); > + ds_put_hex(ds, label, sizeof(*label)); > + ds_put_char(ds, '/'); > + ds_put_hex(ds, (label + 1), sizeof(*label)); > + } > ds_put_cstr(ds, ")"); > } > } > @@ -1016,6 +1030,24 @@ ovs_parse_tnl_push(const char *s, struct > ovs_action_push_tnl *data) > } > > static int > +parse_ct_label(const char *s, struct ovs_key_ct_label *value, > + struct ovs_key_ct_label *mask, char **tail) > +{ > + if (!parse_int_string(s, (uint8_t *)value, sizeof(*value), tail)) { > + if (**tail == '/') { > + if (parse_int_string(s + 1, (uint8_t *)mask, sizeof(mask), > tail)) { > + return -EINVAL; > + } > + } else { > + memset(mask, 0xff, sizeof(*mask)); > + } > + return 0; > + } > + > + return -EINVAL; > +} > + > +static int > parse_conntrack_action(const char *s_, struct ofpbuf *actions) > { > const char *s = s_; > @@ -1027,9 +1059,15 @@ parse_conntrack_action(const char *s_, struct ofpbuf > *actions) > uint32_t value; > uint32_t mask; > } ct_mark = { 0, 0 }; > + struct { > + struct ovs_key_ct_label value; > + struct ovs_key_ct_label mask; > + } ct_label; > size_t start; > char *end; > > + memset(&ct_label, 0, sizeof(ct_label)); > + > s += 2; > if (ovs_scan(s, "(")) { > s++; > @@ -1039,6 +1077,7 @@ parse_conntrack_action(const char *s_, struct ofpbuf > *actions) > } > > while (s != end) { > + char *tail; > int n = -1; > > s += strspn(s, delimiters); > @@ -1061,6 +1100,18 @@ parse_conntrack_action(const char *s_, struct ofpbuf > *actions) > } > continue; > } > + if (ovs_scan(s, "label=%n", &n)) { > + int error; > + > + s += n; > + error = parse_ct_label(s, &ct_label.value, > &ct_label.mask, > + &tail); > + if (error) { > + return error; > + } > + s = tail; > + continue; > + } > > if (n < 0) { > return -EINVAL; > @@ -1080,6 +1131,10 @@ parse_conntrack_action(const char *s_, struct ofpbuf > *actions) > nl_msg_put_unspec(actions, OVS_CT_ATTR_MARK, &ct_mark, > sizeof(ct_mark)); > } > + if (!is_all_zeros(&ct_label.mask, sizeof(ct_label.mask))) { Ditto. > + nl_msg_put_unspec(actions, OVS_CT_ATTR_LABEL, &ct_label, > + sizeof(ct_label)); > + } > nl_msg_end_nested(actions, start); > } > > @@ -1350,6 +1405,7 @@ static const struct attr_len_tbl > ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = > [OVS_KEY_ATTR_CT_STATE] = { .len = 1 }, > [OVS_KEY_ATTR_CT_ZONE] = { .len = 2 }, > [OVS_KEY_ATTR_CT_MARK] = { .len = 4 }, > + [OVS_KEY_ATTR_CT_LABEL] = { .len = sizeof(struct ovs_key_ct_label) }, > }; > > /* Returns the correct length of the payload for a flow key attribute of the > @@ -2237,6 +2293,18 @@ format_odp_key_attr(const struct nlattr *a, const > struct nlattr *ma, > } > break; > > + case OVS_KEY_ATTR_CT_LABEL: { > + const struct ovs_key_ct_label *mask = ma ? nl_attr_get(ma) : NULL; > + > + if (verbose || (mask && !is_all_zeros(mask, sizeof(*mask)))) { > + ds_put_hex(ds, nl_attr_get(a), nl_attr_get_size(a)); > + if (mask && !is_exact) { > + ds_put_char(ds, '/'); > + ds_put_hex(ds, MASK(mask, ct_label), sizeof(*mask)); > + } > + } > + break; > + } > > case OVS_KEY_ATTR_TUNNEL: > format_odp_tun_attr(a, ma, ds, verbose); > @@ -2445,6 +2513,28 @@ generate_all_wildcard_mask(const struct attr_len_tbl > tbl[], int max, > return ofp->base; > } > > +static int > +scan_u128(const char *s_, ovs_u128 *key, ovs_u128 *mask) > +{ > + char *s = CONST_CAST(char *, s_); > + int n; > + > + if (parse_int_string(s, (uint8_t *)key, sizeof(*key), &s)) { > + return 0; > + } > + > + if (ovs_scan(s, "/%n", &n)) { > + s += n; > + if (parse_int_string(s, (uint8_t *)mask, sizeof(*mask), &s)) { > + return 0; > + } > + } else { > + mask->u64.hi = mask->u64.lo = UINT64_MAX; > + } > + > + return s - s_; > +} > + > int > odp_ufid_from_string(const char *s_, ovs_u128 *ufid) > { > @@ -3350,6 +3440,7 @@ parse_odp_key_mask_attr(const char *s, const struct > simap *port_names, > SCAN_SINGLE("ct_state(", uint8_t, ct_state, OVS_KEY_ATTR_CT_STATE); > SCAN_SINGLE("ct_zone(", uint16_t, u16, OVS_KEY_ATTR_CT_ZONE); > SCAN_SINGLE("ct_mark(", uint32_t, u32, OVS_KEY_ATTR_CT_MARK); > + SCAN_SINGLE("ct_label(", ovs_u128, u128, OVS_KEY_ATTR_CT_LABEL); > > SCAN_BEGIN_NESTED("tunnel(", OVS_KEY_ATTR_TUNNEL) { > SCAN_FIELD_NESTED("tun_id=", ovs_be64, be64, OVS_TUNNEL_KEY_ATTR_ID); > @@ -3594,6 +3685,10 @@ odp_flow_key_from_flow__(const struct > odp_flow_key_parms *parms, > if (parms->support.ct_mark) { > nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_MARK, data->ct_mark); > } > + if (parms->support.ct_label) { > + nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABEL, &data->ct_label, > + sizeof(data->ct_label)); > + } > if (parms->support.recirc) { > nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id); > nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash); > @@ -3784,6 +3879,10 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const > struct pkt_metadata *md) > if (md->ct_mark) { > nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_MARK, md->ct_mark); > } > + if (!ovs_u128_is_zero(&md->ct_label)) { > + nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABEL, &md->ct_label, > + sizeof(md->ct_label)); > + } > } > > /* Add an ingress port attribute if 'odp_in_port' is not the magical > @@ -3845,6 +3944,13 @@ odp_key_to_pkt_metadata(const struct nlattr *key, > size_t key_len, > md->ct_mark = nl_attr_get_u32(nla); > wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_MARK); > break; > + case OVS_KEY_ATTR_CT_LABEL: { > + const ovs_u128 *cl = nl_attr_get(nla); > + > + md->ct_label = *cl; > + wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_LABEL); > + break; > + } > case OVS_KEY_ATTR_TUNNEL: { > enum odp_key_fitness res; > > @@ -4411,6 +4517,12 @@ odp_flow_key_to_flow__(const struct nlattr *key, > size_t key_len, > flow->ct_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_CT_MARK]); > expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_MARK; > } > + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_LABEL)) { > + const ovs_u128 *cl = nl_attr_get(attrs[OVS_KEY_ATTR_CT_LABEL]); > + > + flow->ct_label = *cl; > + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_LABEL; > + } > > if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) { > enum odp_key_fitness res; > diff --git a/lib/odp-util.h b/lib/odp-util.h > index fcbbbac..cf8cb02 100644 > --- a/lib/odp-util.h > +++ b/lib/odp-util.h > @@ -123,6 +123,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); > * OVS_KEY_ATTR_CONN_STATE 1 3 4 8 > * OVS_KEY_ATTR_CONN_ZONE 2 2 4 8 > * OVS_KEY_ATTR_CONN_MARK 4 -- 4 8 > + * OVS_KEY_ATTR_CONN_LABEL 16 -- 4 20 > * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 > * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN > ethertype) > * OVS_KEY_ATTR_VLAN 2 2 4 8 > @@ -132,7 +133,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); > * OVS_KEY_ATTR_ICMPV6 2 2 4 8 > * OVS_KEY_ATTR_ND 28 -- 4 32 > * ---------------------------------------------------------- > - * total 512 > + * total 532 > * > * We include some slack space in case the calculation isn't quite right or > we > * add another field and forget to adjust this value. > @@ -174,6 +175,7 @@ struct odp_support { > bool ct_state; > bool ct_zone; > bool ct_mark; > + bool ct_label; > }; > > struct odp_flow_key_parms { > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c > index 8a83249..52a8502 100644 > --- a/lib/ofp-actions.c > +++ b/lib/ofp-actions.c > @@ -6073,6 +6073,12 @@ unsupported_nesting(enum ofpact_type action, enum > ofpact_type outer_action) > return OFPERR_OFPBAC_BAD_ARGUMENT; > } > > +static bool > +field_requires_ct(enum mf_field_id field) > +{ > + return field == MFF_CT_MARK || field == MFF_CT_LABEL; > +} > + > static enum ofperr > ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action) > { > @@ -6084,12 +6090,11 @@ ofpacts_verify_nested(const struct ofpact *a, enum > ofpact_type outer_action) > > field = ofpact_get_mf_field(a->type, a); > if (outer_action == OFPACT_CT > - && (!field > - || (field && field->id != MFF_CT_MARK))) { > + && (!field || (field && !field_requires_ct(field->id)))) { > return unsupported_nesting(a->type, outer_action); > } > > - if (field && outer_action != OFPACT_CT && field->id == MFF_CT_MARK) { > + if (outer_action != OFPACT_CT && field && field_requires_ct(field->id)) { > VLOG_WARN("cannot set CT fields outside of \"ct\" action"); > return OFPERR_OFPBAC_BAD_SET_ARGUMENT; > } Would this also prevent reading from the ct_fields outside of the ct_action? If so, maybe it shouldn’t? > diff --git a/lib/packets.h b/lib/packets.h > index 7181c78..65e2e1c 100644 > --- a/lib/packets.h > +++ b/lib/packets.h > @@ -129,6 +129,7 @@ struct pkt_metadata { > uint8_t ct_state; /* Connection state. */ > uint16_t ct_zone; /* Connection zone. */ > uint32_t ct_mark; /* Connection mark. */ > + ovs_u128 ct_label; /* Connection label. */ > struct flow_tnl tunnel; /* Encapsulating tunnel parameters. Note that > * if 'ip_dst' == 0, the rest of the fields > may > * be uninitialized. */ > diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c > index 881188b..20fb329 100644 > --- a/ofproto/ofproto-dpif-sflow.c > +++ b/ofproto/ofproto-dpif-sflow.c > @@ -1032,6 +1032,7 @@ sflow_read_set_action(const struct nlattr *attr, > case OVS_KEY_ATTR_CT_STATE: > case OVS_KEY_ATTR_CT_ZONE: > case OVS_KEY_ATTR_CT_MARK: > + case OVS_KEY_ATTR_CT_LABEL: > case OVS_KEY_ATTR_UNSPEC: > case __OVS_KEY_ATTR_MAX: > default: > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index de74320..5da8f2f 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -2808,6 +2808,7 @@ clear_conntrack(struct flow *flow) > flow->ct_state = 0; > flow->ct_zone = 0; > flow->ct_mark = 0; > + memset(&flow->ct_label, 0, sizeof flow->ct_label); > } > > static void > @@ -2822,6 +2823,7 @@ compose_output_action__(struct xlate_ctx *ctx, > ofp_port_t ofp_port, > uint32_t flow_pkt_mark, flow_ct_mark; > uint8_t flow_ct_state; > uint16_t flow_ct_zone; > + ovs_u128 flow_ct_label; > uint8_t flow_nw_tos; > odp_port_t out_port, odp_port; > bool tnl_push_pop_send = false; > @@ -2973,6 +2975,7 @@ compose_output_action__(struct xlate_ctx *ctx, > ofp_port_t ofp_port, > flow_ct_state = flow->ct_state; > flow_ct_zone = flow->ct_zone; > flow_ct_mark = flow->ct_mark; > + flow_ct_label = flow->ct_label; > flow_nw_tos = flow->nw_tos; > > if (count_skb_priorities(xport)) { > @@ -3098,6 +3101,7 @@ compose_output_action__(struct xlate_ctx *ctx, > ofp_port_t ofp_port, > flow->ct_state = flow_ct_state; > flow->ct_zone = flow_ct_zone; > flow->ct_mark = flow_ct_mark; > + flow->ct_label = flow_ct_label; > flow->nw_tos = flow_nw_tos; > } > > @@ -4177,6 +4181,32 @@ put_ct_mark(const struct flow *flow, struct flow > *base_flow, > } > > static void > +put_ct_label(const struct flow *flow, struct flow *base_flow, > + struct ofpbuf *odp_actions, struct flow_wildcards *wc) > +{ > + const ovs_u128 *key; > + ovs_u128 *mask, *base; > + > + key = &flow->ct_label; > + base = &base_flow->ct_label; > + mask = &wc->masks.ct_label; > + > + if (!is_all_zeros(mask, sizeof(*mask)) && memcmp(key, base, > sizeof(*key))) { We have u128 specific functions for both uses here. Also, what if we have multiple ct actions with labels? > + struct { > + ovs_u128 key; > + ovs_u128 mask; > + } *odp_ct_label; > + > + odp_ct_label = nl_msg_put_unspec_uninit(odp_actions, > OVS_CT_ATTR_LABEL, > + sizeof(*odp_ct_label)); > + odp_ct_label->key = *key; > + odp_ct_label->mask = *mask; > + base_flow->ct_label = *base; > + wc->masks.ct_label = *mask; These two seem like NOPs. Maybe the intention was to update the base with the new value? > + } > +} > + > +static void > compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc) > { > uint32_t flags = 0; > @@ -4203,6 +4233,7 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct > ofpact_conntrack *ofc) > nl_msg_put_u32(ctx->odp_actions, OVS_CT_ATTR_FLAGS, flags); > nl_msg_put_u16(ctx->odp_actions, OVS_CT_ATTR_ZONE, zone); > put_ct_mark(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc); > + put_ct_label(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, > ctx->wc); > nl_msg_end_nested(ctx->odp_actions, ct_offset); > > if (ofc->recirc_table == NX_CT_RECIRC_NONE) { > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index 78bb4c8..ddf177a 100644 > --- a/ofproto/ofproto-dpif.c > +++ b/ofproto/ofproto-dpif.c > @@ -1264,6 +1264,7 @@ check_##NAME(struct dpif_backer *backer) > \ > CHECK_FEATURE(ct_state) > CHECK_FEATURE(ct_zone) > CHECK_FEATURE(ct_mark) > +CHECK_FEATURE__(ct_label, ct_label.u64.lo) > > #undef CHECK_FEATURE > #undef CHECK_FEATURE__ > @@ -1283,6 +1284,7 @@ check_support(struct dpif_backer *backer) > backer->support.odp.ct_state = check_ct_state(backer); > backer->support.odp.ct_zone = check_ct_zone(backer); > backer->support.odp.ct_mark = check_ct_mark(backer); > + backer->support.odp.ct_label = check_ct_label(backer); > } > > static int > @@ -3990,7 +3992,10 @@ rule_check(struct rule *rule) > > if ((match.wc.masks.ct_state && !support->ct_state) > || (match.wc.masks.ct_zone && !support->ct_zone) > - || (match.wc.masks.ct_mark && !support->ct_mark)) { > + || (match.wc.masks.ct_mark && !support->ct_mark) > + || (!support->ct_label > + && !is_all_zeros(&match.wc.masks.ct_label, > + sizeof(match.wc.masks.ct_label)))) { Ditto. > return OFPERR_OFPBMC_BAD_FIELD; > } > if (match.wc.masks.ct_state & CS_UNSUPPORTED_MASK) { > diff --git a/ofproto/ofproto-unixctl.man b/ofproto/ofproto-unixctl.man > index 87ef80d..53e5549 100644 > --- a/ofproto/ofproto-unixctl.man > +++ b/ofproto/ofproto-unixctl.man > @@ -109,6 +109,8 @@ Connection state of the packet. > Connection tracking zone for packet. > .IP \fIct_mark\fR > Connection mark of the packet. > +.IP \fIct_label\fR > +Connection label of the packet. > .IP \fItun_id\fR > The tunnel ID on which the packet arrived. > .IP \fIin_port\fR > diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at > index 502416f..103f87c 100644 > --- a/tests/dpif-netdev.at > +++ b/tests/dpif-netdev.at > @@ -82,7 +82,7 @@ AT_CHECK([cat ovs-vswitchd.log | grep -A 1 'miss upcall' | > tail -n 1], [0], [dnl > > skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0) > ]) > AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_XOUT], [0], [dnl > -pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,ct_state=0,ct_zone=0,ct_mark=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, > actions: <del> > +pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,ct_state=0,ct_zone=0,ct_mark=0,ct_label=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, > actions: <del> > > recirc_id=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, > actions: <del> > ]) > > diff --git a/tests/odp.at b/tests/odp.at > index bf3fa8a..702d44c 100644 > --- a/tests/odp.at > +++ b/tests/odp.at > @@ -71,7 +71,7 @@ s/$/)/' odp-base.txt > > echo > echo '# Valid forms with conntrack fields.' > - sed > 's/^/skb_priority(0),skb_mark(0),ct_mark(0x12345678),recirc_id(0),dp_hash(0),/' > odp-base.txt > + sed > 's/^/skb_priority(0),skb_mark(0),ct_mark(0x12345678),ct_label(0x1234567890abcdef1234567890abcdef),recirc_id(0),dp_hash(0),/' > odp-base.txt > > echo > echo '# Valid forms with IP first fragment.' > @@ -93,7 +93,7 @@ s/^/ODP_FIT_TOO_LITTLE: / > dnl Some fields are always printed for this test, because wildcards aren't > dnl specified. We can skip these. > sed -i 's/\(skb_mark(0)\),\(ct\)/\1,ct_state(0),ct_zone(0),\2/' odp-out.txt > -sed -i > 's/\(skb_mark([[^)]]*)\),\(recirc\)/\1,ct_state(0),ct_zone(0),ct_mark(0),\2/' > odp-out.txt > +sed -i > 's/\(skb_mark([[^)]]*)\),\(recirc\)/\1,ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),\2/' > odp-out.txt > > AT_CHECK_UNQUOTED([ovstest test-odp parse-keys < odp-in.txt], [0], [`cat > odp-out.txt` > ]) > @@ -163,7 +163,7 @@ s/$/)/' odp-base.txt > > echo > echo '# Valid forms with conntrack fields.' > - sed > 's/\(eth([[^)]]*),?\)/\1,ct_state(+trk),ct_mark(0x12345678\/0xFOFOFOFO),/' > odp-base.txt > + sed > 's/\(eth([[^)]]*),?\)/\1,ct_state(+trk),ct_mark(0x12345678\/0xFOFOFOFO),ct_label(0x1234567890ABCDEF1234567890ABCDEF\/0x102030405060708090A0B0C0D0E0F0),/' > odp-base.txt > > echo > echo '# Valid forms with IP first fragment.' > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > index ce214d9..429b88b 100644 > --- a/tests/ofproto-dpif.at > +++ b/tests/ofproto-dpif.at > @@ -6486,8 +6486,8 @@ for i in 1 2 3 4; do > done > sleep 1 > AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | > STRIP_USED], [0], [dnl > -pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,ct_state=0,ct_zone=0,ct_mark=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, > actions:2 > -pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,ct_state=0,ct_zone=0,ct_mark=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, > actions:drop > +pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,ct_state=0,ct_zone=0,ct_mark=0,ct_label=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, > actions:2 > +pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,ct_state=0,ct_zone=0,ct_mark=0,ct_label=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0, > actions:drop > ]) > AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_DUMP | grep > 'packets:3'], [0], [dnl > > skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), > packets:3, bytes:180, used:0.0s, actions:2 > diff --git a/tests/ofproto.at b/tests/ofproto.at > index 120a991..95cd75b 100644 > --- a/tests/ofproto.at > +++ b/tests/ofproto.at > @@ -1529,7 +1529,7 @@ head_table () { > instructions: > meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table > Write-Actions and Apply-Actions features: > actions: output group set_field strip_vlan push_vlan mod_nw_ttl > dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue > - supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id > tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 > tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 > tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 > tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 > tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 > tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 > tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 > tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 > tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 > tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 > tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 > tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 > tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 > metadata in_port in_port_oxm pkt_mark ct_mark reg0 reg1 reg2 reg3 reg4 reg5 > reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp > mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp > nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src > udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll > + supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id > tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 > tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 > tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 > tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 > tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 > tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 > tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 > tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 > tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 > tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 > tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 > tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 > tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 > metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 > reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid > vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos > ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst > udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll > matching: > dp_hash: arbitrary mask > recirc_id: exact match or wildcard > @@ -1612,6 +1612,7 @@ head_table () { > ct_state: arbitrary mask > ct_zone: exact match or wildcard > ct_mark: arbitrary mask > + ct_label: arbitrary mask > reg0: arbitrary mask > reg1: arbitrary mask > reg2: arbitrary mask > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > index e7c6846..8c198f9 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -658,6 +658,45 @@ SYN_RECV src=10.1.1.3 dst=10.1.1.4 sport=<cleared> > dport=<cleared> src=10.1.1.4 > OVS_TRAFFIC_VSWITCHD_STOP > AT_CLEANUP > > +AT_SETUP([conntrack - ct_label]) > +CHECK_CONNTRACK() > +OVS_TRAFFIC_VSWITCHD_START( > + [set-fail-mode br0 standalone -- ]) > + > +ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") > +ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") > +ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") > + > +dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from > ns1->ns0. How about at_ns2 and at_ns3? > +AT_DATA([flows.txt], [dnl > +priority=1,action=drop > +priority=10,arp,action=normal > +priority=10,icmp,action=normal > +in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2 > +in_port=2,ct_state=-trk,tcp,action=ct(table=0) > +in_port=2,ct_state=+trk,ct_label=000000000000000001,tcp,action=1 > +in_port=3,tcp,action=ct(commit,exec(set_field:2->ct_label)),4 > +in_port=4,ct_state=-trk,tcp,action=ct(table=0) > +in_port=4,ct_state=+trk,ct_label=000000000000000001,tcp,action=3 Explicit priorities would be nice. > +]) > + > +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) > + > +dnl HTTP requests from p0->p1 should work fine. > +NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid]) > +NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o > wget0.log]) > + > +dnl HTTP requests from p2->p3 should fail due to network failure. > +dnl Try 3 times, in 1 second intervals. > +NETNS_DAEMONIZE([at_ns3], [[$PYTHON $srcdir/test-l7.py]], [http1.pid]) > +NS_CHECK_EXEC([at_ns2], [wget 10.1.1.4 -t 3 -T 1 -v -o wget1.log], [4]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + > AT_SETUP([conntrack - ICMP related]) > CHECK_CONNTRACK() > OVS_TRAFFIC_VSWITCHD_START( > diff --git a/tests/test-odp.c b/tests/test-odp.c > index 1c65590..4b89867 100644 > --- a/tests/test-odp.c > +++ b/tests/test-odp.c > @@ -62,6 +62,7 @@ parse_keys(bool wc_keys) > .ct_state = true, > .ct_zone = true, > .ct_mark = true, > + .ct_label = true, > }, > }; > > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in > index 31644b3..b1ee36e 100644 > --- a/utilities/ovs-ofctl.8.in > +++ b/utilities/ovs-ofctl.8.in > @@ -1368,6 +1368,10 @@ Matches connection zone \fIvalue\fR exactly. > Matches connection mark \fIvalue\fR either exactly or with optional > \fImask\fR. > . > +.IP \fBct_label=\fIvalue\fR[\fB/\fImask\fR] > +Matches connection label \fIvalue\fR either exactly or with optional > +\fImask\fR. > +. > .PP > Defining IPv6 flows (those with \fBdl_type\fR equal to 0x86dd) requires > support for NXM. The following shorthand notations are available for > @@ -1630,6 +1634,10 @@ specify a 16-bit range within the field. > Store a 32-bit metadata value (masked by \fImask\fR) with the connection. > This will populate the \fBct_mark\fR flow field if the \fBtable\fR is > specified in the \fBct\fR action. > +.IP \fBlabel=\fR\fIlabel\fR[/\fImask\fR] > +Store a 128-bit metadata value (masked by \fImask\fR) with the connection. > +This will populate the \fBct_label\fR flow field if the \fBtable\fR is > +specified in the \fBct\fR action. Ah, now I get the “table” :-) > .RE > .IP > Currently, connection tracking is only available on Linux kernels with the _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev