1a999696cSStanislav Fomichev // SPDX-License-Identifier: GPL-2.0 2a999696cSStanislav Fomichev 3a999696cSStanislav Fomichev #include <string.h> 4a999696cSStanislav Fomichev 5a999696cSStanislav Fomichev #include <linux/stddef.h> 6a999696cSStanislav Fomichev #include <linux/bpf.h> 7a999696cSStanislav Fomichev #include <linux/in.h> 8a999696cSStanislav Fomichev #include <linux/in6.h> 9a999696cSStanislav Fomichev #include <sys/socket.h> 10a999696cSStanislav Fomichev #include <netinet/tcp.h> 11a999696cSStanislav Fomichev #include <linux/if.h> 12a999696cSStanislav Fomichev #include <errno.h> 13a999696cSStanislav Fomichev 14a999696cSStanislav Fomichev #include <bpf/bpf_helpers.h> 15a999696cSStanislav Fomichev #include <bpf/bpf_endian.h> 16a999696cSStanislav Fomichev 17a999696cSStanislav Fomichev #define SERV4_IP 0xc0a801feU /* 192.168.1.254 */ 18a999696cSStanislav Fomichev #define SERV4_PORT 4040 19a999696cSStanislav Fomichev #define SERV4_REWRITE_IP 0x7f000001U /* 127.0.0.1 */ 20a999696cSStanislav Fomichev #define SERV4_REWRITE_PORT 4444 21a999696cSStanislav Fomichev 22a540c81aSStanislav Fomichev #ifndef IFNAMSIZ 23a540c81aSStanislav Fomichev #define IFNAMSIZ 16 24a540c81aSStanislav Fomichev #endif 25a540c81aSStanislav Fomichev 26a540c81aSStanislav Fomichev static __inline int bind_to_device(struct bpf_sock_addr *ctx) 27a540c81aSStanislav Fomichev { 28a540c81aSStanislav Fomichev char veth1[IFNAMSIZ] = "test_sock_addr1"; 29a540c81aSStanislav Fomichev char veth2[IFNAMSIZ] = "test_sock_addr2"; 30a540c81aSStanislav Fomichev char missing[IFNAMSIZ] = "nonexistent_dev"; 31a540c81aSStanislav Fomichev char del_bind[IFNAMSIZ] = ""; 323218231dSDaniel Borkmann int veth1_idx, veth2_idx; 33a540c81aSStanislav Fomichev 34a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 35a540c81aSStanislav Fomichev &veth1, sizeof(veth1))) 36a540c81aSStanislav Fomichev return 1; 373218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 383218231dSDaniel Borkmann &veth1_idx, sizeof(veth1_idx)) || !veth1_idx) 393218231dSDaniel Borkmann return 1; 40a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 41a540c81aSStanislav Fomichev &veth2, sizeof(veth2))) 42a540c81aSStanislav Fomichev return 1; 433218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 443218231dSDaniel Borkmann &veth2_idx, sizeof(veth2_idx)) || !veth2_idx || 453218231dSDaniel Borkmann veth1_idx == veth2_idx) 463218231dSDaniel Borkmann return 1; 47a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 48a540c81aSStanislav Fomichev &missing, sizeof(missing)) != -ENODEV) 49a540c81aSStanislav Fomichev return 1; 503218231dSDaniel Borkmann if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 513218231dSDaniel Borkmann &veth1_idx, sizeof(veth1_idx))) 523218231dSDaniel Borkmann return 1; 53a540c81aSStanislav Fomichev if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 54a540c81aSStanislav Fomichev &del_bind, sizeof(del_bind))) 55a540c81aSStanislav Fomichev return 1; 56a540c81aSStanislav Fomichev 57a540c81aSStanislav Fomichev return 0; 58a540c81aSStanislav Fomichev } 59a540c81aSStanislav Fomichev 60*6503b9f2SManu Bretelle static __inline int bind_reuseport(struct bpf_sock_addr *ctx) 61*6503b9f2SManu Bretelle { 62*6503b9f2SManu Bretelle int val = 1; 63*6503b9f2SManu Bretelle 64*6503b9f2SManu Bretelle if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 65*6503b9f2SManu Bretelle &val, sizeof(val))) 66*6503b9f2SManu Bretelle return 1; 67*6503b9f2SManu Bretelle if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 68*6503b9f2SManu Bretelle &val, sizeof(val)) || !val) 69*6503b9f2SManu Bretelle return 1; 70*6503b9f2SManu Bretelle val = 0; 71*6503b9f2SManu Bretelle if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 72*6503b9f2SManu Bretelle &val, sizeof(val))) 73*6503b9f2SManu Bretelle return 1; 74*6503b9f2SManu Bretelle if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 75*6503b9f2SManu Bretelle &val, sizeof(val)) || val) 76*6503b9f2SManu Bretelle return 1; 77*6503b9f2SManu Bretelle 78*6503b9f2SManu Bretelle return 0; 79*6503b9f2SManu Bretelle } 80*6503b9f2SManu Bretelle 813218231dSDaniel Borkmann static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt) 823218231dSDaniel Borkmann { 833218231dSDaniel Borkmann int old, tmp, new = 0xeb9f; 843218231dSDaniel Borkmann 853218231dSDaniel Borkmann /* Socket in test case has guarantee that old never equals to new. */ 863218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) || 873218231dSDaniel Borkmann old == new) 883218231dSDaniel Borkmann return 1; 893218231dSDaniel Borkmann if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new))) 903218231dSDaniel Borkmann return 1; 913218231dSDaniel Borkmann if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) || 923218231dSDaniel Borkmann tmp != new) 933218231dSDaniel Borkmann return 1; 943218231dSDaniel Borkmann if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old))) 953218231dSDaniel Borkmann return 1; 963218231dSDaniel Borkmann 973218231dSDaniel Borkmann return 0; 983218231dSDaniel Borkmann } 993218231dSDaniel Borkmann 100a999696cSStanislav Fomichev SEC("cgroup/bind4") 101a999696cSStanislav Fomichev int bind_v4_prog(struct bpf_sock_addr *ctx) 102a999696cSStanislav Fomichev { 103a999696cSStanislav Fomichev struct bpf_sock *sk; 104a999696cSStanislav Fomichev __u32 user_ip4; 105a999696cSStanislav Fomichev __u16 user_port; 106a999696cSStanislav Fomichev 107a999696cSStanislav Fomichev sk = ctx->sk; 108a999696cSStanislav Fomichev if (!sk) 109a999696cSStanislav Fomichev return 0; 110a999696cSStanislav Fomichev 111a999696cSStanislav Fomichev if (sk->family != AF_INET) 112a999696cSStanislav Fomichev return 0; 113a999696cSStanislav Fomichev 114a999696cSStanislav Fomichev if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) 115a999696cSStanislav Fomichev return 0; 116a999696cSStanislav Fomichev 117a999696cSStanislav Fomichev if (ctx->user_ip4 != bpf_htonl(SERV4_IP) || 118a999696cSStanislav Fomichev ctx->user_port != bpf_htons(SERV4_PORT)) 119a999696cSStanislav Fomichev return 0; 120a999696cSStanislav Fomichev 121a999696cSStanislav Fomichev // u8 narrow loads: 122a999696cSStanislav Fomichev user_ip4 = 0; 123a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0; 124a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8; 125a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16; 126a999696cSStanislav Fomichev user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24; 127a999696cSStanislav Fomichev if (ctx->user_ip4 != user_ip4) 128a999696cSStanislav Fomichev return 0; 129a999696cSStanislav Fomichev 130a999696cSStanislav Fomichev user_port = 0; 131a999696cSStanislav Fomichev user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0; 132a999696cSStanislav Fomichev user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8; 133a999696cSStanislav Fomichev if (ctx->user_port != user_port) 134a999696cSStanislav Fomichev return 0; 135a999696cSStanislav Fomichev 136a999696cSStanislav Fomichev // u16 narrow loads: 137a999696cSStanislav Fomichev user_ip4 = 0; 138a999696cSStanislav Fomichev user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0; 139a999696cSStanislav Fomichev user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16; 140a999696cSStanislav Fomichev if (ctx->user_ip4 != user_ip4) 141a999696cSStanislav Fomichev return 0; 142a999696cSStanislav Fomichev 143a540c81aSStanislav Fomichev /* Bind to device and unbind it. */ 144a540c81aSStanislav Fomichev if (bind_to_device(ctx)) 145a540c81aSStanislav Fomichev return 0; 146a540c81aSStanislav Fomichev 1473218231dSDaniel Borkmann /* Test for misc socket options. */ 1483218231dSDaniel Borkmann if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY)) 1493218231dSDaniel Borkmann return 0; 1503218231dSDaniel Borkmann 151*6503b9f2SManu Bretelle /* Set reuseport and unset */ 152*6503b9f2SManu Bretelle if (bind_reuseport(ctx)) 153*6503b9f2SManu Bretelle return 0; 154*6503b9f2SManu Bretelle 155a999696cSStanislav Fomichev ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP); 156a999696cSStanislav Fomichev ctx->user_port = bpf_htons(SERV4_REWRITE_PORT); 157a999696cSStanislav Fomichev 158a999696cSStanislav Fomichev return 1; 159a999696cSStanislav Fomichev } 160a999696cSStanislav Fomichev 161a999696cSStanislav Fomichev char _license[] SEC("license") = "GPL"; 162