Skip to content

Commit c2ac667

Browse files
joestringerdavem330
authored andcommitted
openvswitch: Allow matching on conntrack label
Allow matching and setting the ct_label field. As with ct_mark, this is populated by executing the CT action. The label field may be modified by specifying a label and mask nested under the CT action. It is stored as metadata attached to the connection. Label modification occurs after lookup, and will only persist when the conntrack entry is committed by providing the COMMIT flag to the CT action. Labels are currently fixed to 128 bits in size. Signed-off-by: Joe Stringer <joestringer@nicira.com> Acked-by: Thomas Graf <tgraf@suug.ch> Acked-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 86ca02e commit c2ac667

File tree

10 files changed

+199
-34
lines changed

10 files changed

+199
-34
lines changed

include/uapi/linux/openvswitch.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ enum ovs_key_attr {
326326
OVS_KEY_ATTR_CT_STATE, /* u8 bitmask of OVS_CS_F_* */
327327
OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */
328328
OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */
329+
OVS_KEY_ATTR_CT_LABEL, /* 16-octet connection tracking label */
329330

330331
#ifdef __KERNEL__
331332
OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */
@@ -438,6 +439,11 @@ struct ovs_key_nd {
438439
__u8 nd_tll[ETH_ALEN];
439440
};
440441

442+
#define OVS_CT_LABEL_LEN 16
443+
struct ovs_key_ct_label {
444+
__u8 ct_label[OVS_CT_LABEL_LEN];
445+
};
446+
441447
/* OVS_KEY_ATTR_CT_STATE flags */
442448
#define OVS_CS_F_NEW 0x01 /* Beginning of a new connection. */
443449
#define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */
@@ -617,12 +623,16 @@ struct ovs_action_hash {
617623
* @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the
618624
* mask, the corresponding bit in the value is copied to the connection
619625
* tracking mark field in the connection.
626+
* @OVS_CT_ATTR_LABEL: %OVS_CT_LABEL_LEN value followed by %OVS_CT_LABEL_LEN
627+
* mask. For each bit set in the mask, the corresponding bit in the value is
628+
* copied to the connection tracking label field in the connection.
620629
*/
621630
enum ovs_ct_attr {
622631
OVS_CT_ATTR_UNSPEC,
623632
OVS_CT_ATTR_FLAGS, /* u8 bitmask of OVS_CT_F_*. */
624633
OVS_CT_ATTR_ZONE, /* u16 zone id. */
625634
OVS_CT_ATTR_MARK, /* mark to associate with this connection. */
635+
OVS_CT_ATTR_LABEL, /* label to associate with this connection. */
626636
__OVS_CT_ATTR_MAX
627637
};
628638

net/openvswitch/actions.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,7 @@ static int execute_masked_set_action(struct sk_buff *skb,
969969
case OVS_KEY_ATTR_CT_STATE:
970970
case OVS_KEY_ATTR_CT_ZONE:
971971
case OVS_KEY_ATTR_CT_MARK:
972+
case OVS_KEY_ATTR_CT_LABEL:
972973
err = -EINVAL;
973974
break;
974975
}

net/openvswitch/conntrack.c

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/openvswitch.h>
1616
#include <net/ip.h>
1717
#include <net/netfilter/nf_conntrack_core.h>
18+
#include <net/netfilter/nf_conntrack_labels.h>
1819
#include <net/netfilter/nf_conntrack_zones.h>
1920
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
2021

@@ -34,13 +35,20 @@ struct md_mark {
3435
u32 mask;
3536
};
3637

38+
/* Metadata label for masked write to conntrack label. */
39+
struct md_label {
40+
struct ovs_key_ct_label value;
41+
struct ovs_key_ct_label mask;
42+
};
43+
3744
/* Conntrack action context for execution. */
3845
struct ovs_conntrack_info {
3946
struct nf_conntrack_zone zone;
4047
struct nf_conn *ct;
4148
u32 flags;
4249
u16 family;
4350
struct md_mark mark;
51+
struct md_label label;
4452
};
4553

4654
static u16 key_to_nfproto(const struct sw_flow_key *key)
@@ -90,13 +98,32 @@ static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo)
9098
return ct_state;
9199
}
92100

