7
7
#include <bpf/bpf_endian.h>
8
8
#include <asm/errno.h>
9
9
10
+ #define TC_ACT_OK 0
11
+ #define TC_ACT_SHOT 2
12
+
10
13
#define NSEC_PER_SEC 1000000000L
11
14
12
15
#define ETH_ALEN 6
@@ -80,6 +83,12 @@ extern struct nf_conn *bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx,
80
83
struct bpf_ct_opts * opts ,
81
84
__u32 len_opts ) __ksym ;
82
85
86
+ extern struct nf_conn * bpf_skb_ct_lookup (struct __sk_buff * skb_ctx ,
87
+ struct bpf_sock_tuple * bpf_tuple ,
88
+ u32 len_tuple ,
89
+ struct bpf_ct_opts * opts ,
90
+ u32 len_opts ) __ksym ;
91
+
83
92
extern void bpf_ct_release (struct nf_conn * ct ) __ksym ;
84
93
85
94
static __always_inline void swap_eth_addr (__u8 * a , __u8 * b )
@@ -380,7 +389,7 @@ static __always_inline int tcp_dissect(void *data, void *data_end,
380
389
return XDP_TX ;
381
390
}
382
391
383
- static __always_inline int tcp_lookup (struct xdp_md * ctx , struct header_pointers * hdr )
392
+ static __always_inline int tcp_lookup (void * ctx , struct header_pointers * hdr , bool xdp )
384
393
{
385
394
struct bpf_ct_opts ct_lookup_opts = {
386
395
.netns_id = BPF_F_CURRENT_NETNS ,
@@ -410,7 +419,10 @@ static __always_inline int tcp_lookup(struct xdp_md *ctx, struct header_pointers
410
419
// The verifier can't track that either ipv4 or ipv6 is not NULL.
411
420
return XDP_ABORTED ;
412
421
}
413
- ct = bpf_xdp_ct_lookup (ctx , & tup , tup_size , & ct_lookup_opts , sizeof (ct_lookup_opts ));
422
+ if (xdp )
423
+ ct = bpf_xdp_ct_lookup (ctx , & tup , tup_size , & ct_lookup_opts , sizeof (ct_lookup_opts ));
424
+ else
425
+ ct = bpf_skb_ct_lookup (ctx , & tup , tup_size , & ct_lookup_opts , sizeof (ct_lookup_opts ));
414
426
if (ct ) {
415
427
unsigned long status = ct -> status ;
416
428
@@ -523,8 +535,9 @@ static __always_inline void tcpv6_gen_synack(struct header_pointers *hdr,
523
535
}
524
536
525
537
static __always_inline int syncookie_handle_syn (struct header_pointers * hdr ,
526
- struct xdp_md * ctx ,
527
- void * data , void * data_end )
538
+ void * ctx ,
539
+ void * data , void * data_end ,
540
+ bool xdp )
528
541
{
529
542
__u32 old_pkt_size , new_pkt_size ;
530
543
// Unlike clang 10, clang 11 and 12 generate code that doesn't pass the
@@ -656,8 +669,13 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr,
656
669
// Set the new packet size.
657
670
old_pkt_size = data_end - data ;
658
671
new_pkt_size = sizeof (* hdr -> eth ) + ip_len + hdr -> tcp -> doff * 4 ;
659
- if (bpf_xdp_adjust_tail (ctx , new_pkt_size - old_pkt_size ))
660
- return XDP_ABORTED ;
672
+ if (xdp ) {
673
+ if (bpf_xdp_adjust_tail (ctx , new_pkt_size - old_pkt_size ))
674
+ return XDP_ABORTED ;
675
+ } else {
676
+ if (bpf_skb_change_tail (ctx , new_pkt_size , 0 ))
677
+ return XDP_ABORTED ;
678
+ }
661
679
662
680
values_inc_synacks ();
663
681
@@ -683,68 +701,119 @@ static __always_inline int syncookie_handle_ack(struct header_pointers *hdr)
683
701
return XDP_PASS ;
684
702
}
685
703
686
- SEC ( "xdp/syncookie" )
687
- int syncookie_xdp ( struct xdp_md * ctx )
704
+ static __always_inline int syncookie_part1 ( void * ctx , void * data , void * data_end ,
705
+ struct header_pointers * hdr , bool xdp )
688
706
{
689
- void * data_end = (void * )(long )ctx -> data_end ;
690
- void * data = (void * )(long )ctx -> data ;
691
- struct header_pointers hdr ;
692
- __s64 value ;
693
- int ret ;
694
-
695
707
struct bpf_ct_opts ct_lookup_opts = {
696
708
.netns_id = BPF_F_CURRENT_NETNS ,
697
709
.l4proto = IPPROTO_TCP ,
698
710
};
711
+ int ret ;
699
712
700
- ret = tcp_dissect (data , data_end , & hdr );
713
+ ret = tcp_dissect (data , data_end , hdr );
701
714
if (ret != XDP_TX )
702
715
return ret ;
703
716
704
- ret = tcp_lookup (ctx , & hdr );
717
+ ret = tcp_lookup (ctx , hdr , xdp );
705
718
if (ret != XDP_TX )
706
719
return ret ;
707
720
708
721
// Packet is TCP and doesn't belong to an established connection.
709
722
710
- if ((hdr . tcp -> syn ^ hdr . tcp -> ack ) != 1 )
723
+ if ((hdr -> tcp -> syn ^ hdr -> tcp -> ack ) != 1 )
711
724
return XDP_DROP ;
712
725
713
- // Grow the TCP header to TCP_MAXLEN to be able to pass any hdr. tcp_len
726
+ // Grow the TCP header to TCP_MAXLEN to be able to pass any hdr-> tcp_len
714
727
// to bpf_tcp_raw_gen_syncookie_ipv{4,6} and pass the verifier.
715
- if (bpf_xdp_adjust_tail (ctx , TCP_MAXLEN - hdr .tcp_len ))
716
- return XDP_ABORTED ;
728
+ if (xdp ) {
729
+ if (bpf_xdp_adjust_tail (ctx , TCP_MAXLEN - hdr -> tcp_len ))
730
+ return XDP_ABORTED ;
731
+ } else {
732
+ // Without volatile the verifier throws this error:
733
+ // R9 32-bit pointer arithmetic prohibited
734
+ volatile u64 old_len = data_end - data ;
717
735
718
- data_end = (void * )(long )ctx -> data_end ;
719
- data = (void * )(long )ctx -> data ;
736
+ if (bpf_skb_change_tail (ctx , old_len + TCP_MAXLEN - hdr -> tcp_len , 0 ))
737
+ return XDP_ABORTED ;
738
+ }
739
+
740
+ return XDP_TX ;
741
+ }
720
742
721
- if (hdr .ipv4 ) {
722
- hdr .eth = data ;
723
- hdr .ipv4 = (void * )hdr .eth + sizeof (* hdr .eth );
743
+ static __always_inline int syncookie_part2 (void * ctx , void * data , void * data_end ,
744
+ struct header_pointers * hdr , bool xdp )
745
+ {
746
+ if (hdr -> ipv4 ) {
747
+ hdr -> eth = data ;
748
+ hdr -> ipv4 = (void * )hdr -> eth + sizeof (* hdr -> eth );
724
749
// IPV4_MAXLEN is needed when calculating checksum.
725
750
// At least sizeof(struct iphdr) is needed here to access ihl.
726
- if ((void * )hdr . ipv4 + IPV4_MAXLEN > data_end )
751
+ if ((void * )hdr -> ipv4 + IPV4_MAXLEN > data_end )
727
752
return XDP_ABORTED ;
728
- hdr . tcp = (void * )hdr . ipv4 + hdr . ipv4 -> ihl * 4 ;
729
- } else if (hdr . ipv6 ) {
730
- hdr . eth = data ;
731
- hdr . ipv6 = (void * )hdr . eth + sizeof (* hdr . eth );
732
- hdr . tcp = (void * )hdr . ipv6 + sizeof (* hdr . ipv6 );
753
+ hdr -> tcp = (void * )hdr -> ipv4 + hdr -> ipv4 -> ihl * 4 ;
754
+ } else if (hdr -> ipv6 ) {
755
+ hdr -> eth = data ;
756
+ hdr -> ipv6 = (void * )hdr -> eth + sizeof (* hdr -> eth );
757
+ hdr -> tcp = (void * )hdr -> ipv6 + sizeof (* hdr -> ipv6 );
733
758
} else {
734
759
return XDP_ABORTED ;
735
760
}
736
761
737
- if ((void * )hdr . tcp + TCP_MAXLEN > data_end )
762
+ if ((void * )hdr -> tcp + TCP_MAXLEN > data_end )
738
763
return XDP_ABORTED ;
739
764
740
765
// We run out of registers, tcp_len gets spilled to the stack, and the
741
766
// verifier forgets its min and max values checked above in tcp_dissect.
742
- hdr . tcp_len = hdr . tcp -> doff * 4 ;
743
- if (hdr . tcp_len < sizeof (* hdr . tcp ))
767
+ hdr -> tcp_len = hdr -> tcp -> doff * 4 ;
768
+ if (hdr -> tcp_len < sizeof (* hdr -> tcp ))
744
769
return XDP_ABORTED ;
745
770
746
- return hdr .tcp -> syn ? syncookie_handle_syn (& hdr , ctx , data , data_end ) :
747
- syncookie_handle_ack (& hdr );
771
+ return hdr -> tcp -> syn ? syncookie_handle_syn (hdr , ctx , data , data_end , xdp ) :
772
+ syncookie_handle_ack (hdr );
773
+ }
774
+
775
+ SEC ("xdp/syncookie" )
776
+ int syncookie_xdp (struct xdp_md * ctx )
777
+ {
778
+ void * data_end = (void * )(long )ctx -> data_end ;
779
+ void * data = (void * )(long )ctx -> data ;
780
+ struct header_pointers hdr ;
781
+ int ret ;
782
+
783
+ ret = syncookie_part1 (ctx , data , data_end , & hdr , true);
784
+ if (ret != XDP_TX )
785
+ return ret ;
786
+
787
+ data_end = (void * )(long )ctx -> data_end ;
788
+ data = (void * )(long )ctx -> data ;
789
+
790
+ return syncookie_part2 (ctx , data , data_end , & hdr , true);
791
+ }
792
+
793
+ SEC ("tc" )
794
+ int syncookie_tc (struct __sk_buff * skb )
795
+ {
796
+ void * data_end = (void * )(long )skb -> data_end ;
797
+ void * data = (void * )(long )skb -> data ;
798
+ struct header_pointers hdr ;
799
+ int ret ;
800
+
801
+ ret = syncookie_part1 (skb , data , data_end , & hdr , false);
802
+ if (ret != XDP_TX )
803
+ return ret == XDP_PASS ? TC_ACT_OK : TC_ACT_SHOT ;
804
+
805
+ data_end = (void * )(long )skb -> data_end ;
806
+ data = (void * )(long )skb -> data ;
807
+
808
+ ret = syncookie_part2 (skb , data , data_end , & hdr , false);
809
+ switch (ret ) {
810
+ case XDP_PASS :
811
+ return TC_ACT_OK ;
812
+ case XDP_TX :
813
+ return bpf_redirect (skb -> ifindex , 0 );
814
+ default :
815
+ return TC_ACT_SHOT ;
816
+ }
748
817
}
749
818
750
819
char _license [] SEC ("license" ) = "GPL" ;
0 commit comments