From b117dfa7d5cc1acb75243ef7f181f7948d9b97ab Mon Sep 17 00:00:00 2001 From: mzz <30586220+mzz2017@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:24:03 +0800 Subject: [PATCH] feat: support real process name traffic split (#6) --- .github/workflows/release.yml | 1 + .gitmodules | 3 + README.md | 33 ++++- common/consts/ebpf.go | 2 +- component/control/bpf_utils.go | 18 ++- component/control/control_plane.go | 9 +- component/control/control_plane_core.go | 4 - component/control/kern/headers | 1 + component/control/kern/tproxy.c | 126 +++++++++++++------ component/control/routing_matcher_builder.go | 48 +++---- component/outbound/dialer/direct.go | 2 +- example.dae | 3 +- routing.md | 2 +- 13 files changed, 178 insertions(+), 74 deletions(-) create mode 100644 .gitmodules create mode 160000 component/control/kern/headers diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6532fd..70617a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,6 +92,7 @@ jobs: - name: Get project dependencies run: | + git submodule update --init --recursive go mod download - name: Build dae diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bec387c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "component/control/kern/headers"] + path = component/control/kern/headers + url = https://github.com/v2rayA/dae_bpf_headers diff --git a/README.md b/README.md index f669367..b318da0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ***dae***, means goose, is a lightweight and high-performance transparent proxy solution. -In order to improve the traffic diversion performance as much as possible, dae runs the transparent proxy and traffic diversion suite in the linux kernel by eBPF. Therefore, we have the opportunity to make the direct traffic bypass the forwarding by proxy application and achieve true direct traffic through. Under such a magic trick, there is almost no performance loss and additional resource consumption for direct traffic. +In order to improve the traffic split performance as much as possible, dae runs the transparent proxy and traffic split suite in the linux kernel by eBPF. Therefore, we have the opportunity to make the direct traffic bypass the forwarding by proxy application and achieve true direct traffic through. Under such a magic trick, there is almost no performance loss and additional resource consumption for direct traffic. As a successor of [v2rayA](https://github.com/v2rayA/v2rayA), dae abandoned v2ray-core to meet the needs of users more freely. @@ -43,16 +43,45 @@ Note that if you bind dae to LAN only, dae only provide network service for traf You need bind dae to WAN interface, if you want dae to provide network service for local programs. -This feature requires kernel version of the machine >= 5.2. +This feature requires kernel version of the machine >= 5.7. Note that if you bind dae to WAN only, dae only provide network service for local programs and not impact traffic coming in from other interfaces. +### Kernel Configuration Item + +Usually, mainstream desktop distributions have these items turned on. But in order to reduce kernel size, some items are turned off by default on embedded device distributions like OpenWRT, Armbian, etc. + +Use following commands to check the kernel configuration items on your machine. + +```shell +zcat /proc/config.gz || cat /boot/config || cat /boot/config-$(uname -r) +``` + +**Bind to LAN** + +``` +CONFIG_DEBUG_INFO_BTF +``` + +**Bind to WAN**: + +``` +CONFIG_DEBUG_INFO_BTF +``` + +Check them using command like: + +```shell +(zcat /proc/config.gz || cat /boot/config || cat /boot/config-$(uname -r)) | grep 'CONFIG_DEBUG_INFO_BTF=' +``` + ## TODO 1. Check dns upstream and source loop (whether upstream is also a client of us) and remind the user to add sip rule. 1. Domain routing performance optimization. 1. DisableL4Checksum by link. 1. Handle the case that nodes do not support UDP. +1. Handle the case that nodes do not support IPv6. 1. L4Checksum problem. 1. MACv2 extension extraction. 1. ... diff --git a/common/consts/ebpf.go b/common/consts/ebpf.go index 272b0b8..4bd6b69 100644 --- a/common/consts/ebpf.go +++ b/common/consts/ebpf.go @@ -98,4 +98,4 @@ const ( var BasicFeatureVersion = internal.Version{5, 2, 0} var FtraceFeatureVersion = internal.Version{5, 5, 0} -var CgGetPidFeatureVersion = internal.Version{5, 7, 0} +var CgSocketCookieFeatureVersion = internal.Version{5, 7, 0} diff --git a/component/control/bpf_utils.go b/component/control/bpf_utils.go index e0f7ff3..ef61627 100644 --- a/component/control/bpf_utils.go +++ b/component/control/bpf_utils.go @@ -29,11 +29,19 @@ type _bpfPortRange struct { PortEnd uint16 } -func (r _bpfPortRange) Encode() uint32 { - var b [4]byte - binary.BigEndian.PutUint16(b[:2], r.PortStart) - binary.BigEndian.PutUint16(b[2:], r.PortEnd) - return binary.BigEndian.Uint32(b[:]) +type _bpfMatchSet struct { + // TODO: Need sync with C code. + Value [16]byte + Type uint8 + Not bool + Outbound uint8 + _ [1]byte +} + +func (r _bpfPortRange) Encode() (b [16]byte) { + binary.LittleEndian.PutUint16(b[:2], r.PortStart) + binary.LittleEndian.PutUint16(b[2:], r.PortEnd) + return b } func (o *bpfObjects) newLpmMap(keys []_bpfLpmKey, values []uint32) (m *ebpf.Map, err error) { diff --git a/component/control/control_plane.go b/component/control/control_plane.go index c0fd90b..79994bc 100644 --- a/component/control/control_plane.go +++ b/component/control/control_plane.go @@ -69,6 +69,10 @@ func NewControlPlane( if kernelVersion.Less(consts.BasicFeatureVersion) { return nil, fmt.Errorf("your kernel version %v does not satisfy basic requirement; expect >=%v", c.kernelVersion.String(), consts.BasicFeatureVersion.String()) } + if len(wanInterface) > 0 && kernelVersion.Less(consts.CgSocketCookieFeatureVersion) { + return nil, fmt.Errorf("your kernel version %v does not support bind to WAN; expect >=%v; remove wan_interface in config file and try again", kernelVersion.String(), + consts.CgSocketCookieFeatureVersion.String()) + } // Allow the current process to lock memory for eBPF resources. if err = rlimit.RemoveMemlock(); err != nil { @@ -83,7 +87,8 @@ func NewControlPlane( var ProgramOptions ebpf.ProgramOptions if log.IsLevelEnabled(logrus.TraceLevel) { ProgramOptions = ebpf.ProgramOptions{ - LogLevel: ebpf.LogLevelInstruction | ebpf.LogLevelStats, + LogLevel: ebpf.LogLevelBranch | ebpf.LogLevelStats, + //LogLevel: ebpf.LogLevelInstruction | ebpf.LogLevelStats, } } @@ -92,7 +97,7 @@ func NewControlPlane( if len(lanInterface) > 0 && len(wanInterface) == 0 { // Only bind LAN. obj = &bpfObjectsLan{} - } else if len(wanInterface) == 0 && len(wanInterface) > 0 { + } else if len(lanInterface) == 0 && len(wanInterface) > 0 { // Only bind to WAN. // Trick. Replace the beams with rotten timbers. obj = &bpfObjectsWan{} diff --git a/component/control/control_plane_core.go b/component/control/control_plane_core.go index e942254..2f2371c 100644 --- a/component/control/control_plane_core.go +++ b/component/control/control_plane_core.go @@ -164,10 +164,6 @@ func (c *ControlPlaneCore) BindLan(ifname string) error { } func (c *ControlPlaneCore) BindWan(ifname string) error { - if c.kernelVersion.Less(consts.BasicFeatureVersion) { - return fmt.Errorf("your kernel version %v does not support bind to WAN; expect >=%v; remove wan_interface in config file and try again", c.kernelVersion.String(), consts.CgGetPidFeatureVersion.String()) - } - c.log.Infof("Bind to WAN: %v", ifname) link, err := netlink.LinkByName(ifname) if err != nil { diff --git a/component/control/kern/headers b/component/control/kern/headers new file mode 160000 index 0000000..372c3cc --- /dev/null +++ b/component/control/kern/headers @@ -0,0 +1 @@ +Subproject commit 372c3cc61d2d907b89ebdfb7bec180a09cd28169 diff --git a/component/control/kern/tproxy.c b/component/control/kern/tproxy.c index f59d375..6743b02 100644 --- a/component/control/kern/tproxy.c +++ b/component/control/kern/tproxy.c @@ -3,20 +3,17 @@ * SPDX-License-Identifier: AGPL-3.0-only * Copyright (c) since 2022, v2rayA Organization */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "headers/if_ether_defs.h" +#include "headers/pkt_cls_defs.h" +#include "headers/socket_defs.h" +#include "headers/vmlinux.h" +#include + +#include #include #include +#include // #define __DEBUG_ROUTING // #define __PRINT_ROUTING_RESULT @@ -53,6 +50,8 @@ #define MAX_DST_MAPPING_NUM (65536 * 2) #define MAX_SRC_PID_PNAME_MAPPING_NUM (65536) #define IPV6_MAX_EXTENSIONS 4 +#define MAX_ARG_LEN_TO_PROBE 192 +#define MAX_ARG_SCANNER_BUFFER_SIZE (TASK_COMM_LEN * 4) #define OUTBOUND_DIRECT 0 #define OUTBOUND_BLOCK 1 @@ -229,6 +228,7 @@ struct port_range { */ struct match_set { union { + /// NOTICE: MUST sync with component/control/bpf_utils.go. __u32 __value; // Placeholder for bpf2go. __u32 index; @@ -827,13 +827,13 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], /// TODO: BPF_MAP_UPDATE_BATCH ? __u32 key = MatchType_L4Proto; - if ((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key, &_l4proto_type, - BPF_ANY))) { + if (unlikely((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key, + &_l4proto_type, BPF_ANY)))) { return ret; }; key = MatchType_IpVersion; - if ((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key, &_ipversion_type, - BPF_ANY))) { + if (unlikely((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key, + &_ipversion_type, BPF_ANY)))) { return ret; }; @@ -849,11 +849,13 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], } key = MatchType_SourcePort; - if ((ret = bpf_map_update_elem(&h_port_map, &key, &h_sport, BPF_ANY))) { + if (unlikely( + (ret = bpf_map_update_elem(&h_port_map, &key, &h_sport, BPF_ANY)))) { return ret; }; key = MatchType_Port; - if ((ret = bpf_map_update_elem(&h_port_map, &key, &h_dport, BPF_ANY))) { + if (unlikely( + (ret = bpf_map_update_elem(&h_port_map, &key, &h_dport, BPF_ANY)))) { return ret; }; @@ -862,7 +864,7 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], if (h_dport == 53 && _l4proto_type == L4ProtoType_UDP) { struct ip_port *upstream = bpf_map_lookup_elem(&dns_upstream_map, &zero_key); - if (!upstream) { + if (unlikely(!upstream)) { return -EFAULT; } h_dport = bpf_ntohs(upstream->port); @@ -875,21 +877,21 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], __builtin_memcpy(lpm_key_instance.data, daddr, IPV6_BYTE_LENGTH); // bpf_printk("mac: %pI6", mac); key = MatchType_IpSet; - if ((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, - BPF_ANY))) { + if (unlikely((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, + BPF_ANY)))) { return ret; }; __builtin_memcpy(lpm_key_instance.data, saddr, IPV6_BYTE_LENGTH); key = MatchType_SourceIpSet; - if ((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, - BPF_ANY))) { + if (unlikely((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, + BPF_ANY)))) { return ret; }; if (!_is_wan) { __builtin_memcpy(lpm_key_instance.data, mac, IPV6_BYTE_LENGTH); key = MatchType_Mac; - if ((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, - BPF_ANY))) { + if (unlikely((ret = bpf_map_update_elem(&lpm_key_map, &key, + &lpm_key_instance, BPF_ANY)))) { return ret; }; } @@ -909,7 +911,7 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], for (__u32 i = 0; i < MAX_MATCH_SET_LEN; i++) { __u32 k = i; // Clone to pass code checker. match_set = bpf_map_lookup_elem(&routing_map, &k); - if (!match_set) { + if (unlikely(!match_set)) { return -EFAULT; } if (bad_rule || good_subrule) { @@ -980,8 +982,8 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], if ((domain_routing->bitmap[i / 32] >> (i % 32)) & 1) { good_subrule = true; } - } else if (_is_wan && match_set->type == MatchType_ProcessName) { - if ((equal_ipv6_format(match_set->pname, _pname))) { + } else if (match_set->type == MatchType_ProcessName) { + if (_is_wan && equal_ipv6_format(match_set->pname, _pname)) { good_subrule = true; } } else if (match_set->type == MatchType_Final) { @@ -1298,7 +1300,7 @@ static __always_inline bool pid_is_control_plane(struct __sk_buff *skb, if (p) { *p = NULL; } - if ((skb->mark & 0x80) == 0x80) { + if ((skb->mark & 0x100) == 0x100) { bpf_printk("No pid_pname found. But it should not happen"); /* if (l4proto == IPPROTO_TCP) { @@ -1934,7 +1936,7 @@ int tproxy_wan_ingress(struct __sk_buff *skb) { } static int __always_inline update_map_elem_by_cookie(const __u64 cookie) { - if (!cookie) { + if (unlikely(!cookie)) { bpf_printk("zero cookie"); return -EINVAL; } @@ -1943,15 +1945,67 @@ static int __always_inline update_map_elem_by_cookie(const __u64 cookie) { // Build value. struct pid_pname val; __builtin_memset(&val, 0, sizeof(struct pid_pname)); - val.pid = bpf_get_current_pid_tgid() >> 32; - // struct task_struct *t = (void *)bpf_get_current_task(); - if ((ret = bpf_get_current_comm(val.pname, sizeof(val.pname)))) { - return ret; + char buf[MAX_ARG_SCANNER_BUFFER_SIZE] = {0}; + struct task_struct *current = (void *)bpf_get_current_task(); + unsigned long arg_start = BPF_CORE_READ(current, mm, arg_start); + unsigned long arg_end = BPF_CORE_READ(current, mm, arg_end); + unsigned long arg_len = arg_end - arg_start; + if (arg_len > MAX_ARG_LEN_TO_PROBE) { + arg_len = MAX_ARG_LEN_TO_PROBE; } + /** + For string like: /usr/lib/sddm/sddm-helper --socket /tmp/sddm-auth1 + We extract "sddm-helper" from it. + */ + unsigned long loc, j; + unsigned long last_slash = -1; +#pragma unroll + for (loc = 0, j = 0; j < MAX_ARG_LEN_TO_PROBE; + ++j, loc = ((loc + 1) & (MAX_ARG_SCANNER_BUFFER_SIZE - 1))) { + // volatile unsigned long k = j; // Cheat to unroll. + // if (arg_start + k >= arg_end) { + if (unlikely(arg_start + j >= arg_end)) { + break; + } + if (unlikely(loc == 0)) { + /// WANRING: Do NOT use bpf_core_read_user_str, it will bring terminator + /// 0. + // __builtin_memset(&buf, 0, MAX_ARG_SCANNER_BUFFER_SIZE); + unsigned long to_read = arg_end - (arg_start + j); + if (to_read >= MAX_ARG_SCANNER_BUFFER_SIZE) { + to_read = MAX_ARG_SCANNER_BUFFER_SIZE; + } else { + buf[to_read] = 0; + } + if ((ret = bpf_core_read_user(&buf, to_read, arg_start + j))) { + bpf_printk("failed to read process name: %d", ret); + return ret; + } + } + if (unlikely(buf[loc] == '/')) { + last_slash = j; + } else if (unlikely(buf[loc] == ' ' || buf[loc] == 0)) { + break; + } + } + ++last_slash; + unsigned long length_cpy = j - last_slash; + if (length_cpy > TASK_COMM_LEN) { + length_cpy = TASK_COMM_LEN; + } + if ((ret = bpf_core_read_user(&val.pname, length_cpy, + arg_start + last_slash))) { + bpf_printk("failed to read process name: %d", ret); + return ret; + } + val.pid = BPF_CORE_READ(current, tgid); + // bpf_printk("a start_end: %lu %lu", arg_start, arg_end); + // bpf_printk("b start_end: %lu %lu", arg_start + last_slash, arg_start + j); + // Update map. - if ((ret = - bpf_map_update_elem(&cookie_pid_map, &cookie, &val, BPF_NOEXIST))) { + if (unlikely(ret = bpf_map_update_elem(&cookie_pid_map, &cookie, &val, + BPF_NOEXIST))) { // bpf_printk("setup_mapping_from_sk: failed update map: %d", ret); return ret; } @@ -1970,7 +2024,7 @@ int tproxy_wan_cg_sock_create(struct bpf_sock *sk) { SEC("cgroup/sock_release") int tproxy_wan_cg_sock_release(struct bpf_sock *sk) { __u64 cookie = bpf_get_socket_cookie(sk); - if (!cookie) { + if (unlikely(!cookie)) { bpf_printk("zero cookie"); return 1; } diff --git a/component/control/routing_matcher_builder.go b/component/control/routing_matcher_builder.go index fb450c0..4bb8d49 100644 --- a/component/control/routing_matcher_builder.go +++ b/component/control/routing_matcher_builder.go @@ -6,6 +6,7 @@ package control import ( + "encoding/binary" "fmt" "github.com/cilium/ebpf" "github.com/v2rayA/dae/common" @@ -14,7 +15,6 @@ import ( "github.com/v2rayA/dae/pkg/config_parser" "net/netip" "strconv" - "unsafe" ) type DomainSet struct { @@ -27,7 +27,7 @@ type RoutingMatcherBuilder struct { *routing.DefaultMatcherBuilder outboundName2Id map[string]uint8 bpf *bpfObjects - rules []bpfMatchSet + rules []_bpfMatchSet SimulatedLpmTries [][]netip.Prefix SimulatedDomainSet []DomainSet Final string @@ -74,7 +74,7 @@ func (b *RoutingMatcherBuilder) AddDomain(f *config_parser.Function, key string, RuleIndex: len(b.rules), Domains: values, }) - b.rules = append(b.rules, bpfMatchSet{ + b.rules = append(b.rules, _bpfMatchSet{ Type: uint8(consts.MatchType_DomainSet), Not: f.Not, Outbound: b.OutboundToId(outbound), @@ -94,12 +94,14 @@ 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{ + set := _bpfMatchSet{ + Value: [16]byte{}, Type: uint8(consts.MatchType_Mac), - Value: uint32(lpmTrieIndex), Not: f.Not, Outbound: b.OutboundToId(outbound), - }) + } + binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex)) + b.rules = append(b.rules, set) } @@ -109,12 +111,14 @@ 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{ + set := _bpfMatchSet{ + Value: [16]byte{}, Type: uint8(consts.MatchType_IpSet), - Value: uint32(lpmTrieIndex), Not: f.Not, Outbound: b.OutboundToId(outbound), - }) + } + binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex)) + b.rules = append(b.rules, set) } func (b *RoutingMatcherBuilder) AddPort(f *config_parser.Function, values [][2]uint16, _outbound string) { @@ -123,7 +127,7 @@ func (b *RoutingMatcherBuilder) AddPort(f *config_parser.Function, values [][2]u if i == len(values)-1 { outbound = _outbound } - b.rules = append(b.rules, bpfMatchSet{ + b.rules = append(b.rules, _bpfMatchSet{ Type: uint8(consts.MatchType_Port), Value: _bpfPortRange{ PortStart: value[0], @@ -141,12 +145,14 @@ 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{ + set := _bpfMatchSet{ + Value: [16]byte{}, Type: uint8(consts.MatchType_SourceIpSet), - Value: uint32(lpmTrieIndex), Not: f.Not, Outbound: b.OutboundToId(outbound), - }) + } + binary.LittleEndian.PutUint32(set.Value[:], uint32(lpmTrieIndex)) + b.rules = append(b.rules, set) } func (b *RoutingMatcherBuilder) AddSourcePort(f *config_parser.Function, values [][2]uint16, _outbound string) { @@ -155,7 +161,7 @@ func (b *RoutingMatcherBuilder) AddSourcePort(f *config_parser.Function, values if i == len(values)-1 { outbound = _outbound } - b.rules = append(b.rules, bpfMatchSet{ + b.rules = append(b.rules, _bpfMatchSet{ Type: uint8(consts.MatchType_SourcePort), Value: _bpfPortRange{ PortStart: value[0], @@ -171,9 +177,9 @@ func (b *RoutingMatcherBuilder) AddL4Proto(f *config_parser.Function, values con if b.err != nil { return } - b.rules = append(b.rules, bpfMatchSet{ + b.rules = append(b.rules, _bpfMatchSet{ + Value: [16]byte{byte(values)}, Type: uint8(consts.MatchType_L4Proto), - Value: uint32(values), Not: f.Not, Outbound: b.OutboundToId(outbound), }) @@ -183,9 +189,9 @@ func (b *RoutingMatcherBuilder) AddIpVersion(f *config_parser.Function, values c if b.err != nil { return } - b.rules = append(b.rules, bpfMatchSet{ + b.rules = append(b.rules, _bpfMatchSet{ + Value: [16]byte{byte(values)}, Type: uint8(consts.MatchType_IpVersion), - Value: uint32(values), Not: f.Not, Outbound: b.OutboundToId(outbound), }) @@ -197,12 +203,12 @@ func (b *RoutingMatcherBuilder) AddProcessName(f *config_parser.Function, values if i == len(values)-1 { outbound = _outbound } - matchSet := bpfMatchSet{ + matchSet := _bpfMatchSet{ Type: uint8(consts.MatchType_ProcessName), Not: f.Not, Outbound: b.OutboundToId(outbound), } - copy((*(*[16]byte)(unsafe.Pointer(&matchSet.Value)))[:], value[:]) + copy(matchSet.Value[:], value[:]) b.rules = append(b.rules, matchSet) } } @@ -212,7 +218,7 @@ func (b *RoutingMatcherBuilder) AddFinal(outbound string) { return } b.Final = outbound - b.rules = append(b.rules, bpfMatchSet{ + b.rules = append(b.rules, _bpfMatchSet{ Type: uint8(consts.MatchType_Final), Outbound: b.OutboundToId(outbound), }) diff --git a/component/outbound/dialer/direct.go b/component/outbound/dialer/direct.go index 0abb632..37972bd 100644 --- a/component/outbound/dialer/direct.go +++ b/component/outbound/dialer/direct.go @@ -106,7 +106,7 @@ func init() { func SoMarkControl(c syscall.RawConn) error { return c.Control(func(fd uintptr) { //TODO: force to set 0xff. any chances to customize this value? - err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, fwmarkIoctl, 0x80) + err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, fwmarkIoctl, 0x100) if err != nil { return } diff --git a/example.dae b/example.dae index 49f2515..4f016f7 100644 --- a/example.dae +++ b/example.dae @@ -65,7 +65,8 @@ routing { # See routing.md for full examples. ip(1.1.1.1) && port(53) -> my_group - pname(firefox) && domain(ip.sb) -> direct # pname like firefox not works yet [ FIXME ] + + pname(firefox) && domain(ip.sb) -> direct pname(curl) && domain(ip.sb) -> my_group ip(geoip:private) -> direct diff --git a/routing.md b/routing.md index 0a2d0f2..d0e5c73 100644 --- a/routing.md +++ b/routing.md @@ -45,7 +45,7 @@ ipversion(6) -> ipv6_group # Source MAC rule mac('02:42:ac:11:00:02') -> direct -# Process Name rule (Only support local process) +# Process Name rule (only support local process) pname(curl) -> direct # Multiple domains rule