github.com/yaling888/clash@v1.53.0/component/ebpf/tc/redirect_to_tun.go (about)

     1  //go:build linux
     2  
     3  package tc
     4  
     5  import (
     6  	"fmt"
     7  	"io"
     8  	"net/netip"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/cilium/ebpf"
    13  	"github.com/cilium/ebpf/rlimit"
    14  	"github.com/vishvananda/netlink"
    15  	"golang.org/x/sys/unix"
    16  
    17  	C "github.com/yaling888/clash/constant"
    18  	"github.com/yaling888/clash/transport/socks5"
    19  )
    20  
    21  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf ../bpf/tc.c
    22  
    23  const (
    24  	mapKey1 uint32 = 0
    25  	mapKey2 uint32 = 1
    26  )
    27  
    28  type EBpfTC struct {
    29  	objs   io.Closer
    30  	qdisc  netlink.Qdisc
    31  	filter netlink.Filter
    32  
    33  	ifName     string
    34  	ifIndex    int
    35  	ifMark     uint32
    36  	tunIfIndex uint32
    37  
    38  	bpfPath string
    39  }
    40  
    41  func NewEBpfTc(ifName string, ifIndex int, ifMark uint32, tunIfIndex uint32) *EBpfTC {
    42  	return &EBpfTC{
    43  		ifName:     ifName,
    44  		ifIndex:    ifIndex,
    45  		ifMark:     ifMark,
    46  		tunIfIndex: tunIfIndex,
    47  	}
    48  }
    49  
    50  func (e *EBpfTC) Start() error {
    51  	if err := rlimit.RemoveMemlock(); err != nil {
    52  		return fmt.Errorf("remove memory lock: %w", err)
    53  	}
    54  
    55  	e.bpfPath = filepath.Join(C.BpfFSPath, e.ifName)
    56  	if err := os.MkdirAll(e.bpfPath, os.ModePerm); err != nil {
    57  		return fmt.Errorf("failed to create bpf fs subpath: %w", err)
    58  	}
    59  
    60  	var objs bpfObjects
    61  	if err := loadBpfObjects(&objs, &ebpf.CollectionOptions{
    62  		Maps: ebpf.MapOptions{
    63  			PinPath: e.bpfPath,
    64  		},
    65  	}); err != nil {
    66  		e.Close()
    67  		return fmt.Errorf("loading objects: %w", err)
    68  	}
    69  
    70  	e.objs = &objs
    71  
    72  	if err := objs.bpfMaps.TcParamsMap.Update(mapKey1, e.ifMark, ebpf.UpdateAny); err != nil {
    73  		e.Close()
    74  		return fmt.Errorf("storing objects: %w", err)
    75  	}
    76  
    77  	if err := objs.bpfMaps.TcParamsMap.Update(mapKey2, e.tunIfIndex, ebpf.UpdateAny); err != nil {
    78  		e.Close()
    79  		return fmt.Errorf("storing objects: %w", err)
    80  	}
    81  
    82  	attrs := netlink.QdiscAttrs{
    83  		LinkIndex: e.ifIndex,
    84  		Handle:    netlink.MakeHandle(0xffff, 0),
    85  		Parent:    netlink.HANDLE_CLSACT,
    86  	}
    87  
    88  	qdisc := &netlink.GenericQdisc{
    89  		QdiscAttrs: attrs,
    90  		QdiscType:  "clsact",
    91  	}
    92  
    93  	e.qdisc = qdisc
    94  
    95  	if err := netlink.QdiscAdd(qdisc); err != nil {
    96  		if os.IsExist(err) {
    97  			_ = netlink.QdiscDel(qdisc)
    98  			err = netlink.QdiscAdd(qdisc)
    99  		}
   100  
   101  		if err != nil {
   102  			e.Close()
   103  			return fmt.Errorf("cannot add clsact qdisc: %w", err)
   104  		}
   105  	}
   106  
   107  	filterAttrs := netlink.FilterAttrs{
   108  		LinkIndex: e.ifIndex,
   109  		Parent:    netlink.HANDLE_MIN_EGRESS,
   110  		Handle:    netlink.MakeHandle(0, 1),
   111  		Protocol:  unix.ETH_P_ALL,
   112  		Priority:  1,
   113  	}
   114  
   115  	filter := &netlink.BpfFilter{
   116  		FilterAttrs:  filterAttrs,
   117  		Fd:           objs.bpfPrograms.TcTunFunc.FD(),
   118  		Name:         "clash-tc-" + e.ifName,
   119  		DirectAction: true,
   120  	}
   121  
   122  	if err := netlink.FilterAdd(filter); err != nil {
   123  		e.Close()
   124  		return fmt.Errorf("cannot attach ebpf object to filter: %w", err)
   125  	}
   126  
   127  	e.filter = filter
   128  
   129  	return nil
   130  }
   131  
   132  func (e *EBpfTC) Close() {
   133  	if e.filter != nil {
   134  		_ = netlink.FilterDel(e.filter)
   135  	}
   136  	if e.qdisc != nil {
   137  		_ = netlink.QdiscDel(e.qdisc)
   138  	}
   139  	if e.objs != nil {
   140  		_ = e.objs.Close()
   141  	}
   142  	_ = os.Remove(filepath.Join(e.bpfPath, "tc_params_map"))
   143  }
   144  
   145  func (e *EBpfTC) Lookup(_ netip.AddrPort) (socks5.Addr, error) {
   146  	return nil, fmt.Errorf("not supported")
   147  }