mirror of
https://github.com/daeuniverse/dae.git
synced 2024-12-22 16:24:40 +07:00
feat: support ToS routing rule (#292)
This commit is contained in:
parent
c784ebaf53
commit
7273be6a06
45
cmd/honk.go
45
cmd/honk.go
@ -3,27 +3,26 @@
|
||||
* Copyright (c) 2022-2023, daeuniverse Organization <dae@v2raya.org>
|
||||
*/
|
||||
|
||||
package cmd
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
honkCmd = &cobra.Command{
|
||||
Use: "honk",
|
||||
Short: "Let dae call for you.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Honk! Honk! Honk! This is dae!")
|
||||
os.Exit(0)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(honkCmd)
|
||||
}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
honkCmd = &cobra.Command{
|
||||
Use: "honk",
|
||||
Short: "Let dae call for you.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Honk! Honk! Honk! This is dae!")
|
||||
os.Exit(0)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(honkCmd)
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ const (
|
||||
MatchType_IpVersion
|
||||
MatchType_Mac
|
||||
MatchType_ProcessName
|
||||
MatchType_Tos
|
||||
MatchType_Fallback
|
||||
MatchType_MustRules
|
||||
|
||||
|
@ -22,6 +22,7 @@ const (
|
||||
Function_IpVersion = "ipversion"
|
||||
Function_Mac = "mac"
|
||||
Function_ProcessName = "pname"
|
||||
Function_Tos = "tos"
|
||||
|
||||
Function_QName = "qname"
|
||||
Function_QType = "qtype"
|
||||
|
@ -6,13 +6,17 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/daeuniverse/dae/common"
|
||||
"github.com/daeuniverse/dae/common/consts"
|
||||
"github.com/daeuniverse/dae/pkg/config_parser"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type FunctionParser func(log *logrus.Logger, f *config_parser.Function, key string, paramValueGroup []string, overrideOutbound *Outbound) (err error)
|
||||
@ -137,3 +141,18 @@ func toProcessName(processName string) (procName [consts.TaskCommLen]byte) {
|
||||
copy(procName[:], n)
|
||||
return procName
|
||||
}
|
||||
|
||||
func UintParserFactory[T constraints.Unsigned](callback func(f *config_parser.Function, values []T, overrideOutbound *Outbound) (err error)) FunctionParser {
|
||||
size := binary.Size(new(T))
|
||||
return func(log *logrus.Logger, f *config_parser.Function, key string, paramValueGroup []string, overrideOutbound *Outbound) (err error) {
|
||||
var values []T
|
||||
for _, v := range paramValueGroup {
|
||||
val, err := strconv.ParseUint(v, 10, 8*size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse %v: %w", v, err)
|
||||
}
|
||||
values = append(values, T(val))
|
||||
}
|
||||
return callback(f, values, overrideOutbound)
|
||||
}
|
||||
}
|
||||
|
@ -729,6 +729,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
|
||||
"_qname": qname,
|
||||
"qtype": qtype,
|
||||
"pid": req.routingResult.Pid,
|
||||
"tos": req.routingResult.Tos,
|
||||
"pname": ProcessName2String(req.routingResult.Pname[:]),
|
||||
"mac": Mac2String(req.routingResult.Mac[:]),
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ struct routing_result {
|
||||
__u8 outbound;
|
||||
__u8 pname[TASK_COMM_LEN];
|
||||
__u32 pid;
|
||||
__u8 tos;
|
||||
};
|
||||
|
||||
struct dst_routing_result {
|
||||
@ -155,6 +156,7 @@ struct tuples {
|
||||
__u16 sport;
|
||||
__u16 dport;
|
||||
__u8 l4proto;
|
||||
__u8 tos;
|
||||
};
|
||||
|
||||
struct {
|
||||
@ -282,6 +284,7 @@ enum __attribute__((packed)) MatchType {
|
||||
MatchType_IpVersion,
|
||||
MatchType_Mac,
|
||||
MatchType_ProcessName,
|
||||
MatchType_Tos,
|
||||
MatchType_Fallback,
|
||||
};
|
||||
enum L4ProtoType {
|
||||
@ -317,6 +320,7 @@ struct match_set {
|
||||
enum L4ProtoType l4proto_type;
|
||||
enum IpVersionType ip_version;
|
||||
__u32 pname[TASK_COMM_LEN / 4];
|
||||
__u8 tos;
|
||||
};
|
||||
bool not ; // A subrule flag (this is not a match_set flag).
|
||||
enum MatchType type;
|
||||
@ -380,9 +384,13 @@ get_tuples(const struct __sk_buff *skb, struct tuples *tuples,
|
||||
tuples->dip.u6_addr32[2] = bpf_htonl(0x0000ffff);
|
||||
tuples->dip.u6_addr32[3] = iph->daddr;
|
||||
|
||||
tuples->tos = iph->tos;
|
||||
|
||||
} else {
|
||||
__builtin_memcpy(&tuples->dip, &ipv6h->daddr, IPV6_BYTE_LENGTH);
|
||||
__builtin_memcpy(&tuples->sip, &ipv6h->saddr, IPV6_BYTE_LENGTH);
|
||||
|
||||
tuples->tos = ipv6h->priority;
|
||||
}
|
||||
if (l4proto == IPPROTO_TCP) {
|
||||
tuples->sport = tcph->source;
|
||||
@ -969,6 +977,7 @@ route(const __u32 flag[6], const void *l4hdr, const __be32 saddr[4],
|
||||
#define _ipversion_type flag[1]
|
||||
#define _pname &flag[2]
|
||||
#define _is_wan flag[2]
|
||||
#define _tos flag[3]
|
||||
|
||||
int ret;
|
||||
struct lpm_key lpm_key_instance, *lpm_key;
|
||||
@ -1123,6 +1132,11 @@ route(const __u32 flag[6], const void *l4hdr, const __be32 saddr[4],
|
||||
isdns_must_goodsubrule_badrule |= 0b10;
|
||||
}
|
||||
break;
|
||||
case MatchType_Tos:
|
||||
if (_tos == match_set->tos) {
|
||||
isdns_must_goodsubrule_badrule |= 0b10;
|
||||
}
|
||||
break;
|
||||
case MatchType_Fallback:
|
||||
#ifdef __DEBUG_ROUTING
|
||||
bpf_printk("CHECK: hit fallback");
|
||||
@ -1200,6 +1214,7 @@ route(const __u32 flag[6], const void *l4hdr, const __be32 saddr[4],
|
||||
#undef _ipversion_type
|
||||
#undef _pname
|
||||
#undef _is_wan
|
||||
#undef _tos
|
||||
}
|
||||
|
||||
static bool __always_inline is_not_to_lan(void *_ori_src) {
|
||||
@ -1394,6 +1409,7 @@ new_connection:
|
||||
} else {
|
||||
flag[1] = IpVersionType_6;
|
||||
}
|
||||
flag[3] = tuples.tos;
|
||||
__be32 mac[4] = {
|
||||
0,
|
||||
0,
|
||||
@ -1411,6 +1427,7 @@ new_connection:
|
||||
routing_result.outbound = s64_ret;
|
||||
routing_result.mark = s64_ret >> 8;
|
||||
routing_result.must = (s64_ret >> 40) & 1;
|
||||
routing_result.tos = tuples.tos;
|
||||
__builtin_memcpy(routing_result.mac, ethh.h_source,
|
||||
sizeof(routing_result.mac));
|
||||
/// NOTICE: No pid pname info for LAN packet.
|
||||
@ -1685,6 +1702,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
||||
} else {
|
||||
flag[1] = IpVersionType_6;
|
||||
}
|
||||
flag[3] = tuples.tos;
|
||||
if (pid_is_control_plane(skb, &pid_pname)) {
|
||||
// From control plane. Direct.
|
||||
return TC_ACT_OK;
|
||||
@ -1706,7 +1724,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
outbound = s64_ret;
|
||||
outbound = s64_ret & 0xff;
|
||||
mark = s64_ret >> 8;
|
||||
must = (s64_ret >> 40) & 1;
|
||||
|
||||
@ -1763,6 +1781,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
||||
routing_info.routing_result.outbound = outbound;
|
||||
routing_info.routing_result.mark = mark;
|
||||
routing_info.routing_result.must = must;
|
||||
routing_info.routing_result.tos = tuples.tos;
|
||||
__builtin_memcpy(routing_info.routing_result.mac, ethh.h_source,
|
||||
sizeof(ethh.h_source));
|
||||
if (pid_pname) {
|
||||
@ -1796,6 +1815,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
||||
} else {
|
||||
flag[1] = IpVersionType_6;
|
||||
}
|
||||
flag[3] = tuples.tos;
|
||||
struct pid_pname *pid_pname;
|
||||
if (pid_is_control_plane(skb, &pid_pname)) {
|
||||
// From control plane. Direct.
|
||||
@ -1826,6 +1846,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
|
||||
new_hdr.routing_result.outbound = s64_ret;
|
||||
new_hdr.routing_result.mark = s64_ret >> 8;
|
||||
new_hdr.routing_result.must = (s64_ret >> 40) & 1;
|
||||
new_hdr.routing_result.tos = tuples.tos;
|
||||
__builtin_memcpy(new_hdr.routing_result.mac, ethh.h_source,
|
||||
sizeof(ethh.h_source));
|
||||
if (pid_pname) {
|
||||
|
@ -8,10 +8,11 @@ package control
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/daeuniverse/dae/pkg/trie"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
"github.com/daeuniverse/dae/pkg/trie"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/daeuniverse/dae/common"
|
||||
"github.com/daeuniverse/dae/common/consts"
|
||||
@ -43,6 +44,7 @@ func NewRoutingMatcherBuilder(log *logrus.Logger, rules []*config_parser.Routing
|
||||
rulesBuilder.RegisterFunctionParser(consts.Function_L4Proto, routing.L4ProtoParserFactory(b.addL4Proto))
|
||||
rulesBuilder.RegisterFunctionParser(consts.Function_Mac, routing.MacParserFactory(b.addSourceMac))
|
||||
rulesBuilder.RegisterFunctionParser(consts.Function_ProcessName, routing.ProcessNameParserFactory(b.addProcessName))
|
||||
rulesBuilder.RegisterFunctionParser(consts.Function_Tos, routing.UintParserFactory(b.addTos))
|
||||
rulesBuilder.RegisterFunctionParser(consts.Function_IpVersion, routing.IpVersionParserFactory(b.addIpVersion))
|
||||
if err = rulesBuilder.Apply(rules); err != nil {
|
||||
return nil, err
|
||||
@ -274,6 +276,29 @@ func (b *RoutingMatcherBuilder) addProcessName(f *config_parser.Function, values
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *RoutingMatcherBuilder) addTos(f *config_parser.Function, values []uint8, outbound *routing.Outbound) (err error) {
|
||||
for i, value := range values {
|
||||
outboundName := consts.OutboundLogicalOr.String()
|
||||
if i == len(values)-1 {
|
||||
outboundName = outbound.Name
|
||||
}
|
||||
outboundId, err := b.outboundToId(outboundName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
matchSet := bpfMatchSet{
|
||||
Type: uint8(consts.MatchType_Tos),
|
||||
Not: f.Not,
|
||||
Outbound: outboundId,
|
||||
Mark: outbound.Mark,
|
||||
Must: outbound.Must,
|
||||
}
|
||||
matchSet.Value[0] = value
|
||||
b.rules = append(b.rules, matchSet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *RoutingMatcherBuilder) addFallback(fallbackOutbound config.FunctionOrString) (err error) {
|
||||
outbound, err := routing.ParseOutbound(config.FunctionOrStringToFunction(fallbackOutbound))
|
||||
if err != nil {
|
||||
|
@ -8,12 +8,12 @@ package control
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/daeuniverse/dae/pkg/trie"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/daeuniverse/dae/common/consts"
|
||||
"github.com/daeuniverse/dae/component/routing"
|
||||
"github.com/daeuniverse/dae/pkg/trie"
|
||||
)
|
||||
|
||||
type RoutingMatcher struct {
|
||||
@ -33,6 +33,7 @@ func (m *RoutingMatcher) Match(
|
||||
l4proto consts.L4ProtoType,
|
||||
domain string,
|
||||
processName [16]uint8,
|
||||
tos uint8,
|
||||
mac []byte,
|
||||
) (outboundIndex consts.OutboundIndex, mark uint32, must bool, err error) {
|
||||
if len(sourceAddr) != net.IPv6len || len(destAddr) != net.IPv6len || len(mac) != net.IPv6len {
|
||||
@ -92,6 +93,10 @@ func (m *RoutingMatcher) Match(
|
||||
if processName[0] != 0 && match.Value == processName {
|
||||
goodSubrule = true
|
||||
}
|
||||
case consts.MatchType_Tos:
|
||||
if tos == match.Value[0] {
|
||||
goodSubrule = true
|
||||
}
|
||||
case consts.MatchType_Fallback:
|
||||
goodSubrule = true
|
||||
default:
|
||||
|
@ -182,6 +182,7 @@ func (c *ControlPlane) RouteDialTcp(p *RouteDialParam) (conn netproxy.Conn, err
|
||||
"sniffed": domain,
|
||||
"ip": RefineAddrPortToShow(dst),
|
||||
"pid": routingResult.Pid,
|
||||
"tos": routingResult.Tos,
|
||||
"pname": ProcessName2String(routingResult.Pname[:]),
|
||||
"mac": Mac2String(routingResult.Mac[:]),
|
||||
}).Infof("%v <-> %v", RefineSourceToShow(src, dst.Addr(), consts.LanWanFlag_NotApplicable), dialTarget)
|
||||
|
@ -274,6 +274,7 @@ getNew:
|
||||
"to": realDst.String(),
|
||||
"domain": domain,
|
||||
"pid": routingResult.Pid,
|
||||
"tos": routingResult.Tos,
|
||||
"pname": ProcessName2String(routingResult.Pname[:]),
|
||||
"mac": Mac2String(routingResult.Mac[:]),
|
||||
"from": realSrc.String(),
|
||||
@ -298,6 +299,7 @@ getNew:
|
||||
"domain": domain,
|
||||
"ip": RefineAddrPortToShow(realDst),
|
||||
"pid": routingResult.Pid,
|
||||
"tos": routingResult.Tos,
|
||||
"pname": ProcessName2String(routingResult.Pname[:]),
|
||||
"mac": Mac2String(routingResult.Mac[:]),
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ func (c *ControlPlane) Route(src, dst netip.AddrPort, domain string, l4proto con
|
||||
l4proto,
|
||||
domain,
|
||||
routingResult.Pname,
|
||||
routingResult.Tos,
|
||||
append([]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, routingResult.Mac[:]...),
|
||||
); err != nil {
|
||||
return 0, 0, false, err
|
||||
|
@ -55,6 +55,9 @@ mac('02:42:ac:11:00:02') -> direct
|
||||
### Process Name rule (only support localhost process when binding to WAN)
|
||||
pname(curl) -> direct
|
||||
|
||||
### ToS rule (match ToS/DSCP; is useful for BT bypass)
|
||||
tos(4) -> direct
|
||||
|
||||
### Multiple domains rule
|
||||
domain(keyword: google, suffix: www.twitter.com, suffix: v2raya.org) -> my_group
|
||||
### Multiple IP rule
|
||||
|
@ -55,6 +55,9 @@ mac('02:42:ac:11:00:02') -> direct
|
||||
### 进程名称规则(绑定WAN时仅支持本机进程)
|
||||
pname(curl) -> direct
|
||||
|
||||
### ToS规则(匹配 ToS 和 DSCP,可用于绕过 BT)
|
||||
tos(4) -> direct
|
||||
|
||||
### 多个域名规则
|
||||
domain(keyword: google, suffix: www.twitter.com, suffix: v2raya.org) -> my_group
|
||||
### 多个IP规则
|
||||
|
Loading…
Reference in New Issue
Block a user