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 }