1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Facebook
3 
4 #include <string.h>
5 
6 #include <linux/stddef.h>
7 #include <linux/bpf.h>
8 #include <linux/in.h>
9 #include <linux/in6.h>
10 #include <sys/socket.h>
11 #include <netinet/tcp.h>
12 #include <linux/if.h>
13 #include <errno.h>
14 
15 #include <bpf/bpf_helpers.h>
16 #include <bpf/bpf_endian.h>
17 
18 #define SRC_REWRITE_IP4		0x7f000004U
19 #define DST_REWRITE_IP4		0x7f000001U
20 #define DST_REWRITE_PORT4	4444
21 
22 #ifndef TCP_CA_NAME_MAX
23 #define TCP_CA_NAME_MAX 16
24 #endif
25 
26 #ifndef IFNAMSIZ
27 #define IFNAMSIZ 16
28 #endif
29 
30 int _version SEC("version") = 1;
31 
32 __attribute__ ((noinline))
33 int do_bind(struct bpf_sock_addr *ctx)
34 {
35 	struct sockaddr_in sa = {};
36 
37 	sa.sin_family = AF_INET;
38 	sa.sin_port = bpf_htons(0);
39 	sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
40 
41 	if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
42 		return 0;
43 
44 	return 1;
45 }
46 
47 static __inline int verify_cc(struct bpf_sock_addr *ctx,
48 			      char expected[TCP_CA_NAME_MAX])
49 {
50 	char buf[TCP_CA_NAME_MAX];
51 	int i;
52 
53 	if (bpf_getsockopt(ctx, SOL_TCP, TCP_CONGESTION, &buf, sizeof(buf)))
54 		return 1;
55 
56 	for (i = 0; i < TCP_CA_NAME_MAX; i++) {
57 		if (buf[i] != expected[i])
58 			return 1;
59 		if (buf[i] == 0)
60 			break;
61 	}
62 
63 	return 0;
64 }
65 
66 static __inline int set_cc(struct bpf_sock_addr *ctx)
67 {
68 	char reno[TCP_CA_NAME_MAX] = "reno";
69 	char cubic[TCP_CA_NAME_MAX] = "cubic";
70 
71 	if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, &reno, sizeof(reno)))
72 		return 1;
73 	if (verify_cc(ctx, reno))
74 		return 1;
75 
76 	if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, &cubic, sizeof(cubic)))
77 		return 1;
78 	if (verify_cc(ctx, cubic))
79 		return 1;
80 
81 	return 0;
82 }
83 
84 static __inline int bind_to_device(struct bpf_sock_addr *ctx)
85 {
86 	char veth1[IFNAMSIZ] = "test_sock_addr1";
87 	char veth2[IFNAMSIZ] = "test_sock_addr2";
88 	char missing[IFNAMSIZ] = "nonexistent_dev";
89 	char del_bind[IFNAMSIZ] = "";
90 
91 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
92 				&veth1, sizeof(veth1)))
93 		return 1;
94 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
95 				&veth2, sizeof(veth2)))
96 		return 1;
97 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
98 				&missing, sizeof(missing)) != -ENODEV)
99 		return 1;
100 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
101 				&del_bind, sizeof(del_bind)))
102 		return 1;
103 
104 	return 0;
105 }
106 
107 static __inline int set_keepalive(struct bpf_sock_addr *ctx)
108 {
109 	int zero = 0, one = 1;
110 
111 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)))
112 		return 1;
113 	if (ctx->type == SOCK_STREAM) {
114 		if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPIDLE, &one, sizeof(one)))
115 			return 1;
116 		if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPINTVL, &one, sizeof(one)))
117 			return 1;
118 		if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPCNT, &one, sizeof(one)))
119 			return 1;
120 		if (bpf_setsockopt(ctx, SOL_TCP, TCP_SYNCNT, &one, sizeof(one)))
121 			return 1;
122 		if (bpf_setsockopt(ctx, SOL_TCP, TCP_USER_TIMEOUT, &one, sizeof(one)))
123 			return 1;
124 	}
125 	if (bpf_setsockopt(ctx, SOL_SOCKET, SO_KEEPALIVE, &zero, sizeof(zero)))
126 		return 1;
127 
128 	return 0;
129 }
130 
131 SEC("cgroup/connect4")
132 int connect_v4_prog(struct bpf_sock_addr *ctx)
133 {
134 	struct bpf_sock_tuple tuple = {};
135 	struct bpf_sock *sk;
136 
137 	/* Verify that new destination is available. */
138 	memset(&tuple.ipv4.saddr, 0, sizeof(tuple.ipv4.saddr));
139 	memset(&tuple.ipv4.sport, 0, sizeof(tuple.ipv4.sport));
140 
141 	tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
142 	tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);
143 
144 	/* Bind to device and unbind it. */
145 	if (bind_to_device(ctx))
146 		return 0;
147 
148 	if (set_keepalive(ctx))
149 		return 0;
150 
151 	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
152 		return 0;
153 	else if (ctx->type == SOCK_STREAM)
154 		sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4),
155 				       BPF_F_CURRENT_NETNS, 0);
156 	else
157 		sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4),
158 				       BPF_F_CURRENT_NETNS, 0);
159 
160 	if (!sk)
161 		return 0;
162 
163 	if (sk->src_ip4 != tuple.ipv4.daddr ||
164 	    sk->src_port != DST_REWRITE_PORT4) {
165 		bpf_sk_release(sk);
166 		return 0;
167 	}
168 
169 	bpf_sk_release(sk);
170 
171 	/* Rewrite congestion control. */
172 	if (ctx->type == SOCK_STREAM && set_cc(ctx))
173 		return 0;
174 
175 	/* Rewrite destination. */
176 	ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
177 	ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
178 
179 	return do_bind(ctx) ? 1 : 0;
180 }
181 
182 char _license[] SEC("license") = "GPL";
183