feat: support pname routing (just task command. fixme)

This commit is contained in:
mzz2017 2023-01-31 19:33:53 +08:00
parent b788f03d4a
commit 460ef450a0
9 changed files with 103 additions and 54 deletions

View File

@ -10,6 +10,8 @@ const (
BpfPinRoot = "/sys/fs/bpf"
AddrHdrSize = 20
TaskCommLen = 16
)
type ParamKey uint32
@ -33,15 +35,16 @@ const (
type RoutingType uint8
const (
RoutingType_DomainSet RoutingType = iota
RoutingType_IpSet
RoutingType_SourceIpSet
RoutingType_Port
RoutingType_SourcePort
RoutingType_L4Proto
RoutingType_IpVersion
RoutingType_Mac
RoutingType_Final
MatchType_DomainSet RoutingType = iota
MatchType_IpSet
MatchType_SourceIpSet
MatchType_Port
MatchType_SourcePort
MatchType_L4Proto
MatchType_IpVersion
MatchType_Mac
MatchType_ProcessName
MatchType_Final
)
type OutboundIndex uint8

View File

@ -11,14 +11,15 @@ const (
RoutingDomain_Suffix = "suffix"
RoutingDomain_Regex = "regex"
Function_Domain = "domain"
Function_Ip = "ip"
Function_SourceIp = "sip"
Function_Port = "port"
Function_SourcePort = "sport"
Function_Mac = "mac"
Function_L4Proto = "l4proto"
Function_IpVersion = "ipversion"
Function_Domain = "domain"
Function_Ip = "ip"
Function_SourceIp = "sip"
Function_Port = "port"
Function_SourcePort = "sport"
Function_L4Proto = "l4proto"
Function_IpVersion = "ipversion"
Function_Mac = "mac"
Function_ProcessName = "pname"
Declaration_Final = "final"
)

View File

@ -213,7 +213,7 @@ retryLoadBpf:
}
log.Debugf("RoutingA:\n%vfinal: %v\n", debugBuilder.String(), routingA.Final)
}
if err = routing.ApplyMatcherBuilder(builder, rules, routingA.Final); err != nil {
if err = routing.ApplyMatcherBuilder(log, builder, rules, routingA.Final); err != nil {
return nil, fmt.Errorf("ApplyMatcherBuilder: %w", err)
}
if err = builder.Build(); err != nil {

View File

@ -194,6 +194,7 @@ enum __attribute__((packed)) MatchType {
MatchType_L4Proto,
MatchType_IpVersion,
MatchType_Mac,
MatchType_ProcessName,
MatchType_Final,
};
enum L4ProtoType {
@ -228,6 +229,7 @@ struct match_set {
struct port_range port_range;
enum L4ProtoType l4proto_type;
enum IpVersionType ip_version;
__u32 pname[TASK_COMM_LEN / 4];
};
enum MatchType type;
bool not ; // A subrule flag (this is not a match_set flag).
@ -565,8 +567,8 @@ static __always_inline int get_tproxy_ip(__u8 ipversion, __u32 ifindex,
return 0;
}
static __always_inline int ip_is_host(bool ipversion, __u32 ifindex, __be32 ip[4],
__be32 tproxy_ip[4]) {
static __always_inline int ip_is_host(bool ipversion, __u32 ifindex,
__be32 ip[4], __be32 tproxy_ip[4]) {
if (tproxy_ip) {
int ret;
if ((ret = get_tproxy_ip(ipversion, ifindex, tproxy_ip))) {
@ -807,11 +809,11 @@ static __always_inline int decap_after_udp_hdr(struct __sk_buff *skb,
}
// Do not use __always_inline here because this function is too heavy.
static int routing(__u32 flag[3], void *l4_hdr, __be32 saddr[4],
static int routing(__u32 flag[6], void *l4_hdr, __be32 saddr[4],
__be32 daddr[4], __be32 mac[4]) {
#define _l4proto_type flag[0]
#define _ipversion_type flag[1]
#define _from_localhost flag[2]
#define _pname &flag[2]
int ret;
@ -957,6 +959,10 @@ static int routing(__u32 flag[3], void *l4_hdr, __be32 saddr[4],
if ((domain_routing->bitmap[i / 32] >> (i % 32)) & 1) {
good_subrule = true;
}
} else if (match_set->type == MatchType_ProcessName) {
if ((equal_ipv6_format(match_set->pname, _pname))){
good_subrule = true;
}
} else if (match_set->type == MatchType_Final) {
// bpf_printk("CHECK: hit final");
good_subrule = true;
@ -1005,7 +1011,7 @@ static int routing(__u32 flag[3], void *l4_hdr, __be32 saddr[4],
return -EPERM;
#undef _l4proto_type
#undef _ipversion_type
#undef _from_localhost
#undef _pname
}
// Do DNAT.
@ -1027,7 +1033,6 @@ int tproxy_ingress(struct __sk_buff *skb) {
bpf_printk("parse_transport: %d", ret);
return TC_ACT_OK;
}
// Backup for further use.
__be16 ipv4_tot_len = 0;
@ -1085,13 +1090,12 @@ int tproxy_ingress(struct __sk_buff *skb) {
if (unlikely(tcp_state_syn)) {
// New TCP connection.
// bpf_printk("[%X]New Connection", bpf_ntohl(tcph.seq));
__u32 flag[3] = {L4ProtoType_TCP}; // TCP
__u32 flag[6] = {L4ProtoType_TCP}; // TCP
if (ipversion == 6) {
flag[1] = IpVersionType_6;
} else {
flag[1] = IpVersionType_4;
}
flag[2] = false;
__be32 mac[4] = {
0,
0,
@ -1159,13 +1163,12 @@ int tproxy_ingress(struct __sk_buff *skb) {
new_hdr.port = udph.dest;
// Routing. It decides if we redirect traffic to control plane.
__u32 flag[3] = {L4ProtoType_UDP};
__u32 flag[6] = {L4ProtoType_UDP};
if (ipversion == 6) {
flag[1] = IpVersionType_6;
} else {
flag[1] = IpVersionType_4;
}
flag[2] = false;
__be32 mac[4] = {
0,
0,
@ -1259,7 +1262,6 @@ int tproxy_egress(struct __sk_buff *skb) {
if (ret) {
return TC_ACT_OK;
}
// Parse saddr and daddr as ipv6 format.
__be32 saddr[4];
@ -1456,7 +1458,6 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
if (ret) {
return TC_ACT_OK;
}
__be16 sport;
if (l4proto == IPPROTO_TCP) {
@ -1566,13 +1567,15 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
if (unlikely(tcp_state_syn)) {
// New TCP connection.
// bpf_printk("[%X]New Connection", bpf_ntohl(tcph.seq));
__u32 flag[3] = {L4ProtoType_TCP}; // TCP
__u32 flag[6] = {L4ProtoType_TCP}; // TCP
if (ipversion == 6) {
flag[1] = IpVersionType_6;
} else {
flag[1] = IpVersionType_4;
}
flag[2] = true;
if (pid_pname) {
__builtin_memcpy(&flag[2], pid_pname->pname, TASK_COMM_LEN);
}
__be32 mac[4] = {
0,
0,
@ -1646,13 +1649,15 @@ int tproxy_wan_egress(struct __sk_buff *skb) {
new_hdr.port = udph.dest;
// Routing. It decides if we redirect traffic to control plane.
__u32 flag[3] = {L4ProtoType_UDP};
__u32 flag[6] = {L4ProtoType_UDP};
if (ipversion == 6) {
flag[1] = IpVersionType_6;
} else {
flag[1] = IpVersionType_4;
}
flag[2] = true;
if (pid_pname) {
__builtin_memcpy(&flag[2], pid_pname->pname, TASK_COMM_LEN);
}
__be32 mac[4] = {
0,
0,
@ -1719,7 +1724,6 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {
if (ret) {
return TC_ACT_OK;
}
// bpf_printk("bpf_ntohs(*(__u16 *)&ethh.h_source[4]): %u",
// bpf_ntohs(*(__u16 *)&ethh.h_source[4]));
@ -1857,8 +1861,8 @@ int tproxy_wan_ingress(struct __sk_buff *skb) {
// bpf_printk("should send to: %pI6:%u", tproxy_ip,
// bpf_ntohs(*tproxy_port));
if ((ret =
rewrite_ip(skb, ipversion, l4proto, ihl, daddr, tproxy_ip, true))) {
if ((ret = rewrite_ip(skb, ipversion, l4proto, ihl, daddr, tproxy_ip,
true))) {
bpf_printk("Shot IP: %d", ret);
return TC_ACT_SHOT;
}

View File

@ -14,6 +14,7 @@ import (
"github.com/v2rayA/dae/pkg/config_parser"
"net/netip"
"strconv"
"unsafe"
)
type DomainSet struct {
@ -74,7 +75,7 @@ func (b *RoutingMatcherBuilder) AddDomain(f *config_parser.Function, key string,
Domains: values,
})
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_DomainSet),
Type: uint8(consts.MatchType_DomainSet),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
})
@ -94,7 +95,7 @@ func (b *RoutingMatcherBuilder) AddSourceMac(f *config_parser.Function, macAddrs
lpmTrieIndex := len(b.SimulatedLpmTries)
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_Mac),
Type: uint8(consts.MatchType_Mac),
Value: uint32(lpmTrieIndex),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
@ -109,7 +110,7 @@ func (b *RoutingMatcherBuilder) AddIp(f *config_parser.Function, values []netip.
lpmTrieIndex := len(b.SimulatedLpmTries)
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_IpSet),
Type: uint8(consts.MatchType_IpSet),
Value: uint32(lpmTrieIndex),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
@ -123,7 +124,7 @@ func (b *RoutingMatcherBuilder) AddPort(f *config_parser.Function, values [][2]u
outbound = _outbound
}
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_Port),
Type: uint8(consts.MatchType_Port),
Value: _bpfPortRange{
PortStart: value[0],
PortEnd: value[1],
@ -141,7 +142,7 @@ func (b *RoutingMatcherBuilder) AddSourceIp(f *config_parser.Function, values []
lpmTrieIndex := len(b.SimulatedLpmTries)
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_SourceIpSet),
Type: uint8(consts.MatchType_SourceIpSet),
Value: uint32(lpmTrieIndex),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
@ -155,7 +156,7 @@ func (b *RoutingMatcherBuilder) AddSourcePort(f *config_parser.Function, values
outbound = _outbound
}
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_SourcePort),
Type: uint8(consts.MatchType_SourcePort),
Value: _bpfPortRange{
PortStart: value[0],
PortEnd: value[1],
@ -171,7 +172,7 @@ func (b *RoutingMatcherBuilder) AddL4Proto(f *config_parser.Function, values con
return
}
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_L4Proto),
Type: uint8(consts.MatchType_L4Proto),
Value: uint32(values),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
@ -183,20 +184,36 @@ func (b *RoutingMatcherBuilder) AddIpVersion(f *config_parser.Function, values c
return
}
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_IpVersion),
Type: uint8(consts.MatchType_IpVersion),
Value: uint32(values),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
})
}
func (b *RoutingMatcherBuilder) AddProcessName(f *config_parser.Function, values [][consts.TaskCommLen]byte, _outbound string) {
for i, value := range values {
outbound := routing.FakeOutbound_OR
if i == len(values)-1 {
outbound = _outbound
}
matchSet := bpfMatchSet{
Type: uint8(consts.MatchType_ProcessName),
Not: f.Not,
Outbound: b.OutboundToId(outbound),
}
copy((*(*[16]byte)(unsafe.Pointer(&matchSet.Value)))[:], value[:])
b.rules = append(b.rules, matchSet)
}
}
func (b *RoutingMatcherBuilder) AddFinal(outbound string) {
if b.err != nil {
return
}
b.Final = outbound
b.rules = append(b.rules, bpfMatchSet{
Type: uint8(consts.RoutingType_Final),
Type: uint8(consts.MatchType_Final),
Outbound: b.OutboundToId(outbound),
})
}
@ -226,7 +243,7 @@ func (b *RoutingMatcherBuilder) Build() (err error) {
}
// Write routings.
// Final rule MUST be the last.
if b.rules[len(b.rules)-1].Type != uint8(consts.RoutingType_Final) {
if b.rules[len(b.rules)-1].Type != uint8(consts.MatchType_Final) {
b.err = fmt.Errorf("final rule MUST be the last")
return b.err
}

View File

@ -27,6 +27,7 @@ type MatcherBuilder interface {
AddL4Proto(f *config_parser.Function, values consts.L4ProtoType, outbound string)
AddIpVersion(f *config_parser.Function, values consts.IpVersion, outbound string)
AddSourceMac(f *config_parser.Function, values [][6]byte, outbound string)
AddProcessName(f *config_parser.Function, values [][consts.TaskCommLen]byte, outbound string)
AddFinal(outbound string)
AddAnyBefore(f *config_parser.Function, key string, values []string, outbound string)
AddAnyAfter(f *config_parser.Function, key string, values []string, outbound string)
@ -59,9 +60,15 @@ func ParsePrefixes(values []string) (cidrs []netip.Prefix, err error) {
return cidrs, nil
}
func ApplyMatcherBuilder(builder MatcherBuilder, rules []*config_parser.RoutingRule, finalOutbound string) (err error) {
func ToProcessName(processName string) (procName [consts.TaskCommLen]byte) {
n := []byte(processName)
copy(procName[:], n)
return procName
}
func ApplyMatcherBuilder(log *logrus.Logger, builder MatcherBuilder, rules []*config_parser.RoutingRule, finalOutbound string) (err error) {
for _, rule := range rules {
logrus.Debugln("[rule]", rule.String(true))
log.Debugln("[rule]", rule.String(true))
// rule is like: domain(domain:baidu.com) && port(443) -> proxy
for iFunc, f := range rule.AndFunctions {
@ -84,7 +91,7 @@ func ApplyMatcherBuilder(builder MatcherBuilder, rules []*config_parser.RoutingR
if f.Not {
symNot = "!"
}
logrus.Debugf("\t%v%v(%v) -> %v", symNot, f.Name, key, outbound)
log.Debugf("\t%v%v(%v) -> %v", symNot, f.Name, key, outbound)
}
builder.AddAnyBefore(f, key, paramValueGroup, outbound)
@ -147,6 +154,15 @@ func ApplyMatcherBuilder(builder MatcherBuilder, rules []*config_parser.RoutingR
}
}
builder.AddIpVersion(f, ipVersion, outbound)
case consts.Function_ProcessName:
var procNames [][consts.TaskCommLen]byte
for _, v := range paramValueGroup {
if len([]byte(v)) > consts.TaskCommLen {
log.Infof(`pname routing: trim "%v" to "%v" because it is too long.`, v, string([]byte(v)[:consts.TaskCommLen]))
}
procNames = append(procNames, ToProcessName(v))
}
builder.AddProcessName(f, procNames, outbound)
default:
return fmt.Errorf("unsupported function name: %v", f.Name)
}
@ -186,6 +202,8 @@ func (d *DefaultMatcherBuilder) AddSourceMac(f *config_parser.Function, values [
func (d *DefaultMatcherBuilder) AddFinal(outbound string) {}
func (d *DefaultMatcherBuilder) AddAnyBefore(f *config_parser.Function, key string, values []string, outbound string) {
}
func (d *DefaultMatcherBuilder) AddProcessName(f *config_parser.Function, values [][consts.TaskCommLen]byte, outbound string) {
}
func (d *DefaultMatcherBuilder) AddAnyAfter(f *config_parser.Function, key string, values []string, outbound string) {
}
func (d *DefaultMatcherBuilder) Build() (err error) { return nil }

View File

@ -16,7 +16,7 @@ type Global struct {
TproxyPort uint16 `mapstructure:"tproxy_port" default:"12345"`
CheckUrl string `mapstructure:"check_url" default:"https://connectivitycheck.gstatic.com/generate_204"`
CheckInterval time.Duration `mapstructure:"check_interval" default:"15s"`
DnsUpstream string `mapstructure:"dns_upstream" default:"208.67.222.222:5353"`
DnsUpstream string `mapstructure:"dns_upstream" default:"1.1.1.1:53"`
LanInterface string `mapstructure:"lan_interface"`
WanInterface string `mapstructure:"wan_interface"`
}

View File

@ -48,6 +48,9 @@ group {
routing {
# See routing.md for full examples.
ip(1.1.1.1) && port(53) -> my_group
pname(firefox) && domain(ip.sb) -> direct
pname(curl) && domain(ip.sb) -> my_group
ip(geoip:private) -> direct
ip(geoip:cn) -> direct

View File

@ -34,9 +34,6 @@ port(10080-30000) -> direct
sport(38563) -> direct
sport(10080-30000) -> direct
# Source MAC rule
mac('02:42:ac:11:00:02') -> direct
# Level 4 protocol rule:
l4proto(tcp) -> my_group
l4proto(udp) -> direct
@ -45,6 +42,12 @@ l4proto(udp) -> direct
ipversion(4) -> block
ipversion(6) -> ipv6_group
# Source MAC rule
mac('02:42:ac:11:00:02') -> direct
# Process Name rule (Only support local process)
pname(curl) -> direct
# Multiple domains rule
domain(keyword: google, suffix: www.twitter.com, suffix: v2raya.org) -> my_group
# Multiple IP rule