support ebpf
This commit is contained in:
99
component/ebpf/bpf/bpf_endian.h
Normal file
99
component/ebpf/bpf/bpf_endian.h
Normal file
@ -0,0 +1,99 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_ENDIAN__
|
||||
#define __BPF_ENDIAN__
|
||||
|
||||
/*
|
||||
* Isolate byte #n and put it into byte #m, for __u##b type.
|
||||
* E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64:
|
||||
* 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx
|
||||
* 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000
|
||||
* 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn
|
||||
* 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000
|
||||
*/
|
||||
#define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8))
|
||||
|
||||
#define ___bpf_swab16(x) ((__u16)( \
|
||||
___bpf_mvb(x, 16, 0, 1) | \
|
||||
___bpf_mvb(x, 16, 1, 0)))
|
||||
|
||||
#define ___bpf_swab32(x) ((__u32)( \
|
||||
___bpf_mvb(x, 32, 0, 3) | \
|
||||
___bpf_mvb(x, 32, 1, 2) | \
|
||||
___bpf_mvb(x, 32, 2, 1) | \
|
||||
___bpf_mvb(x, 32, 3, 0)))
|
||||
|
||||
#define ___bpf_swab64(x) ((__u64)( \
|
||||
___bpf_mvb(x, 64, 0, 7) | \
|
||||
___bpf_mvb(x, 64, 1, 6) | \
|
||||
___bpf_mvb(x, 64, 2, 5) | \
|
||||
___bpf_mvb(x, 64, 3, 4) | \
|
||||
___bpf_mvb(x, 64, 4, 3) | \
|
||||
___bpf_mvb(x, 64, 5, 2) | \
|
||||
___bpf_mvb(x, 64, 6, 1) | \
|
||||
___bpf_mvb(x, 64, 7, 0)))
|
||||
|
||||
/* LLVM's BPF target selects the endianness of the CPU
|
||||
* it compiles on, or the user specifies (bpfel/bpfeb),
|
||||
* respectively. The used __BYTE_ORDER__ is defined by
|
||||
* the compiler, we cannot rely on __BYTE_ORDER from
|
||||
* libc headers, since it doesn't reflect the actual
|
||||
* requested byte order.
|
||||
*
|
||||
* Note, LLVM's BPF target has different __builtin_bswapX()
|
||||
* semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE
|
||||
* in bpfel and bpfeb case, which means below, that we map
|
||||
* to cpu_to_be16(). We could use it unconditionally in BPF
|
||||
* case, but better not rely on it, so that this header here
|
||||
* can be used from application and BPF program side, which
|
||||
* use different targets.
|
||||
*/
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define __bpf_ntohs(x) __builtin_bswap16(x)
|
||||
# define __bpf_htons(x) __builtin_bswap16(x)
|
||||
# define __bpf_constant_ntohs(x) ___bpf_swab16(x)
|
||||
# define __bpf_constant_htons(x) ___bpf_swab16(x)
|
||||
# define __bpf_ntohl(x) __builtin_bswap32(x)
|
||||
# define __bpf_htonl(x) __builtin_bswap32(x)
|
||||
# define __bpf_constant_ntohl(x) ___bpf_swab32(x)
|
||||
# define __bpf_constant_htonl(x) ___bpf_swab32(x)
|
||||
# define __bpf_be64_to_cpu(x) __builtin_bswap64(x)
|
||||
# define __bpf_cpu_to_be64(x) __builtin_bswap64(x)
|
||||
# define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x)
|
||||
# define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define __bpf_ntohs(x) (x)
|
||||
# define __bpf_htons(x) (x)
|
||||
# define __bpf_constant_ntohs(x) (x)
|
||||
# define __bpf_constant_htons(x) (x)
|
||||
# define __bpf_ntohl(x) (x)
|
||||
# define __bpf_htonl(x) (x)
|
||||
# define __bpf_constant_ntohl(x) (x)
|
||||
# define __bpf_constant_htonl(x) (x)
|
||||
# define __bpf_be64_to_cpu(x) (x)
|
||||
# define __bpf_cpu_to_be64(x) (x)
|
||||
# define __bpf_constant_be64_to_cpu(x) (x)
|
||||
# define __bpf_constant_cpu_to_be64(x) (x)
|
||||
#else
|
||||
# error "Fix your compiler's __BYTE_ORDER__?!"
|
||||
#endif
|
||||
|
||||
#define bpf_htons(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_htons(x) : __bpf_htons(x))
|
||||
#define bpf_ntohs(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_ntohs(x) : __bpf_ntohs(x))
|
||||
#define bpf_htonl(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_htonl(x) : __bpf_htonl(x))
|
||||
#define bpf_ntohl(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_ntohl(x) : __bpf_ntohl(x))
|
||||
#define bpf_cpu_to_be64(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x))
|
||||
#define bpf_be64_to_cpu(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x))
|
||||
|
||||
#endif /* __BPF_ENDIAN__ */
|
4139
component/ebpf/bpf/bpf_helper_defs.h
Normal file
4139
component/ebpf/bpf/bpf_helper_defs.h
Normal file
File diff suppressed because it is too large
Load Diff
262
component/ebpf/bpf/bpf_helpers.h
Normal file
262
component/ebpf/bpf/bpf_helpers.h
Normal file
@ -0,0 +1,262 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_HELPERS__
|
||||
#define __BPF_HELPERS__
|
||||
|
||||
/*
|
||||
* Note that bpf programs need to include either
|
||||
* vmlinux.h (auto-generated from BTF) or linux/types.h
|
||||
* in advance since bpf_helper_defs.h uses such types
|
||||
* as __u64.
|
||||
*/
|
||||
#include "bpf_helper_defs.h"
|
||||
|
||||
#define __uint(name, val) int (*name)[val]
|
||||
#define __type(name, val) typeof(val) *name
|
||||
#define __array(name, val) typeof(val) *name[]
|
||||
|
||||
/*
|
||||
* Helper macro to place programs, maps, license in
|
||||
* different sections in elf_bpf file. Section names
|
||||
* are interpreted by libbpf depending on the context (BPF programs, BPF maps,
|
||||
* extern variables, etc).
|
||||
* To allow use of SEC() with externs (e.g., for extern .maps declarations),
|
||||
* make sure __attribute__((unused)) doesn't trigger compilation warning.
|
||||
*/
|
||||
#define SEC(name) \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \
|
||||
__attribute__((section(name), used)) \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
|
||||
/* Avoid 'linux/stddef.h' definition of '__always_inline'. */
|
||||
#undef __always_inline
|
||||
#define __always_inline inline __attribute__((always_inline))
|
||||
|
||||
#ifndef __noinline
|
||||
#define __noinline __attribute__((noinline))
|
||||
#endif
|
||||
#ifndef __weak
|
||||
#define __weak __attribute__((weak))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use __hidden attribute to mark a non-static BPF subprogram effectively
|
||||
* static for BPF verifier's verification algorithm purposes, allowing more
|
||||
* extensive and permissive BPF verification process, taking into account
|
||||
* subprogram's caller context.
|
||||
*/
|
||||
#define __hidden __attribute__((visibility("hidden")))
|
||||
|
||||
/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include
|
||||
* any system-level headers (such as stddef.h, linux/version.h, etc), and
|
||||
* commonly-used macros like NULL and KERNEL_VERSION aren't available through
|
||||
* vmlinux.h. This just adds unnecessary hurdles and forces users to re-define
|
||||
* them on their own. So as a convenience, provide such definitions here.
|
||||
*/
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_VERSION
|
||||
#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper macros to manipulate data structures
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
void *__mptr = (void *)(ptr); \
|
||||
((type *)(__mptr - offsetof(type, member))); \
|
||||
})
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper macro to throw a compilation error if __bpf_unreachable() gets
|
||||
* built into the resulting code. This works given BPF back end does not
|
||||
* implement __builtin_trap(). This is useful to assert that certain paths
|
||||
* of the program code are never used and hence eliminated by the compiler.
|
||||
*
|
||||
* For example, consider a switch statement that covers known cases used by
|
||||
* the program. __bpf_unreachable() can then reside in the default case. If
|
||||
* the program gets extended such that a case is not covered in the switch
|
||||
* statement, then it will throw a build error due to the default case not
|
||||
* being compiled out.
|
||||
*/
|
||||
#ifndef __bpf_unreachable
|
||||
# define __bpf_unreachable() __builtin_trap()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper function to perform a tail call with a constant/immediate map slot.
|
||||
*/
|
||||
#if __clang_major__ >= 8 && defined(__bpf__)
|
||||
static __always_inline void
|
||||
bpf_tail_call_static(void *ctx, const void *map, const __u32 slot)
|
||||
{
|
||||
if (!__builtin_constant_p(slot))
|
||||
__bpf_unreachable();
|
||||
|
||||
/*
|
||||
* Provide a hard guarantee that LLVM won't optimize setting r2 (map
|
||||
* pointer) and r3 (constant map index) from _different paths_ ending
|
||||
* up at the _same_ call insn as otherwise we won't be able to use the
|
||||
* jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel
|
||||
* given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key
|
||||
* tracking for prog array pokes") for details on verifier tracking.
|
||||
*
|
||||
* Note on clobber list: we need to stay in-line with BPF calling
|
||||
* convention, so even if we don't end up using r0, r4, r5, we need
|
||||
* to mark them as clobber so that LLVM doesn't end up using them
|
||||
* before / after the call.
|
||||
*/
|
||||
asm volatile("r1 = %[ctx]\n\t"
|
||||
"r2 = %[map]\n\t"
|
||||
"r3 = %[slot]\n\t"
|
||||
"call 12"
|
||||
:: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper structure used by eBPF C program
|
||||
* to describe BPF map attributes to libbpf loader
|
||||
*/
|
||||
struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
};
|
||||
|
||||
enum libbpf_pin_type {
|
||||
LIBBPF_PIN_NONE,
|
||||
/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
|
||||
LIBBPF_PIN_BY_NAME,
|
||||
};
|
||||
|
||||
enum libbpf_tristate {
|
||||
TRI_NO = 0,
|
||||
TRI_YES = 1,
|
||||
TRI_MODULE = 2,
|
||||
};
|
||||
|
||||
#define __kconfig __attribute__((section(".kconfig")))
|
||||
#define __ksym __attribute__((section(".ksyms")))
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_fill0(arr, p, x) do {} while (0)
|
||||
#define ___bpf_fill1(arr, p, x) arr[p] = x
|
||||
#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)
|
||||
#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)
|
||||
#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)
|
||||
#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)
|
||||
#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)
|
||||
#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)
|
||||
#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)
|
||||
#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)
|
||||
#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)
|
||||
#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)
|
||||
#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)
|
||||
#define ___bpf_fill(arr, args...) \
|
||||
___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)
|
||||
|
||||
/*
|
||||
* BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values
|
||||
* in a structure.
|
||||
*/
|
||||
#define BPF_SEQ_PRINTF(seq, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of
|
||||
* an array of u64.
|
||||
*/
|
||||
#define BPF_SNPRINTF(out, out_size, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_snprintf(out, out_size, ___fmt, \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
#ifdef BPF_NO_GLOBAL_DATA
|
||||
#define BPF_PRINTK_FMT_MOD
|
||||
#else
|
||||
#define BPF_PRINTK_FMT_MOD static const
|
||||
#endif
|
||||
|
||||
#define __bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments
|
||||
* instead of an array of u64.
|
||||
*/
|
||||
#define __bpf_vprintk(fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_trace_vprintk(___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args
|
||||
* Otherwise use __bpf_vprintk
|
||||
*/
|
||||
#define ___bpf_pick_printk(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \
|
||||
__bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \
|
||||
__bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\
|
||||
__bpf_printk /*1*/, __bpf_printk /*0*/)
|
||||
|
||||
/* Helper macro to print out debug messages */
|
||||
#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args)
|
||||
|
||||
#endif
|
342
component/ebpf/bpf/redir.c
Normal file
342
component/ebpf/bpf/redir.c
Normal file
@ -0,0 +1,342 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
//#include <linux/types.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
//#include <linux/if_packet.h>
|
||||
//#include <linux/if_vlan.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/tcp.h>
|
||||
//#include <linux/udp.h>
|
||||
|
||||
#include <linux/pkt_cls.h>
|
||||
|
||||
#include "bpf_endian.h"
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check))
|
||||
#define IP_DST_OFF (ETH_HLEN + offsetof(struct iphdr, daddr))
|
||||
#define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr))
|
||||
#define IP_PROTO_OFF (ETH_HLEN + offsetof(struct iphdr, protocol))
|
||||
#define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check))
|
||||
#define TCP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, source))
|
||||
#define TCP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest))
|
||||
//#define UDP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, check))
|
||||
//#define UDP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, source))
|
||||
//#define UDP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, dest))
|
||||
#define IS_PSEUDO 0x10
|
||||
|
||||
struct origin_info {
|
||||
__be32 ip;
|
||||
__be16 port;
|
||||
__u16 pad;
|
||||
};
|
||||
|
||||
struct origin_info *origin_info_unused __attribute__((unused));
|
||||
|
||||
struct redir_info {
|
||||
__be32 sip;
|
||||
__be32 dip;
|
||||
__be16 sport;
|
||||
__be16 dport;
|
||||
};
|
||||
|
||||
struct redir_info *redir_info_unused __attribute__((unused));
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||
__type(key, struct redir_info);
|
||||
__type(value, struct origin_info);
|
||||
__uint(max_entries, 65535);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} pair_original_dst_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, __u32);
|
||||
__type(value, __u32);
|
||||
__uint(max_entries, 3);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} redir_params_map SEC(".maps");
|
||||
|
||||
static __always_inline int rewrite_ip(struct __sk_buff *skb, __be32 new_ip, bool is_dest) {
|
||||
int ret, off = 0, flags = IS_PSEUDO;
|
||||
__be32 old_ip;
|
||||
|
||||
if (is_dest)
|
||||
ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4);
|
||||
else
|
||||
ret = bpf_skb_load_bytes(skb, IP_SRC_OFF, &old_ip, 4);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
off = TCP_CSUM_OFF;
|
||||
// __u8 proto;
|
||||
//
|
||||
// ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1);
|
||||
// if (ret < 0) {
|
||||
// return BPF_DROP;
|
||||
// }
|
||||
//
|
||||
// switch (proto) {
|
||||
// case IPPROTO_TCP:
|
||||
// off = TCP_CSUM_OFF;
|
||||
// break;
|
||||
//
|
||||
// case IPPROTO_UDP:
|
||||
// off = UDP_CSUM_OFF;
|
||||
// flags |= BPF_F_MARK_MANGLED_0;
|
||||
// break;
|
||||
//
|
||||
// case IPPROTO_ICMPV6:
|
||||
// off = offsetof(struct icmp6hdr, icmp6_cksum);
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if (off) {
|
||||
ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip, flags | sizeof(new_ip));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
// }
|
||||
|
||||
ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_dest)
|
||||
ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0);
|
||||
else
|
||||
ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __always_inline int rewrite_port(struct __sk_buff *skb, __be16 new_port, bool is_dest) {
|
||||
int ret, off = 0;
|
||||
__be16 old_port;
|
||||
|
||||
if (is_dest)
|
||||
ret = bpf_skb_load_bytes(skb, TCP_DST_OFF, &old_port, 2);
|
||||
else
|
||||
ret = bpf_skb_load_bytes(skb, TCP_SRC_OFF, &old_port, 2);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
off = TCP_CSUM_OFF;
|
||||
|
||||
ret = bpf_l4_csum_replace(skb, off, old_port, new_port, sizeof(new_port));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_dest)
|
||||
ret = bpf_skb_store_bytes(skb, TCP_DST_OFF, &new_port, sizeof(new_port), 0);
|
||||
else
|
||||
ret = bpf_skb_store_bytes(skb, TCP_SRC_OFF, &new_port, sizeof(new_port), 0);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __always_inline bool is_lan_ip(__be32 addr) {
|
||||
if (addr == 0xffffffff)
|
||||
return true;
|
||||
|
||||
__u8 fist = (__u8)(addr & 0xff);
|
||||
|
||||
if (fist == 127 || fist == 10)
|
||||
return true;
|
||||
|
||||
__u8 second = (__u8)((addr >> 8) & 0xff);
|
||||
|
||||
if (fist == 172 && second >= 16 && second <= 31)
|
||||
return true;
|
||||
|
||||
if (fist == 192 && second == 168)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SEC("tc_clash_auto_redir_ingress")
|
||||
int tc_redir_ingress_func(struct __sk_buff *skb) {
|
||||
void *data = (void *)(long)skb->data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
struct ethhdr *eth = data;
|
||||
|
||||
if ((void *)(eth + 1) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP))
|
||||
return TC_ACT_OK;
|
||||
|
||||
struct iphdr *iph = (struct iphdr *)(eth + 1);
|
||||
if ((void *)(iph + 1) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
__u32 key = 0, *route_index, *redir_ip, *redir_port;
|
||||
|
||||
route_index = bpf_map_lookup_elem(&redir_params_map, &key);
|
||||
if (!route_index)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (iph->protocol == IPPROTO_ICMP && *route_index != 0)
|
||||
return bpf_redirect(*route_index, 0);
|
||||
|
||||
if (iph->protocol != IPPROTO_TCP)
|
||||
return TC_ACT_OK;
|
||||
|
||||
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
|
||||
if ((void *)(tcph + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
key = 1;
|
||||
redir_ip = bpf_map_lookup_elem(&redir_params_map, &key);
|
||||
if (!redir_ip)
|
||||
return TC_ACT_OK;
|
||||
|
||||
key = 2;
|
||||
redir_port = bpf_map_lookup_elem(&redir_params_map, &key);
|
||||
if (!redir_port)
|
||||
return TC_ACT_OK;
|
||||
|
||||
__be32 new_ip = bpf_htonl(*redir_ip);
|
||||
__be16 new_port = bpf_htonl(*redir_port) >> 16;
|
||||
__be32 old_ip = iph->daddr;
|
||||
__be16 old_port = tcph->dest;
|
||||
|
||||
if (old_ip == new_ip || is_lan_ip(old_ip) || bpf_ntohs(old_port) == 53) {
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
struct redir_info p_key = {
|
||||
.sip = iph->saddr,
|
||||
.sport = tcph->source,
|
||||
.dip = new_ip,
|
||||
.dport = new_port,
|
||||
};
|
||||
|
||||
if (tcph->syn && !tcph->ack) {
|
||||
struct origin_info origin = {
|
||||
.ip = old_ip,
|
||||
.port = old_port,
|
||||
};
|
||||
|
||||
bpf_map_update_elem(&pair_original_dst_map, &p_key, &origin, BPF_NOEXIST);
|
||||
|
||||
if (rewrite_ip(skb, new_ip, true) < 0) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (rewrite_port(skb, new_port, true) < 0) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
} else {
|
||||
struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key);
|
||||
if (!origin) {
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
if (rewrite_ip(skb, new_ip, true) < 0) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (rewrite_port(skb, new_port, true) < 0) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
}
|
||||
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
SEC("tc_clash_auto_redir_egress")
|
||||
int tc_redir_egress_func(struct __sk_buff *skb) {
|
||||
void *data = (void *)(long)skb->data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
struct ethhdr *eth = data;
|
||||
|
||||
if ((void *)(eth + 1) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP))
|
||||
return TC_ACT_OK;
|
||||
|
||||
__u32 key = 0, *redir_ip, *redir_port; // *clash_mark
|
||||
|
||||
// clash_mark = bpf_map_lookup_elem(&redir_params_map, &key);
|
||||
// if (clash_mark && *clash_mark != 0 && *clash_mark == skb->mark)
|
||||
// return TC_ACT_OK;
|
||||
|
||||
struct iphdr *iph = (struct iphdr *)(eth + 1);
|
||||
if ((void *)(iph + 1) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (iph->protocol != IPPROTO_TCP)
|
||||
return TC_ACT_OK;
|
||||
|
||||
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
|
||||
if ((void *)(tcph + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
key = 1;
|
||||
redir_ip = bpf_map_lookup_elem(&redir_params_map, &key);
|
||||
if (!redir_ip)
|
||||
return TC_ACT_OK;
|
||||
|
||||
key = 2;
|
||||
redir_port = bpf_map_lookup_elem(&redir_params_map, &key);
|
||||
if (!redir_port)
|
||||
return TC_ACT_OK;
|
||||
|
||||
__be32 new_ip = bpf_htonl(*redir_ip);
|
||||
__be16 new_port = bpf_htonl(*redir_port) >> 16;
|
||||
__be32 old_ip = iph->saddr;
|
||||
__be16 old_port = tcph->source;
|
||||
|
||||
if (old_ip != new_ip || old_port != new_port) {
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
struct redir_info p_key = {
|
||||
.sip = iph->daddr,
|
||||
.sport = tcph->dest,
|
||||
.dip = iph->saddr,
|
||||
.dport = tcph->source,
|
||||
};
|
||||
|
||||
struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key);
|
||||
if (!origin) {
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
if (tcph->fin && tcph->ack) {
|
||||
bpf_map_delete_elem(&pair_original_dst_map, &p_key);
|
||||
}
|
||||
|
||||
if (rewrite_ip(skb, origin->ip, false) < 0) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (rewrite_port(skb, origin->port, false) < 0) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
103
component/ebpf/bpf/tc.c
Normal file
103
component/ebpf/bpf/tc.c
Normal file
@ -0,0 +1,103 @@
|
||||
#include <stdbool.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
//#include <linux/tcp.h>
|
||||
//#include <linux/udp.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
|
||||
#include "bpf_endian.h"
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, __u32);
|
||||
__type(value, __u32);
|
||||
__uint(max_entries, 2);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} tc_params_map SEC(".maps");
|
||||
|
||||
static __always_inline bool is_lan_ip(__be32 addr) {
|
||||
if (addr == 0xffffffff)
|
||||
return true;
|
||||
|
||||
__u8 fist = (__u8)(addr & 0xff);
|
||||
|
||||
if (fist == 127 || fist == 10)
|
||||
return true;
|
||||
|
||||
__u8 second = (__u8)((addr >> 8) & 0xff);
|
||||
|
||||
if (fist == 172 && second >= 16 && second <= 31)
|
||||
return true;
|
||||
|
||||
if (fist == 192 && second == 168)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SEC("tc_clash_redirect_to_tun")
|
||||
int tc_tun_func(struct __sk_buff *skb) {
|
||||
void *data = (void *)(long)skb->data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
struct ethhdr *eth = data;
|
||||
|
||||
if ((void *)(eth + 1) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (eth->h_proto == bpf_htons(ETH_P_ARP))
|
||||
return TC_ACT_OK;
|
||||
|
||||
__u32 key = 0, *clash_mark, *tun_ifindex;
|
||||
|
||||
clash_mark = bpf_map_lookup_elem(&tc_params_map, &key);
|
||||
if (!clash_mark)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (skb->mark == *clash_mark)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
|
||||
struct iphdr *iph = (struct iphdr *)(eth + 1);
|
||||
if ((void *)(iph + 1) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (iph->protocol == IPPROTO_ICMP)
|
||||
return TC_ACT_OK;
|
||||
|
||||
__be32 daddr = iph->daddr;
|
||||
|
||||
if (is_lan_ip(daddr))
|
||||
return TC_ACT_OK;
|
||||
|
||||
// if (iph->protocol == IPPROTO_TCP) {
|
||||
// struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
|
||||
// if ((void *)(tcph + 1) > data_end)
|
||||
// return TC_ACT_OK;
|
||||
//
|
||||
// __u16 source = bpf_ntohs(tcph->source);
|
||||
// if (source == 22 || source == 80 || source == 443 || source == 8080 || source == 8443 || source == 9090 || (source >= 7890 && source <= 7895))
|
||||
// return TC_ACT_OK;
|
||||
// } else if (iph->protocol == IPPROTO_UDP) {
|
||||
// struct udphdr *udph = (struct udphdr *)(iph + 1);
|
||||
// if ((void *)(udph + 1) > data_end)
|
||||
// return TC_ACT_OK;
|
||||
//
|
||||
// __u16 source = bpf_ntohs(udph->source);
|
||||
// if (source == 53 || (source >= 135 && source <= 139))
|
||||
// return TC_ACT_OK;
|
||||
// }
|
||||
}
|
||||
|
||||
key = 1;
|
||||
tun_ifindex = bpf_map_lookup_elem(&tc_params_map, &key);
|
||||
if (!tun_ifindex)
|
||||
return TC_ACT_OK;
|
||||
|
||||
//return bpf_redirect(*tun_ifindex, BPF_F_INGRESS); // __bpf_rx_skb
|
||||
return bpf_redirect(*tun_ifindex, 0); // __bpf_tx_skb / __dev_xmit_skb
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
Reference in New Issue
Block a user