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 12 struct sockaddr_in6 srv_sa6 = {}; 13 struct 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 23 static 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 66 static 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 172 release: 173 bpf_sk_release(bpf_skc); 174 return TC_ACT_OK; 175 } 176 177 SEC("tc") 178 int 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 196 char _license[] SEC("license") = "GPL"; 197