1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2020 Facebook */ |
3 | |
4 | #include <string.h> |
5 | #include <errno.h> |
6 | #include <netinet/in.h> |
7 | #include <linux/stddef.h> |
8 | #include <linux/bpf.h> |
9 | #include <linux/ipv6.h> |
10 | #include <linux/tcp.h> |
11 | #include <linux/if_ether.h> |
12 | #include <linux/pkt_cls.h> |
13 | |
14 | #include <bpf/bpf_helpers.h> |
15 | #include <bpf/bpf_endian.h> |
16 | #include "bpf_tcp_helpers.h" |
17 | |
18 | struct sockaddr_in6 srv_sa6 = {}; |
19 | __u16 listen_tp_sport = 0; |
20 | __u16 req_sk_sport = 0; |
21 | __u32 recv_cookie = 0; |
22 | __u32 gen_cookie = 0; |
23 | __u32 linum = 0; |
24 | |
25 | #define LOG() ({ if (!linum) linum = __LINE__; }) |
26 | |
27 | static void test_syncookie_helper(struct ipv6hdr *ip6h, struct tcphdr *th, |
28 | struct tcp_sock *tp, |
29 | struct __sk_buff *skb) |
30 | { |
31 | if (th->syn) { |
32 | __s64 mss_cookie; |
33 | void *data_end; |
34 | |
35 | data_end = (void *)(long)(skb->data_end); |
36 | |
37 | if (th->doff * 4 != 40) { |
38 | LOG(); |
39 | return; |
40 | } |
41 | |
42 | if ((void *)th + 40 > data_end) { |
43 | LOG(); |
44 | return; |
45 | } |
46 | |
47 | mss_cookie = bpf_tcp_gen_syncookie(tp, ip6h, sizeof(*ip6h), |
48 | th, 40); |
49 | if (mss_cookie < 0) { |
50 | if (mss_cookie != -ENOENT) |
51 | LOG(); |
52 | } else { |
53 | gen_cookie = (__u32)mss_cookie; |
54 | } |
55 | } else if (gen_cookie) { |
56 | /* It was in cookie mode */ |
57 | int ret = bpf_tcp_check_syncookie(tp, ip6h, sizeof(*ip6h), |
58 | th, sizeof(*th)); |
59 | |
60 | if (ret < 0) { |
61 | if (ret != -ENOENT) |
62 | LOG(); |
63 | } else { |
64 | recv_cookie = bpf_ntohl(th->ack_seq) - 1; |
65 | } |
66 | } |
67 | } |
68 | |
69 | static int handle_ip6_tcp(struct ipv6hdr *ip6h, struct __sk_buff *skb) |
70 | { |
71 | struct bpf_sock_tuple *tuple; |
72 | struct bpf_sock *bpf_skc; |
73 | unsigned int tuple_len; |
74 | struct tcphdr *th; |
75 | void *data_end; |
76 | |
77 | data_end = (void *)(long)(skb->data_end); |
78 | |
79 | th = (struct tcphdr *)(ip6h + 1); |
80 | if (th + 1 > data_end) |
81 | return TC_ACT_OK; |
82 | |
83 | /* Is it the testing traffic? */ |
84 | if (th->dest != srv_sa6.sin6_port) |
85 | return TC_ACT_OK; |
86 | |
87 | tuple_len = sizeof(tuple->ipv6); |
88 | tuple = (struct bpf_sock_tuple *)&ip6h->saddr; |
89 | if ((void *)tuple + tuple_len > data_end) { |
90 | LOG(); |
91 | return TC_ACT_OK; |
92 | } |
93 | |
94 | bpf_skc = bpf_skc_lookup_tcp(skb, tuple, tuple_len, |
95 | BPF_F_CURRENT_NETNS, 0); |
96 | if (!bpf_skc) { |
97 | LOG(); |
98 | return TC_ACT_OK; |
99 | } |
100 | |
101 | if (bpf_skc->state == BPF_TCP_NEW_SYN_RECV) { |
102 | struct request_sock *req_sk; |
103 | |
104 | req_sk = (struct request_sock *)bpf_skc_to_tcp_request_sock(bpf_skc); |
105 | if (!req_sk) { |
106 | LOG(); |
107 | goto release; |
108 | } |
109 | |
110 | if (bpf_sk_assign(skb, req_sk, 0)) { |
111 | LOG(); |
112 | goto release; |
113 | } |
114 | |
115 | req_sk_sport = req_sk->__req_common.skc_num; |
116 | |
117 | bpf_sk_release(req_sk); |
118 | return TC_ACT_OK; |
119 | } else if (bpf_skc->state == BPF_TCP_LISTEN) { |
120 | struct tcp_sock *tp; |
121 | |
122 | tp = bpf_skc_to_tcp_sock(bpf_skc); |
123 | if (!tp) { |
124 | LOG(); |
125 | goto release; |
126 | } |
127 | |
128 | if (bpf_sk_assign(skb, tp, 0)) { |
129 | LOG(); |
130 | goto release; |
131 | } |
132 | |
133 | listen_tp_sport = tp->inet_conn.icsk_inet.sk.__sk_common.skc_num; |
134 | |
135 | test_syncookie_helper(ip6h, th, tp, skb); |
136 | bpf_sk_release(tp); |
137 | return TC_ACT_OK; |
138 | } |
139 | |
140 | if (bpf_sk_assign(skb, bpf_skc, 0)) |
141 | LOG(); |
142 | |
143 | release: |
144 | bpf_sk_release(bpf_skc); |
145 | return TC_ACT_OK; |
146 | } |
147 | |
148 | SEC("tc" ) |
149 | int cls_ingress(struct __sk_buff *skb) |
150 | { |
151 | struct ipv6hdr *ip6h; |
152 | struct ethhdr *eth; |
153 | void *data_end; |
154 | |
155 | data_end = (void *)(long)(skb->data_end); |
156 | |
157 | eth = (struct ethhdr *)(long)(skb->data); |
158 | if (eth + 1 > data_end) |
159 | return TC_ACT_OK; |
160 | |
161 | if (eth->h_proto != bpf_htons(ETH_P_IPV6)) |
162 | return TC_ACT_OK; |
163 | |
164 | ip6h = (struct ipv6hdr *)(eth + 1); |
165 | if (ip6h + 1 > data_end) |
166 | return TC_ACT_OK; |
167 | |
168 | if (ip6h->nexthdr == IPPROTO_TCP) |
169 | return handle_ip6_tcp(ip6h, skb); |
170 | |
171 | return TC_ACT_OK; |
172 | } |
173 | |
174 | char _license[] SEC("license" ) = "GPL" ; |
175 | |