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 SERV4_IP 0xc0a801feU /* 192.168.1.254 */ |
16 | #define SERV4_PORT 4040 |
17 | #define SERV4_REWRITE_IP 0x7f000001U /* 127.0.0.1 */ |
18 | #define SERV4_REWRITE_PORT 4444 |
19 | |
20 | #ifndef IFNAMSIZ |
21 | #define IFNAMSIZ 16 |
22 | #endif |
23 | |
24 | static __inline int bind_to_device(struct bpf_sock_addr *ctx) |
25 | { |
26 | char veth1[IFNAMSIZ] = "test_sock_addr1" ; |
27 | char veth2[IFNAMSIZ] = "test_sock_addr2" ; |
28 | char missing[IFNAMSIZ] = "nonexistent_dev" ; |
29 | char del_bind[IFNAMSIZ] = "" ; |
30 | int veth1_idx, veth2_idx; |
31 | |
32 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
33 | &veth1, sizeof(veth1))) |
34 | return 1; |
35 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, |
36 | &veth1_idx, sizeof(veth1_idx)) || !veth1_idx) |
37 | return 1; |
38 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
39 | &veth2, sizeof(veth2))) |
40 | return 1; |
41 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, |
42 | &veth2_idx, sizeof(veth2_idx)) || !veth2_idx || |
43 | veth1_idx == veth2_idx) |
44 | return 1; |
45 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
46 | &missing, sizeof(missing)) != -ENODEV) |
47 | return 1; |
48 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, |
49 | &veth1_idx, sizeof(veth1_idx))) |
50 | return 1; |
51 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, |
52 | &del_bind, sizeof(del_bind))) |
53 | return 1; |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static __inline int bind_reuseport(struct bpf_sock_addr *ctx) |
59 | { |
60 | int val = 1; |
61 | |
62 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
63 | &val, sizeof(val))) |
64 | return 1; |
65 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
66 | &val, sizeof(val)) || !val) |
67 | return 1; |
68 | val = 0; |
69 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
70 | &val, sizeof(val))) |
71 | return 1; |
72 | if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, |
73 | &val, sizeof(val)) || val) |
74 | return 1; |
75 | |
76 | return 0; |
77 | } |
78 | |
79 | static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt) |
80 | { |
81 | int old, tmp, new = 0xeb9f; |
82 | |
83 | /* Socket in test case has guarantee that old never equals to new. */ |
84 | if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) || |
85 | old == new) |
86 | return 1; |
87 | if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new))) |
88 | return 1; |
89 | if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) || |
90 | tmp != new) |
91 | return 1; |
92 | if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old))) |
93 | return 1; |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | SEC("cgroup/bind4" ) |
99 | int bind_v4_prog(struct bpf_sock_addr *ctx) |
100 | { |
101 | struct bpf_sock *sk; |
102 | __u32 user_ip4; |
103 | __u16 user_port; |
104 | |
105 | sk = ctx->sk; |
106 | if (!sk) |
107 | return 0; |
108 | |
109 | if (sk->family != AF_INET) |
110 | return 0; |
111 | |
112 | if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) |
113 | return 0; |
114 | |
115 | if (ctx->user_ip4 != bpf_htonl(SERV4_IP) || |
116 | ctx->user_port != bpf_htons(SERV4_PORT)) |
117 | return 0; |
118 | |
119 | // u8 narrow loads: |
120 | user_ip4 = 0; |
121 | user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0; |
122 | user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8; |
123 | user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16; |
124 | user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24; |
125 | if (ctx->user_ip4 != user_ip4) |
126 | return 0; |
127 | |
128 | user_port = 0; |
129 | user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0; |
130 | user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8; |
131 | if (ctx->user_port != user_port) |
132 | return 0; |
133 | |
134 | // u16 narrow loads: |
135 | user_ip4 = 0; |
136 | user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0; |
137 | user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16; |
138 | if (ctx->user_ip4 != user_ip4) |
139 | return 0; |
140 | |
141 | /* Bind to device and unbind it. */ |
142 | if (bind_to_device(ctx)) |
143 | return 0; |
144 | |
145 | /* Test for misc socket options. */ |
146 | if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY)) |
147 | return 0; |
148 | |
149 | /* Set reuseport and unset */ |
150 | if (bind_reuseport(ctx)) |
151 | return 0; |
152 | |
153 | ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP); |
154 | ctx->user_port = bpf_htons(SERV4_REWRITE_PORT); |
155 | |
156 | return 1; |
157 | } |
158 | |
159 | char _license[] SEC("license" ) = "GPL" ; |
160 | |