101+
static void ovs_ct_get_label(const struct nf_conn *ct,
102+
struct ovs_key_ct_label *label)
103+
{
104+
struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL;
105+
106+
if (cl) {
107+
size_t len = cl->words * sizeof(long);
108+
109+
if (len > OVS_CT_LABEL_LEN)
110+
len = OVS_CT_LABEL_LEN;
111+
else if (len < OVS_CT_LABEL_LEN)
112+
memset(label, 0, OVS_CT_LABEL_LEN);
113+
memcpy(label, cl->bits, len);
114+
} else {
115+
memset(label, 0, OVS_CT_LABEL_LEN);
116+
}
117+
}
118+
93119
static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
94120
const struct nf_conntrack_zone *zone,
95121
const struct nf_conn *ct)
96122
{
97123
key->ct.state = state;
98124
key->ct.zone = zone->id;
99125
key->ct.mark = ct ? ct->mark : 0;
126+
ovs_ct_get_label(ct, &key->ct.label);
100127
}
101128

102129
/* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has
@@ -140,6 +167,11 @@ int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb)
140167
nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark))
141168
return -EMSGSIZE;
142169

170+
if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABEL) &&
171+
nla_put(skb, OVS_KEY_ATTR_CT_LABEL, sizeof(key->ct.label),
172+
&key->ct.label))
173+
return -EMSGSIZE;
174+
143175
return 0;
144176
}
145177

@@ -168,6 +200,40 @@ static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key,
168200
return 0;
169201
}
170202

203+
static int ovs_ct_set_label(struct sk_buff *skb, struct sw_flow_key *key,
204+
const struct ovs_key_ct_label *label,
205+
const struct ovs_key_ct_label *mask)
206+
{
207+
enum ip_conntrack_info ctinfo;
208+
struct nf_conn_labels *cl;
209+
struct nf_conn *ct;
210+
int err;
211+
212+
if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS))
213+
return -ENOTSUPP;
214+
215+
/* The connection could be invalid, in which case set_label is no-op.*/
216+
ct = nf_ct_get(skb, &ctinfo);
217+
if (!ct)
218+
return 0;
219+
220+
cl = nf_ct_labels_find(ct);
221+
if (!cl) {
222+
nf_ct_labels_ext_add(ct);
223+
cl = nf_ct_labels_find(ct);
224+
}
225+
if (!cl || cl->words * sizeof(long) < OVS_CT_LABEL_LEN)
226+
return -ENOSPC;
227+
228+
err = nf_connlabels_replace(ct, (u32 *)label, (u32 *)mask,
229+
OVS_CT_LABEL_LEN / sizeof(u32));
230+
if (err)
231+
return err;
232+
233+
ovs_ct_get_label(ct, &key->ct.label);
234+
return 0;
235+
}
236+
171237
static int handle_fragments(struct net *net, struct sw_flow_key *key,
172238
u16 zone, struct sk_buff *skb)
173239
{
@@ -327,6 +393,17 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
327393
return 0;
328394
}
329395

396+
static bool label_nonzero(const struct ovs_key_ct_label *label)
397+
{
398+
size_t i;
399+
400+
for (i = 0; i < sizeof(*label); i++)
401+
if (label->ct_label[i])
402+
return true;
403+
404+
return false;
405+
}
406+
330407
int ovs_ct_execute(struct net *net, struct sk_buff *skb,
331408
struct sw_flow_key *key,
332409
const struct ovs_conntrack_info *info)
@@ -351,9 +428,15 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
351428
if (err)
352429
goto err;
353430

