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  }