162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <string.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/stddef.h> 662306a36Sopenharmony_ci#include <linux/bpf.h> 762306a36Sopenharmony_ci#include <linux/in.h> 862306a36Sopenharmony_ci#include <linux/in6.h> 962306a36Sopenharmony_ci#include <linux/if.h> 1062306a36Sopenharmony_ci#include <errno.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <bpf/bpf_helpers.h> 1362306a36Sopenharmony_ci#include <bpf/bpf_endian.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define SERV4_IP 0xc0a801feU /* 192.168.1.254 */ 1662306a36Sopenharmony_ci#define SERV4_PORT 4040 1762306a36Sopenharmony_ci#define SERV4_REWRITE_IP 0x7f000001U /* 127.0.0.1 */ 1862306a36Sopenharmony_ci#define SERV4_REWRITE_PORT 4444 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#ifndef IFNAMSIZ 2162306a36Sopenharmony_ci#define IFNAMSIZ 16 2262306a36Sopenharmony_ci#endif 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic __inline int bind_to_device(struct bpf_sock_addr *ctx) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci char veth1[IFNAMSIZ] = "test_sock_addr1"; 2762306a36Sopenharmony_ci char veth2[IFNAMSIZ] = "test_sock_addr2"; 2862306a36Sopenharmony_ci char missing[IFNAMSIZ] = "nonexistent_dev"; 2962306a36Sopenharmony_ci char del_bind[IFNAMSIZ] = ""; 3062306a36Sopenharmony_ci int veth1_idx, veth2_idx; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 3362306a36Sopenharmony_ci &veth1, sizeof(veth1))) 3462306a36Sopenharmony_ci return 1; 3562306a36Sopenharmony_ci if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 3662306a36Sopenharmony_ci &veth1_idx, sizeof(veth1_idx)) || !veth1_idx) 3762306a36Sopenharmony_ci return 1; 3862306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 3962306a36Sopenharmony_ci &veth2, sizeof(veth2))) 4062306a36Sopenharmony_ci return 1; 4162306a36Sopenharmony_ci if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 4262306a36Sopenharmony_ci &veth2_idx, sizeof(veth2_idx)) || !veth2_idx || 4362306a36Sopenharmony_ci veth1_idx == veth2_idx) 4462306a36Sopenharmony_ci return 1; 4562306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 4662306a36Sopenharmony_ci &missing, sizeof(missing)) != -ENODEV) 4762306a36Sopenharmony_ci return 1; 4862306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 4962306a36Sopenharmony_ci &veth1_idx, sizeof(veth1_idx))) 5062306a36Sopenharmony_ci return 1; 5162306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 5262306a36Sopenharmony_ci &del_bind, sizeof(del_bind))) 5362306a36Sopenharmony_ci return 1; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic __inline int bind_reuseport(struct bpf_sock_addr *ctx) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci int val = 1; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 6362306a36Sopenharmony_ci &val, sizeof(val))) 6462306a36Sopenharmony_ci return 1; 6562306a36Sopenharmony_ci if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 6662306a36Sopenharmony_ci &val, sizeof(val)) || !val) 6762306a36Sopenharmony_ci return 1; 6862306a36Sopenharmony_ci val = 0; 6962306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 7062306a36Sopenharmony_ci &val, sizeof(val))) 7162306a36Sopenharmony_ci return 1; 7262306a36Sopenharmony_ci if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT, 7362306a36Sopenharmony_ci &val, sizeof(val)) || val) 7462306a36Sopenharmony_ci return 1; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic __inline int misc_opts(struct bpf_sock_addr *ctx, int opt) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int old, tmp, new = 0xeb9f; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Socket in test case has guarantee that old never equals to new. */ 8462306a36Sopenharmony_ci if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) || 8562306a36Sopenharmony_ci old == new) 8662306a36Sopenharmony_ci return 1; 8762306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new))) 8862306a36Sopenharmony_ci return 1; 8962306a36Sopenharmony_ci if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) || 9062306a36Sopenharmony_ci tmp != new) 9162306a36Sopenharmony_ci return 1; 9262306a36Sopenharmony_ci if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old))) 9362306a36Sopenharmony_ci return 1; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciSEC("cgroup/bind4") 9962306a36Sopenharmony_ciint bind_v4_prog(struct bpf_sock_addr *ctx) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct bpf_sock *sk; 10262306a36Sopenharmony_ci __u32 user_ip4; 10362306a36Sopenharmony_ci __u16 user_port; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci sk = ctx->sk; 10662306a36Sopenharmony_ci if (!sk) 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (sk->family != AF_INET) 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (ctx->user_ip4 != bpf_htonl(SERV4_IP) || 11662306a36Sopenharmony_ci ctx->user_port != bpf_htons(SERV4_PORT)) 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci // u8 narrow loads: 12062306a36Sopenharmony_ci user_ip4 = 0; 12162306a36Sopenharmony_ci user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0; 12262306a36Sopenharmony_ci user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8; 12362306a36Sopenharmony_ci user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16; 12462306a36Sopenharmony_ci user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24; 12562306a36Sopenharmony_ci if (ctx->user_ip4 != user_ip4) 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci user_port = 0; 12962306a36Sopenharmony_ci user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0; 13062306a36Sopenharmony_ci user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8; 13162306a36Sopenharmony_ci if (ctx->user_port != user_port) 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci // u16 narrow loads: 13562306a36Sopenharmony_ci user_ip4 = 0; 13662306a36Sopenharmony_ci user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0; 13762306a36Sopenharmony_ci user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16; 13862306a36Sopenharmony_ci if (ctx->user_ip4 != user_ip4) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Bind to device and unbind it. */ 14262306a36Sopenharmony_ci if (bind_to_device(ctx)) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Test for misc socket options. */ 14662306a36Sopenharmony_ci if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY)) 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Set reuseport and unset */ 15062306a36Sopenharmony_ci if (bind_reuseport(ctx)) 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP); 15462306a36Sopenharmony_ci ctx->user_port = bpf_htons(SERV4_REWRITE_PORT); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 1; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cichar _license[] SEC("license") = "GPL"; 160