185 lines · c
1// SPDX-License-Identifier: GPL-2.02 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 0x1234567819#define SERV6_IP_2 0x0000000020#define SERV6_IP_3 0x0000abcd21#define SERV6_PORT 606022#define SERV6_REWRITE_IP_0 0x0000000023#define SERV6_REWRITE_IP_1 0x0000000024#define SERV6_REWRITE_IP_2 0x0000000025#define SERV6_REWRITE_IP_3 0x0000000126#define SERV6_REWRITE_PORT 666627 28#ifndef IFNAMSIZ29#define IFNAMSIZ 1630#endif31 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 |= 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 178SEC("cgroup/bind6")179int bind_v6_deny_prog(struct bpf_sock_addr *ctx)180{181 return 0;182}183 184char _license[] SEC("license") = "GPL";185