dae/control/control_plane.go

459 lines
14 KiB
Go
Raw Normal View History

2023-01-23 18:54:21 +07:00
/*
* SPDX-License-Identifier: AGPL-3.0-only
2023-01-28 12:56:06 +07:00
* Copyright (c) since 2022, v2rayA Organization <team@v2raya.org>
2023-01-23 18:54:21 +07:00
*/
package control
import (
"context"
"fmt"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/rlimit"
2023-01-29 12:38:15 +07:00
"github.com/mzz2017/softwind/pool"
"github.com/sirupsen/logrus"
2023-01-23 19:01:24 +07:00
"github.com/v2rayA/dae/common"
"github.com/v2rayA/dae/common/consts"
"github.com/v2rayA/dae/component/outbound"
"github.com/v2rayA/dae/component/outbound/dialer"
"github.com/v2rayA/dae/component/routing"
2023-01-28 00:50:21 +07:00
"github.com/v2rayA/dae/config"
"github.com/v2rayA/dae/pkg/config_parser"
2023-01-30 22:21:00 +07:00
internal "github.com/v2rayA/dae/pkg/ebpf_internal"
2023-01-23 18:54:21 +07:00
"golang.org/x/sys/unix"
"net"
"net/netip"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
2023-01-23 18:54:21 +07:00
)
type ControlPlane struct {
log *logrus.Logger
core *ControlPlaneCore
deferFuncs []func() error
2023-02-07 20:11:12 +07:00
listenIp string
2023-01-23 18:54:21 +07:00
// TODO: add mutex?
outbounds []*outbound.DialerGroup
outboundName2Id map[string]uint8
SimulatedLpmTries [][]netip.Prefix
SimulatedDomainSet []DomainSet
Final string
// mutex protects the dnsCache.
2023-01-27 01:10:27 +07:00
mutex sync.Mutex
dnsCache map[string]*dnsCache
2023-01-24 16:15:27 +07:00
dnsUpstream netip.AddrPort
2023-01-23 18:54:21 +07:00
}
2023-01-28 00:50:21 +07:00
func NewControlPlane(
log *logrus.Logger,
nodes []string,
groups []config.Group,
routingA *config.Routing,
global *config.Global,
2023-01-28 00:50:21 +07:00
) (c *ControlPlane, err error) {
kernelVersion, e := internal.KernelVersion()
if e != nil {
return nil, fmt.Errorf("failed to get kernel version: %w", e)
}
2023-02-08 14:38:13 +07:00
/// Check linux kernel requirements.
// Check version from high to low to reduce the number of user upgrading kernel.
2023-02-04 10:38:01 +07:00
if kernelVersion.Less(consts.ChecksumFeatureVersion) {
return nil, fmt.Errorf("your kernel version %v does not support checksum related features; expect >=%v; upgrade your kernel and try again",
kernelVersion.String(),
2023-02-04 10:38:01 +07:00
consts.ChecksumFeatureVersion.String())
}
if len(global.WanInterface) > 0 && kernelVersion.Less(consts.CgSocketCookieFeatureVersion) {
return nil, fmt.Errorf("your kernel version %v does not support bind to WAN; expect >=%v; remove wan_interface in config file and try again",
kernelVersion.String(),
consts.CgSocketCookieFeatureVersion.String())
}
if len(global.LanInterface) > 0 && kernelVersion.Less(consts.SkAssignFeatureVersion) {
return nil, fmt.Errorf("your kernel version %v does not support bind to LAN; expect >=%v; remove lan_interface in config file and try again",
2023-02-06 13:01:02 +07:00
kernelVersion.String(),
consts.SkAssignFeatureVersion.String())
}
2023-02-04 10:38:01 +07:00
if kernelVersion.Less(consts.BasicFeatureVersion) {
return nil, fmt.Errorf("your kernel version %v does not satisfy basic requirement; expect >=%v",
kernelVersion.String(),
consts.BasicFeatureVersion.String())
2023-02-04 10:38:01 +07:00
}
2023-02-08 14:38:13 +07:00
/// Allow the current process to lock memory for eBPF resources.
2023-01-28 00:50:21 +07:00
if err = rlimit.RemoveMemlock(); err != nil {
2023-01-23 18:54:21 +07:00
return nil, fmt.Errorf("rlimit.RemoveMemlock:%v", err)
}
pinPath := filepath.Join(consts.BpfPinRoot, consts.AppName)
2023-02-07 22:05:30 +07:00
if err = os.MkdirAll(pinPath, 0755); err != nil && !os.IsExist(err) {
2023-02-07 21:29:50 +07:00
return nil, err
}
2023-01-27 01:10:27 +07:00
2023-02-08 14:38:13 +07:00
/// Load pre-compiled programs and maps into the kernel.
log.Infof("Loading eBPF programs and maps into the kernel")
2023-01-23 18:54:21 +07:00
var bpf bpfObjects
2023-02-07 20:33:38 +07:00
var ProgramOptions = ebpf.ProgramOptions{
KernelTypes: nil,
}
2023-02-04 17:18:06 +07:00
if log.Level == logrus.PanicLevel {
2023-02-07 20:33:38 +07:00
ProgramOptions.LogLevel = ebpf.LogLevelBranch | ebpf.LogLevelStats
// ProgramOptions.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelStats
}
2023-02-08 14:38:13 +07:00
collectionOpts := &ebpf.CollectionOptions{
2023-01-23 18:54:21 +07:00
Maps: ebpf.MapOptions{
PinPath: pinPath,
},
Programs: ProgramOptions,
2023-01-23 18:54:21 +07:00
}
2023-02-08 14:38:13 +07:00
if err = selectivelyLoadBpfObjects(log, &bpf, &loadBpfOptions{
PinPath: pinPath,
CollectionOptions: collectionOpts,
BindLan: len(global.LanInterface) > 0,
BindWan: len(global.WanInterface) > 0,
2023-02-08 14:38:13 +07:00
}); err != nil {
return nil, fmt.Errorf("load eBPF objects: %w", err)
}
2023-01-27 01:10:27 +07:00
// Write params.
2023-01-28 00:50:21 +07:00
if err = bpf.ParamMap.Update(consts.DisableL4TxChecksumKey, consts.DisableL4ChecksumPolicy_SetZero, ebpf.UpdateAny); err != nil {
2023-01-23 18:54:21 +07:00
return nil, err
}
2023-01-28 00:50:21 +07:00
if err = bpf.ParamMap.Update(consts.DisableL4RxChecksumKey, consts.DisableL4ChecksumPolicy_SetZero, ebpf.UpdateAny); err != nil {
2023-01-23 18:54:21 +07:00
return nil, err
}
// Write tproxy (control plane) PID.
if err = bpf.ParamMap.Update(consts.ControlPlaneOidKey, uint32(os.Getpid()), ebpf.UpdateAny); err != nil {
return nil, err
}
2023-02-08 14:38:13 +07:00
// Write ip_proto to hdr_size mapping for IPv6 extension extraction (it is just for eBPF code insns optimization).
2023-01-28 10:47:02 +07:00
if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_HOPOPTS), int32(-1), ebpf.UpdateAny); err != nil {
return nil, err
}
if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_ROUTING), int32(-1), ebpf.UpdateAny); err != nil {
return nil, err
}
if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_FRAGMENT), int32(4), ebpf.UpdateAny); err != nil {
return nil, err
}
2023-01-30 14:50:55 +07:00
if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_TCP), int32(-2), ebpf.UpdateAny); err != nil {
2023-01-28 10:47:02 +07:00
return nil, err
}
2023-01-30 14:50:55 +07:00
if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_UDP), int32(-2), ebpf.UpdateAny); err != nil {
2023-01-28 10:47:02 +07:00
return nil, err
}
2023-01-28 14:47:43 +07:00
core := &ControlPlaneCore{
log: log,
deferFuncs: []func() error{bpf.Close},
bpf: &bpf,
kernelVersion: &kernelVersion,
}
defer func() {
if err != nil {
_ = core.Close()
}
}()
/// Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection.
// Add clsact qdisc
for _, ifname := range common.Deduplicate(append(append([]string{}, global.LanInterface...), global.WanInterface...)) {
_ = core.addQdisc(ifname)
}
// Bind to LAN
if len(global.LanInterface) > 0 {
if err = core.setupRoutingPolicy(); err != nil {
return nil, err
}
for _, ifname := range global.LanInterface {
if err = core.bindLan(ifname); err != nil {
return nil, fmt.Errorf("bindLan: %v: %w", ifname, err)
}
}
}
// Bind to WAN
if len(global.WanInterface) > 0 {
if err = core.setupSkPidMonitor(); err != nil {
return nil, err
}
for _, ifname := range global.WanInterface {
if err = core.bindWan(ifname); err != nil {
return nil, fmt.Errorf("bindWan: %v: %w", ifname, err)
}
}
}
2023-02-08 14:38:13 +07:00
/// DialerGroups (outbounds).
ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancel()
tcpCheckOption, err := dialer.ParseTcpCheckOption(ctx, global.TcpCheckUrl)
if err != nil {
return nil, fmt.Errorf("failed to parse tcp_check_url: %w", err)
}
udpCheckOption, err := dialer.ParseUdpCheckOption(ctx, global.UdpCheckDns)
if err != nil {
return nil, fmt.Errorf("failed to parse udp_check_dns: %w", err)
}
2023-01-28 00:50:21 +07:00
option := &dialer.GlobalOption{
Log: log,
TcpCheckOption: tcpCheckOption,
UdpCheckOption: udpCheckOption,
CheckInterval: global.CheckInterval,
2023-01-28 00:50:21 +07:00
}
2023-01-23 18:54:21 +07:00
outbounds := []*outbound.DialerGroup{
2023-01-28 00:50:21 +07:00
outbound.NewDialerGroup(option, consts.OutboundDirect.String(),
[]*dialer.Dialer{dialer.NewDirectDialer(option, true)},
2023-01-23 18:54:21 +07:00
outbound.DialerSelectionPolicy{
Policy: consts.DialerSelectionPolicy_Fixed,
FixedIndex: 0,
}, core.OutboundAliveChangeCallback(0)),
2023-01-28 00:50:21 +07:00
outbound.NewDialerGroup(option, consts.OutboundBlock.String(),
[]*dialer.Dialer{dialer.NewBlockDialer(option)},
2023-01-23 18:54:21 +07:00
outbound.DialerSelectionPolicy{
2023-01-27 01:10:27 +07:00
Policy: consts.DialerSelectionPolicy_Fixed,
FixedIndex: 0,
}, core.OutboundAliveChangeCallback(1)),
2023-01-23 18:54:21 +07:00
}
2023-01-28 00:50:21 +07:00
// Filter out groups.
dialerSet := outbound.NewDialerSetFromLinks(option, nodes)
for _, group := range groups {
2023-01-28 14:47:43 +07:00
// Parse policy.
policy, err := outbound.NewDialerSelectionPolicyFromGroupParam(&group.Param)
if err != nil {
return nil, fmt.Errorf("failed to create group %v: %w", group.Name, err)
}
// Filter nodes with user given filters.
2023-01-28 00:50:21 +07:00
dialers, err := dialerSet.Filter(group.Param.Filter)
if err != nil {
return nil, fmt.Errorf(`failed to create group "%v": %w`, group.Name, err)
}
2023-01-28 14:47:43 +07:00
// Convert node links to dialers.
log.Infof(`Group "%v" node list:`, group.Name)
for _, d := range dialers {
log.Infoln("\t" + d.Name())
// We only activate check of nodes that have a group.
d.ActivateCheck()
2023-01-28 14:47:43 +07:00
}
if len(dialers) == 0 {
log.Infoln("\t<Empty>")
2023-01-28 00:50:21 +07:00
}
2023-01-28 14:47:43 +07:00
// Create dialer group and append it to outbounds.
dialerGroup := outbound.NewDialerGroup(option, group.Name, dialers, *policy, core.OutboundAliveChangeCallback(uint8(len(outbounds))))
2023-01-28 00:50:21 +07:00
outbounds = append(outbounds, dialerGroup)
}
2023-02-08 14:38:13 +07:00
/// Routing.
2023-01-23 18:54:21 +07:00
// Generate outboundName2Id from outbounds.
if len(outbounds) > int(consts.OutboundUserDefinedMax) {
2023-01-23 18:54:21 +07:00
return nil, fmt.Errorf("too many outbounds")
}
outboundName2Id := make(map[string]uint8)
for i, o := range outbounds {
if _, exist := outboundName2Id[o.Name]; exist {
return nil, fmt.Errorf("duplicated outbound name: %v", o.Name)
}
2023-01-23 18:54:21 +07:00
outboundName2Id[o.Name] = uint8(i)
}
builder := NewRoutingMatcherBuilder(outboundName2Id, &bpf)
2023-01-28 00:50:21 +07:00
var rules []*config_parser.RoutingRule
if rules, err = routing.ApplyRulesOptimizers(routingA.Rules,
&routing.RefineFunctionParamKeyOptimizer{},
&routing.DatReaderOptimizer{Logger: log},
&routing.MergeAndSortRulesOptimizer{},
&routing.DeduplicateParamsOptimizer{},
); err != nil {
2023-02-01 13:08:01 +07:00
return nil, fmt.Errorf("ApplyRulesOptimizers error:\n%w", err)
}
2023-01-29 10:19:58 +07:00
if log.IsLevelEnabled(logrus.DebugLevel) {
var debugBuilder strings.Builder
for _, rule := range rules {
2023-01-28 00:50:21 +07:00
debugBuilder.WriteString(rule.String(true) + "\n")
}
2023-01-29 10:19:58 +07:00
log.Debugf("RoutingA:\n%vfinal: %v\n", debugBuilder.String(), routingA.Final)
}
if err = routing.ApplyMatcherBuilder(log, builder, rules, routingA.Final); err != nil {
2023-01-23 18:54:21 +07:00
return nil, fmt.Errorf("ApplyMatcherBuilder: %w", err)
}
2023-01-28 00:50:21 +07:00
if err = builder.Build(); err != nil {
2023-01-23 18:54:21 +07:00
return nil, fmt.Errorf("RoutingMatcherBuilder.Build: %w", err)
}
2023-01-27 01:10:27 +07:00
2023-02-08 14:38:13 +07:00
/// DNS upstream.
2023-02-06 15:22:07 +07:00
var dnsAddrPort netip.AddrPort
if !global.DnsUpstream.Empty {
if dnsAddrPort, err = resolveDnsUpstream(global.DnsUpstream.Url); err != nil {
return nil, err
2023-02-06 15:22:07 +07:00
}
dnsAddr16 := dnsAddrPort.Addr().As16()
if err = bpf.DnsUpstreamMap.Update(consts.ZeroKey, bpfIpPort{
Ip: common.Ipv6ByteSliceToUint32Array(dnsAddr16[:]),
Port: internal.Htons(dnsAddrPort.Port()),
}, ebpf.UpdateAny); err != nil {
return nil, err
}
} else {
// Empty.
2023-02-06 15:22:07 +07:00
if err = bpf.DnsUpstreamMap.Update(consts.ZeroKey, bpfIpPort{
Ip: [4]uint32{},
// Zero port indicates no element, because bpf_map_lookup_elem cannot return 0 for map_type_array.
Port: 0,
}, ebpf.UpdateAny); err != nil {
return nil, err
}
2023-01-27 01:10:27 +07:00
}
2023-01-23 18:54:21 +07:00
2023-02-08 14:38:13 +07:00
/// Listen address.
2023-02-07 22:40:19 +07:00
listenIp := "::1"
if len(global.WanInterface) > 0 {
2023-02-07 20:11:12 +07:00
listenIp = "0.0.0.0"
}
2023-01-23 18:54:21 +07:00
return &ControlPlane{
log: log,
core: core,
deferFuncs: nil,
2023-02-07 20:11:12 +07:00
listenIp: listenIp,
2023-01-23 18:54:21 +07:00
outbounds: outbounds,
outboundName2Id: outboundName2Id,
SimulatedLpmTries: builder.SimulatedLpmTries,
SimulatedDomainSet: builder.SimulatedDomainSet,
2023-01-28 00:50:21 +07:00
Final: routingA.Final,
2023-01-23 18:54:21 +07:00
mutex: sync.Mutex{},
dnsCache: make(map[string]*dnsCache),
2023-01-28 00:50:21 +07:00
dnsUpstream: dnsAddrPort,
2023-01-23 18:54:21 +07:00
}, nil
}
func (c *ControlPlane) ListenAndServe(port uint16) (err error) {
// Listen.
var listenConfig = net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
return dialer.TproxyControl(c)
},
}
2023-02-07 20:11:12 +07:00
listenAddr := net.JoinHostPort(c.listenIp, strconv.Itoa(int(port)))
tcpListener, err := listenConfig.Listen(context.TODO(), "tcp", listenAddr)
2023-01-23 18:54:21 +07:00
if err != nil {
return fmt.Errorf("listenTCP: %w", err)
}
defer tcpListener.Close()
2023-02-07 20:11:12 +07:00
packetConn, err := listenConfig.ListenPacket(context.TODO(), "udp", listenAddr)
2023-01-23 18:54:21 +07:00
if err != nil {
return fmt.Errorf("listenUDP: %w", err)
}
defer packetConn.Close()
udpConn := packetConn.(*net.UDPConn)
2023-01-23 18:54:21 +07:00
/// Serve.
// TCP socket.
tcpFile, err := tcpListener.(*net.TCPListener).File()
if err != nil {
return fmt.Errorf("failed to retrieve copy of the underlying TCP connection file")
}
c.deferFuncs = append(c.deferFuncs, func() error {
return tcpFile.Close()
})
if err := c.core.bpf.ListenSocketMap.Update(consts.ZeroKey, uint64(tcpFile.Fd()), ebpf.UpdateAny); err != nil {
return err
}
// UDP socket.
udpFile, err := udpConn.File()
if err != nil {
return fmt.Errorf("failed to retrieve copy of the underlying UDP connection file")
}
c.deferFuncs = append(c.deferFuncs, func() error {
return udpFile.Close()
})
if err := c.core.bpf.ListenSocketMap.Update(consts.OneKey, uint64(udpFile.Fd()), ebpf.UpdateAny); err != nil {
return err
}
// Port.
if err := c.core.bpf.ParamMap.Update(consts.BigEndianTproxyPortKey, uint32(internal.Htons(port)), ebpf.UpdateAny); err != nil {
2023-01-23 18:54:21 +07:00
return err
}
ctx, cancel := context.WithCancel(context.Background())
c.deferFuncs = append(c.deferFuncs, func() error {
cancel()
return nil
})
go func() {
defer cancel()
for {
lconn, err := tcpListener.Accept()
2023-01-23 18:54:21 +07:00
if err != nil {
if !strings.Contains(err.Error(), "use of closed network connection") {
c.log.Errorf("Error when accept: %v", err)
}
break
}
go func() {
if err := c.handleConn(lconn); err != nil {
c.log.Warnln("handleConn:", err)
}
}()
}
}()
go func() {
defer cancel()
for {
var buf [65535]byte
var oob [120]byte // Size for original dest
n, oobn, _, src, err := udpConn.ReadMsgUDPAddrPort(buf[:], oob[:])
2023-01-23 18:54:21 +07:00
if err != nil {
if !strings.Contains(err.Error(), "use of closed network connection") {
c.log.Errorf("ReadFromUDPAddrPort: %v, %v", src.String(), err)
2023-01-23 18:54:21 +07:00
}
break
}
dst := RetrieveOriginalDest(oob[:oobn])
2023-02-07 20:11:12 +07:00
var newBuf []byte
outboundIndex, err := c.core.RetrieveOutboundIndex(src, dst, unix.IPPROTO_UDP)
2023-02-07 20:11:12 +07:00
if err != nil {
// WAN. Old method.
addrHdr, dataOffset, err := ParseAddrHdr(buf[:n])
if err != nil {
c.log.Warnf("No AddrPort presented")
continue
}
newBuf = pool.Get(n - dataOffset)
copy(newBuf, buf[dataOffset:n])
outboundIndex = consts.OutboundIndex(addrHdr.Outbound)
src = netip.AddrPortFrom(dst.Addr(), src.Port())
dst = addrHdr.Dest
} else {
newBuf = pool.Get(n)
copy(newBuf, buf[:n])
2023-01-23 18:54:21 +07:00
}
2023-02-07 20:11:12 +07:00
go func(data []byte, src, dst netip.AddrPort, outboundIndex consts.OutboundIndex) {
if e := c.handlePkt(newBuf, src, dst, outboundIndex); e != nil {
2023-01-23 18:54:21 +07:00
c.log.Warnln("handlePkt:", e)
}
pool.Put(newBuf)
2023-02-07 20:11:12 +07:00
}(newBuf, src, dst, outboundIndex)
2023-01-23 18:54:21 +07:00
}
}()
<-ctx.Done()
return nil
}
func (c *ControlPlane) Close() (err error) {
// Invoke defer funcs in reverse order.
for i := len(c.deferFuncs) - 1; i >= 0; i-- {
if e := c.deferFuncs[i](); e != nil {
// Combine errors.
if err != nil {
err = fmt.Errorf("%w; %v", err, e)
} else {
err = e
}
}
}
return c.core.Close()
2023-01-23 18:54:21 +07:00
}