feat: support real process name traffic split (#6)

This commit is contained in:
mzz 2023-02-04 11:24:03 +08:00 committed by GitHub
parent 0793534f72
commit b117dfa7d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 178 additions and 74 deletions

View File

@ -92,6 +92,7 @@ jobs:
- name: Get project dependencies - name: Get project dependencies
run: | run: |
git submodule update --init --recursive
go mod download go mod download
- name: Build dae - name: Build dae

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "component/control/kern/headers"]
path = component/control/kern/headers
url = https://github.com/v2rayA/dae_bpf_headers

View File

@ -4,7 +4,7 @@
***dae***, means goose, is a lightweight and high-performance transparent proxy solution. ***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. 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. 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. 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 ## 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. 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. Domain routing performance optimization.
1. DisableL4Checksum by link. 1. DisableL4Checksum by link.
1. Handle the case that nodes do not support UDP. 1. Handle the case that nodes do not support UDP.
1. Handle the case that nodes do not support IPv6.
1. L4Checksum problem. 1. L4Checksum problem.
1. MACv2 extension extraction. 1. MACv2 extension extraction.
1. ... 1. ...

View File

@ -98,4 +98,4 @@ const (
var BasicFeatureVersion = internal.Version{5, 2, 0} var BasicFeatureVersion = internal.Version{5, 2, 0}
var FtraceFeatureVersion = internal.Version{5, 5, 0} var FtraceFeatureVersion = internal.Version{5, 5, 0}
var CgGetPidFeatureVersion = internal.Version{5, 7, 0} var CgSocketCookieFeatureVersion = internal.Version{5, 7, 0}

View File

@ -29,11 +29,19 @@ type _bpfPortRange struct {
PortEnd uint16 PortEnd uint16
} }
func (r _bpfPortRange) Encode() uint32 { type _bpfMatchSet struct {
var b [4]byte // TODO: Need sync with C code.
binary.BigEndian.PutUint16(b[:2], r.PortStart) Value [16]byte
binary.BigEndian.PutUint16(b[2:], r.PortEnd) Type uint8
return binary.BigEndian.Uint32(b[:]) 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) { func (o *bpfObjects) newLpmMap(keys []_bpfLpmKey, values []uint32) (m *ebpf.Map, err error) {

View File

@ -69,6 +69,10 @@ func NewControlPlane(
if kernelVersion.Less(consts.BasicFeatureVersion) { 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()) 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. // Allow the current process to lock memory for eBPF resources.
if err = rlimit.RemoveMemlock(); err != nil { if err = rlimit.RemoveMemlock(); err != nil {
@ -83,7 +87,8 @@ func NewControlPlane(
var ProgramOptions ebpf.ProgramOptions var ProgramOptions ebpf.ProgramOptions
if log.IsLevelEnabled(logrus.TraceLevel) { if log.IsLevelEnabled(logrus.TraceLevel) {
ProgramOptions = ebpf.ProgramOptions{ 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 { if len(lanInterface) > 0 && len(wanInterface) == 0 {
// Only bind LAN. // Only bind LAN.
obj = &bpfObjectsLan{} obj = &bpfObjectsLan{}
} else if len(wanInterface) == 0 && len(wanInterface) > 0 { } else if len(lanInterface) == 0 && len(wanInterface) > 0 {
// Only bind to WAN. // Only bind to WAN.
// Trick. Replace the beams with rotten timbers. // Trick. Replace the beams with rotten timbers.
obj = &bpfObjectsWan{} obj = &bpfObjectsWan{}

View File

@ -164,10 +164,6 @@ func (c *ControlPlaneCore) BindLan(ifname string) error {
} }
func (c *ControlPlaneCore) BindWan(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) c.log.Infof("Bind to WAN: %v", ifname)
link, err := netlink.LinkByName(ifname) link, err := netlink.LinkByName(ifname)
if err != nil { if err != nil {

@ -0,0 +1 @@
Subproject commit 372c3cc61d2d907b89ebdfb7bec180a09cd28169

View File

@ -3,20 +3,17 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
* Copyright (c) since 2022, v2rayA Organization <team@v2raya.org> * Copyright (c) since 2022, v2rayA Organization <team@v2raya.org>
*/ */
#include <asm-generic/errno-base.h> #include "headers/if_ether_defs.h"
#include <linux/bpf.h> #include "headers/pkt_cls_defs.h"
#include <linux/if_ether.h> #include "headers/socket_defs.h"
#include <linux/in.h> #include "headers/vmlinux.h"
#include <linux/in6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <stdbool.h>
#include <asm-generic/errno-base.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_endian.h> #include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h> #include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
// #define __DEBUG_ROUTING // #define __DEBUG_ROUTING
// #define __PRINT_ROUTING_RESULT // #define __PRINT_ROUTING_RESULT
@ -53,6 +50,8 @@
#define MAX_DST_MAPPING_NUM (65536 * 2) #define MAX_DST_MAPPING_NUM (65536 * 2)
#define MAX_SRC_PID_PNAME_MAPPING_NUM (65536) #define MAX_SRC_PID_PNAME_MAPPING_NUM (65536)
#define IPV6_MAX_EXTENSIONS 4 #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_DIRECT 0
#define OUTBOUND_BLOCK 1 #define OUTBOUND_BLOCK 1
@ -229,6 +228,7 @@ struct port_range {
*/ */
struct match_set { struct match_set {
union { union {
/// NOTICE: MUST sync with component/control/bpf_utils.go.
__u32 __value; // Placeholder for bpf2go. __u32 __value; // Placeholder for bpf2go.
__u32 index; __u32 index;
@ -827,13 +827,13 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4],
/// TODO: BPF_MAP_UPDATE_BATCH ? /// TODO: BPF_MAP_UPDATE_BATCH ?
__u32 key = MatchType_L4Proto; __u32 key = MatchType_L4Proto;
if ((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key, &_l4proto_type, if (unlikely((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key,
BPF_ANY))) { &_l4proto_type, BPF_ANY)))) {
return ret; return ret;
}; };
key = MatchType_IpVersion; key = MatchType_IpVersion;
if ((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key, &_ipversion_type, if (unlikely((ret = bpf_map_update_elem(&l4proto_ipversion_map, &key,
BPF_ANY))) { &_ipversion_type, BPF_ANY)))) {
return ret; return ret;
}; };
@ -849,11 +849,13 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4],
} }
key = MatchType_SourcePort; 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; return ret;
}; };
key = MatchType_Port; 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; 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) { if (h_dport == 53 && _l4proto_type == L4ProtoType_UDP) {
struct ip_port *upstream = struct ip_port *upstream =
bpf_map_lookup_elem(&dns_upstream_map, &zero_key); bpf_map_lookup_elem(&dns_upstream_map, &zero_key);
if (!upstream) { if (unlikely(!upstream)) {
return -EFAULT; return -EFAULT;
} }
h_dport = bpf_ntohs(upstream->port); 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); __builtin_memcpy(lpm_key_instance.data, daddr, IPV6_BYTE_LENGTH);
// bpf_printk("mac: %pI6", mac); // bpf_printk("mac: %pI6", mac);
key = MatchType_IpSet; key = MatchType_IpSet;
if ((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, if (unlikely((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance,
BPF_ANY))) { BPF_ANY)))) {
return ret; return ret;
}; };
__builtin_memcpy(lpm_key_instance.data, saddr, IPV6_BYTE_LENGTH); __builtin_memcpy(lpm_key_instance.data, saddr, IPV6_BYTE_LENGTH);
key = MatchType_SourceIpSet; key = MatchType_SourceIpSet;
if ((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, if (unlikely((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance,
BPF_ANY))) { BPF_ANY)))) {
return ret; return ret;
}; };
if (!_is_wan) { if (!_is_wan) {
__builtin_memcpy(lpm_key_instance.data, mac, IPV6_BYTE_LENGTH); __builtin_memcpy(lpm_key_instance.data, mac, IPV6_BYTE_LENGTH);
key = MatchType_Mac; key = MatchType_Mac;
if ((ret = bpf_map_update_elem(&lpm_key_map, &key, &lpm_key_instance, if (unlikely((ret = bpf_map_update_elem(&lpm_key_map, &key,
BPF_ANY))) { &lpm_key_instance, BPF_ANY)))) {
return ret; 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++) { for (__u32 i = 0; i < MAX_MATCH_SET_LEN; i++) {
__u32 k = i; // Clone to pass code checker. __u32 k = i; // Clone to pass code checker.
match_set = bpf_map_lookup_elem(&routing_map, &k); match_set = bpf_map_lookup_elem(&routing_map, &k);
if (!match_set) { if (unlikely(!match_set)) {
return -EFAULT; return -EFAULT;
} }
if (bad_rule || good_subrule) { 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) { if ((domain_routing->bitmap[i / 32] >> (i % 32)) & 1) {
good_subrule = true; good_subrule = true;
} }
} else if (_is_wan && match_set->type == MatchType_ProcessName) { } else if (match_set->type == MatchType_ProcessName) {
if ((equal_ipv6_format(match_set->pname, _pname))) { if (_is_wan && equal_ipv6_format(match_set->pname, _pname)) {
good_subrule = true; good_subrule = true;
} }
} else if (match_set->type == MatchType_Final) { } 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) { if (p) {
*p = NULL; *p = NULL;
} }
if ((skb->mark & 0x80) == 0x80) { if ((skb->mark & 0x100) == 0x100) {
bpf_printk("No pid_pname found. But it should not happen"); bpf_printk("No pid_pname found. But it should not happen");
/* /*
if (l4proto == IPPROTO_TCP) { 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) { static int __always_inline update_map_elem_by_cookie(const __u64 cookie) {
if (!cookie) { if (unlikely(!cookie)) {
bpf_printk("zero cookie"); bpf_printk("zero cookie");
return -EINVAL; return -EINVAL;
} }
@ -1943,15 +1945,67 @@ static int __always_inline update_map_elem_by_cookie(const __u64 cookie) {
// Build value. // Build value.
struct pid_pname val; struct pid_pname val;
__builtin_memset(&val, 0, sizeof(struct pid_pname)); __builtin_memset(&val, 0, sizeof(struct pid_pname));
val.pid = bpf_get_current_pid_tgid() >> 32; char buf[MAX_ARG_SCANNER_BUFFER_SIZE] = {0};
// struct task_struct *t = (void *)bpf_get_current_task(); struct task_struct *current = (void *)bpf_get_current_task();
if ((ret = bpf_get_current_comm(val.pname, sizeof(val.pname)))) { unsigned long arg_start = BPF_CORE_READ(current, mm, arg_start);
return ret; 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. // Update map.
if ((ret = if (unlikely(ret = bpf_map_update_elem(&cookie_pid_map, &cookie, &val,
bpf_map_update_elem(&cookie_pid_map, &cookie, &val, BPF_NOEXIST))) { BPF_NOEXIST))) {
// bpf_printk("setup_mapping_from_sk: failed update map: %d", ret); // bpf_printk("setup_mapping_from_sk: failed update map: %d", ret);
return ret; return ret;
} }
@ -1970,7 +2024,7 @@ int tproxy_wan_cg_sock_create(struct bpf_sock *sk) {
SEC("cgroup/sock_release") SEC("cgroup/sock_release")
int tproxy_wan_cg_sock_release(struct bpf_sock *sk) { int tproxy_wan_cg_sock_release(struct bpf_sock *sk) {
__u64 cookie = bpf_get_socket_cookie(sk); __u64 cookie = bpf_get_socket_cookie(sk);
if (!cookie) { if (unlikely(!cookie)) {
bpf_printk("zero cookie"); bpf_printk("zero cookie");
return 1; return 1;
} }

View File

@ -6,6 +6,7 @@
package control package control
import ( import (
"encoding/binary"
"fmt" "fmt"
"github.com/cilium/ebpf" "github.com/cilium/ebpf"
"github.com/v2rayA/dae/common" "github.com/v2rayA/dae/common"
@ -14,7 +15,6 @@ import (
"github.com/v2rayA/dae/pkg/config_parser" "github.com/v2rayA/dae/pkg/config_parser"
"net/netip" "net/netip"
"strconv" "strconv"
"unsafe"
) )
type DomainSet struct { type DomainSet struct {
@ -27,7 +27,7 @@ type RoutingMatcherBuilder struct {
*routing.DefaultMatcherBuilder *routing.DefaultMatcherBuilder
outboundName2Id map[string]uint8 outboundName2Id map[string]uint8
bpf *bpfObjects bpf *bpfObjects
rules []bpfMatchSet rules []_bpfMatchSet
SimulatedLpmTries [][]netip.Prefix SimulatedLpmTries [][]netip.Prefix
SimulatedDomainSet []DomainSet SimulatedDomainSet []DomainSet
Final string Final string
@ -74,7 +74,7 @@ func (b *RoutingMatcherBuilder) AddDomain(f *config_parser.Function, key string,
RuleIndex: len(b.rules), RuleIndex: len(b.rules),
Domains: values, Domains: values,
}) })
b.rules = append(b.rules, bpfMatchSet{ b.rules = append(b.rules, _bpfMatchSet{
Type: uint8(consts.MatchType_DomainSet), Type: uint8(consts.MatchType_DomainSet),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), Outbound: b.OutboundToId(outbound),
@ -94,12 +94,14 @@ func (b *RoutingMatcherBuilder) AddSourceMac(f *config_parser.Function, macAddrs
} }
lpmTrieIndex := len(b.SimulatedLpmTries) lpmTrieIndex := len(b.SimulatedLpmTries)
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values) b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
b.rules = append(b.rules, bpfMatchSet{ set := _bpfMatchSet{
Value: [16]byte{},
Type: uint8(consts.MatchType_Mac), Type: uint8(consts.MatchType_Mac),
Value: uint32(lpmTrieIndex),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), 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) lpmTrieIndex := len(b.SimulatedLpmTries)
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values) b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
b.rules = append(b.rules, bpfMatchSet{ set := _bpfMatchSet{
Value: [16]byte{},
Type: uint8(consts.MatchType_IpSet), Type: uint8(consts.MatchType_IpSet),
Value: uint32(lpmTrieIndex),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), 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) { 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 { if i == len(values)-1 {
outbound = _outbound outbound = _outbound
} }
b.rules = append(b.rules, bpfMatchSet{ b.rules = append(b.rules, _bpfMatchSet{
Type: uint8(consts.MatchType_Port), Type: uint8(consts.MatchType_Port),
Value: _bpfPortRange{ Value: _bpfPortRange{
PortStart: value[0], PortStart: value[0],
@ -141,12 +145,14 @@ func (b *RoutingMatcherBuilder) AddSourceIp(f *config_parser.Function, values []
} }
lpmTrieIndex := len(b.SimulatedLpmTries) lpmTrieIndex := len(b.SimulatedLpmTries)
b.SimulatedLpmTries = append(b.SimulatedLpmTries, values) b.SimulatedLpmTries = append(b.SimulatedLpmTries, values)
b.rules = append(b.rules, bpfMatchSet{ set := _bpfMatchSet{
Value: [16]byte{},
Type: uint8(consts.MatchType_SourceIpSet), Type: uint8(consts.MatchType_SourceIpSet),
Value: uint32(lpmTrieIndex),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), 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) { 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 { if i == len(values)-1 {
outbound = _outbound outbound = _outbound
} }
b.rules = append(b.rules, bpfMatchSet{ b.rules = append(b.rules, _bpfMatchSet{
Type: uint8(consts.MatchType_SourcePort), Type: uint8(consts.MatchType_SourcePort),
Value: _bpfPortRange{ Value: _bpfPortRange{
PortStart: value[0], PortStart: value[0],
@ -171,9 +177,9 @@ func (b *RoutingMatcherBuilder) AddL4Proto(f *config_parser.Function, values con
if b.err != nil { if b.err != nil {
return return
} }
b.rules = append(b.rules, bpfMatchSet{ b.rules = append(b.rules, _bpfMatchSet{
Value: [16]byte{byte(values)},
Type: uint8(consts.MatchType_L4Proto), Type: uint8(consts.MatchType_L4Proto),
Value: uint32(values),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), Outbound: b.OutboundToId(outbound),
}) })
@ -183,9 +189,9 @@ func (b *RoutingMatcherBuilder) AddIpVersion(f *config_parser.Function, values c
if b.err != nil { if b.err != nil {
return return
} }
b.rules = append(b.rules, bpfMatchSet{ b.rules = append(b.rules, _bpfMatchSet{
Value: [16]byte{byte(values)},
Type: uint8(consts.MatchType_IpVersion), Type: uint8(consts.MatchType_IpVersion),
Value: uint32(values),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), Outbound: b.OutboundToId(outbound),
}) })
@ -197,12 +203,12 @@ func (b *RoutingMatcherBuilder) AddProcessName(f *config_parser.Function, values
if i == len(values)-1 { if i == len(values)-1 {
outbound = _outbound outbound = _outbound
} }
matchSet := bpfMatchSet{ matchSet := _bpfMatchSet{
Type: uint8(consts.MatchType_ProcessName), Type: uint8(consts.MatchType_ProcessName),
Not: f.Not, Not: f.Not,
Outbound: b.OutboundToId(outbound), Outbound: b.OutboundToId(outbound),
} }
copy((*(*[16]byte)(unsafe.Pointer(&matchSet.Value)))[:], value[:]) copy(matchSet.Value[:], value[:])
b.rules = append(b.rules, matchSet) b.rules = append(b.rules, matchSet)
} }
} }
@ -212,7 +218,7 @@ func (b *RoutingMatcherBuilder) AddFinal(outbound string) {
return return
} }
b.Final = outbound b.Final = outbound
b.rules = append(b.rules, bpfMatchSet{ b.rules = append(b.rules, _bpfMatchSet{
Type: uint8(consts.MatchType_Final), Type: uint8(consts.MatchType_Final),
Outbound: b.OutboundToId(outbound), Outbound: b.OutboundToId(outbound),
}) })

View File

@ -106,7 +106,7 @@ func init() {
func SoMarkControl(c syscall.RawConn) error { func SoMarkControl(c syscall.RawConn) error {
return c.Control(func(fd uintptr) { return c.Control(func(fd uintptr) {
//TODO: force to set 0xff. any chances to customize this value? //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 { if err != nil {
return return
} }

View File

@ -65,7 +65,8 @@ routing {
# See routing.md for full examples. # See routing.md for full examples.
ip(1.1.1.1) && port(53) -> my_group 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 pname(curl) && domain(ip.sb) -> my_group
ip(geoip:private) -> direct ip(geoip:private) -> direct

View File

@ -45,7 +45,7 @@ ipversion(6) -> ipv6_group
# Source MAC rule # Source MAC rule
mac('02:42:ac:11:00:02') -> direct 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 pname(curl) -> direct
# Multiple domains rule # Multiple domains rule