summaryrefslogtreecommitdiffstats
path: root/net/openvswitch/conntrack.c
diff options
context:
space:
mode:
authorJoe Stringer <joestringer@nicira.com>2015-08-26 14:31:52 -0400
committerDavid S. Miller <davem@davemloft.net>2015-08-27 14:40:43 -0400
commitc2ac667358708d7cce64c78f58af6adf4c1e848b (patch)
treef24ccd3b0e54fe0ba09ecadb837eef3ecaa014c8 /net/openvswitch/conntrack.c
parent86ca02e77408bb58ba596c1a411ec7f631733690 (diff)
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>
Diffstat (limited to 'net/openvswitch/conntrack.c')
-rw-r--r--net/openvswitch/conntrack.c128
1 files changed, 126 insertions, 2 deletions
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index e53dafccf21f..a0417fb33d1d 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -15,6 +15,7 @@
15#include <linux/openvswitch.h> 15#include <linux/openvswitch.h>
16#include <net/ip.h> 16#include <net/ip.h>
17#include <net/netfilter/nf_conntrack_core.h> 17#include <net/netfilter/nf_conntrack_core.h>
18#include <net/netfilter/nf_conntrack_labels.h>
18#include <net/netfilter/nf_conntrack_zones.h> 19#include <net/netfilter/nf_conntrack_zones.h>
19#include <net/netfilter/ipv6/nf_defrag_ipv6.h> 20#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
20 21
@@ -34,6 +35,12 @@ struct md_mark {
34 u32 mask; 35 u32 mask;
35}; 36};
36 37
38/* Metadata label for masked write to conntrack label. */
39struct md_label {
40 struct ovs_key_ct_label value;
41 struct ovs_key_ct_label mask;
42};
43
37/* Conntrack action context for execution. */ 44/* Conntrack action context for execution. */
38struct ovs_conntrack_info { 45struct ovs_conntrack_info {
39 struct nf_conntrack_zone zone; 46 struct nf_conntrack_zone zone;
@@ -41,6 +48,7 @@ struct ovs_conntrack_info {
41 u32 flags; 48 u32 flags;
42 u16 family; 49 u16 family;
43 struct md_mark mark; 50 struct md_mark mark;
51 struct md_label label;
44}; 52};
45 53
46static u16 key_to_nfproto(const struct sw_flow_key *key) 54static u16 key_to_nfproto(const struct sw_flow_key *key)
@@ -90,6 +98,24 @@ static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo)
90 return ct_state; 98 return ct_state;
91} 99}
92 100
101static 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
93static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state, 119static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
94 const struct nf_conntrack_zone *zone, 120 const struct nf_conntrack_zone *zone,
95 const struct nf_conn *ct) 121 const struct nf_conn *ct)
@@ -97,6 +123,7 @@ static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
97 key->ct.state = state; 123 key->ct.state = state;
98 key->ct.zone = zone->id; 124 key->ct.zone = zone->id;
99 key->ct.mark = ct ? ct->mark : 0; 125 key->ct.mark = ct ? ct->mark : 0;
126 ovs_ct_get_label(ct, &key->ct.label);
100} 127}
101 128
102/* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has 129/* 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)
140 nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark)) 167 nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark))
141 return -EMSGSIZE; 168 return -EMSGSIZE;
142 169
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
143 return 0; 175 return 0;
144} 176}
145 177
@@ -168,6 +200,40 @@ static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key,
168 return 0; 200 return 0;
169} 201}
170 202
203static 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
171static int handle_fragments(struct net *net, struct sw_flow_key *key, 237static int handle_fragments(struct net *net, struct sw_flow_key *key,
172 u16 zone, struct sk_buff *skb) 238 u16 zone, struct sk_buff *skb)
173{ 239{
@@ -327,6 +393,17 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
327 return 0; 393 return 0;
328} 394}
329 395
396static 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
330int ovs_ct_execute(struct net *net, struct sk_buff *skb, 407int ovs_ct_execute(struct net *net, struct sk_buff *skb,
331 struct sw_flow_key *key, 408 struct sw_flow_key *key,
332 const struct ovs_conntrack_info *info) 409 const struct ovs_conntrack_info *info)
@@ -351,9 +428,15 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
351 if (err) 428 if (err)
352 goto err; 429 goto err;
353 430
354 if (info->mark.mask) 431 if (info->mark.mask) {
355 err = ovs_ct_set_mark(skb, key, info->mark.value, 432 err = ovs_ct_set_mark(skb, key, info->mark.value,
356 info->mark.mask); 433 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);
357err: 440err:
358 skb_push(skb, nh_ofs); 441 skb_push(skb, nh_ofs);
359 return err; 442 return err;
@@ -366,6 +449,8 @@ static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
366 .maxlen = sizeof(u16) }, 449 .maxlen = sizeof(u16) },
367 [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), 450 [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark),
368 .maxlen = sizeof(struct md_mark) }, 451 .maxlen = sizeof(struct md_mark) },
452 [OVS_CT_ATTR_LABEL] = { .minlen = sizeof(struct md_label),
453 .maxlen = sizeof(struct md_label) },
369}; 454};
370 455
371static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, 456static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
@@ -409,6 +494,14 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
409 break; 494 break;
410 } 495 }
411#endif 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 }
504#endif
412 default: 505 default:
413 OVS_NLERR(log, "Unknown conntrack attr (%d)", 506 OVS_NLERR(log, "Unknown conntrack attr (%d)",
414 type); 507 type);
@@ -424,7 +517,7 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
424 return 0; 517 return 0;
425} 518}
426 519
427bool ovs_ct_verify(enum ovs_key_attr attr) 520bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr)
428{ 521{
429 if (attr == OVS_KEY_ATTR_CT_STATE) 522 if (attr == OVS_KEY_ATTR_CT_STATE)
430 return true; 523 return true;
@@ -434,6 +527,12 @@ bool ovs_ct_verify(enum ovs_key_attr attr)
434 if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 527 if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) &&
435 attr == OVS_KEY_ATTR_CT_MARK) 528 attr == OVS_KEY_ATTR_CT_MARK)
436 return true; 529 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 }
437 536
438 return false; 537 return false;
439} 538}
@@ -500,6 +599,10 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
500 nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark), 599 nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark),
501 &ct_info->mark)) 600 &ct_info->mark))
502 return -EMSGSIZE; 601 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;
503 606
504 nla_nest_end(skb, start); 607 nla_nest_end(skb, start);
505 608
@@ -513,3 +616,24 @@ void ovs_ct_free_action(const struct nlattr *a)
513 if (ct_info->ct) 616 if (ct_info->ct)
514 nf_ct_put(ct_info->ct); 617 nf_ct_put(ct_info->ct);
515} 618}
619
620void 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
633void 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}