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