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 SEC("cgroup/connect4")
108 int connect_v4_prog(struct bpf_sock_addr *ctx)
109 {
110 	struct bpf_sock_tuple tuple = {};
111 	struct bpf_sock *sk;
112 
113 	/* Verify that new destination is available. */
114 	memset(&tuple.ipv4.saddr, 0, sizeof(tuple.ipv4.saddr));
115 	memset(&tuple.ipv4.sport, 0, sizeof(tuple.ipv4.sport));
116 
117 	tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
118 	tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);
119 
120 	/* Bind to device and unbind it. */
121 	if (bind_to_device(ctx))
122 		return 0;
123 
124 	if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
125 		return 0;
126 	else if (ctx->type == SOCK_STREAM)
127 		sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4),
128 				       BPF_F_CURRENT_NETNS, 0);
129 	else
130 		sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4),
131 				       BPF_F_CURRENT_NETNS, 0);
132 
133 	if (!sk)
134 		return 0;
135 
136 	if (sk->src_ip4 != tuple.ipv4.daddr ||
137 	    sk->src_port != DST_REWRITE_PORT4) {
138 		bpf_sk_release(sk);
139 		return 0;
140 	}
141 
142 	bpf_sk_release(sk);
143 
144 	/* Rewrite congestion control. */
145 	if (ctx->type == SOCK_STREAM && set_cc(ctx))
146 		return 0;
147 
148 	/* Rewrite destination. */
149 	ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
150 	ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
151 
152 	return do_bind(ctx) ? 1 : 0;
153 }
154 
155 char _license[] SEC("license") = "GPL";
156