linux/tools/testing/selftests/bpf/progs/bind4_prog.c
<<
>>
Prefs
   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
  26static __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
  60static __inline int bind_reuseport(struct bpf_sock_addr *ctx)
  61{
  62        int val = 1;
  63
  64        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
  65                           &val, sizeof(val)))
  66                return 1;
  67        if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
  68                           &val, sizeof(val)) || !val)
  69                return 1;
  70        val = 0;
  71        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
  72                           &val, sizeof(val)))
  73                return 1;
  74        if (bpf_getsockopt(ctx, SOL_SOCKET, SO_REUSEPORT,
  75                           &val, sizeof(val)) || val)
  76                return 1;
  77
  78        return 0;
  79}
  80
  81static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
  82{
  83        int old, tmp, new = 0xeb9f;
  84
  85        /* Socket in test case has guarantee that old never equals to new. */
  86        if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
  87            old == new)
  88                return 1;
  89        if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
  90                return 1;
  91        if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
  92            tmp != new)
  93                return 1;
  94        if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
  95                return 1;
  96
  97        return 0;
  98}
  99
 100SEC("cgroup/bind4")
 101int bind_v4_prog(struct bpf_sock_addr *ctx)
 102{
 103        struct bpf_sock *sk;
 104        __u32 user_ip4;
 105        __u16 user_port;
 106
 107        sk = ctx->sk;
 108        if (!sk)
 109                return 0;
 110
 111        if (sk->family != AF_INET)
 112                return 0;
 113
 114        if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
 115                return 0;
 116
 117        if (ctx->user_ip4 != bpf_htonl(SERV4_IP) ||
 118            ctx->user_port != bpf_htons(SERV4_PORT))
 119                return 0;
 120
 121        // u8 narrow loads:
 122        user_ip4 = 0;
 123        user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[0] << 0;
 124        user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[1] << 8;
 125        user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[2] << 16;
 126        user_ip4 |= ((volatile __u8 *)&ctx->user_ip4)[3] << 24;
 127        if (ctx->user_ip4 != user_ip4)
 128                return 0;
 129
 130        user_port = 0;
 131        user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0;
 132        user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8;
 133        if (ctx->user_port != user_port)
 134                return 0;
 135
 136        // u16 narrow loads:
 137        user_ip4 = 0;
 138        user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[0] << 0;
 139        user_ip4 |= ((volatile __u16 *)&ctx->user_ip4)[1] << 16;
 140        if (ctx->user_ip4 != user_ip4)
 141                return 0;
 142
 143        /* Bind to device and unbind it. */
 144        if (bind_to_device(ctx))
 145                return 0;
 146
 147        /* Test for misc socket options. */
 148        if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
 149                return 0;
 150
 151        /* Set reuseport and unset */
 152        if (bind_reuseport(ctx))
 153                return 0;
 154
 155        ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP);
 156        ctx->user_port = bpf_htons(SERV4_REWRITE_PORT);
 157
 158        return 1;
 159}
 160
 161char _license[] SEC("license") = "GPL";
 162