From 14b215752fef2d62324b47360fb24cf96e8cb92e Mon Sep 17 00:00:00 2001 From: mzz2017 <2017@duck.com> Date: Tue, 24 Jan 2023 16:27:19 +0800 Subject: [PATCH] fix: ip matching problem and add control plane direct --- README.md | 5 +-- common/consts/ebpf.go | 10 ++--- component/control/control_plane.go | 41 ++++++++++---------- component/control/kern/tproxy.c | 25 +++++++----- component/control/routing_matcher_builder.go | 5 ++- component/control/tcp.go | 5 +-- component/control/udp.go | 5 +-- component/control/utils.go | 12 ++++-- main.go | 2 + 9 files changed, 63 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 87bc333..e73a1eb 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,7 @@ As a successor of [v2rayA](https://github.com/v2rayA/v2rayA), dae abandoned v2ra ## TODO -1. Control plane does support MAC and other matching yet. +1. Control plane does not support MAC and other matching yet. 1. Dns upstream. Check dns upstream and source loop (whether upstream is also a client of us) and remind user to add source rule. -1. Control plane route. 1. Routing performance optimization. -1. ... \ No newline at end of file +1. ... diff --git a/common/consts/ebpf.go b/common/consts/ebpf.go index a69c17f..981e1ca 100644 --- a/common/consts/ebpf.go +++ b/common/consts/ebpf.go @@ -50,17 +50,17 @@ const ( type OutboundIndex uint8 const ( - OutboundDirect OutboundIndex = 0 - OutboundControlPlaneRoute OutboundIndex = 0xFE - OutboundLogicalAnd OutboundIndex = 0xFF + OutboundDirect OutboundIndex = 0 + OutboundControlPlaneDirect OutboundIndex = 0xFE + OutboundLogicalAnd OutboundIndex = 0xFF ) func (i OutboundIndex) String() string { switch i { case OutboundDirect: return "direct" - case OutboundControlPlaneRoute: - return "" + case OutboundControlPlaneDirect: + return "" case OutboundLogicalAnd: return "" default: diff --git a/component/control/control_plane.go b/component/control/control_plane.go index 2532a92..a4134a4 100644 --- a/component/control/control_plane.go +++ b/component/control/control_plane.go @@ -111,26 +111,6 @@ retry_load: //} /**/ - - rules, final, err := routing.Parse(routingA) - if err != nil { - return nil, fmt.Errorf("routingA error:\n%w", err) - } - if rules, err = routing.ApplyRulesOptimizers(rules, - &routing.RefineFunctionParamKeyOptimizer{}, - &routing.DatReaderOptimizer{Logger: log}, - &routing.MergeAndSortRulesOptimizer{}, - &routing.DeduplicateParamsOptimizer{}, - ); err != nil { - return nil, fmt.Errorf("ApplyRulesOptimizers error: \n %w", err) - } - if log.IsLevelEnabled(logrus.TraceLevel) { - var debugBuilder strings.Builder - for _, rule := range rules { - debugBuilder.WriteString(rule.String(true)) - } - log.Tracef("RoutingA:\n%vfinal: %v\n", debugBuilder.String(), final) - } // TODO: d, err := dialer.NewFromLink("socks5://localhost:1080#proxy") if err != nil { @@ -158,6 +138,27 @@ retry_load: outboundName2Id[o.Name] = uint8(i) } builder := NewRoutingMatcherBuilder(outboundName2Id, &bpf) + + // Routing. + rules, final, err := routing.Parse(routingA) + if err != nil { + return nil, fmt.Errorf("routingA error:\n%w", err) + } + if rules, err = routing.ApplyRulesOptimizers(rules, + &routing.RefineFunctionParamKeyOptimizer{}, + &routing.DatReaderOptimizer{Logger: log}, + &routing.MergeAndSortRulesOptimizer{}, + &routing.DeduplicateParamsOptimizer{}, + ); err != nil { + return nil, fmt.Errorf("ApplyRulesOptimizers error: \n %w", err) + } + if log.IsLevelEnabled(logrus.TraceLevel) { + var debugBuilder strings.Builder + for _, rule := range rules { + debugBuilder.WriteString(rule.String(true)) + } + log.Tracef("RoutingA:\n%vfinal: %v\n", debugBuilder.String(), final) + } if err := routing.ApplyMatcherBuilder(builder, rules, final); err != nil { return nil, fmt.Errorf("ApplyMatcherBuilder: %w", err) } diff --git a/component/control/kern/tproxy.c b/component/control/kern/tproxy.c index dbb9298..619bcda 100644 --- a/component/control/kern/tproxy.c +++ b/component/control/kern/tproxy.c @@ -47,7 +47,7 @@ #define IPV6_MAX_EXTENSIONS 4 #define OUTBOUND_DIRECT 0 -#define OUTBOUND_CONTROL_PLANE_ROUTE 0xFE +#define OUTBOUND_CONTROL_PLANE_DIRECT 0xFE #define OUTBOUND_LOGICAL_AND 0xFF enum { @@ -753,10 +753,6 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4], h_dport = bpf_ntohs(((struct udphdr *)l4_hdr)->dest); h_sport = bpf_ntohs(((struct udphdr *)l4_hdr)->source); } - // Redirect all DNS packet to control plane. - if (_network == NETWORK_TYPE_UDP && h_dport == 53) { - return OUTBOUND_CONTROL_PLANE_ROUTE; - } struct lpm_key lpm_key_saddr, lpm_key_daddr, lpm_key_mac, *lpm_key; lpm_key_saddr.trie_key.prefixlen = IPV6_BYTE_LENGTH * 8; lpm_key_daddr.trie_key.prefixlen = IPV6_BYTE_LENGTH * 8; @@ -775,7 +771,7 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4], /// this branch will never hit. // if (domain_routing && domain_routing->epoch != *epoch) { // // Dirty (epoch dismatch) traffic should be routed by the control plane. - // return OUTBOUND_CONTROL_PLANE_ROUTE; + // return OUTBOUND_CONTROL_PLANE_DIRECT; // } #pragma unroll @@ -790,10 +786,10 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4], } /// NOTICE: switch is not implemented efficiently by clang yet. if (likely(routing->type == ROUTING_TYPE_IP_SET)) { - lpm_key = &lpm_key_saddr; + lpm_key = &lpm_key_daddr; goto lookup_lpm; } else if (routing->type == ROUTING_TYPE_SOURCE_IP_SET) { - lpm_key = &lpm_key_daddr; + lpm_key = &lpm_key_saddr; lookup_lpm: lpm = bpf_map_lookup_elem(&lpm_array_map, &routing->index); if (unlikely(!lpm)) { @@ -802,6 +798,9 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4], if (!bpf_map_lookup_elem(lpm, lpm_key)) { // Routing not hit. bad_rule = true; + bpf_printk("index: %u not hit", routing->index); + } else { + bpf_printk("index: %u hit", routing->index); } } else if (routing->type == ROUTING_TYPE_DOMAIN_SET) { // Bottleneck of insns limit. @@ -842,7 +841,8 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4], lpm_key = &lpm_key_mac; goto lookup_lpm; } else if (routing->type == ROUTING_TYPE_FINAL) { - return routing->outbound; + // Redirect all DNS packet to control plane. + bad_rule = false; } else { return -EINVAL; } @@ -852,11 +852,18 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4], // Tail of a rule (line). // Decide whether to hit. if (!bad_rule) { + if (routing->outbound == OUTBOUND_DIRECT && h_dport == 53 && + _network == NETWORK_TYPE_UDP) { + // DNS packet should go through control plane. + return OUTBOUND_CONTROL_PLANE_DIRECT; + } return routing->outbound; } bad_rule = false; } } + bpf_printk( + "Did coder forget to sync common/consts/ebpf.go with enum ROUTING_TYPE?"); return -EPERM; #undef _network #undef _ip_version diff --git a/component/control/routing_matcher_builder.go b/component/control/routing_matcher_builder.go index c958d9c..1d78723 100644 --- a/component/control/routing_matcher_builder.go +++ b/component/control/routing_matcher_builder.go @@ -7,10 +7,10 @@ package control import ( "fmt" + "github.com/cilium/ebpf" "github.com/v2rayA/dae/common" "github.com/v2rayA/dae/common/consts" "github.com/v2rayA/dae/component/routing" - "github.com/cilium/ebpf" "net/netip" "strconv" ) @@ -88,6 +88,9 @@ func (b *RoutingMatcherBuilder) AddIp(values []netip.Prefix, outbound string) { } func (b *RoutingMatcherBuilder) AddFinal(outbound string) { + if b.err != nil { + return + } b.Final = outbound b.rules = append(b.rules, bpfRouting{ Type: uint8(consts.RoutingType_Final), diff --git a/component/control/tcp.go b/component/control/tcp.go index d4228a7..7058c08 100644 --- a/component/control/tcp.go +++ b/component/control/tcp.go @@ -37,11 +37,10 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) { switch consts.OutboundIndex(value.Outbound) { case consts.OutboundDirect: - case consts.OutboundControlPlaneRoute: - // FIXME: check and re-route. + case consts.OutboundControlPlaneDirect: value.Outbound = uint8(consts.OutboundDirect) c.log.Debugf("outbound: %v => %v", - consts.OutboundControlPlaneRoute.String(), + consts.OutboundControlPlaneDirect.String(), consts.OutboundIndex(value.Outbound).String(), ) default: diff --git a/component/control/udp.go b/component/control/udp.go index 12f4e00..977a322 100644 --- a/component/control/udp.go +++ b/component/control/udp.go @@ -91,12 +91,11 @@ func (c *ControlPlane) RelayToUDP(lConn *net.UDPConn, to netip.AddrPort, isDNS b func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort netip.AddrPort, addrHdr *AddrHdr) (err error) { switch consts.OutboundIndex(addrHdr.Outbound) { case consts.OutboundDirect: - case consts.OutboundControlPlaneRoute: - // FIXME: check and re-route. + case consts.OutboundControlPlaneDirect: addrHdr.Outbound = uint8(consts.OutboundDirect) c.log.Debugf("outbound: %v => %v", - consts.OutboundControlPlaneRoute.String(), + consts.OutboundControlPlaneDirect.String(), consts.OutboundIndex(addrHdr.Outbound).String(), ) default: diff --git a/component/control/utils.go b/component/control/utils.go index db7e271..8012444 100644 --- a/component/control/utils.go +++ b/component/control/utils.go @@ -6,8 +6,8 @@ package control import ( - "github.com/v2rayA/dae/common" "github.com/cilium/ebpf" + "github.com/v2rayA/dae/common" "net/netip" ) @@ -17,7 +17,13 @@ type bpfLpmKey struct { } func (o *bpfObjects) NewLpmMap(keys []bpfLpmKey, values []uint32) (m *ebpf.Map, err error) { - m, err = o.UnusedLpmType.Clone() + m, err = ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.LPMTrie, + Flags: o.UnusedLpmType.Flags(), + MaxEntries: o.UnusedLpmType.MaxEntries(), + KeySize: o.UnusedLpmType.KeySize(), + ValueSize: o.UnusedLpmType.ValueSize(), + }) if err != nil { return nil, err } @@ -35,10 +41,10 @@ func swap16(a uint16) uint16 { func cidrToBpfLpmKey(prefix netip.Prefix) bpfLpmKey { bits := prefix.Bits() - ip := prefix.Addr().As16() if prefix.Addr().Is4() { bits += 96 } + ip := prefix.Addr().As16() return bpfLpmKey{ PrefixLen: uint32(bits), Data: common.Ipv6ByteSliceToUint32Array(ip[:]), diff --git a/main.go b/main.go index f65c60e..67a6c63 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,8 @@ func main() { log.Println("Running") t, err := control.NewControlPlane(log, ` default:proxy +ip(119.29.29.29) -> proxy +ip(223.5.5.5) -> direct ip(geoip:cn) -> direct domain(geosite:cn, domain:"ip.sb") -> direct ip("91.105.192.0/23","91.108.4.0/22","91.108.8.0/21","91.108.16.0/21","91.108.56.0/22","95.161.64.0/20","149.154.160.0/20","185.76.151.0/24")->proxy