feat: support ToS routing rule (#292)

This commit is contained in:
mzz 2023-08-20 01:55:42 +08:00 committed by GitHub
parent c784ebaf53
commit 7273be6a06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 110 additions and 28 deletions

View File

@ -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)
}

View File

@ -52,6 +52,7 @@ const (
MatchType_IpVersion
MatchType_Mac
MatchType_ProcessName
MatchType_Tos
MatchType_Fallback
MatchType_MustRules

View File

@ -22,6 +22,7 @@ const (
Function_IpVersion = "ipversion"
Function_Mac = "mac"
Function_ProcessName = "pname"
Function_Tos = "tos"
Function_QName = "qname"
Function_QType = "qtype"

View File

@ -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)
}
}

View File

@ -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[:]),
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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:

View File

@ -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)

View File

@ -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[:]),
}

View File

@ -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

View File

@ -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

View File

@ -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规则