feat: support must_xxx outbounds

This commit is contained in:
mzz2017 2023-04-02 11:07:53 +08:00
parent 3e55f85d91
commit 006b7fbfd2
18 changed files with 115 additions and 62 deletions

View File

@ -64,14 +64,13 @@ const (
OutboundDirect OutboundIndex = iota OutboundDirect OutboundIndex = iota
OutboundBlock OutboundBlock
OutboundMustDirect OutboundIndex = 0xFC
OutboundControlPlaneRouting OutboundIndex = 0xFD OutboundControlPlaneRouting OutboundIndex = 0xFD
OutboundLogicalOr OutboundIndex = 0xFE OutboundLogicalOr OutboundIndex = 0xFE
OutboundLogicalAnd OutboundIndex = 0xFF OutboundLogicalAnd OutboundIndex = 0xFF
OutboundLogicalMask OutboundIndex = 0xFE OutboundLogicalMask OutboundIndex = 0xFE
OutboundMax = OutboundLogicalAnd OutboundMax = OutboundLogicalAnd
OutboundUserDefinedMax = OutboundMustDirect - 1 OutboundUserDefinedMax = OutboundControlPlaneRouting - 1
) )
func (i OutboundIndex) String() string { func (i OutboundIndex) String() string {
@ -80,8 +79,6 @@ func (i OutboundIndex) String() string {
return "direct" return "direct"
case OutboundBlock: case OutboundBlock:
return "block" return "block"
case OutboundMustDirect:
return "must_direct"
case OutboundControlPlaneRouting: case OutboundControlPlaneRouting:
return "<Control Plane Routing>" return "<Control Plane Routing>"
case OutboundLogicalOr: case OutboundLogicalOr:

View File

@ -112,6 +112,12 @@ func (b *RequestMatcherBuilder) addFallback(fallbackOutbound config.FunctionOrSt
if err != nil { if err != nil {
return err return err
} }
if upstream.Must {
return fmt.Errorf("unsupported param: must")
}
if upstream.Mark != 0 {
return fmt.Errorf("unsupported param: mark")
}
upstreamId, err := b.upstreamToId(upstream.Name) upstreamId, err := b.upstreamToId(upstream.Name)
if err != nil { if err != nil {
return err return err

View File

@ -195,6 +195,12 @@ func (b *ResponseMatcherBuilder) addFallback(fallbackOutbound config.FunctionOrS
if err != nil { if err != nil {
return err return err
} }
if upstream.Must {
return fmt.Errorf("unsupported param: must")
}
if upstream.Mark != 0 {
return fmt.Errorf("unsupported param: mark")
}
upstreamId, err := b.upstreamToId(upstream.Name) upstreamId, err := b.upstreamToId(upstream.Name)
if err != nil { if err != nil {
return err return err

View File

@ -7,11 +7,11 @@ package domain_matcher
import ( import (
"fmt" "fmt"
"github.com/sirupsen/logrus"
"github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/component/routing" "github.com/daeuniverse/dae/component/routing"
"github.com/daeuniverse/dae/config" "github.com/daeuniverse/dae/config"
"github.com/daeuniverse/dae/pkg/config_parser" "github.com/daeuniverse/dae/pkg/config_parser"
"github.com/sirupsen/logrus"
"hash/fnv" "hash/fnv"
"math/rand" "math/rand"
"reflect" "reflect"
@ -132,7 +132,7 @@ func getDomain() (simulatedDomainSet []routing.DomainSet, err error) {
sections, err := config_parser.Parse(` sections, err := config_parser.Parse(`
routing { routing {
domain(geosite:bing)->us domain(geosite:bing)->us
domain(full:dns.google) -> direct domain(full:dns.google.com) -> direct
domain(geosite:category-ads-all) -> block domain(geosite:category-ads-all) -> block
domain(geosite:cn) -> direct domain(geosite:cn) -> direct
}`) }`)

View File

@ -7,9 +7,9 @@ package routing
import ( import (
"fmt" "fmt"
"github.com/sirupsen/logrus"
"github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/pkg/config_parser" "github.com/daeuniverse/dae/pkg/config_parser"
"github.com/sirupsen/logrus"
"strconv" "strconv"
) )
@ -22,6 +22,7 @@ type DomainSet struct {
type Outbound struct { type Outbound struct {
Name string Name string
Mark uint32 Mark uint32
Must bool
} }
type RulesBuilder struct { type RulesBuilder struct {
@ -62,6 +63,7 @@ func (b *RulesBuilder) Apply(rules []*config_parser.RoutingRule) (err error) {
overrideOutbound := &Outbound{ overrideOutbound := &Outbound{
Name: consts.OutboundLogicalOr.String(), Name: consts.OutboundLogicalOr.String(),
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
} }
if jMatchSet == len(keyOrder)-1 { if jMatchSet == len(keyOrder)-1 {
overrideOutbound.Name = consts.OutboundLogicalAnd.String() overrideOutbound.Name = consts.OutboundLogicalAnd.String()
@ -103,6 +105,7 @@ func ParseOutbound(rawOutbound *config_parser.Function) (outbound *Outbound, err
outbound = &Outbound{ outbound = &Outbound{
Name: rawOutbound.Name, Name: rawOutbound.Name,
Mark: 0, Mark: 0,
Must: false,
} }
for _, p := range rawOutbound.Params { for _, p := range rawOutbound.Params {
switch p.Key { switch p.Key {
@ -113,8 +116,14 @@ func ParseOutbound(rawOutbound *config_parser.Function) (outbound *Outbound, err
return nil, fmt.Errorf("failed to parse mark: %v", err) return nil, fmt.Errorf("failed to parse mark: %v", err)
} }
outbound.Mark = uint32(_mark) outbound.Mark = uint32(_mark)
case "":
if p.Val == "must" {
outbound.Must = true
} else {
return nil, fmt.Errorf("unknown outbound param: %v", p.Val)
}
default: default:
return nil, fmt.Errorf("unknown outbound param: %v", p.Key) return nil, fmt.Errorf("unknown outbound param key: %v", p.Key)
} }
} }
return outbound, nil return outbound, nil

View File

@ -18,7 +18,7 @@ type Global struct {
// We use DirectTcpCheckUrl to check (tcp)*(ipv4/ipv6) connectivity for direct. // We use DirectTcpCheckUrl to check (tcp)*(ipv4/ipv6) connectivity for direct.
//DirectTcpCheckUrl string `mapstructure:"direct_tcp_check_url" default:"http://www.qualcomm.cn/generate_204"` //DirectTcpCheckUrl string `mapstructure:"direct_tcp_check_url" default:"http://www.qualcomm.cn/generate_204"`
TcpCheckUrl string `mapstructure:"tcp_check_url" default:"http://keep-alv.google.com/generate_204"` TcpCheckUrl string `mapstructure:"tcp_check_url" default:"http://keep-alv.google.com/generate_204"`
UdpCheckDns string `mapstructure:"udp_check_dns" default:"dns.google:53"` UdpCheckDns string `mapstructure:"udp_check_dns" default:"dns.google.com:53"`
CheckInterval time.Duration `mapstructure:"check_interval" default:"30s"` CheckInterval time.Duration `mapstructure:"check_interval" default:"30s"`
CheckTolerance time.Duration `mapstructure:"check_tolerance" default:"0"` CheckTolerance time.Duration `mapstructure:"check_tolerance" default:"0"`
LanInterface []string `mapstructure:"lan_interface"` LanInterface []string `mapstructure:"lan_interface"`

View File

@ -7,12 +7,15 @@ package config
import ( import (
"github.com/daeuniverse/dae/common/consts" "github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/pkg/config_parser"
"strings"
) )
type patch func(params *Config) error type patch func(params *Config) error
var patches = []patch{ var patches = []patch{
patchEmptyDns, patchEmptyDns,
patchMustOutbound,
} }
func patchEmptyDns(params *Config) error { func patchEmptyDns(params *Config) error {
@ -24,3 +27,22 @@ func patchEmptyDns(params *Config) error {
} }
return nil return nil
} }
func patchMustOutbound(params *Config) error {
for i := range params.Routing.Rules {
if strings.HasPrefix(params.Routing.Rules[i].Outbound.Name, "must_") {
params.Routing.Rules[i].Outbound.Name = strings.TrimPrefix(params.Routing.Rules[i].Outbound.Name, "must_")
params.Routing.Rules[i].Outbound.Params = append(params.Routing.Rules[i].Outbound.Params, &config_parser.Param{
Val: "must",
})
}
}
if f := FunctionOrStringToFunction(params.Routing.Fallback); strings.HasPrefix(f.Name, "must_") {
f.Name = strings.TrimPrefix(f.Name, "must_")
f.Params = append(f.Params, &config_parser.Param{
Val: "must",
})
params.Routing.Fallback = f
}
return nil
}

View File

@ -757,10 +757,6 @@ func (c *ControlPlane) chooseBestDnsDialer(
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Already "must direct".
if outboundIndex == consts.OutboundMustDirect {
outboundIndex = consts.OutboundDirect
}
if int(outboundIndex) >= len(c.outbounds) { if int(outboundIndex) >= len(c.outbounds) {
return nil, fmt.Errorf("bad outbound index: %v", outboundIndex) return nil, fmt.Errorf("bad outbound index: %v", outboundIndex)
} }

View File

@ -60,7 +60,6 @@
#define OUTBOUND_DIRECT 0 #define OUTBOUND_DIRECT 0
#define OUTBOUND_BLOCK 1 #define OUTBOUND_BLOCK 1
#define OUTBOUND_MUST_DIRECT 0xFC
#define OUTBOUND_CONTROL_PLANE_ROUTING 0xFD #define OUTBOUND_CONTROL_PLANE_ROUTING 0xFD
#define OUTBOUND_LOGICAL_OR 0xFE #define OUTBOUND_LOGICAL_OR 0xFE
#define OUTBOUND_LOGICAL_AND 0xFF #define OUTBOUND_LOGICAL_AND 0xFF
@ -135,6 +134,7 @@ struct ip_port {
struct routing_result { struct routing_result {
__u32 mark; __u32 mark;
__u8 must;
__u8 mac[6]; __u8 mac[6];
__u8 outbound; __u8 outbound;
__u8 pname[TASK_COMM_LEN]; __u8 pname[TASK_COMM_LEN];
@ -307,7 +307,7 @@ struct match_set {
bool not ; // A subrule flag (this is not a match_set flag). bool not ; // A subrule flag (this is not a match_set flag).
enum MatchType type; enum MatchType type;
__u8 outbound; // User-defined value range is [0, 252]. __u8 outbound; // User-defined value range is [0, 252].
__u8 unused; bool must;
__u32 mark; __u32 mark;
}; };
struct { struct {
@ -1133,11 +1133,13 @@ routing(const __u32 flag[6], const void *l4hdr, const __be32 saddr[4],
// DNS requests should routed by control plane if outbound is not // DNS requests should routed by control plane if outbound is not
// must_direct. // must_direct.
if (match_set->outbound != OUTBOUND_MUST_DIRECT && h_dport == 53 && if (!match_set->must && h_dport == 53 &&
_l4proto_type == L4ProtoType_UDP) { _l4proto_type == L4ProtoType_UDP) {
return OUTBOUND_CONTROL_PLANE_ROUTING | (match_set->mark << 8); return (__s64)OUTBOUND_CONTROL_PLANE_ROUTING |
((__s64)match_set->mark << 8) | ((__s64)match_set->must << 40);
} }
return match_set->outbound | (match_set->mark << 8); return (__s64)match_set->outbound | ((__s64)match_set->mark << 8) |
((__s64)match_set->must << 40);
} }
bad_rule = false; bad_rule = false;
} }
@ -1325,14 +1327,16 @@ new_connection:
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) + bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
(ethh.h_source[4] << 8) + (ethh.h_source[5])), (ethh.h_source[4] << 8) + (ethh.h_source[5])),
}; };
if ((ret = routing(flag, l4hdr, tuples.sip.u6_addr32, tuples.dip.u6_addr32, __s64 s64_ret;
mac)) < 0) { if ((s64_ret = routing(flag, l4hdr, tuples.sip.u6_addr32,
bpf_printk("shot routing: %d", ret); tuples.dip.u6_addr32, mac)) < 0) {
bpf_printk("shot routing: %d", s64_ret);
return TC_ACT_SHOT; return TC_ACT_SHOT;
} }
struct routing_result routing_result = {0}; struct routing_result routing_result = {0};
routing_result.outbound = ret; routing_result.outbound = s64_ret;
routing_result.mark = ret >> 8; routing_result.mark = s64_ret >> 8;
routing_result.must = (s64_ret >> 40) & 1;
__builtin_memcpy(routing_result.mac, ethh.h_source, __builtin_memcpy(routing_result.mac, ethh.h_source,
sizeof(routing_result.mac)); sizeof(routing_result.mac));
/// NOTICE: No pid pname info for LAN packet. /// NOTICE: No pid pname info for LAN packet.
@ -1361,8 +1365,7 @@ new_connection:
bpf_ntohs(tuples.dport)); bpf_ntohs(tuples.dport));
} }
#endif #endif
if (routing_result.outbound == OUTBOUND_DIRECT || if (routing_result.outbound == OUTBOUND_DIRECT) {
routing_result.outbound == OUTBOUND_MUST_DIRECT) {
skb->mark = routing_result.mark; skb->mark = routing_result.mark;
goto direct; goto direct;
} else if (unlikely(routing_result.outbound == OUTBOUND_BLOCK)) { } else if (unlikely(routing_result.outbound == OUTBOUND_BLOCK)) {
@ -1593,6 +1596,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
__builtin_memcpy(&key_src.ip, &tuples.dip, IPV6_BYTE_LENGTH); __builtin_memcpy(&key_src.ip, &tuples.dip, IPV6_BYTE_LENGTH);
key_src.port = tcph.source; key_src.port = tcph.source;
__u8 outbound; __u8 outbound;
bool must;
__u32 mark; __u32 mark;
struct pid_pname *pid_pname = NULL; struct pid_pname *pid_pname = NULL;
if (unlikely(tcp_state_syn)) { if (unlikely(tcp_state_syn)) {
@ -1618,14 +1622,16 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) + bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
(ethh.h_source[4] << 8) + (ethh.h_source[5])), (ethh.h_source[4] << 8) + (ethh.h_source[5])),
}; };
if ((ret = routing(flag, &tcph, tuples.sip.u6_addr32, __s64 s64_ret;
tuples.dip.u6_addr32, mac)) < 0) { if ((s64_ret = routing(flag, &tcph, tuples.sip.u6_addr32,
bpf_printk("shot routing: %d", ret); tuples.dip.u6_addr32, mac)) < 0) {
bpf_printk("shot routing: %d", s64_ret);
return TC_ACT_SHOT; return TC_ACT_SHOT;
} }
outbound = ret; outbound = s64_ret;
mark = ret >> 8; mark = s64_ret >> 8;
must = (s64_ret >> 40) & 1;
#if defined(__DEBUG_ROUTING) || defined(__PRINT_ROUTING_RESULT) #if defined(__DEBUG_ROUTING) || defined(__PRINT_ROUTING_RESULT)
// Print only new connection. // Print only new connection.
@ -1646,9 +1652,10 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
} }
outbound = dst->routing_result.outbound; outbound = dst->routing_result.outbound;
mark = dst->routing_result.mark; mark = dst->routing_result.mark;
must = dst->routing_result.must;
} }
if ((outbound == OUTBOUND_DIRECT || outbound == OUTBOUND_MUST_DIRECT) && if (outbound == OUTBOUND_DIRECT &&
mark == 0 // If mark is not zero, we should re-route it, so we send it mark == 0 // If mark is not zero, we should re-route it, so we send it
// to control plane in WAN. // to control plane in WAN.
) { ) {
@ -1678,6 +1685,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
routing_info.port = tcph.dest; routing_info.port = tcph.dest;
routing_info.routing_result.outbound = outbound; routing_info.routing_result.outbound = outbound;
routing_info.routing_result.mark = mark; routing_info.routing_result.mark = mark;
routing_info.routing_result.must = must;
__builtin_memcpy(routing_info.routing_result.mac, ethh.h_source, __builtin_memcpy(routing_info.routing_result.mac, ethh.h_source,
sizeof(ethh.h_source)); sizeof(ethh.h_source));
if (pid_pname) { if (pid_pname) {
@ -1726,9 +1734,10 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) + bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) +
(ethh.h_source[4] << 8) + (ethh.h_source[5])), (ethh.h_source[4] << 8) + (ethh.h_source[5])),
}; };
if ((ret = routing(flag, &udph, tuples.sip.u6_addr32, __s64 s64_ret;
tuples.dip.u6_addr32, mac)) < 0) { if ((s64_ret = routing(flag, &udph, tuples.sip.u6_addr32,
bpf_printk("shot routing: %d", ret); tuples.dip.u6_addr32, mac)) < 0) {
bpf_printk("shot routing: %d", s64_ret);
return TC_ACT_SHOT; return TC_ACT_SHOT;
} }
// Construct new hdr to encap. // Construct new hdr to encap.
@ -1736,8 +1745,9 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
__builtin_memset(&new_hdr, 0, sizeof(new_hdr)); __builtin_memset(&new_hdr, 0, sizeof(new_hdr));
__builtin_memcpy(new_hdr.ip, &tuples.dip, IPV6_BYTE_LENGTH); __builtin_memcpy(new_hdr.ip, &tuples.dip, IPV6_BYTE_LENGTH);
new_hdr.port = udph.dest; new_hdr.port = udph.dest;
new_hdr.routing_result.outbound = ret; new_hdr.routing_result.outbound = s64_ret;
new_hdr.routing_result.mark = ret >> 8; new_hdr.routing_result.mark = s64_ret >> 8;
new_hdr.routing_result.must = (s64_ret >> 40) & 1;
__builtin_memcpy(new_hdr.routing_result.mac, ethh.h_source, __builtin_memcpy(new_hdr.routing_result.mac, ethh.h_source,
sizeof(ethh.h_source)); sizeof(ethh.h_source));
if (pid_pname) { if (pid_pname) {
@ -1754,8 +1764,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
bpf_ntohs(tuples.dport)); bpf_ntohs(tuples.dport));
#endif #endif
if ((new_hdr.routing_result.outbound == OUTBOUND_DIRECT || if (new_hdr.routing_result.outbound == OUTBOUND_DIRECT &&
new_hdr.routing_result.outbound == OUTBOUND_MUST_DIRECT) &&
new_hdr.routing_result.mark == new_hdr.routing_result.mark ==
0 // If mark is not zero, we should re-route it, so we 0 // If mark is not zero, we should re-route it, so we
// send it to control plane in WAN. // send it to control plane in WAN.

View File

@ -56,8 +56,6 @@ func NewRoutingMatcherBuilder(log *logrus.Logger, rules []*config_parser.Routing
func (b *RoutingMatcherBuilder) outboundToId(outbound string) (uint8, error) { func (b *RoutingMatcherBuilder) outboundToId(outbound string) (uint8, error) {
var outboundId uint8 var outboundId uint8
switch outbound { switch outbound {
case consts.OutboundMustDirect.String():
outboundId = uint8(consts.OutboundMustDirect)
case consts.OutboundLogicalOr.String(): case consts.OutboundLogicalOr.String():
outboundId = uint8(consts.OutboundLogicalOr) outboundId = uint8(consts.OutboundLogicalOr)
case consts.OutboundLogicalAnd.String(): case consts.OutboundLogicalAnd.String():
@ -95,6 +93,7 @@ func (b *RoutingMatcherBuilder) addDomain(f *config_parser.Function, key string,
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
}) })
return nil return nil
} }
@ -119,6 +118,7 @@ func (b *RoutingMatcherBuilder) addSourceMac(f *config_parser.Function, macAddrs
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
} }
binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex)) binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex))
b.rules = append(b.rules, set) b.rules = append(b.rules, set)
@ -138,6 +138,7 @@ func (b *RoutingMatcherBuilder) addIp(f *config_parser.Function, values []netip.
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
} }
binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex)) binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex))
b.rules = append(b.rules, set) b.rules = append(b.rules, set)
@ -163,6 +164,7 @@ func (b *RoutingMatcherBuilder) addPort(f *config_parser.Function, values [][2]u
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
}) })
} }
return nil return nil
@ -181,6 +183,7 @@ func (b *RoutingMatcherBuilder) addSourceIp(f *config_parser.Function, values []
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
} }
binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex)) binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex))
b.rules = append(b.rules, set) b.rules = append(b.rules, set)
@ -206,6 +209,7 @@ func (b *RoutingMatcherBuilder) addSourcePort(f *config_parser.Function, values
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
}) })
} }
return nil return nil
@ -222,6 +226,7 @@ func (b *RoutingMatcherBuilder) addL4Proto(f *config_parser.Function, values con
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
}) })
return nil return nil
} }
@ -237,6 +242,7 @@ func (b *RoutingMatcherBuilder) addIpVersion(f *config_parser.Function, values c
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
}) })
return nil return nil
} }
@ -256,6 +262,7 @@ func (b *RoutingMatcherBuilder) addProcessName(f *config_parser.Function, values
Not: f.Not, Not: f.Not,
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
} }
copy(matchSet.Value[:], value[:]) copy(matchSet.Value[:], value[:])
b.rules = append(b.rules, matchSet) b.rules = append(b.rules, matchSet)
@ -276,6 +283,7 @@ func (b *RoutingMatcherBuilder) addFallback(fallbackOutbound config.FunctionOrSt
Type: uint8(consts.MatchType_Fallback), Type: uint8(consts.MatchType_Fallback),
Outbound: outboundId, Outbound: outboundId,
Mark: outbound.Mark, Mark: outbound.Mark,
Must: outbound.Must,
}) })
return nil return nil
} }

