1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <string.h> |
4 | |
5 | #include <linux/stddef.h> |
6 | #include <linux/bpf.h> |
7 | #include <linux/in.h> |
8 | #include <linux/in6.h> |
9 | #include <linux/if.h> |
10 | #include <errno.h> |
11 | |
12 | #include <bpf/bpf_helpers.h> |
13 | #include <bpf/bpf_endian.h> |
14 | |
15 | #define SERV6_IP_0 0xfaceb00c /* face:b00c:1234:5678::abcd */ |
16 | #define SERV6_IP_1 0x12345678 |
17 | #define SERV6_IP_2 0x00000000 |
18 | #define SERV6_IP_3 0x0000abcd |
19 | #define SERV6_PORT 6060 |
20 | #define SERV6_REWRITE_IP_0 0x00000000 |
21 | #define SERV6_REWRITE_IP_1 0x00000000 |
22 | #define SERV6_REWRITE_IP_2 0x00000000 |
23 | #define SERV6_REWRITE_IP_3 0x00000001 |
24 | #define SERV6_REWRITE_PORT 6666 |
25 | |
26 | #ifndef IFNAMSIZ |
27 | #define IFNAMSIZ 16 |
28 | #endif |
29 | |
30 | static __inline int bind_to_device(struct bpf_sock_addr *ctx) |
31 | { |
32 | char veth1[IFNAMSIZ] = "test_sock_addr1" ; |
33 | char veth2[IFNAMSIZ] = "test_sock_addr2" ; |
34 | char missing[IFNAMSIZ] = "nonexistent_dev" ; |
35 | char del_bind[IFNAMSIZ] = "" ; |
36 | int veth1_idx, veth2_idx; |
37 | |
38 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
39 | &veth1, sizeof(veth1))) |
40 | return 1; |
41 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, |
42 | &veth1_idx, sizeof(veth1_idx)) || !veth1_idx) |
43 | return 1; |
44 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
45 | &veth2, sizeof(veth2))) |
46 | return 1; |
47 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, |
48 | &veth2_idx, sizeof(veth2_idx)) || !veth2_idx || |
49 | veth1_idx == veth2_idx) |
50 | return 1; |
51 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
52 | &missing, sizeof(missing)) != -ENODEV) |
53 | return 1; |
54 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, |
55 | &veth1_idx, sizeof(veth1_idx))) |
56 | return 1; |
57 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
58 | &del_bind, sizeof(del_bind))) |
59 | return 1; |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | static __inline int bind_reuseport(struct bpf_sock_addr *ctx) |
65 | { |
66 | int val = 1; |
67 | |
68 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
69 | &val, sizeof(val))) |
70 | return 1; |
71 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
72 | &val, sizeof(val)) || !val) |
73 | return 1; |
74 | val = 0; |
75 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
76 | &val, sizeof(val))) |
77 | return 1; |
78 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
79 | &val, sizeof(val)) || val) |
80 | return 1; |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt) |
86 | { |
87 | int old, tmp, new = 0xeb9f; |
88 | |
89 | /* Socket in test case has guarantee that old never equals to new. */ |
90 | if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) || |
91 | old == new) |
92 | return 1; |
93 | if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new))) |
94 | return 1; |
95 | if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) || |
96 | tmp != new) |
97 | return 1; |
98 | if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old))) |
99 | return 1; |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | SEC("cgroup/bind6" ) |
105 | int bind_v6_prog(struct bpf_sock_addr *ctx) |
106 | { |
107 | struct bpf_sock *sk; |
108 | __u32 user_ip6; |
109 | __u16 user_port; |
110 | int i; |
111 | |
112 | sk = ctx->sk; |
113 | if (!sk) |
114 | return 0; |
115 | |
116 | if (sk->family != AF_INET6) |
117 | return 0; |
118 | |
119 | if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) |
120 | return 0; |
121 | |
122 | if (ctx->user_ip6[0] != bpf_htonl(SERV6_IP_0) || |
123 | ctx->user_ip6[1] != bpf_htonl(SERV6_IP_1) || |
124 | ctx->user_ip6[2] != bpf_htonl(SERV6_IP_2) || |
125 | ctx->user_ip6[3] != bpf_htonl(SERV6_IP_3) || |
126 | ctx->user_port != bpf_htons(SERV6_PORT)) |
127 | return 0; |
128 | |
129 | // u8 narrow loads: |
130 | for (i = 0; i < 4; i++) { |
131 | user_ip6 = 0; |
132 | user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[0] << 0; |
133 | user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[1] << 8; |
134 | user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[2] << 16; |
135 | user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[3] << 24; |
136 | if (ctx->user_ip6[i] != user_ip6) |
137 | return 0; |
138 | } |
139 | |
140 | user_port = 0; |
141 | user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0; |
142 | user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8; |
143 | if (ctx->user_port != user_port) |
144 | return 0; |
145 | |
146 | // u16 narrow loads: |
147 | for (i = 0; i < 4; i++) { |
148 | user_ip6 = 0; |
149 | user_ip6 |= ((volatile __u16 *)&ctx->user_ip6[i])[0] << 0; |
150 | user_ip6 |= ((volatile __u16 *)&ctx->user_ip6[i])[1] << 16; |
151 | if (ctx->user_ip6[i] != user_ip6) |
152 | return 0; |
153 | } |
154 | |
155 | /* Bind to device and unbind it. */ |
156 | if (bind_to_device(ctx)) |
157 | return 0; |
158 | |
159 | /* Test for misc socket options. */ |
160 | if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY)) |
161 | return 0; |
162 | |
163 | /* Set reuseport and unset */ |
164 | if (bind_reuseport(ctx)) |
165 | return 0; |
166 | |
167 | ctx->user_ip6[0] = bpf_htonl(SERV6_REWRITE_IP_0); |
168 | ctx->user_ip6[1] = bpf_htonl(SERV6_REWRITE_IP_1); |
169 | ctx->user_ip6[2] = bpf_htonl(SERV6_REWRITE_IP_2); |
170 | ctx->user_ip6[3] = bpf_htonl(SERV6_REWRITE_IP_3); |
171 | ctx->user_port = bpf_htons(SERV6_REWRITE_PORT); |
172 | |
173 | return 1; |
174 | } |
175 | |
176 | char _license[] SEC("license" ) = "GPL" ; |
177 | |