1 // SPDX-License-Identifier: GPL-2.0 2 #include <stddef.h> 3 #include <string.h> 4 #include <netinet/in.h> 5 #include <linux/bpf.h> 6 #include <linux/if_ether.h> 7 #include <linux/if_packet.h> 8 #include <linux/ip.h> 9 #include <linux/ipv6.h> 10 #include <linux/types.h> 11 #include <linux/socket.h> 12 #include <linux/tcp.h> 13 #include <bpf/bpf_helpers.h> 14 #include <bpf/bpf_endian.h> 15 #include "bpf_tcp_helpers.h" 16 #include "test_tcpbpf.h" 17 18 struct tcpbpf_globals global = {}; 19 20 /** 21 * SOL_TCP is defined in <netinet/tcp.h> while 22 * TCP_SAVED_SYN is defined in already included <linux/tcp.h> 23 */ 24 #ifndef SOL_TCP 25 #define SOL_TCP 6 26 #endif 27 28 static __always_inline int get_tp_window_clamp(struct bpf_sock_ops *skops) 29 { 30 struct bpf_sock *sk; 31 struct tcp_sock *tp; 32 33 sk = skops->sk; 34 if (!sk) 35 return -1; 36 tp = bpf_skc_to_tcp_sock(sk); 37 if (!tp) 38 return -1; 39 return tp->window_clamp; 40 } 41 42 SEC("sockops") 43 int bpf_testcb(struct bpf_sock_ops *skops) 44 { 45 char header[sizeof(struct ipv6hdr) + sizeof(struct tcphdr)]; 46 struct bpf_sock_ops *reuse = skops; 47 struct tcphdr *thdr; 48 int window_clamp = 9216; 49 int good_call_rv = 0; 50 int bad_call_rv = 0; 51 int save_syn = 1; 52 int rv = -1; 53 int v = 0; 54 int op; 55 56 /* Test reading fields in bpf_sock_ops using single register */ 57 asm volatile ( 58 "%[reuse] = *(u32 *)(%[reuse] +96)" 59 : [reuse] "+r"(reuse) 60 :); 61 62 asm volatile ( 63 "%[op] = *(u32 *)(%[skops] +96)" 64 : [op] "+r"(op) 65 : [skops] "r"(skops) 66 :); 67 68 asm volatile ( 69 "r9 = %[skops];\n" 70 "r8 = *(u32 *)(r9 +164);\n" 71 "*(u32 *)(r9 +164) = r8;\n" 72 :: [skops] "r"(skops) 73 : "r9", "r8"); 74 75 asm volatile ( 76 "r1 = %[skops];\n" 77 "r1 = *(u64 *)(r1 +184);\n" 78 "if r1 == 0 goto +1;\n" 79 "r1 = *(u32 *)(r1 +4);\n" 80 :: [skops] "r"(skops):"r1"); 81 82 asm volatile ( 83 "r9 = %[skops];\n" 84 "r9 = *(u64 *)(r9 +184);\n" 85 "if r9 == 0 goto +1;\n" 86 "r9 = *(u32 *)(r9 +4);\n" 87 :: [skops] "r"(skops):"r9"); 88 89 asm volatile ( 90 "r1 = %[skops];\n" 91 "r2 = *(u64 *)(r1 +184);\n" 92 "if r2 == 0 goto +1;\n" 93 "r2 = *(u32 *)(r2 +4);\n" 94 :: [skops] "r"(skops):"r1", "r2"); 95 96 op = (int) skops->op; 97 98 global.event_map |= (1 << op); 99 100 switch (op) { 101 case BPF_SOCK_OPS_TCP_CONNECT_CB: 102 rv = bpf_setsockopt(skops, SOL_TCP, TCP_WINDOW_CLAMP, 103 &window_clamp, sizeof(window_clamp)); 104 global.window_clamp_client = get_tp_window_clamp(skops); 105 break; 106 case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: 107 /* Test failure to set largest cb flag (assumes not defined) */ 108 global.bad_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, 0x80); 109 /* Set callback */ 110 global.good_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, 111 BPF_SOCK_OPS_STATE_CB_FLAG); 112 break; 113 case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: 114 skops->sk_txhash = 0x12345f; 115 v = 0xff; 116 rv = bpf_setsockopt(skops, SOL_IPV6, IPV6_TCLASS, &v, 117 sizeof(v)); 118 if (skops->family == AF_INET6) { 119 v = bpf_getsockopt(skops, IPPROTO_TCP, TCP_SAVED_SYN, 120 header, (sizeof(struct ipv6hdr) + 121 sizeof(struct tcphdr))); 122 if (!v) { 123 int offset = sizeof(struct ipv6hdr); 124 125 thdr = (struct tcphdr *)(header + offset); 126 v = thdr->syn; 127 128 global.tcp_saved_syn = v; 129 } 130 } 131 rv = bpf_setsockopt(skops, SOL_TCP, TCP_WINDOW_CLAMP, 132 &window_clamp, sizeof(window_clamp)); 133 134 global.window_clamp_server = get_tp_window_clamp(skops); 135 break; 136 case BPF_SOCK_OPS_RTO_CB: 137 break; 138 case BPF_SOCK_OPS_RETRANS_CB: 139 break; 140 case BPF_SOCK_OPS_STATE_CB: 141 if (skops->args[1] == BPF_TCP_CLOSE) { 142 if (skops->args[0] == BPF_TCP_LISTEN) { 143 global.num_listen++; 144 } else { 145 global.total_retrans = skops->total_retrans; 146 global.data_segs_in = skops->data_segs_in; 147 global.data_segs_out = skops->data_segs_out; 148 global.bytes_received = skops->bytes_received; 149 global.bytes_acked = skops->bytes_acked; 150 } 151 global.num_close_events++; 152 } 153 break; 154 case BPF_SOCK_OPS_TCP_LISTEN_CB: 155 bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG); 156 v = bpf_setsockopt(skops, IPPROTO_TCP, TCP_SAVE_SYN, 157 &save_syn, sizeof(save_syn)); 158 /* Update global map w/ result of setsock opt */ 159 global.tcp_save_syn = v; 160 break; 161 default: 162 rv = -1; 163 } 164 skops->reply = rv; 165 return 1; 166 } 167 char _license[] SEC("license") = "GPL"; 168