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