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