View File

@ -70,8 +70,6 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) {
switch outboundIndex { switch outboundIndex {
case consts.OutboundDirect: case consts.OutboundDirect:
case consts.OutboundMustDirect:
outboundIndex = consts.OutboundDirect
case consts.OutboundControlPlaneRouting: case consts.OutboundControlPlaneRouting:
if outboundIndex, routingResult.Mark, err = c.Route(src, dst, domain, consts.L4ProtoType_TCP, routingResult); err != nil { if outboundIndex, routingResult.Mark, err = c.Route(src, dst, domain, consts.L4ProtoType_TCP, routingResult); err != nil {
return err return err

View File

@ -143,11 +143,11 @@ func (c *ControlPlane) handlePkt(lConn *net.UDPConn, data []byte, src, pktDst, r
outboundIndex = consts.OutboundControlPlaneRouting outboundIndex = consts.OutboundControlPlaneRouting
} }
if routingResult.Must > 0 {
isDns = false // Regard as plain traffic.
}
switch outboundIndex { switch outboundIndex {
case consts.OutboundDirect: case consts.OutboundDirect:
case consts.OutboundMustDirect:
outboundIndex = consts.OutboundDirect
isDns = false // Regard as plain traffic.
case consts.OutboundControlPlaneRouting: case consts.OutboundControlPlaneRouting:
if isDns { if isDns {
// Routing of DNS packets are managed by DNS controller. // Routing of DNS packets are managed by DNS controller.

View File

@ -15,7 +15,7 @@ dns {
# If dial_mode is "ip", the upstream DNS answer SHOULD NOT be polluted, so domestic public DNS is not recommended. # If dial_mode is "ip", the upstream DNS answer SHOULD NOT be polluted, so domestic public DNS is not recommended.
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
} }
# The routing format of 'request' and 'response' is similar with section 'routing'. # The routing format of 'request' and 'response' is similar with section 'routing'.
# See https://github.com/daeuniverse/dae/blob/main/docs/routing.md # See https://github.com/daeuniverse/dae/blob/main/docs/routing.md
@ -64,7 +64,7 @@ dns {
# Use alidns for China mainland domains and googledns for others. # Use alidns for China mainland domains and googledns for others.
dns { dns {
upstream { upstream {
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
} }
routing { routing {
@ -84,7 +84,7 @@ dns {
# Use alidns for all DNS queries and fallback to googledns if pollution result detected. # Use alidns for all DNS queries and fallback to googledns if pollution result detected.
dns { dns {
upstream { upstream {
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
} }
routing { routing {

View File

@ -137,7 +137,7 @@ subscription {
# See https://github.com/daeuniverse/dae/blob/main/docs/dns.md for full examples. # See https://github.com/daeuniverse/dae/blob/main/docs/dns.md for full examples.
dns { dns {
upstream { upstream {
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
} }
routing { routing {

View File

@ -133,7 +133,7 @@ subscription {
# 更多的 DNS 样例见 https://github.com/daeuniverse/dae/blob/main/docs/dns.md # 更多的 DNS 样例见 https://github.com/daeuniverse/dae/blob/main/docs/dns.md
dns { dns {
upstream { upstream {
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
} }
routing { routing {
@ -174,7 +174,7 @@ routing {
```shell ```shell
dns { dns {
upstream { upstream {
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
} }
routing { routing {

View File

@ -7,13 +7,13 @@ If you use a external DNS like AdguardHome, you could refer to the following gui
## External DNS on localhost ## External DNS on localhost
If you set up a external DNS on localhost, you may want to let the DNS queries to dns.google proxied. For example, if you have following configuration in AdguardHome: If you set up a external DNS on localhost, you may want to let the DNS queries to dns.google.com proxied. For example, if you have following configuration in AdguardHome:
``` ```
Listen on: the same machine with dae, port 53. Listen on: the same machine with dae, port 53.
China mainland: udp://223.5.5.5:53 China mainland: udp://223.5.5.5:53
Others: https://dns.google/dns-query Others: https://dns.google.com/dns-query
``` ```
You should configure dae as follows: You should configure dae as follows:
@ -26,7 +26,7 @@ You should configure dae as follows:
pname(AdGuardHome) && l4proto(udp) && dport(53) -> must_direct pname(AdGuardHome) && l4proto(udp) && dport(53) -> must_direct
``` ```
And make sure domain `dns.google` will be proxied in routing rules. And make sure domain `dns.google.com` will be proxied in routing rules.
3. Add upstream and request to section "dns". 3. Add upstream and request to section "dns".
@ -51,13 +51,13 @@ You should configure dae as follows:
## External DNS on another machine in LAN ## External DNS on another machine in LAN
If you set up a external DNS on another machine in LAN, you may want to let the DNS queries to dns.google proxied. For example, if you have following configuration in AdguardHome: If you set up a external DNS on another machine in LAN, you may want to let the DNS queries to dns.google.com proxied. For example, if you have following configuration in AdguardHome:
``` ```
Listen on: 192.168.30.3:53 (mac address: 8c:16:45:36:1c:5a) Listen on: 192.168.30.3:53 (mac address: 8c:16:45:36:1c:5a)
China mainland: udp://223.5.5.5:53 China mainland: udp://223.5.5.5:53
Others: https://dns.google/dns-query Others: https://dns.google.com/dns-query
``` ```
You should configure dae as follows: You should configure dae as follows:
@ -72,7 +72,7 @@ You should configure dae as follows:
# mac(8c:16:45:36:1c:5a) && l4proto(udp) && dport(53) -> must_direct # mac(8c:16:45:36:1c:5a) && l4proto(udp) && dport(53) -> must_direct
``` ```
And make sure domain `dns.google` will be proxied in routing rules. And make sure domain `dns.google.com` will be proxied in routing rules.
3. Add upstream and request to section "dns". 3. Add upstream and request to section "dns".

View File

@ -3,9 +3,11 @@
## Examples: ## Examples:
```shell ```shell
### Built-in outbounds: block, direct, must_direct ### Built-in outbounds: block, direct
# The difference between "direct" and "must_direct" is that "direct" will intercept and process DNS request (for traffic # The difference between "direct" and "must_direct" is that "direct" will hijack and process DNS request (for traffic
# split use), but "must_direct" will not. "must_direct" is useful when there are traffic loops of DNS requests. # split use), but "must_direct" will not. "must_direct" is useful when there are traffic loops of DNS requests.
# "must_direct" can be written as "direct(must)".
# Similarly, "must_groupname" is also supported to NOT hijack and process DNS traffic, which equals to "groupname(must)".
### fallback outbound ### fallback outbound
# If no rule matches, traffic will go through the outbound defined by fallback. # If no rule matches, traffic will go through the outbound defined by fallback.
@ -14,7 +16,7 @@ fallback: my_group
### Domain rule ### Domain rule
domain(suffix: v2raya.org) -> my_group domain(suffix: v2raya.org) -> my_group
# equals to domain(v2raya.org) -> my_group # equals to domain(v2raya.org) -> my_group
domain(full: dns.google) -> my_group domain(full: dns.google.com) -> my_group
domain(keyword: facebook) -> my_group domain(keyword: facebook) -> my_group
domain(regexp: '\.goo.*\.com$') -> my_group domain(regexp: '\.goo.*\.com$') -> my_group
domain(geosite:category-ads) -> block domain(geosite:category-ads) -> block

View File

@ -15,7 +15,7 @@ global {
# This DNS will be used to check UDP connectivity of nodes. And if dns_upstream below contains tcp, it also be used to check # This DNS will be used to check UDP connectivity of nodes. And if dns_upstream below contains tcp, it also be used to check
# TCP DNS connectivity of nodes. # TCP DNS connectivity of nodes.
# This DNS should have both IPv4 and IPv6 if you have double stack in local. # This DNS should have both IPv4 and IPv6 if you have double stack in local.
udp_check_dns: 'dns.google:53' udp_check_dns: 'dns.google.com:53'
check_interval: 30s check_interval: 30s
@ -87,7 +87,7 @@ dns {
# If dial_mode is "ip", the upstream DNS answer SHOULD NOT be polluted, so domestic public DNS is not recommended. # If dial_mode is "ip", the upstream DNS answer SHOULD NOT be polluted, so domestic public DNS is not recommended.
alidns: 'udp://dns.alidns.com:53' alidns: 'udp://dns.alidns.com:53'
googledns: 'tcp+udp://dns.google:53' googledns: 'tcp+udp://dns.google.com:53'
} }
routing { routing {
# According to the request of dns query, decide to use which DNS upstream. # According to the request of dns query, decide to use which DNS upstream.