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"
2023-01-24 13:25:21 +07:00
"github.com/cilium/ebpf"
2023-01-31 17:08:38 +07:00
ciliumLink "github.com/cilium/ebpf/link"
2023-01-24 13:25:21 +07:00
"github.com/cilium/ebpf/rlimit"
2023-01-29 12:38:15 +07:00
"github.com/mzz2017/softwind/pool"
2023-01-24 13:25:21 +07:00
"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"
2023-01-29 06:31:52 +07:00
"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
2023-02-01 00:34:50 +07:00
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 ,
2023-02-01 14:16:25 +07:00
bindLan bool ,
bindWan bool ,
2023-01-28 00:50:21 +07:00
) ( c * ControlPlane , err error ) {
2023-02-01 00:34:50 +07:00
kernelVersion , e := internal . KernelVersion ( )
if e != nil {
return nil , fmt . Errorf ( "failed to get kernel version: %w" , e )
}
2023-02-01 14:16:25 +07:00
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-02-01 00:34:50 +07:00
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
2023-01-29 06:31:52 +07:00
var ProgramOptions ebpf . ProgramOptions
if log . IsLevelEnabled ( logrus . TraceLevel ) {
ProgramOptions = ebpf . ProgramOptions {
LogLevel : ebpf . LogLevelStats ,
}
}
2023-02-01 14:16:25 +07:00
var obj interface { } = & bpf // Bind both LAN and WAN.
if bindLan && ! bindWan {
2023-02-01 00:34:50 +07:00
// Trick. Replace the beams with rotten timbers.
2023-02-01 09:59:57 +07:00
obj = & bpfObjectsLan { }
2023-02-01 14:16:25 +07:00
} else if ! bindLan && bindWan {
// Trick. Replace the beams with rotten timbers.
obj = & bpfObjectsWan { }
2023-02-01 00:34:50 +07:00
}
2023-01-27 01:10:27 +07:00
retryLoadBpf :
2023-02-01 00:34:50 +07:00
if err = loadBpfObjects ( obj , & ebpf . CollectionOptions {
2023-01-23 18:54:21 +07:00
Maps : ebpf . MapOptions {
PinPath : pinPath ,
} ,
2023-01-29 06:31:52 +07:00
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 "
2023-01-24 13:25:21 +07:00
_ , 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 )
}
2023-01-24 13:25:21 +07:00
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)
2023-01-29 06:31:52 +07:00
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" ) )
}
}
}
}
2023-02-01 20:15:58 +07:00
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 {
2023-02-01 00:34:50 +07:00
// Reverse takeover.
2023-02-01 09:59:57 +07:00
AssignBpfObjects ( & bpf , obj )
2023-02-01 00:34:50 +07:00
}
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
}
2023-01-31 17:08:38 +07:00
// 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 )
2023-01-24 15:27:19 +07:00
// Routing.
2023-01-28 00:50:21 +07:00
var rules [ ] * config_parser . RoutingRule
if rules , err = routing . ApplyRulesOptimizers ( routingA . Rules ,
2023-01-24 15:27:19 +07:00
& 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-24 15:27:19 +07:00
}
2023-01-29 10:19:58 +07:00
if log . IsLevelEnabled ( logrus . DebugLevel ) {
2023-01-24 15:27:19 +07:00
var debugBuilder strings . Builder
for _ , rule := range rules {
2023-01-28 00:50:21 +07:00
debugBuilder . WriteString ( rule . String ( true ) + "\n" )
2023-01-24 15:27:19 +07:00
}
2023-01-29 10:19:58 +07:00
log . Debugf ( "RoutingA:\n%vfinal: %v\n" , debugBuilder . String ( ) , routingA . Final )
2023-01-24 15:27:19 +07:00
}
2023-01-31 18:33:53 +07:00
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 ,
2023-02-01 00:34:50 +07:00
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
}
2023-01-31 17:08:38 +07:00
// 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 {
2023-02-01 09:24:11 +07:00
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
}