354-
if (info->mark.mask)
431+
if (info->mark.mask) {
355432
err = ovs_ct_set_mark(skb, key, info->mark.value,
356433
info->mark.mask);
434+
if (err)
435+
goto err;
436+
}
437+
if (label_nonzero(&info->label.mask))
438+
err = ovs_ct_set_label(skb, key, &info->label.value,
439+
&info->label.mask);
357440
err:
358441
skb_push(skb, nh_ofs);
359442
return err;
@@ -366,6 +449,8 @@ static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
366449
.maxlen = sizeof(u16) },
367450
[OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark),
368451
.maxlen = sizeof(struct md_mark) },
452+
[OVS_CT_ATTR_LABEL] = { .minlen = sizeof(struct md_label),
453+
.maxlen = sizeof(struct md_label) },
369454
};
370455

371456
static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
@@ -408,6 +493,14 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
408493
info->mark = *mark;
409494
break;
410495
}
496+
#endif
497+
#ifdef CONFIG_NF_CONNTRACK_LABELS
498+
case OVS_CT_ATTR_LABEL: {
499+
struct md_label *label = nla_data(a);
500+
501+
info->label = *label;
502+
break;
503+
}
411504
#endif
412505
default:
413506
OVS_NLERR(log, "Unknown conntrack attr (%d)",
@@ -424,7 +517,7 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
424517
return 0;
425518
}
426519

427-
bool ovs_ct_verify(enum ovs_key_attr attr)
520+
bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr)
428521
{
429522
if (attr == OVS_KEY_ATTR_CT_STATE)
430523
return true;
@@ -434,6 +527,12 @@ bool ovs_ct_verify(enum ovs_key_attr attr)
434527
if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
435528
attr == OVS_KEY_ATTR_CT_MARK)
436529
return true;
530+
if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
531+
attr == OVS_KEY_ATTR_CT_LABEL) {
532+
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
533+
534+
return ovs_net->xt_label;
535+
}
437536

438537
return false;
439538
}
@@ -500,6 +599,10 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
500599
nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark),
501600
&ct_info->mark))
502601
return -EMSGSIZE;
602+
if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
603+
nla_put(skb, OVS_CT_ATTR_LABEL, sizeof(ct_info->label),
604+
&ct_info->label))
605+
return -EMSGSIZE;
503606

504607
nla_nest_end(skb, start);
505608

@@ -513,3 +616,24 @@ void ovs_ct_free_action(const struct nlattr *a)
513616
if (ct_info->ct)
514617
nf_ct_put(ct_info->ct);
515618
}
619+
620+
void ovs_ct_init(struct net *net)
621+
{
622+
unsigned int n_bits = sizeof(struct ovs_key_ct_label) * BITS_PER_BYTE;
623+
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
624+
625+
if (nf_connlabels_get(net, n_bits)) {
626+
ovs_net->xt_label = false;
627+
OVS_NLERR(true, "Failed to set connlabel length");
628+
} else {
629+
ovs_net->xt_label = true;
630+
}
631+
}
632+
633+
void ovs_ct_exit(struct net *net)
634+
{
635+
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
636+
637+
if (ovs_net->xt_label)
638+
nf_connlabels_put(net);
639+
}

net/openvswitch/conntrack.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ struct ovs_conntrack_info;
2020
enum ovs_key_attr;
2121

2222
#if defined(CONFIG_OPENVSWITCH_CONNTRACK)
23-
bool ovs_ct_verify(enum ovs_key_attr attr);
23+
void ovs_ct_init(struct net *);
24+
void ovs_ct_exit(struct net *);
25+
bool ovs_ct_verify(struct net *, enum ovs_key_attr attr);
2426
int ovs_ct_copy_action(struct net *, const struct nlattr *,
2527
const struct sw_flow_key *, struct sw_flow_actions **,
2628
bool log);
@@ -35,7 +37,11 @@ void ovs_ct_free_action(const struct nlattr *a);
3537
#else
3638
#include <linux/errno.h>
3739

