fix: ip matching problem and add control plane direct

This commit is contained in:
mzz2017
2023-01-24 16:27:19 +08:00
parent 799cd006c0
commit 14b215752f
9 changed files with 63 additions and 47 deletions

View File

@ -10,8 +10,7 @@ As a successor of [v2rayA](https://github.com/v2rayA/v2rayA), dae abandoned v2ra
## TODO ## 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. 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. Routing performance optimization.
1. ... 1. ...

View File

@ -50,17 +50,17 @@ const (
type OutboundIndex uint8 type OutboundIndex uint8
const ( const (
OutboundDirect OutboundIndex = 0 OutboundDirect OutboundIndex = 0
OutboundControlPlaneRoute OutboundIndex = 0xFE OutboundControlPlaneDirect OutboundIndex = 0xFE
OutboundLogicalAnd OutboundIndex = 0xFF OutboundLogicalAnd OutboundIndex = 0xFF
) )
func (i OutboundIndex) String() string { func (i OutboundIndex) String() string {
switch i { switch i {
case OutboundDirect: case OutboundDirect:
return "direct" return "direct"
case OutboundControlPlaneRoute: case OutboundControlPlaneDirect:
return "<Control Plane Route>" return "<Control Plane Direct>"
case OutboundLogicalAnd: case OutboundLogicalAnd:
return "<AND>" return "<AND>"
default: default:

View File

@ -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: // TODO:
d, err := dialer.NewFromLink("socks5://localhost:1080#proxy") d, err := dialer.NewFromLink("socks5://localhost:1080#proxy")
if err != nil { if err != nil {
@ -158,6 +138,27 @@ retry_load:
outboundName2Id[o.Name] = uint8(i) outboundName2Id[o.Name] = uint8(i)
} }
builder := NewRoutingMatcherBuilder(outboundName2Id, &bpf) 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 { if err := routing.ApplyMatcherBuilder(builder, rules, final); err != nil {
return nil, fmt.Errorf("ApplyMatcherBuilder: %w", err) return nil, fmt.Errorf("ApplyMatcherBuilder: %w", err)
} }

View File

@ -47,7 +47,7 @@
#define IPV6_MAX_EXTENSIONS 4 #define IPV6_MAX_EXTENSIONS 4
#define OUTBOUND_DIRECT 0 #define OUTBOUND_DIRECT 0
#define OUTBOUND_CONTROL_PLANE_ROUTE 0xFE #define OUTBOUND_CONTROL_PLANE_DIRECT 0xFE
#define OUTBOUND_LOGICAL_AND 0xFF #define OUTBOUND_LOGICAL_AND 0xFF
enum { 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_dport = bpf_ntohs(((struct udphdr *)l4_hdr)->dest);
h_sport = bpf_ntohs(((struct udphdr *)l4_hdr)->source); 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; 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_saddr.trie_key.prefixlen = IPV6_BYTE_LENGTH * 8;
lpm_key_daddr.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. /// this branch will never hit.
// if (domain_routing && domain_routing->epoch != *epoch) { // if (domain_routing && domain_routing->epoch != *epoch) {
// // Dirty (epoch dismatch) traffic should be routed by the control plane. // // Dirty (epoch dismatch) traffic should be routed by the control plane.
// return OUTBOUND_CONTROL_PLANE_ROUTE; // return OUTBOUND_CONTROL_PLANE_DIRECT;
// } // }
#pragma unroll #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. /// NOTICE: switch is not implemented efficiently by clang yet.
if (likely(routing->type == ROUTING_TYPE_IP_SET)) { if (likely(routing->type == ROUTING_TYPE_IP_SET)) {
lpm_key = &lpm_key_saddr; lpm_key = &lpm_key_daddr;
goto lookup_lpm; goto lookup_lpm;
} else if (routing->type == ROUTING_TYPE_SOURCE_IP_SET) { } else if (routing->type == ROUTING_TYPE_SOURCE_IP_SET) {
lpm_key = &lpm_key_daddr; lpm_key = &lpm_key_saddr;
lookup_lpm: lookup_lpm:
lpm = bpf_map_lookup_elem(&lpm_array_map, &routing->index); lpm = bpf_map_lookup_elem(&lpm_array_map, &routing->index);
if (unlikely(!lpm)) { 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)) { if (!bpf_map_lookup_elem(lpm, lpm_key)) {
// Routing not hit. // Routing not hit.
bad_rule = true; 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) { } else if (routing->type == ROUTING_TYPE_DOMAIN_SET) {
// Bottleneck of insns limit. // 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; lpm_key = &lpm_key_mac;
goto lookup_lpm; goto lookup_lpm;
} else if (routing->type == ROUTING_TYPE_FINAL) { } else if (routing->type == ROUTING_TYPE_FINAL) {
return routing->outbound; // Redirect all DNS packet to control plane.
bad_rule = false;
} else { } else {
return -EINVAL; return -EINVAL;
} }
@ -852,11 +852,18 @@ static long routing(__u8 flag[2], void *l4_hdr, __be32 saddr[4],
// Tail of a rule (line). // Tail of a rule (line).
// Decide whether to hit. // Decide whether to hit.
if (!bad_rule) { 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; return routing->outbound;
} }
bad_rule = false; bad_rule = false;
} }
} }
bpf_printk(
"Did coder forget to sync common/consts/ebpf.go with enum ROUTING_TYPE?");
return -EPERM; return -EPERM;
#undef _network #undef _network
#undef _ip_version #undef _ip_version

View File

@ -7,10 +7,10 @@ package control
import ( import (
"fmt" "fmt"
"github.com/cilium/ebpf"
"github.com/v2rayA/dae/common" "github.com/v2rayA/dae/common"
"github.com/v2rayA/dae/common/consts" "github.com/v2rayA/dae/common/consts"
"github.com/v2rayA/dae/component/routing" "github.com/v2rayA/dae/component/routing"
"github.com/cilium/ebpf"
"net/netip" "net/netip"
"strconv" "strconv"
) )
@ -88,6 +88,9 @@ func (b *RoutingMatcherBuilder) AddIp(values []netip.Prefix, outbound string) {
} }
func (b *RoutingMatcherBuilder) AddFinal(outbound string) { func (b *RoutingMatcherBuilder) AddFinal(outbound string) {
if b.err != nil {
return
}
b.Final = outbound b.Final = outbound
b.rules = append(b.rules, bpfRouting{ b.rules = append(b.rules, bpfRouting{
Type: uint8(consts.RoutingType_Final), Type: uint8(consts.RoutingType_Final),

View File

@ -37,11 +37,10 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) {
switch consts.OutboundIndex(value.Outbound) { switch consts.OutboundIndex(value.Outbound) {
case consts.OutboundDirect: case consts.OutboundDirect:
case consts.OutboundControlPlaneRoute: case consts.OutboundControlPlaneDirect:
// FIXME: check and re-route.
value.Outbound = uint8(consts.OutboundDirect) value.Outbound = uint8(consts.OutboundDirect)
c.log.Debugf("outbound: %v => %v", c.log.Debugf("outbound: %v => %v",
consts.OutboundControlPlaneRoute.String(), consts.OutboundControlPlaneDirect.String(),
consts.OutboundIndex(value.Outbound).String(), consts.OutboundIndex(value.Outbound).String(),
) )
default: default:

View File

@ -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) { func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort netip.AddrPort, addrHdr *AddrHdr) (err error) {
switch consts.OutboundIndex(addrHdr.Outbound) { switch consts.OutboundIndex(addrHdr.Outbound) {
case consts.OutboundDirect: case consts.OutboundDirect:
case consts.OutboundControlPlaneRoute: case consts.OutboundControlPlaneDirect:
// FIXME: check and re-route.
addrHdr.Outbound = uint8(consts.OutboundDirect) addrHdr.Outbound = uint8(consts.OutboundDirect)
c.log.Debugf("outbound: %v => %v", c.log.Debugf("outbound: %v => %v",
consts.OutboundControlPlaneRoute.String(), consts.OutboundControlPlaneDirect.String(),
consts.OutboundIndex(addrHdr.Outbound).String(), consts.OutboundIndex(addrHdr.Outbound).String(),
) )
default: default:

View File

@ -6,8 +6,8 @@
package control package control
import ( import (
"github.com/v2rayA/dae/common"
"github.com/cilium/ebpf" "github.com/cilium/ebpf"
"github.com/v2rayA/dae/common"
"net/netip" "net/netip"
) )
@ -17,7 +17,13 @@ type bpfLpmKey struct {
} }
func (o *bpfObjects) NewLpmMap(keys []bpfLpmKey, values []uint32) (m *ebpf.Map, err error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -35,10 +41,10 @@ func swap16(a uint16) uint16 {
func cidrToBpfLpmKey(prefix netip.Prefix) bpfLpmKey { func cidrToBpfLpmKey(prefix netip.Prefix) bpfLpmKey {
bits := prefix.Bits() bits := prefix.Bits()
ip := prefix.Addr().As16()
if prefix.Addr().Is4() { if prefix.Addr().Is4() {
bits += 96 bits += 96
} }
ip := prefix.Addr().As16()
return bpfLpmKey{ return bpfLpmKey{
PrefixLen: uint32(bits), PrefixLen: uint32(bits),
Data: common.Ipv6ByteSliceToUint32Array(ip[:]), Data: common.Ipv6ByteSliceToUint32Array(ip[:]),

View File

@ -26,6 +26,8 @@ func main() {
log.Println("Running") log.Println("Running")
t, err := control.NewControlPlane(log, ` t, err := control.NewControlPlane(log, `
default:proxy default:proxy
ip(119.29.29.29) -> proxy
ip(223.5.5.5) -> direct
ip(geoip:cn) -> direct ip(geoip:cn) -> direct
domain(geosite:cn, domain:"ip.sb") -> 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 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