mirror of
https://github.com/daeuniverse/dae.git
synced 2024-12-23 01:14:46 +07:00
optimize: lower kernel version requirement from 5.6 to 5.2
This commit is contained in:
parent
1eaaa4349c
commit
6e3637d018
2
Makefile
2
Makefile
@ -6,7 +6,7 @@
|
||||
# The development version of clang is distributed as the 'clang' binary,
|
||||
# while stable/released versions have a version number attached.
|
||||
# Pin the default clang to a stable version.
|
||||
CLANG ?= clang-14
|
||||
CLANG ?= clang
|
||||
STRIP ?= llvm-strip
|
||||
#CFLAGS := -O2 -g -Wall -Werror $(CFLAGS)
|
||||
CFLAGS := -O2 -Wall -Werror $(CFLAGS)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package infra
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -1,4 +1,4 @@
|
||||
package infra
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/v2rayA/dae/cmd/infra"
|
||||
"github.com/v2rayA/dae/cmd/internal"
|
||||
"github.com/v2rayA/dae/component/control"
|
||||
"github.com/v2rayA/dae/config"
|
||||
"github.com/v2rayA/dae/pkg/config_parser"
|
||||
@ -43,13 +43,13 @@ func Run() (err error) {
|
||||
}
|
||||
|
||||
// Require "sudo" if necessary.
|
||||
infra.AutoSu()
|
||||
internal.AutoSu()
|
||||
|
||||
// Resolve subscriptions to nodes.
|
||||
nodeList := make([]string, len(param.Node))
|
||||
copy(nodeList, param.Node)
|
||||
for _, sub := range param.Subscription {
|
||||
nodes, err := infra.ResolveSubscription(log, sub)
|
||||
nodes, err := internal.ResolveSubscription(log, sub)
|
||||
if err != nil {
|
||||
log.Warnf(`failed to resolve subscription "%v": %v`, sub, err)
|
||||
}
|
||||
|
@ -6,9 +6,12 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/v2rayA/dae/common"
|
||||
"github.com/v2rayA/dae/pkg/ebpf_internal"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type _bpfLpmKey struct {
|
||||
@ -27,7 +30,7 @@ func (o *bpfObjects) newLpmMap(keys []_bpfLpmKey, values []uint32) (m *ebpf.Map,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = m.BatchUpdate(keys, values, &ebpf.BatchOptions{
|
||||
if _, err = BatchUpdate(m, keys, values, &ebpf.BatchOptions{
|
||||
ElemFlags: uint64(ebpf.UpdateAny),
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
@ -50,3 +53,47 @@ func cidrToBpfLpmKey(prefix netip.Prefix) _bpfLpmKey {
|
||||
Data: common.Ipv6ByteSliceToUint32Array(ip[:]),
|
||||
}
|
||||
}
|
||||
|
||||
// A utility to convert the values to proper strings.
|
||||
func int8ToStr(arr []int8) string {
|
||||
b := make([]byte, 0, len(arr))
|
||||
for _, v := range arr {
|
||||
if v == 0x00 {
|
||||
break
|
||||
}
|
||||
b = append(b, byte(v))
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
func BatchUpdate(m *ebpf.Map, keys interface{}, values interface{}, opts *ebpf.BatchOptions) (n int, err error) {
|
||||
var old bool
|
||||
version, e := internal.KernelVersion()
|
||||
if e != nil || version.Less(internal.Version{5, 6, 0}) {
|
||||
old = true
|
||||
}
|
||||
if !old {
|
||||
return m.BatchUpdate(keys, values, opts)
|
||||
} else {
|
||||
vKeys := reflect.ValueOf(keys)
|
||||
if vKeys.Kind() != reflect.Slice {
|
||||
return 0, fmt.Errorf("keys must be slice")
|
||||
}
|
||||
vVals := reflect.ValueOf(values)
|
||||
if vVals.Kind() != reflect.Slice {
|
||||
return 0, fmt.Errorf("values must be slice")
|
||||
}
|
||||
length := vKeys.Len()
|
||||
if vVals.Len() != length {
|
||||
return 0, fmt.Errorf("keys and values must have same length")
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
vKey := vKeys.Index(i)
|
||||
vVal := vVals.Index(i)
|
||||
if err = m.Update(vKey.Interface(), vVal.Interface(), ebpf.MapUpdateFlags(opts.ElemFlags)); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
return vKeys.Len(), nil
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func (c *ControlPlane) BatchUpdateDomainRouting(cache *dnsCache) error {
|
||||
Bitmap: cache.DomainBitmap,
|
||||
})
|
||||
}
|
||||
if _, err := c.bpf.DomainRoutingMap.BatchUpdate(keys, vals, &ebpf.BatchOptions{
|
||||
if _, err := BatchUpdate(c.bpf.DomainRoutingMap, keys, vals, &ebpf.BatchOptions{
|
||||
ElemFlags: uint64(ebpf.UpdateAny),
|
||||
}); err != nil {
|
||||
return err
|
||||
|
@ -379,7 +379,7 @@ handle_ipv6_extensions(void *data, void *data_end, __u32 hdr,
|
||||
struct tcphdr **tcph, struct udphdr **udph, __u8 *ihl) {
|
||||
__u8 hdr_length = 0;
|
||||
__s32 *p_s32;
|
||||
__u8 nexthdr;
|
||||
__u8 nexthdr = 0;
|
||||
*ihl = sizeof(struct ipv6hdr) / 4;
|
||||
// We only process TCP and UDP traffic.
|
||||
|
||||
@ -395,6 +395,7 @@ handle_ipv6_extensions(void *data, void *data_end, __u32 hdr,
|
||||
if (!(p_s32 = bpf_map_lookup_elem(&ipproto_hdrsize_map, &hdr))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (*p_s32) {
|
||||
case -1:
|
||||
if ((void *)((__u8 *)data + 2) > data_end) {
|
||||
@ -410,7 +411,7 @@ handle_ipv6_extensions(void *data, void *data_end, __u32 hdr,
|
||||
nexthdr = *(__u8 *)data;
|
||||
break;
|
||||
case 4:
|
||||
hdr_length = *p_s32;
|
||||
hdr_length = 4;
|
||||
goto special_n1;
|
||||
case 0:
|
||||
if (hdr == IPPROTO_TCP) {
|
||||
@ -433,6 +434,9 @@ handle_ipv6_extensions(void *data, void *data_end, __u32 hdr,
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
// Unknown hdr.
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
bpf_printk("exceeds IPV6_MAX_EXTENSIONS limit");
|
||||
@ -836,8 +840,7 @@ static long routing(__u32 flag[3], void *l4_hdr, __be32 saddr[4],
|
||||
*p_u32 > routing->port_range.port_end) {
|
||||
bad_rule = true;
|
||||
}
|
||||
} else if ((p_u32 = bpf_map_lookup_elem(&l4proto_ipversion_map,
|
||||
&key))) {
|
||||
} else if ((p_u32 = bpf_map_lookup_elem(&l4proto_ipversion_map, &key))) {
|
||||
if (!(*p_u32 & routing->__value)) {
|
||||
bad_rule = true;
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ func (b *RoutingMatcherBuilder) Build() (err error) {
|
||||
}
|
||||
routingsLen := uint32(len(b.rules))
|
||||
routingsKeys := common.ARangeU32(routingsLen)
|
||||
if _, err = b.bpf.RoutingMap.BatchUpdate(routingsKeys, b.rules, &ebpf.BatchOptions{
|
||||
if _, err = BatchUpdate(b.bpf.RoutingMap, routingsKeys, b.rules, &ebpf.BatchOptions{
|
||||
ElemFlags: uint64(ebpf.UpdateAny),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("BatchUpdate: %w", err)
|
||||
|
8
pkg/ebpf_internal/align.go
Normal file
8
pkg/ebpf_internal/align.go
Normal file
@ -0,0 +1,8 @@
|
||||
// Copied from https://github.com/cilium/ebpf/blob/v0.10.0/internal/align.go
|
||||
|
||||
package internal
|
||||
|
||||
// Align returns 'n' updated to 'alignment' boundary.
|
||||
func Align(n, alignment int) int {
|
||||
return (int(n) + alignment - 1) / alignment * alignment
|
||||
}
|
104
pkg/ebpf_internal/elf.go
Normal file
104
pkg/ebpf_internal/elf.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Copied from https://github.com/cilium/ebpf/blob/v0.10.0/internal/elf.go
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type SafeELFFile struct {
|
||||
*elf.File
|
||||
}
|
||||
|
||||
// NewSafeELFFile reads an ELF safely.
|
||||
//
|
||||
// Any panic during parsing is turned into an error. This is necessary since
|
||||
// there are a bunch of unfixed bugs in debug/elf.
|
||||
//
|
||||
// https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+debug%2Felf+in%3Atitle
|
||||
func NewSafeELFFile(r io.ReaderAt) (safe *SafeELFFile, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
safe = nil
|
||||
err = fmt.Errorf("reading ELF file panicked: %s", r)
|
||||
}()
|
||||
|
||||
file, err := elf.NewFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SafeELFFile{file}, nil
|
||||
}
|
||||
|
||||
// OpenSafeELFFile reads an ELF from a file.
|
||||
//
|
||||
// It works like NewSafeELFFile, with the exception that safe.Close will
|
||||
// close the underlying file.
|
||||
func OpenSafeELFFile(path string) (safe *SafeELFFile, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
safe = nil
|
||||
err = fmt.Errorf("reading ELF file panicked: %s", r)
|
||||
}()
|
||||
|
||||
file, err := elf.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SafeELFFile{file}, nil
|
||||
}
|
||||
|
||||
// Symbols is the safe version of elf.File.Symbols.
|
||||
func (se *SafeELFFile) Symbols() (syms []elf.Symbol, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
syms = nil
|
||||
err = fmt.Errorf("reading ELF symbols panicked: %s", r)
|
||||
}()
|
||||
|
||||
syms, err = se.File.Symbols()
|
||||
return
|
||||
}
|
||||
|
||||
// DynamicSymbols is the safe version of elf.File.DynamicSymbols.
|
||||
func (se *SafeELFFile) DynamicSymbols() (syms []elf.Symbol, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
syms = nil
|
||||
err = fmt.Errorf("reading ELF dynamic symbols panicked: %s", r)
|
||||
}()
|
||||
|
||||
syms, err = se.File.DynamicSymbols()
|
||||
return
|
||||
}
|
||||
|
||||
// SectionsByType returns all sections in the file with the specified section type.
|
||||
func (se *SafeELFFile) SectionsByType(typ elf.SectionType) []*elf.Section {
|
||||
sections := make([]*elf.Section, 0, 1)
|
||||
for _, section := range se.Sections {
|
||||
if section.Type == typ {
|
||||
sections = append(sections, section)
|
||||
}
|
||||
}
|
||||
return sections
|
||||
}
|
15
pkg/ebpf_internal/endian_be.go
Normal file
15
pkg/ebpf_internal/endian_be.go
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build armbe || arm64be || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
// +build armbe arm64be mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64
|
||||
|
||||
// Copied from https://github.com/cilium/ebpf/blob/v0.10.0/internal/endian_be.go
|
||||
|
||||
package internal
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// NativeEndian is set to either binary.BigEndian or binary.LittleEndian,
|
||||
// depending on the host's endianness.
|
||||
var NativeEndian binary.ByteOrder = binary.BigEndian
|
||||
|
||||
// ClangEndian is set to either "el" or "eb" depending on the host's endianness.
|
||||
const ClangEndian = "eb"
|
15
pkg/ebpf_internal/endian_le.go
Normal file
15
pkg/ebpf_internal/endian_le.go
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || mipsle || mips64le || mips64p32le || ppc64le || riscv64
|
||||
// +build 386 amd64 amd64p32 arm arm64 mipsle mips64le mips64p32le ppc64le riscv64
|
||||
|
||||
// Copied from https://github.com/cilium/ebpf/blob/v0.10.0/internal/endian_le.go
|
||||
|
||||
package internal
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// NativeEndian is set to either binary.BigEndian or binary.LittleEndian,
|
||||
// depending on the host's endianness.
|
||||
var NativeEndian binary.ByteOrder = binary.LittleEndian
|
||||
|
||||
// ClangEndian is set to either "el" or "eb" depending on the host's endianness.
|
||||
const ClangEndian = "el"
|
13
pkg/ebpf_internal/internal/unix/doc.go
Normal file
13
pkg/ebpf_internal/internal/unix/doc.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copied from https://github.com/cilium/ebpf/tree/v0.10.0/internal/unix
|
||||
|
||||
// Package unix re-exports Linux specific parts of golang.org/x/sys/unix.
|
||||
//
|
||||
// It avoids breaking compilation on other OS by providing stubs as follows:
|
||||
// - Invoking a function always returns an error.
|
||||
// - Errnos have distinct, non-zero values.
|
||||
// - Constants have distinct but meaningless values.
|
||||
// - Types use the same names for members, but may or may not follow the
|
||||
// Linux layout.
|
||||
package unix
|
||||
|
||||
// Note: please don't add any custom API to this package. Use internal/sys instead.
|
192
pkg/ebpf_internal/internal/unix/types_linux.go
Normal file
192
pkg/ebpf_internal/internal/unix/types_linux.go
Normal file
@ -0,0 +1,192 @@
|
||||
//go:build linux
|
||||
|
||||
// Copied from https://github.com/cilium/ebpf/tree/v0.10.0/internal/unix
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
linux "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
ENOENT = linux.ENOENT
|
||||
EEXIST = linux.EEXIST
|
||||
EAGAIN = linux.EAGAIN
|
||||
ENOSPC = linux.ENOSPC
|
||||
EINVAL = linux.EINVAL
|
||||
EPOLLIN = linux.EPOLLIN
|
||||
EINTR = linux.EINTR
|
||||
EPERM = linux.EPERM
|
||||
ESRCH = linux.ESRCH
|
||||
ENODEV = linux.ENODEV
|
||||
EBADF = linux.EBADF
|
||||
E2BIG = linux.E2BIG
|
||||
EFAULT = linux.EFAULT
|
||||
EACCES = linux.EACCES
|
||||
EILSEQ = linux.EILSEQ
|
||||
EOPNOTSUPP = linux.EOPNOTSUPP
|
||||
)
|
||||
|
||||
const (
|
||||
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
|
||||
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
|
||||
BPF_F_RDONLY = linux.BPF_F_RDONLY
|
||||
BPF_F_WRONLY = linux.BPF_F_WRONLY
|
||||
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
|
||||
BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
|
||||
BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE
|
||||
BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE
|
||||
BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
|
||||
BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN
|
||||
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
|
||||
BPF_TAG_SIZE = linux.BPF_TAG_SIZE
|
||||
BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT
|
||||
BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT
|
||||
BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ
|
||||
SYS_BPF = linux.SYS_BPF
|
||||
F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
|
||||
EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
|
||||
EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC
|
||||
O_CLOEXEC = linux.O_CLOEXEC
|
||||
O_NONBLOCK = linux.O_NONBLOCK
|
||||
PROT_READ = linux.PROT_READ
|
||||
PROT_WRITE = linux.PROT_WRITE
|
||||
MAP_SHARED = linux.MAP_SHARED
|
||||
PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1
|
||||
PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE
|
||||
PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT
|
||||
PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
|
||||
PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE
|
||||
PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE
|
||||
PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF
|
||||
PerfBitWatermark = linux.PerfBitWatermark
|
||||
PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
|
||||
PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
|
||||
RLIM_INFINITY = linux.RLIM_INFINITY
|
||||
RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
|
||||
BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME
|
||||
PERF_RECORD_LOST = linux.PERF_RECORD_LOST
|
||||
PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE
|
||||
AT_FDCWD = linux.AT_FDCWD
|
||||
RENAME_NOREPLACE = linux.RENAME_NOREPLACE
|
||||
SO_ATTACH_BPF = linux.SO_ATTACH_BPF
|
||||
SO_DETACH_BPF = linux.SO_DETACH_BPF
|
||||
SOL_SOCKET = linux.SOL_SOCKET
|
||||
SIGPROF = linux.SIGPROF
|
||||
SIG_BLOCK = linux.SIG_BLOCK
|
||||
SIG_UNBLOCK = linux.SIG_UNBLOCK
|
||||
EM_NONE = linux.EM_NONE
|
||||
EM_BPF = linux.EM_BPF
|
||||
)
|
||||
|
||||
type Statfs_t = linux.Statfs_t
|
||||
type Stat_t = linux.Stat_t
|
||||
type Rlimit = linux.Rlimit
|
||||
type Signal = linux.Signal
|
||||
type Sigset_t = linux.Sigset_t
|
||||
type PerfEventMmapPage = linux.PerfEventMmapPage
|
||||
type EpollEvent = linux.EpollEvent
|
||||
type PerfEventAttr = linux.PerfEventAttr
|
||||
type Utsname = linux.Utsname
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
return linux.Syscall(trap, a1, a2, a3)
|
||||
}
|
||||
|
||||
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
||||
return linux.PthreadSigmask(how, set, oldset)
|
||||
}
|
||||
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
return linux.FcntlInt(fd, cmd, arg)
|
||||
}
|
||||
|
||||
func IoctlSetInt(fd int, req uint, value int) error {
|
||||
return linux.IoctlSetInt(fd, req, value)
|
||||
}
|
||||
|
||||
func Statfs(path string, buf *Statfs_t) (err error) {
|
||||
return linux.Statfs(path, buf)
|
||||
}
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
return linux.Close(fd)
|
||||
}
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
return linux.EpollWait(epfd, events, msec)
|
||||
}
|
||||
|
||||
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||
return linux.EpollCtl(epfd, op, fd, event)
|
||||
}
|
||||
|
||||
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||
return linux.Eventfd(initval, flags)
|
||||
}
|
||||
|
||||
func Write(fd int, p []byte) (n int, err error) {
|
||||
return linux.Write(fd, p)
|
||||
}
|
||||
|
||||
func EpollCreate1(flag int) (fd int, err error) {
|
||||
return linux.EpollCreate1(flag)
|
||||
}
|
||||
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
return linux.SetNonblock(fd, nonblocking)
|
||||
}
|
||||
|
||||
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||
return linux.Mmap(fd, offset, length, prot, flags)
|
||||
}
|
||||
|
||||
func Munmap(b []byte) (err error) {
|
||||
return linux.Munmap(b)
|
||||
}
|
||||
|
||||
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
||||
return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags)
|
||||
}
|
||||
|
||||
func Uname(buf *Utsname) (err error) {
|
||||
return linux.Uname(buf)
|
||||
}
|
||||
|
||||
func Getpid() int {
|
||||
return linux.Getpid()
|
||||
}
|
||||
|
||||
func Gettid() int {
|
||||
return linux.Gettid()
|
||||
}
|
||||
|
||||
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
||||
return linux.Tgkill(tgid, tid, sig)
|
||||
}
|
||||
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
return linux.BytePtrFromString(s)
|
||||
}
|
||||
|
||||
func ByteSliceToString(s []byte) string {
|
||||
return linux.ByteSliceToString(s)
|
||||
}
|
||||
|
||||
func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {
|
||||
return linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags)
|
||||
}
|
||||
|
||||
func Prlimit(pid, resource int, new, old *Rlimit) error {
|
||||
return linux.Prlimit(pid, resource, new, old)
|
||||
}
|
||||
|
||||
func Open(path string, mode int, perm uint32) (int, error) {
|
||||
return linux.Open(path, mode, perm)
|
||||
}
|
||||
|
||||
func Fstat(fd int, stat *Stat_t) error {
|
||||
return linux.Fstat(fd, stat)
|
||||
}
|
272
pkg/ebpf_internal/internal/unix/types_other.go
Normal file
272
pkg/ebpf_internal/internal/unix/types_other.go
Normal file
@ -0,0 +1,272 @@
|
||||
//go:build !linux
|
||||
|
||||
// Copied from https://github.com/cilium/ebpf/tree/v0.10.0/internal/unix
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
// Errnos are distinct and non-zero.
|
||||
const (
|
||||
ENOENT syscall.Errno = iota + 1
|
||||
EEXIST
|
||||
EAGAIN
|
||||
ENOSPC
|
||||
EINVAL
|
||||
EINTR
|
||||
EPERM
|
||||
ESRCH
|
||||
ENODEV
|
||||
EBADF
|
||||
E2BIG
|
||||
EFAULT
|
||||
EACCES
|
||||
EILSEQ
|
||||
EOPNOTSUPP
|
||||
)
|
||||
|
||||
// Constants are distinct to avoid breaking switch statements.
|
||||
const (
|
||||
BPF_F_NO_PREALLOC = iota
|
||||
BPF_F_NUMA_NODE
|
||||
BPF_F_RDONLY
|
||||
BPF_F_WRONLY
|
||||
BPF_F_RDONLY_PROG
|
||||
BPF_F_WRONLY_PROG
|
||||
BPF_F_SLEEPABLE
|
||||
BPF_F_MMAPABLE
|
||||
BPF_F_INNER_MAP
|
||||
BPF_F_KPROBE_MULTI_RETURN
|
||||
BPF_OBJ_NAME_LEN
|
||||
BPF_TAG_SIZE
|
||||
BPF_RINGBUF_BUSY_BIT
|
||||
BPF_RINGBUF_DISCARD_BIT
|
||||
BPF_RINGBUF_HDR_SZ
|
||||
SYS_BPF
|
||||
F_DUPFD_CLOEXEC
|
||||
EPOLLIN
|
||||
EPOLL_CTL_ADD
|
||||
EPOLL_CLOEXEC
|
||||
O_CLOEXEC
|
||||
O_NONBLOCK
|
||||
PROT_READ
|
||||
PROT_WRITE
|
||||
MAP_SHARED
|
||||
PERF_ATTR_SIZE_VER1
|
||||
PERF_TYPE_SOFTWARE
|
||||
PERF_TYPE_TRACEPOINT
|
||||
PERF_COUNT_SW_BPF_OUTPUT
|
||||
PERF_EVENT_IOC_DISABLE
|
||||
PERF_EVENT_IOC_ENABLE
|
||||
PERF_EVENT_IOC_SET_BPF
|
||||
PerfBitWatermark
|
||||
PERF_SAMPLE_RAW
|
||||
PERF_FLAG_FD_CLOEXEC
|
||||
RLIM_INFINITY
|
||||
RLIMIT_MEMLOCK
|
||||
BPF_STATS_RUN_TIME
|
||||
PERF_RECORD_LOST
|
||||
PERF_RECORD_SAMPLE
|
||||
AT_FDCWD
|
||||
RENAME_NOREPLACE
|
||||
SO_ATTACH_BPF
|
||||
SO_DETACH_BPF
|
||||
SOL_SOCKET
|
||||
SIGPROF
|
||||
SIG_BLOCK
|
||||
SIG_UNBLOCK
|
||||
EM_NONE
|
||||
EM_BPF
|
||||
)
|
||||
|
||||
type Statfs_t struct {
|
||||
Type int64
|
||||
Bsize int64
|
||||
Blocks uint64
|
||||
Bfree uint64
|
||||
Bavail uint64
|
||||
Files uint64
|
||||
Ffree uint64
|
||||
Fsid [2]int32
|
||||
Namelen int64
|
||||
Frsize int64
|
||||
Flags int64
|
||||
Spare [4]int64
|
||||
}
|
||||
|
||||
type Stat_t struct{}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type Signal int
|
||||
|
||||
type Sigset_t struct {
|
||||
Val [4]uint64
|
||||
}
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
return 0, 0, syscall.ENOTSUP
|
||||
}
|
||||
|
||||
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
return -1, errNonLinux
|
||||
}
|
||||
|
||||
func IoctlSetInt(fd int, req uint, value int) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Statfs(path string, buf *Statfs_t) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
type EpollEvent struct {
|
||||
Events uint32
|
||||
Fd int32
|
||||
Pad int32
|
||||
}
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
func Write(fd int, p []byte) (n int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
func EpollCreate1(flag int) (fd int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
type PerfEventMmapPage struct {
|
||||
Version uint32
|
||||
Compat_version uint32
|
||||
Lock uint32
|
||||
Index uint32
|
||||
Offset int64
|
||||
Time_enabled uint64
|
||||
Time_running uint64
|
||||
Capabilities uint64
|
||||
Pmc_width uint16
|
||||
Time_shift uint16
|
||||
Time_mult uint32
|
||||
Time_offset uint64
|
||||
Time_zero uint64
|
||||
Size uint32
|
||||
|
||||
Data_head uint64
|
||||
Data_tail uint64
|
||||
Data_offset uint64
|
||||
Data_size uint64
|
||||
Aux_head uint64
|
||||
Aux_tail uint64
|
||||
Aux_offset uint64
|
||||
Aux_size uint64
|
||||
}
|
||||
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||
return []byte{}, errNonLinux
|
||||
}
|
||||
|
||||
func Munmap(b []byte) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
type PerfEventAttr struct {
|
||||
Type uint32
|
||||
Size uint32
|
||||
Config uint64
|
||||
Sample uint64
|
||||
Sample_type uint64
|
||||
Read_format uint64
|
||||
Bits uint64
|
||||
Wakeup uint32
|
||||
Bp_type uint32
|
||||
Ext1 uint64
|
||||
Ext2 uint64
|
||||
Branch_sample_type uint64
|
||||
Sample_regs_user uint64
|
||||
Sample_stack_user uint32
|
||||
Clockid int32
|
||||
Sample_regs_intr uint64
|
||||
Aux_watermark uint32
|
||||
Sample_max_stack uint16
|
||||
}
|
||||
|
||||
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
type Utsname struct {
|
||||
Release [65]byte
|
||||
Version [65]byte
|
||||
}
|
||||
|
||||
func Uname(buf *Utsname) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Getpid() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func Gettid() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
return nil, errNonLinux
|
||||
}
|
||||
|
||||
func ByteSliceToString(s []byte) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Prlimit(pid, resource int, new, old *Rlimit) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func Open(path string, mode int, perm uint32) (int, error) {
|
||||
return -1, errNonLinux
|
||||
}
|
||||
|
||||
func Fstat(fd int, stat *Stat_t) error {
|
||||
return errNonLinux
|
||||
}
|
155
pkg/ebpf_internal/vdso.go
Normal file
155
pkg/ebpf_internal/vdso.go
Normal file
@ -0,0 +1,155 @@
|
||||
// Copied from https://github.com/cilium/ebpf/blob/v0.10.0/internal/vdso.go
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"github.com/v2rayA/dae/pkg/ebpf_internal/internal/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
errAuxvNoVDSO = errors.New("no vdso address found in auxv")
|
||||
)
|
||||
|
||||
// vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library
|
||||
// linked into the current process image.
|
||||
func vdsoVersion() (uint32, error) {
|
||||
// Read data from the auxiliary vector, which is normally passed directly
|
||||
// to the process. Go does not expose that data, so we must read it from procfs.
|
||||
// https://man7.org/linux/man-pages/man3/getauxval.3.html
|
||||
av, err := os.Open("/proc/self/auxv")
|
||||
if errors.Is(err, unix.EACCES) {
|
||||
return 0, fmt.Errorf("opening auxv: %w (process may not be dumpable due to file capabilities)", err)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("opening auxv: %w", err)
|
||||
}
|
||||
defer av.Close()
|
||||
|
||||
vdsoAddr, err := vdsoMemoryAddress(av)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("finding vDSO memory address: %w", err)
|
||||
}
|
||||
|
||||
// Use /proc/self/mem rather than unsafe.Pointer tricks.
|
||||
mem, err := os.Open("/proc/self/mem")
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("opening mem: %w", err)
|
||||
}
|
||||
defer mem.Close()
|
||||
|
||||
// Open ELF at provided memory address, as offset into /proc/self/mem.
|
||||
c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading linux version code: %w", err)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// vdsoMemoryAddress returns the memory address of the vDSO library
|
||||
// linked into the current process image. r is an io.Reader into an auxv blob.
|
||||
func vdsoMemoryAddress(r io.Reader) (uint64, error) {
|
||||
const (
|
||||
_AT_NULL = 0 // End of vector
|
||||
_AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image
|
||||
)
|
||||
|
||||
// Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`,
|
||||
// the address of a page containing the virtual Dynamic Shared Object (vDSO).
|
||||
aux := struct{ Tag, Val uint64 }{}
|
||||
for {
|
||||
if err := binary.Read(r, NativeEndian, &aux); err != nil {
|
||||
return 0, fmt.Errorf("reading auxv entry: %w", err)
|
||||
}
|
||||
|
||||
switch aux.Tag {
|
||||
case _AT_SYSINFO_EHDR:
|
||||
if aux.Val != 0 {
|
||||
return aux.Val, nil
|
||||
}
|
||||
return 0, fmt.Errorf("invalid vDSO address in auxv")
|
||||
// _AT_NULL is always the last tag/val pair in the aux vector
|
||||
// and can be treated like EOF.
|
||||
case _AT_NULL:
|
||||
return 0, errAuxvNoVDSO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)'
|
||||
type elfNoteHeader struct {
|
||||
NameSize int32
|
||||
DescSize int32
|
||||
Type int32
|
||||
}
|
||||
|
||||
// vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in
|
||||
// the ELF notes section of the binary provided by the reader.
|
||||
func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
|
||||
hdr, err := NewSafeELFFile(r)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading vDSO ELF: %w", err)
|
||||
}
|
||||
|
||||
sections := hdr.SectionsByType(elf.SHT_NOTE)
|
||||
if len(sections) == 0 {
|
||||
return 0, fmt.Errorf("no note section found in vDSO ELF")
|
||||
}
|
||||
|
||||
for _, sec := range sections {
|
||||
sr := sec.Open()
|
||||
var n elfNoteHeader
|
||||
|
||||
// Read notes until we find one named 'Linux'.
|
||||
for {
|
||||
if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// We looked at all the notes in this section
|
||||
break
|
||||
}
|
||||
return 0, fmt.Errorf("reading note header: %w", err)
|
||||
}
|
||||
|
||||
// If a note name is defined, it follows the note header.
|
||||
var name string
|
||||
if n.NameSize > 0 {
|
||||
// Read the note name, aligned to 4 bytes.
|
||||
buf := make([]byte, Align(int(n.NameSize), 4))
|
||||
if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {
|
||||
return 0, fmt.Errorf("reading note name: %w", err)
|
||||
}
|
||||
|
||||
// Read nul-terminated string.
|
||||
name = unix.ByteSliceToString(buf[:n.NameSize])
|
||||
}
|
||||
|
||||
// If a note descriptor is defined, it follows the name.
|
||||
// It is possible for a note to have a descriptor but not a name.
|
||||
if n.DescSize > 0 {
|
||||
// LINUX_VERSION_CODE is a uint32 value.
|
||||
if name == "Linux" && n.DescSize == 4 && n.Type == 0 {
|
||||
var version uint32
|
||||
if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil {
|
||||
return 0, fmt.Errorf("reading note descriptor: %w", err)
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// Discard the note descriptor if it exists but we're not interested in it.
|
||||
if _, err := io.CopyN(io.Discard, sr, int64(Align(int(n.DescSize), 4))); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("no Linux note in ELF")
|
||||
}
|
124
pkg/ebpf_internal/version.go
Normal file
124
pkg/ebpf_internal/version.go
Normal file
@ -0,0 +1,124 @@
|
||||
// Copied from https://github.com/cilium/ebpf/blob/v0.10.0/internal/version.go
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/v2rayA/dae/pkg/ebpf_internal/internal/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version constant used in ELF binaries indicating that the loader needs to
|
||||
// substitute the eBPF program's version with the value of the kernel's
|
||||
// KERNEL_VERSION compile-time macro. Used for compatibility with BCC, gobpf
|
||||
// and RedSift.
|
||||
MagicKernelVersion = 0xFFFFFFFE
|
||||
)
|
||||
|
||||
var (
|
||||
kernelVersion = struct {
|
||||
once sync.Once
|
||||
version Version
|
||||
err error
|
||||
}{}
|
||||
)
|
||||
|
||||
// A Version in the form Major.Minor.Patch.
|
||||
type Version [3]uint16
|
||||
|
||||
// NewVersion creates a version from a string like "Major.Minor.Patch".
|
||||
//
|
||||
// Patch is optional.
|
||||
func NewVersion(ver string) (Version, error) {
|
||||
var major, minor, patch uint16
|
||||
n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
|
||||
if n < 2 {
|
||||
return Version{}, fmt.Errorf("invalid version: %s", ver)
|
||||
}
|
||||
return Version{major, minor, patch}, nil
|
||||
}
|
||||
|
||||
// NewVersionFromCode creates a version from a LINUX_VERSION_CODE.
|
||||
func NewVersionFromCode(code uint32) Version {
|
||||
return Version{
|
||||
uint16(uint8(code >> 16)),
|
||||
uint16(uint8(code >> 8)),
|
||||
uint16(uint8(code)),
|
||||
}
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v[2] == 0 {
|
||||
return fmt.Sprintf("v%d.%d", v[0], v[1])
|
||||
}
|
||||
return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2])
|
||||
}
|
||||
|
||||
// Less returns true if the version is less than another version.
|
||||
func (v Version) Less(other Version) bool {
|
||||
for i, a := range v {
|
||||
if a == other[i] {
|
||||
continue
|
||||
}
|
||||
return a < other[i]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Unspecified returns true if the version is all zero.
|
||||
func (v Version) Unspecified() bool {
|
||||
return v[0] == 0 && v[1] == 0 && v[2] == 0
|
||||
}
|
||||
|
||||
// Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h.
|
||||
// It represents the kernel version and patch level as a single value.
|
||||
func (v Version) Kernel() uint32 {
|
||||
|
||||
// Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid
|
||||
// overflowing into PATCHLEVEL.
|
||||
// See kernel commit 9b82f13e7ef3 ("kbuild: clamp SUBLEVEL to 255").
|
||||
s := v[2]
|
||||
if s > 255 {
|
||||
s = 255
|
||||
}
|
||||
|
||||
// Truncate members to uint8 to prevent them from spilling over into
|
||||
// each other when overflowing 8 bits.
|
||||
return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s))
|
||||
}
|
||||
|
||||
// KernelVersion returns the version of the currently running kernel.
|
||||
func KernelVersion() (Version, error) {
|
||||
kernelVersion.once.Do(func() {
|
||||
kernelVersion.version, kernelVersion.err = detectKernelVersion()
|
||||
})
|
||||
|
||||
if kernelVersion.err != nil {
|
||||
return Version{}, kernelVersion.err
|
||||
}
|
||||
return kernelVersion.version, nil
|
||||
}
|
||||
|
||||
// detectKernelVersion returns the version of the running kernel.
|
||||
func detectKernelVersion() (Version, error) {
|
||||
vc, err := vdsoVersion()
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
return NewVersionFromCode(vc), nil
|
||||
}
|
||||
|
||||
// KernelRelease returns the release string of the running kernel.
|
||||
// Its format depends on the Linux distribution and corresponds to directory
|
||||
// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
|
||||
// 4.19.0-16-amd64.
|
||||
func KernelRelease() (string, error) {
|
||||
var uname unix.Utsname
|
||||
if err := unix.Uname(&uname); err != nil {
|
||||
return "", fmt.Errorf("uname failed: %w", err)
|
||||
}
|
||||
|
||||
return unix.ByteSliceToString(uname.Release[:]), nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user