diff --git a/common/consts/ebpf.go b/common/consts/ebpf.go index 98d4478..38c43ae 100644 --- a/common/consts/ebpf.go +++ b/common/consts/ebpf.go @@ -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 diff --git a/common/consts/routing.go b/common/consts/routing.go index 3e0f423..6adddec 100644 --- a/common/consts/routing.go +++ b/common/consts/routing.go @@ -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" ) diff --git a/component/control/control_plane.go b/component/control/control_plane.go index b8495eb..5db0287 100644 --- a/component/control/control_plane.go +++ b/component/control/control_plane.go @@ -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 { diff --git a/component/control/kern/tproxy.c b/component/control/kern/tproxy.c index 55814b9..b92fef4 100644 --- a/component/control/kern/tproxy.c +++ b/component/control/kern/tproxy.c @@ -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 *)ðh.h_source[4]): %u", // bpf_ntohs(*(__u16 *)ðh.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; } diff --git a/component/control/routing_matcher_builder.go b/component/control/routing_matcher_builder.go index 6483e21..f945312 100644 --- a/component/control/routing_matcher_builder.go +++ b/component/control/routing_matcher_builder.go @@ -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 } diff --git a/component/routing/matcher_builder.go b/component/routing/matcher_builder.go index 277123c..3d6b3c3 100644 --- a/component/routing/matcher_builder.go +++ b/component/routing/matcher_builder.go @@ -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 } diff --git a/config/config.go b/config/config.go index 364f39d..2e86ffa 100644 --- a/config/config.go +++ b/config/config.go @@ -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"` } diff --git a/example.dae b/example.dae index 33de9b6..2ad1b28 100644 --- a/example.dae +++ b/example.dae @@ -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 diff --git a/routing.md b/routing.md index f5dd784..0a2d0f2 100644 --- a/routing.md +++ b/routing.md @@ -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