@@ -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
@@ -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", 2, 2),
+ "conn label": ("MFS_CT_LABEL", 16, 16),
"Ethernet": ("MFS_ETHERNET", 6, 6),
"IPv4": ("MFS_IPV4", 4, 4),
"IPv6": ("MFS_IPV6", 16, 16),
@@ -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 */
@@ -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];
+};
+
/* 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. */
__OVS_CT_ATTR_MAX
};
@@ -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
+ || !ovs_u128_is_zero(&flow->ct_label)) {
return EINVAL;
}
@@ -486,6 +486,11 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
if (md->ct_state) {
miniflow_push_uint32(mf, ct_mark, md->ct_mark);
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 / sizeof(uint64_t));
+ }
}
/* Initialize packet's layer pointer and offsets. */
@@ -847,6 +852,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 (!ovs_u128_is_zero(&flow->ct_label)) {
+ match_set_ct_label(flow_metadata, flow->ct_label);
+ }
}
char *
@@ -1131,6 +1139,9 @@ flow_format(struct ds *ds, const struct flow *flow)
if (!flow->ct_mark) {
WC_UNMASK_FIELD(wc, ct_mark);
}
+ if (ovs_u128_is_zero(&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]);
@@ -1208,6 +1219,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);
@@ -1314,6 +1326,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))) {
@@ -106,6 +106,7 @@ struct flow {
uint16_t ct_zone; /* Connection tracking zone. */
uint32_t ct_mark; /* Connection mark.*/
uint8_t pad1[4]; /* Pad to 64 bits. */
+ ovs_u128 ct_label; /* Connection label. */
uint32_t conj_id; /* Conjunction ID. */
ofp_port_t actset_output; /* Output port in action set. */
uint8_t pad2[2]; /* 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 == 34);
/* Incremental points at which flow classification may be performed in
@@ -786,6 +787,14 @@ miniflow_get__(const struct miniflow *mf, size_t idx)
[FLOW_U64_OFFREM(FIELD) / sizeof(TYPE)] \
: 0)
+/* Get a pointer to the ovs_u128 value of struct flow 'FIELD' from miniflow
+ * 'FLOW'. */
+#define MINIFLOW_GET_U128_PTR(FLOW, FIELD) \
+ (MINIFLOW_IN_MAP(FLOW, FLOW_U64_OFFSET(FIELD)) \
+ ? &((OVS_FORCE const ovs_u128 *)miniflow_get__(FLOW, FLOW_U64_OFFSET(FIELD))) \
+ [FLOW_U64_OFFREM(FIELD) / sizeof(ovs_u128)] \
+ : NULL)
+
#define MINIFLOW_GET_U8(FLOW, FIELD) \
MINIFLOW_GET_TYPE(FLOW, uint8_t, FIELD)
#define MINIFLOW_GET_U16(FLOW, FIELD) \
@@ -987,6 +996,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)
@@ -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 (!ovs_u128_is_zero(mask)) {
+ ds_put_format(s, "ct_label=");
+ ds_put_hex(s, key, sizeof(*key));
+ if (!is_all_ones(mask, sizeof(*mask))) {
+ 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 (!ovs_u128_is_zero(&wc->masks.ct_label)) {
+ 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)) {
@@ -88,6 +88,8 @@ void match_set_ct_state_masked(struct match *, uint16_t ct_state, uint16_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 );
@@ -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 ovs_u128_is_zero(&wc->masks.ct_label);
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:
+ value->u128 = flow->ct_label;
+ 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:
+ flow->ct_label = value->u128;
+ 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 @@ syntax_error:
}
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->be16, &mask->be16);
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(ovs_be16 value, ovs_be16 mask, struct ds *s)
ntohs(mask), UINT16_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
@@ -2382,6 +2449,10 @@ mf_format(const struct mf_field *mf,
mask ? mask->be16 : OVS_BE16_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;
@@ -787,6 +787,25 @@ 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.
+ *
+ * Writable only from nested actions within the NXAST_CT action.
+ *
+ * Type: u128.
+ * Maskable: bitwise.
+ * Formatting: conn label.
+ * Prerequisites: none.
+ * Access: read/write.
+ * NXM: NXM_NX_CT_LABEL(108) since v2.5.
+ * OXM: none.
+ */
+ MFF_CT_LABEL,
+
#if FLOW_N_REGS == 8
/* "reg<N>".
*
@@ -1764,6 +1783,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,
@@ -1831,6 +1851,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);
@@ -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,10 @@ 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 (!ovs_u128_is_zero(&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,
@@ -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:
@@ -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,23 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
}
static int
+parse_ct_label(const char *s, ovs_u128 *value, ovs_u128 *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 +1058,15 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
uint32_t value;
uint32_t mask;
} ct_mark = { 0, 0 };
+ struct {
+ ovs_u128 value;
+ ovs_u128 mask;
+ } ct_label;
size_t start;
char *end;
+ memset(&ct_label, 0, sizeof(ct_label));
+
s += 2;
if (ovs_scan(s, "(")) {
s++;
@@ -1039,6 +1076,7 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
}
while (s != end) {
+ char *tail;
int n = -1;
s += strspn(s, delimiters);
@@ -1061,6 +1099,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 +1130,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 (!ovs_u128_is_zero(&ct_label.mask)) {
+ nl_msg_put_unspec(actions, OVS_CT_ATTR_LABEL, &ct_label,
+ sizeof(ct_label));
+ }
nl_msg_end_nested(actions, start);
}
@@ -1350,6 +1404,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 +2292,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 +2512,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 +3439,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 +3684,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 +3878,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 +3943,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 +4516,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;
@@ -123,6 +123,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
* OVS_KEY_ATTR_CONN_STATE 2 2 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 {
@@ -6142,6 +6142,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)
{
@@ -6153,12 +6159,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;
}
@@ -130,6 +130,7 @@ struct pkt_metadata {
uint16_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. */
@@ -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:
@@ -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
@@ -4167,8 +4168,33 @@ 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 (!ovs_u128_is_zero(mask) && !ovs_u128_equals(key, base)) {
+ 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;
+ }
+}
+
+static void
compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
{
+ ovs_u128 old_ct_label = ctx->base_flow.ct_label;
uint32_t old_ct_mark = ctx->base_flow.ct_mark;
uint32_t flags = 0;
size_t ct_offset;
@@ -4194,11 +4220,13 @@ 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);
/* Restore the original ct fields in the key. These should only be exposed
* after recirculation to another table. */
ctx->base_flow.ct_mark = old_ct_mark;
+ ctx->base_flow.ct_label = old_ct_label;
if (ofc->recirc_table == NX_CT_RECIRC_NONE) {
/* If we do not recirculate as part of this action, hide the results of
@@ -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
@@ -3982,19 +3984,27 @@ static enum ofperr
rule_check(struct rule *rule)
{
uint16_t ct_state_mask, ct_zone_mask;
+ const ovs_u128 *labelp;
+ ovs_u128 ct_label_mask = { { 0, 0 } };
uint32_t ct_mark_mask;
ct_state_mask = MINIFLOW_GET_U16(&rule->cr.match.mask->masks, ct_state);
ct_zone_mask = MINIFLOW_GET_U16(&rule->cr.match.mask->masks, ct_zone);
ct_mark_mask = MINIFLOW_GET_U32(&rule->cr.match.mask->masks, ct_mark);
+ labelp = MINIFLOW_GET_U128_PTR(&rule->cr.match.mask->masks, ct_label);
+ if (labelp) {
+ ct_label_mask = *labelp;
+ }
- if (ct_state_mask || ct_zone_mask || ct_mark_mask) {
+ if (ct_state_mask || ct_zone_mask || ct_mark_mask
+ || !ovs_u128_is_zero(&ct_label_mask)) {
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->ofproto);
const struct odp_support *support = &ofproto_dpif_get_support(ofproto)->odp;
if ((ct_state_mask && !support->ct_state)
|| (ct_zone_mask && !support->ct_zone)
- || (ct_mark_mask && !support->ct_mark)) {
+ || (ct_mark_mask && !support->ct_mark)
+ || (!ovs_u128_is_zero(&ct_label_mask) && !support->ct_label)) {
return OFPERR_OFPBMC_BAD_FIELD;
}
if (ct_state_mask & CS_UNSUPPORTED_MASK) {
@@ -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
@@ -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>
])
@@ -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.'
@@ -6523,8 +6523,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
@@ -1538,7 +1538,7 @@ head_table () {
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
+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
@@ -1621,6 +1621,7 @@ metadata in_port in_port_oxm pkt_mark ct_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6
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
@@ -661,6 +661,46 @@ 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 traffic between ns0<->ns1 using the ct_label.
+dnl Check that different labels do not match for traffic between ns2<->ns3.
+AT_DATA([flows.txt], [dnl
+priority=1,action=drop
+priority=10,arp,action=normal
+priority=10,icmp,action=normal
+priority=100,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
+priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0)
+priority=100,in_port=2,ct_state=+trk,ct_label=000000000000000001,tcp,action=1
+priority=100,in_port=3,tcp,action=ct(commit,exec(set_field:2->ct_label)),4
+priority=100,in_port=4,ct_state=-trk,tcp,action=ct(table=0)
+priority=100,in_port=4,ct_state=+trk,ct_label=000000000000000001,tcp,action=3
+])
+
+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(
@@ -61,6 +61,7 @@ parse_keys(bool wc_keys)
.ct_state = true,
.ct_zone = true,
.ct_mark = true,
+ .ct_label = true,
},
};
@@ -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
@@ -1641,6 +1645,10 @@ field:
Store a 32-bit metadata value with the connection. This will populate the
\fBct_mark\fR flow field if the \fBtable\fR is specified in the \fBct\fR
action.
+.IP \fBset_field:\fIvalue\fR->ct_label\fR
+Store a 128-bit metadata value with the connection. This will populate the
+\fBct_label\fR flow field if the \fBtable\fR is specified in the \fBct\fR
+action.
.RE
.
.RE