15
15
#include <linux/openvswitch.h>
16
16
#include <net/ip.h>
17
17
#include <net/netfilter/nf_conntrack_core.h>
18
+ #include <net/netfilter/nf_conntrack_helper.h>
18
19
#include <net/netfilter/nf_conntrack_labels.h>
19
20
#include <net/netfilter/nf_conntrack_zones.h>
20
21
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
@@ -43,6 +44,7 @@ struct md_label {
43
44
44
45
/* Conntrack action context for execution. */
45
46
struct ovs_conntrack_info {
47
+ struct nf_conntrack_helper * helper ;
46
48
struct nf_conntrack_zone zone ;
47
49
struct nf_conn * ct ;
48
50
u32 flags ;
@@ -234,6 +236,51 @@ static int ovs_ct_set_label(struct sk_buff *skb, struct sw_flow_key *key,
234
236
return 0 ;
235
237
}
236
238
239
+ /* 'skb' should already be pulled to nh_ofs. */
240
+ static int ovs_ct_helper (struct sk_buff * skb , u16 proto )
241
+ {
242
+ const struct nf_conntrack_helper * helper ;
243
+ const struct nf_conn_help * help ;
244
+ enum ip_conntrack_info ctinfo ;
245
+ unsigned int protoff ;
246
+ struct nf_conn * ct ;
247
+
248
+ ct = nf_ct_get (skb , & ctinfo );
249
+ if (!ct || ctinfo == IP_CT_RELATED_REPLY )
250
+ return NF_ACCEPT ;
251
+
252
+ help = nfct_help (ct );
253
+ if (!help )
254
+ return NF_ACCEPT ;
255
+
256
+ helper = rcu_dereference (help -> helper );
257
+ if (!helper )
258
+ return NF_ACCEPT ;
259
+
260
+ switch (proto ) {
261
+ case NFPROTO_IPV4 :
262
+ protoff = ip_hdrlen (skb );
263
+ break ;
264
+ case NFPROTO_IPV6 : {
265
+ u8 nexthdr = ipv6_hdr (skb )-> nexthdr ;
266
+ __be16 frag_off ;
267
+
268
+ protoff = ipv6_skip_exthdr (skb , sizeof (struct ipv6hdr ),
269
+ & nexthdr , & frag_off );
270
+ if (protoff < 0 || (frag_off & htons (~0x7 )) != 0 ) {
271
+ pr_debug ("proto header not found\n" );
272
+ return NF_ACCEPT ;
273
+ }
274
+ break ;
275
+ }
276
+ default :
277
+ WARN_ONCE (1 , "helper invoked on non-IP family!" );
278
+ return NF_DROP ;
279
+ }
280
+
281
+ return helper -> help (skb , protoff , ct , ctinfo );
282
+ }
283
+
237
284
static int handle_fragments (struct net * net , struct sw_flow_key * key ,
238
285
u16 zone , struct sk_buff * skb )
239
286
{
@@ -306,6 +353,13 @@ static bool skb_nfct_cached(const struct net *net, const struct sk_buff *skb,
306
353
return false;
307
354
if (!nf_ct_zone_equal_any (info -> ct , nf_ct_zone (ct )))
308
355
return false;
356
+ if (info -> helper ) {
357
+ struct nf_conn_help * help ;
358
+
359
+ help = nf_ct_ext_find (ct , NF_CT_EXT_HELPER );
360
+ if (help && rcu_access_pointer (help -> helper ) != info -> helper )
361
+ return false;
362
+ }
309
363
310
364
return true;
311
365
}
@@ -334,6 +388,11 @@ static int __ovs_ct_lookup(struct net *net, const struct sw_flow_key *key,
334
388
if (nf_conntrack_in (net , info -> family , NF_INET_PRE_ROUTING ,
335
389
skb ) != NF_ACCEPT )
336
390
return - ENOENT ;
391
+
392
+ if (ovs_ct_helper (skb , info -> family ) != NF_ACCEPT ) {
393
+ WARN_ONCE (1 , "helper rejected packet" );
394
+ return - EINVAL ;
395
+ }
337
396
}
338
397
339
398
return 0 ;
@@ -442,6 +501,30 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
442
501
return err ;
443
502
}
444
503
504
+ static int ovs_ct_add_helper (struct ovs_conntrack_info * info , const char * name ,
505
+ const struct sw_flow_key * key , bool log )
506
+ {
507
+ struct nf_conntrack_helper * helper ;
508
+ struct nf_conn_help * help ;
509
+
510
+ helper = nf_conntrack_helper_try_module_get (name , info -> family ,
511
+ key -> ip .proto );
512
+ if (!helper ) {
513
+ OVS_NLERR (log , "Unknown helper \"%s\"" , name );
514
+ return - EINVAL ;
515
+ }
516
+
517
+ help = nf_ct_helper_ext_add (info -> ct , helper , GFP_KERNEL );
518
+ if (!help ) {
519
+ module_put (helper -> me );
520
+ return - ENOMEM ;
521
+ }
522
+
523
+ rcu_assign_pointer (help -> helper , helper );
524
+ info -> helper = helper ;
525
+ return 0 ;
526
+ }
527
+
445
528
static const struct ovs_ct_len_tbl ovs_ct_attr_lens [OVS_CT_ATTR_MAX + 1 ] = {
446
529
[OVS_CT_ATTR_FLAGS ] = { .minlen = sizeof (u32 ),
447
530
.maxlen = sizeof (u32 ) },
@@ -451,10 +534,12 @@ static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
451
534
.maxlen = sizeof (struct md_mark ) },
452
535
[OVS_CT_ATTR_LABEL ] = { .minlen = sizeof (struct md_label ),
453
536
.maxlen = sizeof (struct md_label ) },
537
+ [OVS_CT_ATTR_HELPER ] = { .minlen = 1 ,
538
+ .maxlen = NF_CT_HELPER_NAME_LEN }
454
539
};
455
540
456
541
static int parse_ct (const struct nlattr * attr , struct ovs_conntrack_info * info ,
457
- bool log )
542
+ const char * * helper , bool log )
458
543
{
459
544
struct nlattr * a ;
460
545
int rem ;
@@ -502,6 +587,13 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
502
587
break ;
503
588
}
504
589
#endif
590
+ case OVS_CT_ATTR_HELPER :
591
+ * helper = nla_data (a );
592
+ if (!memchr (* helper , '\0' , nla_len (a ))) {
593
+ OVS_NLERR (log , "Invalid conntrack helper" );
594
+ return - EINVAL ;
595
+ }
596
+ break ;
505
597
default :
506
598
OVS_NLERR (log , "Unknown conntrack attr (%d)" ,
507
599
type );
@@ -542,6 +634,7 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,
542
634
struct sw_flow_actions * * sfa , bool log )
543
635
{
544
636
struct ovs_conntrack_info ct_info ;
637
+ const char * helper = NULL ;
545
638
u16 family ;
546
639
int err ;
547
640
@@ -557,7 +650,7 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,
557
650
nf_ct_zone_init (& ct_info .zone , NF_CT_DEFAULT_ZONE_ID ,
558
651
NF_CT_DEFAULT_ZONE_DIR , 0 );
559
652
560
- err = parse_ct (attr , & ct_info , log );
653
+ err = parse_ct (attr , & ct_info , & helper , log );
561
654
if (err )
562
655
return err ;
563
656
@@ -567,6 +660,11 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,
567
660
OVS_NLERR (log , "Failed to allocate conntrack template" );
568
661
return - ENOMEM ;
569
662
}
663
+ if (helper ) {
664
+ err = ovs_ct_add_helper (& ct_info , helper , key , log );
665
+ if (err )
666
+ goto err_free_ct ;
667
+ }
570
668
571
669
err = ovs_nla_add_action (sfa , OVS_ACTION_ATTR_CT , & ct_info ,
572
670
sizeof (ct_info ), log );
@@ -603,6 +701,11 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
603
701
nla_put (skb , OVS_CT_ATTR_LABEL , sizeof (ct_info -> label ),
604
702
& ct_info -> label ))
605
703
return - EMSGSIZE ;
704
+ if (ct_info -> helper ) {
705
+ if (nla_put_string (skb , OVS_CT_ATTR_HELPER ,
706
+ ct_info -> helper -> name ))
707
+ return - EMSGSIZE ;
708
+ }
606
709
607
710
nla_nest_end (skb , start );
608
711
@@ -613,6 +716,8 @@ void ovs_ct_free_action(const struct nlattr *a)
613
716
{
614
717
struct ovs_conntrack_info * ct_info = nla_data (a );
615
718
719
+ if (ct_info -> helper )
720
+ module_put (ct_info -> helper -> me );
616
721
if (ct_info -> ct )
617
722
nf_ct_put (ct_info -> ct );
618
723
}
0 commit comments