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