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

     1  package ebpf
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  
     7  	"github.com/vishvananda/netlink"
     8  
     9  	"github.com/yaling888/clash/common/cmd"
    10  	"github.com/yaling888/clash/component/dialer"
    11  	"github.com/yaling888/clash/component/ebpf/redir"
    12  	"github.com/yaling888/clash/component/ebpf/tc"
    13  	C "github.com/yaling888/clash/constant"
    14  )
    15  
    16  // NewTcEBpfProgram new redirect to tun ebpf program
    17  func NewTcEBpfProgram(ifaceNames []string, tunName string) (*TcEBpfProgram, error) {
    18  	tunIface, err := netlink.LinkByName(tunName)
    19  	if err != nil {
    20  		return nil, fmt.Errorf("lookup network iface %q: %w", tunName, err)
    21  	}
    22  
    23  	tunIndex := uint32(tunIface.Attrs().Index)
    24  
    25  	dialer.DefaultRoutingMark.Store(C.ClashTrafficMark)
    26  
    27  	ifMark := uint32(dialer.DefaultRoutingMark.Load())
    28  
    29  	var pros []C.EBpf
    30  	for _, ifaceName := range ifaceNames {
    31  		iface, err := netlink.LinkByName(ifaceName)
    32  		if err != nil {
    33  			return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err)
    34  		}
    35  		if iface.Attrs().OperState != netlink.OperUp {
    36  			return nil, fmt.Errorf("network iface %q is down", ifaceName)
    37  		}
    38  
    39  		attrs := iface.Attrs()
    40  		index := attrs.Index
    41  
    42  		tcPro := tc.NewEBpfTc(ifaceName, index, ifMark, tunIndex)
    43  		if err = tcPro.Start(); err != nil {
    44  			return nil, err
    45  		}
    46  
    47  		pros = append(pros, tcPro)
    48  	}
    49  
    50  	systemSetting(ifaceNames...)
    51  
    52  	return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil
    53  }
    54  
    55  // NewRedirEBpfProgram new auto redirect ebpf program
    56  func NewRedirEBpfProgram(ifaceNames []string, redirPort uint16, defaultRouteInterfaceName string) (*TcEBpfProgram, error) {
    57  	defaultRouteInterface, err := netlink.LinkByName(defaultRouteInterfaceName)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("lookup network iface %q: %w", defaultRouteInterfaceName, err)
    60  	}
    61  
    62  	defaultRouteIndex := uint32(defaultRouteInterface.Attrs().Index)
    63  
    64  	var pros []C.EBpf
    65  	for _, ifaceName := range ifaceNames {
    66  		iface, err := netlink.LinkByName(ifaceName)
    67  		if err != nil {
    68  			return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err)
    69  		}
    70  
    71  		attrs := iface.Attrs()
    72  		index := attrs.Index
    73  
    74  		addrs, err := netlink.AddrList(iface, netlink.FAMILY_V4)
    75  		if err != nil {
    76  			return nil, fmt.Errorf("lookup network iface %q address: %w", ifaceName, err)
    77  		}
    78  
    79  		if len(addrs) == 0 {
    80  			return nil, fmt.Errorf("network iface %q does not contain any ipv4 addresses", ifaceName)
    81  		}
    82  
    83  		address, _ := netip.AddrFromSlice(addrs[0].IP)
    84  		redirAddrPort := netip.AddrPortFrom(address, redirPort)
    85  
    86  		redirPro := redir.NewEBpfRedirect(ifaceName, index, 0, defaultRouteIndex, redirAddrPort)
    87  		if err = redirPro.Start(); err != nil {
    88  			return nil, err
    89  		}
    90  
    91  		pros = append(pros, redirPro)
    92  	}
    93  
    94  	systemSetting(ifaceNames...)
    95  
    96  	return NewAutoRedirProgram(pros, ifaceNames, defaultRouteInterfaceName), nil
    97  }
    98  
    99  func systemSetting(ifaceNames ...string) {
   100  	_, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1")
   101  	_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding=1")
   102  	_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_local=1")
   103  	_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_redirects=1")
   104  	_, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.rp_filter=0")
   105  	_, _ = cmd.ExecCmd("iptables -t filter -P FORWARD ACCEPT")
   106  
   107  	for _, ifaceName := range ifaceNames {
   108  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.forwarding=1", ifaceName))
   109  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_local=1", ifaceName))
   110  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_redirects=1", ifaceName))
   111  		_, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.rp_filter=0", ifaceName))
   112  	}
   113  }