github.com/kelleygo/clashcore@v1.0.2/component/ebpf/ebpf_linux.go (about) 1 //go:build !android 2 3 package ebpf 4 5 import ( 6 "fmt" 7 "net/netip" 8 9 "github.com/kelleygo/clashcore/common/cmd" 10 "github.com/kelleygo/clashcore/component/dialer" 11 "github.com/kelleygo/clashcore/component/ebpf/redir" 12 "github.com/kelleygo/clashcore/component/ebpf/tc" 13 C "github.com/kelleygo/clashcore/constant" 14 "github.com/sagernet/netlink" 15 ) 16 17 func GetAutoDetectInterface() (string, error) { 18 routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) 19 if err != nil { 20 return "", err 21 } 22 23 for _, route := range routes { 24 if route.Dst == nil { 25 lk, err := netlink.LinkByIndex(route.LinkIndex) 26 if err != nil { 27 return "", err 28 } 29 30 if lk.Type() == "tuntap" { 31 continue 32 } 33 34 return lk.Attrs().Name, nil 35 } 36 } 37 38 return "", fmt.Errorf("interface not found") 39 } 40 41 // NewTcEBpfProgram new redirect to tun ebpf program 42 func NewTcEBpfProgram(ifaceNames []string, tunName string) (*TcEBpfProgram, error) { 43 tunIface, err := netlink.LinkByName(tunName) 44 if err != nil { 45 return nil, fmt.Errorf("lookup network iface %q: %w", tunName, err) 46 } 47 48 tunIndex := uint32(tunIface.Attrs().Index) 49 50 dialer.DefaultRoutingMark.Store(C.YiClashCoreTrafficMark) 51 52 ifMark := uint32(dialer.DefaultRoutingMark.Load()) 53 54 var pros []C.EBpf 55 for _, ifaceName := range ifaceNames { 56 iface, err := netlink.LinkByName(ifaceName) 57 if err != nil { 58 return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err) 59 } 60 if iface.Attrs().OperState != netlink.OperUp { 61 return nil, fmt.Errorf("network iface %q is down", ifaceName) 62 } 63 64 attrs := iface.Attrs() 65 index := attrs.Index 66 67 tcPro := tc.NewEBpfTc(ifaceName, index, ifMark, tunIndex) 68 if err = tcPro.Start(); err != nil { 69 return nil, err 70 } 71 72 pros = append(pros, tcPro) 73 } 74 75 systemSetting(ifaceNames...) 76 77 return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil 78 } 79 80 // NewRedirEBpfProgram new auto redirect ebpf program 81 func NewRedirEBpfProgram(ifaceNames []string, redirPort uint16, defaultRouteInterfaceName string) (*TcEBpfProgram, error) { 82 defaultRouteInterface, err := netlink.LinkByName(defaultRouteInterfaceName) 83 if err != nil { 84 return nil, fmt.Errorf("lookup network iface %q: %w", defaultRouteInterfaceName, err) 85 } 86 87 defaultRouteIndex := uint32(defaultRouteInterface.Attrs().Index) 88 89 var pros []C.EBpf 90 for _, ifaceName := range ifaceNames { 91 iface, err := netlink.LinkByName(ifaceName) 92 if err != nil { 93 return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err) 94 } 95 96 attrs := iface.Attrs() 97 index := attrs.Index 98 99 addrs, err := netlink.AddrList(iface, netlink.FAMILY_V4) 100 if err != nil { 101 return nil, fmt.Errorf("lookup network iface %q address: %w", ifaceName, err) 102 } 103 104 if len(addrs) == 0 { 105 return nil, fmt.Errorf("network iface %q does not contain any ipv4 addresses", ifaceName) 106 } 107 108 address, _ := netip.AddrFromSlice(addrs[0].IP) 109 redirAddrPort := netip.AddrPortFrom(address, redirPort) 110 111 redirPro := redir.NewEBpfRedirect(ifaceName, index, 0, defaultRouteIndex, redirAddrPort) 112 if err = redirPro.Start(); err != nil { 113 return nil, err 114 } 115 116 pros = append(pros, redirPro) 117 } 118 119 systemSetting(ifaceNames...) 120 121 return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil 122 } 123 124 func systemSetting(ifaceNames ...string) { 125 _, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1") 126 _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding=1") 127 _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_local=1") 128 _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_redirects=1") 129 _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.rp_filter=0") 130 131 for _, ifaceName := range ifaceNames { 132 _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.forwarding=1", ifaceName)) 133 _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_local=1", ifaceName)) 134 _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_redirects=1", ifaceName)) 135 _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.rp_filter=0", ifaceName)) 136 } 137 }