38-
static inline bool ovs_ct_verify(int attr)
40+
static inline void ovs_ct_init(struct net *net) { }
41+
42+
static inline void ovs_ct_exit(struct net *net) { }
43+
44+
static inline bool ovs_ct_verify(struct net *net, int attr)
3945
{
4046
return false;
4147
}
@@ -66,6 +72,7 @@ static inline void ovs_ct_fill_key(const struct sk_buff *skb,
6672
key->ct.state = 0;
6773
key->ct.zone = 0;
6874
key->ct.mark = 0;
75+
memset(&key->ct.label, 0, sizeof(key->ct.label));
6976
}
7077

7178
static inline int ovs_ct_put_key(const struct sw_flow_key *key,

net/openvswitch/datapath.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,8 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
599599
if (IS_ERR(flow))
600600
goto err_kfree_skb;
601601

602-
err = ovs_flow_key_extract_userspace(a[OVS_PACKET_ATTR_KEY], packet,
603-
&flow->key, log);
602+
err = ovs_flow_key_extract_userspace(net, a[OVS_PACKET_ATTR_KEY],
603+
packet, &flow->key, log);
604604
if (err)
605605
goto err_flow_free;
606606

@@ -947,7 +947,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
947947

948948
/* Extract key. */
949949
ovs_match_init(&match, &key, &mask);
950-
error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY],
950+
error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
951951
a[OVS_FLOW_ATTR_MASK], log);
952952
if (error)
953953
goto err_kfree_flow;
@@ -1118,7 +1118,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
11181118

11191119
ufid_present = ovs_nla_get_ufid(&sfid, a[OVS_FLOW_ATTR_UFID], log);
11201120
ovs_match_init(&match, &key, &mask);
1121-
error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY],
1121+
error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
11221122
a[OVS_FLOW_ATTR_MASK], log);
11231123
if (error)
11241124
goto error;
@@ -1208,6 +1208,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
12081208
{
12091209
struct nlattr **a = info->attrs;
12101210
struct ovs_header *ovs_header = info->userhdr;
1211+
struct net *net = sock_net(skb->sk);
12111212
struct sw_flow_key key;
12121213
struct sk_buff *reply;
12131214
struct sw_flow *flow;
@@ -1222,7 +1223,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
12221223
ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log);
12231224
if (a[OVS_FLOW_ATTR_KEY]) {
12241225
ovs_match_init(&match, &key, NULL);
1225-
err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL,
1226+
err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], NULL,
12261227
log);
12271228
} else if (!ufid_present) {
12281229
OVS_NLERR(log,
@@ -1266,6 +1267,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
12661267
{
12671268
struct nlattr **a = info->attrs;
12681269
struct ovs_header *ovs_header = info->userhdr;
1270+
struct net *net = sock_net(skb->sk);
12691271
struct sw_flow_key key;
12701272
struct sk_buff *reply;
12711273
struct sw_flow *flow = NULL;
@@ -1280,8 +1282,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
12801282
ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log);
12811283
if (a[OVS_FLOW_ATTR_KEY]) {
12821284
ovs_match_init(&match, &key, NULL);
1283-
err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL,
1284-
log);
1285+
err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
1286+
NULL, log);
12851287
if (unlikely(err))
12861288
return err;
12871289
}
@@ -2237,6 +2239,7 @@ static int __net_init ovs_init_net(struct net *net)
22372239

22382240
INIT_LIST_HEAD(&ovs_net->dps);
22392241
INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq);
2242+
ovs_ct_init(net);
22402243
return 0;
22412244
}
22422245

@@ -2271,6 +2274,7 @@ static void __net_exit ovs_exit_net(struct net *dnet)
22712274
struct net *net;
22722275
LIST_HEAD(head);
22732276

2277+
ovs_ct_exit(dnet);
22742278
ovs_lock();
22752279
list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
22762280
__dp_destroy(dp);

net/openvswitch/datapath.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ struct ovs_net {
138138
struct list_head dps;
139139
struct work_struct dp_notify_work;
140140
struct vport_net vport_net;
141+
142+
/* Module reference for configuring conntrack. */
143+
bool xt_label;
141144
};
142145

143146
extern int ovs_net_id;

0 commit comments

Comments
 (0)