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. */
3845struct 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
4654static 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+
93119static 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+
171237static 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+
330407int 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 );
357440err :
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
371456static 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+ }
0 commit comments