dae/component/control/control_plane.go

637 lines
18 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"
"errors"
"fmt"
"github.com/cilium/ebpf"
ciliumLink "github.com/cilium/ebpf/link"
"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
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
"net"
"net/netip"
"os"
"path/filepath"
"reflect"
2023-01-23 18:54:21 +07:00
"strconv"
"strings"
"sync"
2023-01-28 14:47:43 +07:00
"time"
2023-01-23 18:54:21 +07:00
)
type ControlPlane struct {
log *logrus.Logger
kernelVersion *internal.Version
2023-01-23 18:54:21 +07:00
// TODO: add mutex?
outbounds []*outbound.DialerGroup
outboundName2Id map[string]uint8
bpf *bpfObjects
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
deferFuncs []func() error
}
2023-01-28 00:50:21 +07:00
func NewControlPlane(
log *logrus.Logger,
nodes []string,
groups []config.Group,
routingA *config.Routing,
dnsUpstream string,
checkUrl string,
2023-01-28 14:47:43 +07:00
checkInterval time.Duration,
bindLan bool,
bindWan bool,
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)
}
if kernelVersion.Less(consts.BasicFeatureVersion) {
return nil, fmt.Errorf("your kernel version %v does not satisfy basic requirement; expect >=%v", c.kernelVersion.String(), consts.BasicFeatureVersion.String())
}
if bindWan && kernelVersion.Less(consts.FtraceFeatureVersion) {
// Not support ftrace (fentry/fexit).
// PID bypass needs it.
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", c.kernelVersion.String(), consts.FtraceFeatureVersion.String())
}
2023-01-23 18:54:21 +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)
os.MkdirAll(pinPath, 0755)
2023-01-27 01:10:27 +07:00
2023-01-23 18:54:21 +07:00
// Load pre-compiled programs and maps into the kernel.
var bpf bpfObjects
var ProgramOptions ebpf.ProgramOptions
if log.IsLevelEnabled(logrus.TraceLevel) {
ProgramOptions = ebpf.ProgramOptions{
LogLevel: ebpf.LogLevelStats,
}
}
var obj interface{} = &bpf // Bind both LAN and WAN.
if bindLan && !bindWan {
// Trick. Replace the beams with rotten timbers.
2023-02-01 09:59:57 +07:00
obj = &bpfObjectsLan{}
} else if !bindLan && bindWan {
// Trick. Replace the beams with rotten timbers.
obj = &bpfObjectsWan{}
}
2023-01-27 01:10:27 +07:00
retryLoadBpf:
if err = loadBpfObjects(obj, &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
}); err != nil {
if errors.Is(err, ebpf.ErrMapIncompatible) {
2023-01-27 01:10:27 +07:00
// Map property is incompatible. Remove the old map and try again.
2023-01-23 18:54:21 +07:00
prefix := "use pinned map "
_, after, ok := strings.Cut(err.Error(), prefix)
if !ok {
2023-01-23 18:54:21 +07:00
return nil, fmt.Errorf("loading objects: bad format: %w", err)
}
mapName, _, _ := strings.Cut(after, ":")
2023-01-23 18:54:21 +07:00
_ = os.Remove(filepath.Join(pinPath, mapName))
2023-01-29 10:19:58 +07:00
log.Infof("Incompatible new map format with existing map %v detected; removed the old one.", mapName)
2023-01-27 01:10:27 +07:00
goto retryLoadBpf
2023-01-23 18:54:21 +07:00
}
2023-01-29 10:19:58 +07:00
// Get detailed log from ebpf.internal.(*VerifierError)
if log.IsLevelEnabled(logrus.TraceLevel) {
if v := reflect.Indirect(reflect.ValueOf(errors.Unwrap(errors.Unwrap(err)))); v.Kind() == reflect.Struct {
if log := v.FieldByName("Log"); log.IsValid() {
if strSlice, ok := log.Interface().([]string); ok {
err = fmt.Errorf("%v", strings.Join(strSlice, "\n"))
}
}
}
}
err := fmt.Errorf("loading objects: %w", err)
if IsNotSupportFtraceError(err) {
err = fmt.Errorf("%w: Maybe your kernel has no ftrace support. Make sure the kernel config items are on: CONFIG_FUNCTION_TRACER, CONFIG_FUNCTION_GRAPH_TRACER, CONFIG_STACK_TRACER, CONFIG_DYNAMIC_FTRACE", err)
} else if IsNoBtfError(err) {
err = fmt.Errorf("%w: Make sure the kernel config item is on: CONFIG_DEBUG_INFO_BTF", err)
}
return nil, err
2023-01-23 18:54:21 +07:00
}
2023-02-01 09:59:57 +07:00
if _, ok := obj.(*bpfObjects); !ok {
// Reverse takeover.
2023-02-01 09:59:57 +07:00
AssignBpfObjects(&bpf, obj)
}
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
}
// Write ip_proto to hdr_size map for IPv6 extension extraction.
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
2023-01-27 01:10:27 +07:00
// DialerGroups (outbounds).
2023-01-28 00:50:21 +07:00
option := &dialer.GlobalOption{
2023-01-28 14:47:43 +07:00
Log: log,
CheckUrl: checkUrl,
CheckInterval: 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,
}),
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,
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.
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())
d.ActiveCheck()
}
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.
2023-01-28 00:50:21 +07:00
dialerGroup := outbound.NewDialerGroup(option, group.Name, dialers, *policy)
outbounds = append(outbounds, dialerGroup)
}
2023-01-23 18:54:21 +07:00
// Generate outboundName2Id from outbounds.
if len(outbounds) > 0xff {
return nil, fmt.Errorf("too many outbounds")
}
outboundName2Id := make(map[string]uint8)
for i, o := range outbounds {
outboundName2Id[o.Name] = uint8(i)
}
builder := NewRoutingMatcherBuilder(outboundName2Id, &bpf)
// Routing.
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
// DNS upstream.
2023-01-28 00:50:21 +07:00
dnsAddrPort, err := netip.ParseAddrPort(dnsUpstream)
if err != nil {
return nil, fmt.Errorf("failed to parse DNS upstream: %v: %w", dnsUpstream, err)
}
dnsAddr16 := dnsAddrPort.Addr().As16()
if err = bpf.DnsUpstreamMap.Update(consts.ZeroKey, bpfIpPort{
Ip: common.Ipv6ByteSliceToUint32Array(dnsAddr16[:]),
2023-01-30 22:21:00 +07:00
Port: internal.Htons(dnsAddrPort.Port()),
2023-01-27 01:10:27 +07:00
}, ebpf.UpdateAny); err != nil {
return nil, err
}
2023-01-23 18:54:21 +07:00
return &ControlPlane{
log: log,
kernelVersion: &kernelVersion,
2023-01-23 18:54:21 +07:00
outbounds: outbounds,
outboundName2Id: outboundName2Id,
bpf: &bpf,
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-27 01:10:27 +07:00
deferFuncs: []func() error{bpf.Close},
2023-01-23 18:54:21 +07:00
}, nil
}
2023-01-30 14:50:55 +07:00
func (c *ControlPlane) BindLan(ifname string) error {
2023-01-23 18:54:21 +07:00
link, err := netlink.LinkByName(ifname)
if err != nil {
return err
}
// Insert an elem into IfindexIpsMap.
// TODO: We should monitor IP change of the link.
ipnets, err := netlink.AddrList(link, netlink.FAMILY_ALL)
if err != nil {
return err
}
// TODO: If we monitor IP change of the link, we should remove code below.
if len(ipnets) == 0 {
return fmt.Errorf("interface %v has no ip", ifname)
}
var linkIp bpfIfIp
for _, ipnet := range ipnets {
ip, ok := netip.AddrFromSlice(ipnet.IP)
if !ok {
continue
}
if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
continue
}
if (ip.Is6() && linkIp.HasIp6) ||
(ip.Is4() && linkIp.HasIp4) {
continue
}
ip6format := ip.As16()
if ip.Is4() {
linkIp.HasIp4 = true
linkIp.Ip4 = common.Ipv6ByteSliceToUint32Array(ip6format[:])
} else {
linkIp.HasIp6 = true
linkIp.Ip6 = common.Ipv6ByteSliceToUint32Array(ip6format[:])
}
if linkIp.HasIp4 && linkIp.HasIp6 {
break
}
}
2023-01-27 01:10:27 +07:00
if err := c.bpf.IfindexTproxyIpMap.Update(uint32(link.Attrs().Index), linkIp, ebpf.UpdateAny); err != nil {
2023-01-23 18:54:21 +07:00
return fmt.Errorf("update IfindexIpsMap: %w", err)
}
2023-01-27 01:10:27 +07:00
// FIXME: not only this link ip.
if linkIp.HasIp4 {
2023-01-28 10:47:02 +07:00
if err := c.bpf.HostIpLpm.Update(_bpfLpmKey{
2023-01-27 01:10:27 +07:00
PrefixLen: 128,
Data: linkIp.Ip4,
}, uint32(1), ebpf.UpdateAny); err != nil {
return fmt.Errorf("update IfindexIpsMap: %w", err)
}
}
if linkIp.HasIp6 {
2023-01-28 10:47:02 +07:00
if err := c.bpf.HostIpLpm.Update(_bpfLpmKey{
2023-01-27 01:10:27 +07:00
PrefixLen: 128,
Data: linkIp.Ip6,
}, uint32(1), ebpf.UpdateAny); err != nil {
return fmt.Errorf("update IfindexIpsMap: %w", err)
}
}
2023-01-23 18:54:21 +07:00
// Insert qdisc and filters.
qdisc := &netlink.GenericQdisc{
QdiscAttrs: netlink.QdiscAttrs{
LinkIndex: link.Attrs().Index,
Handle: netlink.MakeHandle(0xffff, 0),
Parent: netlink.HANDLE_CLSACT,
},
QdiscType: "clsact",
}
if err := netlink.QdiscAdd(qdisc); err != nil {
if os.IsExist(err) {
_ = netlink.QdiscDel(qdisc)
err = netlink.QdiscAdd(qdisc)
}
if err != nil {
return fmt.Errorf("cannot add clsact qdisc: %w", err)
}
}
c.deferFuncs = append(c.deferFuncs, func() error {
2023-01-28 00:50:21 +07:00
if err := netlink.QdiscDel(qdisc); err != nil {
return fmt.Errorf("QdiscDel: %w", err)
}
return nil
2023-01-23 18:54:21 +07:00
})
2023-01-28 00:50:21 +07:00
filterIngress := &netlink.BpfFilter{
2023-01-23 18:54:21 +07:00
FilterAttrs: netlink.FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: netlink.HANDLE_MIN_INGRESS,
Handle: netlink.MakeHandle(0, 1),
Protocol: unix.ETH_P_ALL,
Priority: 0,
},
Fd: c.bpf.bpfPrograms.TproxyIngress.FD(),
Name: consts.AppName + "_ingress",
DirectAction: true,
}
2023-01-28 00:50:21 +07:00
if err := netlink.FilterAdd(filterIngress); err != nil {
2023-01-23 18:54:21 +07:00
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
}
filterEgress := &netlink.BpfFilter{
FilterAttrs: netlink.FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: netlink.HANDLE_MIN_EGRESS,
Handle: netlink.MakeHandle(0, 1),
Protocol: unix.ETH_P_ALL,
Priority: 0,
},
Fd: c.bpf.bpfPrograms.TproxyEgress.FD(),
Name: consts.AppName + "_egress",
DirectAction: true,
}
if err := netlink.FilterAdd(filterEgress); err != nil {
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
}
return nil
}
2023-01-30 14:50:55 +07:00
func (c *ControlPlane) BindWan(ifname string) error {
link, err := netlink.LinkByName(ifname)
if err != nil {
return err
}
// Set-up SrcPidMapper.
// Attach programs to support pname routing.
// ipv4 tcp/udp: send
inetSendPrepare, err := ciliumLink.AttachTracing(ciliumLink.TracingOptions{
Program: c.bpf.InetSendPrepare,
})
if err != nil {
return fmt.Errorf("AttachTracing InetSendPrepare: %w", err)
}
c.deferFuncs = append(c.deferFuncs, func() error {
if err := inetSendPrepare.Close(); err != nil {
return fmt.Errorf("inetSendPrepare.Close(): %w", err)
}
return nil
})
// ipv4 tcp/udp: listen
inetBind, err := ciliumLink.AttachTracing(ciliumLink.TracingOptions{
Program: c.bpf.InetBind,
})
if err != nil {
return fmt.Errorf("AttachTracing InetBind: %w", err)
}
c.deferFuncs = append(c.deferFuncs, func() error {
if err := inetBind.Close(); err != nil {
return fmt.Errorf("inetBind.Close(): %w", err)
}
return nil
})
// ipv4 udp: sendto/sendmsg
inetAutoBind, err := ciliumLink.AttachTracing(ciliumLink.TracingOptions{
Program: c.bpf.InetAutobind,
})
if err != nil {
return fmt.Errorf("AttachTracing InetAutobind: %w", err)
}
c.deferFuncs = append(c.deferFuncs, func() error {
if err := inetAutoBind.Close(); err != nil {
return fmt.Errorf("inetAutoBind.Close(): %w", err)
}
return nil
})
// ipv4 tcp: connect
tcpConnect, err := ciliumLink.AttachTracing(ciliumLink.TracingOptions{
Program: c.bpf.TcpConnect,
})
if err != nil {
return fmt.Errorf("AttachTracing TcpConnect: %w", err)
}
c.deferFuncs = append(c.deferFuncs, func() error {
if err := tcpConnect.Close(); err != nil {
return fmt.Errorf("inetStreamConnect.Close(): %w", err)
}
return nil
})
// ipv6 tcp/udp: listen
inet6Bind, err := ciliumLink.AttachTracing(ciliumLink.TracingOptions{
Program: c.bpf.Inet6Bind,
})
if err != nil {
return fmt.Errorf("AttachTracing Inet6Bind: %w", err)
}
c.deferFuncs = append(c.deferFuncs, func() error {
if err := inet6Bind.Close(); err != nil {
return fmt.Errorf("inet6Bind.Close(): %w", err)
}
return nil
})
2023-01-30 22:21:00 +07:00
// Insert qdisc and tc filters.
2023-01-30 14:50:55 +07:00
qdisc := &netlink.GenericQdisc{
QdiscAttrs: netlink.QdiscAttrs{
LinkIndex: link.Attrs().Index,
Handle: netlink.MakeHandle(0xffff, 0),
Parent: netlink.HANDLE_CLSACT,
},
QdiscType: "clsact",
}
if err := netlink.QdiscAdd(qdisc); err != nil {
if os.IsExist(err) {
_ = netlink.QdiscDel(qdisc)
err = netlink.QdiscAdd(qdisc)
}
if err != nil {
return fmt.Errorf("cannot add clsact qdisc: %w", err)
}
}
c.deferFuncs = append(c.deferFuncs, func() error {
if err := netlink.QdiscDel(qdisc); err != nil {
return fmt.Errorf("QdiscDel: %w", err)
}
return nil
})
filterEgress := &netlink.BpfFilter{
FilterAttrs: netlink.FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: netlink.HANDLE_MIN_EGRESS,
Handle: netlink.MakeHandle(0, 1),
Protocol: unix.ETH_P_ALL,
Priority: 0,
},
Fd: c.bpf.bpfPrograms.TproxyWanEgress.FD(),
Name: consts.AppName + "_egress",
DirectAction: true,
}
if err := netlink.FilterAdd(filterEgress); err != nil {
return fmt.Errorf("cannot attach ebpf object to filter egress: %w", err)
}
filterIngress := &netlink.BpfFilter{
FilterAttrs: netlink.FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: netlink.HANDLE_MIN_INGRESS,
Handle: netlink.MakeHandle(0, 1),
Protocol: unix.ETH_P_ALL,
Priority: 0,
},
Fd: c.bpf.bpfPrograms.TproxyWanIngress.FD(),
Name: consts.AppName + "_ingress",
DirectAction: true,
}
if err := netlink.FilterAdd(filterIngress); err != nil {
return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err)
}
return nil
}
2023-01-23 18:54:21 +07:00
func (c *ControlPlane) ListenAndServe(port uint16) (err error) {
// Listen.
listener, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(int(port)))
if err != nil {
return fmt.Errorf("listenTCP: %w", err)
}
defer listener.Close()
lConn, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.IP{0, 0, 0, 0},
Port: int(port),
})
if err != nil {
return fmt.Errorf("listenUDP: %w", err)
}
defer lConn.Close()
// Serve.
2023-01-30 22:21:00 +07:00
if err := c.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 := listener.Accept()
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
2023-01-23 18:54:21 +07:00
n, lAddrPort, err := lConn.ReadFromUDPAddrPort(buf[:])
if err != nil {
if !strings.Contains(err.Error(), "use of closed network connection") {
c.log.Errorf("ReadFromUDPAddrPort: %v, %v", lAddrPort.String(), err)
}
break
}
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])
go func(data []byte, lConn *net.UDPConn, lAddrPort netip.AddrPort, addrHdr *AddrHdr) {
if e := c.handlePkt(newBuf, lConn, lAddrPort, addrHdr); e != nil {
c.log.Warnln("handlePkt:", e)
}
pool.Put(newBuf)
}(newBuf, lConn, lAddrPort, addrHdr)
}
}()
<-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 err
}