linux/tools/testing/selftests/bpf/progs/bind6_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 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
  32static __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
  66static __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
  87static __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
 106SEC("cgroup/bind6")
 107int 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 |= ((volatile __u8 *)&ctx->user_ip6[i])[0] << 0;
 135                user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[1] << 8;
 136                user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[2] << 16;
 137                user_ip6 |= ((volatile __u8 *)&ctx->user_ip6[i])[3] << 24;
 138                if (ctx->user_ip6[i] != user_ip6)
 139                        return 0;
 140        }
 141
 142        user_port = 0;
 143        user_port |= ((volatile __u8 *)&ctx->user_port)[0] << 0;
 144        user_port |= ((volatile __u8 *)&ctx->user_port)[1] << 8;
 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 |= ((volatile __u16 *)&ctx->user_ip6[i])[0] << 0;
 152                user_ip6 |= ((volatile __u16 *)&ctx->user_ip6[i])[1] << 16;
 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
 178char _license[] SEC("license") = "GPL";
 179