From d5e4f83c7c10795a21dc8495b60d6e9ff74f8cd3 Mon Sep 17 00:00:00 2001 From: Ycarus Date: Mon, 31 Dec 2018 14:52:47 +0100 Subject: [PATCH] Add eBPF support to ShadowSocks --- libbpf/Makefile | 52 ++ shadowsocks-libev/Makefile | 6 +- .../files/shadowsocks-libev.init | 8 +- shadowsocks-libev/patches/030-eBPF.patch | 884 ++++++++++++++++++ 4 files changed, 947 insertions(+), 3 deletions(-) create mode 100644 libbpf/Makefile create mode 100644 shadowsocks-libev/patches/030-eBPF.patch diff --git a/libbpf/Makefile b/libbpf/Makefile new file mode 100644 index 000000000..69dcfb27c --- /dev/null +++ b/libbpf/Makefile @@ -0,0 +1,52 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=libbpf +PKG_VERSION:=556e0a0 +PKG_RELEASE:=1 + +PKG_SOURCE_URL:=https://github.com/libbpf/libbpf.git +PKG_SOURCE_VERSION:=556e0a0def956ece0476d99ccb0570ee4ecd4d23 +PKG_MAINTAINER:=Yannick Chabanois (Ycarus) + +PKG_SOURCE_PROTO:=git + +include $(INCLUDE_DIR)/package.mk + +define Package/libbpf + SECTION:=net + CATEGORY:=Network + TITLE:=libbpf + DEPENDS:=+libstdcpp +endef + +define Package/libbpf/description + libbpf +endef + +#EXTRA_CPPFLAGS += -fpermissive -Wno-variadic-macros + +TARGET_CFLAGS += -DCOMPAT_NEED_REALLOCARRAY + +define Build/Configure +endef + +define Build/Compile + BUILD_SHARED=y $(MAKE) -C $(PKG_BUILD_DIR)/src \ + CC="$(TARGET_CC)" \ + CFLAGS="$(TARGET_CFLAGS)" \ + LDFLAGS="$(TARGET_LDFLAGS)" +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_BUILD_DIR)/src/libbpf.so $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/include/bpf + $(CP) $(PKG_BUILD_DIR)/src/*.h $(1)/usr/include/bpf +endef + +define Package/libbpf/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_BUILD_DIR)/src/libbpf.so $(1)/usr/lib +endef + +$(eval $(call BuildPackage,libbpf)) diff --git a/shadowsocks-libev/Makefile b/shadowsocks-libev/Makefile index de08fa778..bb578a74c 100644 --- a/shadowsocks-libev/Makefile +++ b/shadowsocks-libev/Makefile @@ -32,7 +32,7 @@ PKG_USE_MIPS16:=0 PKG_BUILD_PARALLEL:=1 include $(INCLUDE_DIR)/package.mk - +include $(INCLUDE_DIR)/kernel.mk define Package/shadowsocks-libev-config SECTION:=net @@ -57,7 +57,7 @@ define Package/shadowsocks-libev/Default SUBMENU:=Web Servers/Proxies TITLE:=shadowsocks-libev $(1) URL:=https://github.com/shadowsocks/shadowsocks-libev - DEPENDS:=+libcares +libev +libmbedtls +libpcre +libpthread +libsodium +shadowsocks-libev-config +zlib + DEPENDS:=+libcares +libev +libmbedtls +libpcre +libpthread +libsodium +shadowsocks-libev-config +zlib +libpcap +libbpf +libcap +libstdcpp +libelf endef define Package/shadowsocks-libev-$(1)/install @@ -122,6 +122,8 @@ CONFIGURE_ARGS += \ --disable-assert \ --disable-ssp \ +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include + $(eval $(call BuildPackage,shadowsocks-libev-config)) $(eval $(call BuildPackage,shadowsocks-libev-ss-rules)) $(foreach component,$(SHADOWSOCKS_COMPONENTS), \ diff --git a/shadowsocks-libev/files/shadowsocks-libev.init b/shadowsocks-libev/files/shadowsocks-libev.init index a83917748..d90da2c4c 100644 --- a/shadowsocks-libev/files/shadowsocks-libev.init +++ b/shadowsocks-libev/files/shadowsocks-libev.init @@ -50,6 +50,10 @@ ss_mkjson_server_conf_() { password="${password//\"/\\\"}" key="$(echo $key | sed 's/+/-/g; s/\//_/g;')" [ "$method" = "none" -a -z "$key" ] && key="none" + [ "$method" = "none-ebpf" -a -z "$key" ] && { + key="none" + method="none" + } cat <<-EOF ${server:+${q}server${q}: ${q}$server${q},} "server_port": $server_port, @@ -66,12 +70,14 @@ ss_mkjson_common_conf() { [ "$reuse_port" = 0 ] && reuse_port=false || reuse_port=true [ "$mptcp" = 0 ] && mptcp=false || mptcp=true [ "$syslog" = 0 ] && syslog=false || syslog=true + [ "$method" = "none-ebpf" ] && ebpf=true || ebpf=false cat <<-EOF "use_syslog": $syslog, "ipv6_first": $ipv6_first, "fast_open": $fast_open, "no_delay": $no_delay, "mptcp": $mptcp, + "ebpf": $ebpf, "reuse_port": $reuse_port, ${local_address:+${q}local_address${q}: ${q}$local_address${q},} ${local_port:+${q}local_port${q}: $local_port,} @@ -383,7 +389,7 @@ validate_common_server_options_() { local cfgtype="$1"; shift local cfg="$1"; shift local func="$1"; shift - local stream_methods='"none", "table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", "chacha20-ietf"' + local stream_methods='"none-ebpf", "none", "table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", "chacha20-ietf"' local aead_methods='"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305"' "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ diff --git a/shadowsocks-libev/patches/030-eBPF.patch b/shadowsocks-libev/patches/030-eBPF.patch new file mode 100644 index 000000000..1e8e7b6d7 --- /dev/null +++ b/shadowsocks-libev/patches/030-eBPF.patch @@ -0,0 +1,884 @@ +diff -aurN shadowsocks-libev-nocrypto-nocrypto/configure.ac shadowsocks-libev-nocrypto-ebpf/configure.ac +--- shadowsocks-libev-nocrypto-nocrypto/configure.ac 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/configure.ac 2018-08-14 13:45:18.000000000 +0200 +@@ -245,6 +245,11 @@ + [AC_DEFINE([USE_SYSTEM_SHARED_LIB], [1], [Define if use system shared lib.])], + [AC_CONFIG_FILES([libbloom/Makefile libcork/Makefile libipset/Makefile])]) + ++AC_CHECK_HEADERS([linux/bpf.h bpf/bpf.h bpf/libbpf.h], [], ++ [AC_MSG_ERROR([Missing libbpf headers. Install libbpf from kernel library sources])]) ++ ++AC_CHECK_LIB([elf], [elf_getdata], [LIBS="-lelf $LIBS"], [AC_MSG_ERROR([Couldn't find libelf])]) ++ + AC_ARG_ENABLE(connmarktos, + [AS_HELP_STRING(--enable-connmarktos, Enable saved connmark to IP TOS QoS feature)], + [ +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/bpf_helpers.h shadowsocks-libev-nocrypto-ebpf/src/bpf_helpers.h +--- shadowsocks-libev-nocrypto-nocrypto/src/bpf_helpers.h 1970-01-01 01:00:00.000000000 +0100 ++++ shadowsocks-libev-nocrypto-ebpf/src/bpf_helpers.h 2018-08-14 13:45:18.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __BPF_HELPERS_H ++#define __BPF_HELPERS_H ++ ++/* helper macro to place programs, maps, license in ++ * different sections in elf_bpf file. Section names ++ * are interpreted by elf_bpf loader ++ */ ++#define SEC(NAME) __attribute__((section(NAME), used)) ++ ++/* helper functions called from eBPF programs written in C */ ++static void *(*bpf_map_lookup_elem)(void *map, void *key) = ++ (void *) BPF_FUNC_map_lookup_elem; ++static int (*bpf_map_update_elem)(void *map, void *key, void *value, ++ unsigned long long flags) = ++ (void *) BPF_FUNC_map_update_elem; ++static int (*bpf_map_delete_elem)(void *map, void *key) = ++ (void *) BPF_FUNC_map_delete_elem; ++static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = ++ (void *) BPF_FUNC_probe_read; ++static unsigned long long (*bpf_ktime_get_ns)(void) = ++ (void *) BPF_FUNC_ktime_get_ns; ++static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = ++ (void *) BPF_FUNC_trace_printk; ++static void (*bpf_tail_call)(void *ctx, void *map, int index) = ++ (void *) BPF_FUNC_tail_call; ++static unsigned long long (*bpf_get_smp_processor_id)(void) = ++ (void *) BPF_FUNC_get_smp_processor_id; ++static unsigned long long (*bpf_get_current_pid_tgid)(void) = ++ (void *) BPF_FUNC_get_current_pid_tgid; ++static unsigned long long (*bpf_get_current_uid_gid)(void) = ++ (void *) BPF_FUNC_get_current_uid_gid; ++static int (*bpf_get_current_comm)(void *buf, int buf_size) = ++ (void *) BPF_FUNC_get_current_comm; ++static unsigned long long (*bpf_perf_event_read)(void *map, ++ unsigned long long flags) = ++ (void *) BPF_FUNC_perf_event_read; ++static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) = ++ (void *) BPF_FUNC_clone_redirect; ++static int (*bpf_redirect)(int ifindex, int flags) = ++ (void *) BPF_FUNC_redirect; ++static int (*bpf_redirect_map)(void *map, int key, int flags) = ++ (void *) BPF_FUNC_redirect_map; ++static int (*bpf_perf_event_output)(void *ctx, void *map, ++ unsigned long long flags, void *data, ++ int size) = ++ (void *) BPF_FUNC_perf_event_output; ++static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = ++ (void *) BPF_FUNC_get_stackid; ++static int (*bpf_probe_write_user)(void *dst, void *src, int size) = ++ (void *) BPF_FUNC_probe_write_user; ++static int (*bpf_current_task_under_cgroup)(void *map, int index) = ++ (void *) BPF_FUNC_current_task_under_cgroup; ++static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = ++ (void *) BPF_FUNC_skb_get_tunnel_key; ++static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = ++ (void *) BPF_FUNC_skb_set_tunnel_key; ++static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = ++ (void *) BPF_FUNC_skb_get_tunnel_opt; ++static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = ++ (void *) BPF_FUNC_skb_set_tunnel_opt; ++static unsigned long long (*bpf_get_prandom_u32)(void) = ++ (void *) BPF_FUNC_get_prandom_u32; ++static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = ++ (void *) BPF_FUNC_xdp_adjust_head; ++static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, ++ int optlen) = ++ (void *) BPF_FUNC_setsockopt; ++static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) = ++ (void *) BPF_FUNC_sk_redirect_map; ++static int (*bpf_sock_map_update)(void *map, void *key, void *value, ++ unsigned long long flags) = ++ (void *) BPF_FUNC_sock_map_update; ++ ++ ++/* llvm builtin functions that eBPF C program may use to ++ * emit BPF_LD_ABS and BPF_LD_IND instructions ++ */ ++struct sk_buff; ++unsigned long long load_byte(void *skb, ++ unsigned long long off) asm("llvm.bpf.load.byte"); ++unsigned long long load_half(void *skb, ++ unsigned long long off) asm("llvm.bpf.load.half"); ++unsigned long long load_word(void *skb, ++ unsigned long long off) asm("llvm.bpf.load.word"); ++ ++/* a helper structure used by eBPF C program ++ * to describe map attributes to elf_bpf loader ++ */ ++struct bpf_map_def { ++ unsigned int type; ++ unsigned int key_size; ++ unsigned int value_size; ++ unsigned int max_entries; ++ unsigned int map_flags; ++ unsigned int inner_map_idx; ++ unsigned int numa_node; ++}; ++ ++static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = ++ (void *) BPF_FUNC_skb_load_bytes; ++static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = ++ (void *) BPF_FUNC_skb_store_bytes; ++static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = ++ (void *) BPF_FUNC_l3_csum_replace; ++static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = ++ (void *) BPF_FUNC_l4_csum_replace; ++static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = ++ (void *) BPF_FUNC_skb_under_cgroup; ++static int (*bpf_skb_change_head)(void *, int len, int flags) = ++ (void *) BPF_FUNC_skb_change_head; ++ ++#if defined(__x86_64__) ++ ++#define PT_REGS_PARM1(x) ((x)->di) ++#define PT_REGS_PARM2(x) ((x)->si) ++#define PT_REGS_PARM3(x) ((x)->dx) ++#define PT_REGS_PARM4(x) ((x)->cx) ++#define PT_REGS_PARM5(x) ((x)->r8) ++#define PT_REGS_RET(x) ((x)->sp) ++#define PT_REGS_FP(x) ((x)->bp) ++#define PT_REGS_RC(x) ((x)->ax) ++#define PT_REGS_SP(x) ((x)->sp) ++#define PT_REGS_IP(x) ((x)->ip) ++ ++#elif defined(__s390x__) ++ ++#define PT_REGS_PARM1(x) ((x)->gprs[2]) ++#define PT_REGS_PARM2(x) ((x)->gprs[3]) ++#define PT_REGS_PARM3(x) ((x)->gprs[4]) ++#define PT_REGS_PARM4(x) ((x)->gprs[5]) ++#define PT_REGS_PARM5(x) ((x)->gprs[6]) ++#define PT_REGS_RET(x) ((x)->gprs[14]) ++#define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ ++#define PT_REGS_RC(x) ((x)->gprs[2]) ++#define PT_REGS_SP(x) ((x)->gprs[15]) ++#define PT_REGS_IP(x) ((x)->psw.addr) ++ ++#elif defined(__aarch64__) ++ ++#define PT_REGS_PARM1(x) ((x)->regs[0]) ++#define PT_REGS_PARM2(x) ((x)->regs[1]) ++#define PT_REGS_PARM3(x) ((x)->regs[2]) ++#define PT_REGS_PARM4(x) ((x)->regs[3]) ++#define PT_REGS_PARM5(x) ((x)->regs[4]) ++#define PT_REGS_RET(x) ((x)->regs[30]) ++#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ ++#define PT_REGS_RC(x) ((x)->regs[0]) ++#define PT_REGS_SP(x) ((x)->sp) ++#define PT_REGS_IP(x) ((x)->pc) ++ ++#elif defined(__mips__) ++ ++#define PT_REGS_PARM1(x) ((x)->regs[4]) ++#define PT_REGS_PARM2(x) ((x)->regs[5]) ++#define PT_REGS_PARM3(x) ((x)->regs[6]) ++#define PT_REGS_PARM4(x) ((x)->regs[7]) ++#define PT_REGS_PARM5(x) ((x)->regs[8]) ++#define PT_REGS_RET(x) ((x)->regs[31]) ++#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */ ++#define PT_REGS_RC(x) ((x)->regs[1]) ++#define PT_REGS_SP(x) ((x)->regs[29]) ++#define PT_REGS_IP(x) ((x)->cp0_epc) ++ ++#elif defined(__powerpc__) ++ ++#define PT_REGS_PARM1(x) ((x)->gpr[3]) ++#define PT_REGS_PARM2(x) ((x)->gpr[4]) ++#define PT_REGS_PARM3(x) ((x)->gpr[5]) ++#define PT_REGS_PARM4(x) ((x)->gpr[6]) ++#define PT_REGS_PARM5(x) ((x)->gpr[7]) ++#define PT_REGS_RC(x) ((x)->gpr[3]) ++#define PT_REGS_SP(x) ((x)->sp) ++#define PT_REGS_IP(x) ((x)->nip) ++ ++#elif defined(__sparc__) ++ ++#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0]) ++#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1]) ++#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2]) ++#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3]) ++#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4]) ++#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7]) ++#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0]) ++#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP]) ++#if defined(__arch64__) ++#define PT_REGS_IP(x) ((x)->tpc) ++#else ++#define PT_REGS_IP(x) ((x)->pc) ++#endif ++ ++#endif ++ ++#ifdef __powerpc__ ++#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) ++#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP ++#elif defined(__sparc__) ++#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) ++#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP ++#else ++#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ ++ bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) ++#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \ ++ bpf_probe_read(&(ip), sizeof(ip), \ ++ (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) ++#endif ++ ++#endif +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/common.h shadowsocks-libev-nocrypto-ebpf/src/common.h +--- shadowsocks-libev-nocrypto-nocrypto/src/common.h 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/src/common.h 2018-08-14 13:45:18.000000000 +0200 +@@ -65,6 +65,7 @@ + GETOPT_VAL_ACL, + GETOPT_VAL_MTU, + GETOPT_VAL_MPTCP, ++ GETOPT_VAL_EBPF, + GETOPT_VAL_PLUGIN, + GETOPT_VAL_PLUGIN_OPTS, + GETOPT_VAL_PASSWORD, +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/jconf.c shadowsocks-libev-nocrypto-ebpf/src/jconf.c +--- shadowsocks-libev-nocrypto-nocrypto/src/jconf.c 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/src/jconf.c 2018-08-14 13:45:18.000000000 +0200 +@@ -309,11 +309,16 @@ + check_json_value_type(value, json_integer, + "invalid config file: option 'mtu' must be an integer"); + conf.mtu = value->u.integer; ++ } else if (strcmp(name, "ebpf") == 0) { ++ check_json_value_type(value, json_boolean, ++ "invalid config file: option 'ebpf' must be a boolean"); ++ conf.ebpf = value->u.boolean; + } else if (strcmp(name, "mptcp") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'mptcp' must be a boolean"); + conf.mptcp = value->u.boolean; +- } else if (strcmp(name, "ipv6_first") == 0) { ++ } ++ else if (strcmp(name, "ipv6_first") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/jconf.h shadowsocks-libev-nocrypto-ebpf/src/jconf.h +--- shadowsocks-libev-nocrypto-nocrypto/src/jconf.h 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/src/jconf.h 2018-08-14 13:45:18.000000000 +0200 +@@ -84,6 +84,7 @@ + int mode; + int mtu; + int mptcp; ++ int ebpf; + int ipv6_first; + int no_delay; + } jconf_t; +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/Makefile.am shadowsocks-libev-nocrypto-ebpf/src/Makefile.am +--- shadowsocks-libev-nocrypto-nocrypto/src/Makefile.am 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/src/Makefile.am 2018-08-14 13:45:18.000000000 +0200 +@@ -105,6 +105,9 @@ + ss_redir_CFLAGS = $(AM_CFLAGS) -DMODULE_REDIR + ss_redir_LDADD = $(SS_COMMON_LIBS) + ss_redir_LDADD += -lcares ++ss_redir_LDADD += -lcap ++ss_redir_LDADD += -lbpf ++ss_redir_LDADD += -lelf + endif + + lib_LTLIBRARIES = libshadowsocks-libev.la +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/redir.c shadowsocks-libev-nocrypto-ebpf/src/redir.c +--- shadowsocks-libev-nocrypto-nocrypto/src/redir.c 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/src/redir.c 2018-08-14 13:45:18.000000000 +0200 +@@ -43,6 +43,10 @@ + + #include + ++#include ++#include ++#include ++ + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif +@@ -69,6 +73,9 @@ + #define IP6T_SO_ORIGINAL_DST 80 + #endif + ++#define PARSE_PROG_FILENAME "sockmap_parse.o.ebpf" ++#define VERDICT_PROG_FILENAME "sockmap_verdict.o.ebpf" ++ + static void accept_cb(EV_P_ ev_io *w, int revents); + static void server_recv_cb(EV_P_ ev_io *w, int revents); + static void server_send_cb(EV_P_ ev_io *w, int revents); +@@ -86,7 +93,10 @@ + int verbose = 0; + int reuse_port = 0; ++int ebpf = 0; + + static crypto_t *crypto; ++ebpf_conn_t *connections[65536]; ++obufs_t obufs; + + static int ipv6first = 0; + static int mode = TCP_ONLY; +@@ -128,6 +138,18 @@ + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } + ++int ++is_open(int sock) ++{ ++ int err; ++ err = recv(sock, NULL, 0, MSG_PEEK|MSG_DONTWAIT); ++ if(err == 0) ++ return 1; ++ if(err == -1 && errno == EAGAIN) ++ return 1; ++ return 0; ++} ++ + int + create_and_bind(const char *addr, const char *port) + { +@@ -187,6 +209,316 @@ + return listen_sock; + } + ++int ++bpf_prog_load_obj_buf(void *obj_buf, int obj_buf_sz, const char *file, enum bpf_prog_type type, ++ struct bpf_object **pobj, int *prog_fd) ++{ ++ struct bpf_program *prog; ++ struct bpf_object *obj; ++ int err; ++ ++ obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, file); ++ if (libbpf_get_error(obj)) ++ return -ENOENT; ++ ++ prog = bpf_program__next(NULL, obj); ++ if (!prog) { ++ bpf_object__close(obj); ++ return -ENOENT; ++ } ++ ++ bpf_program__set_type(prog, type); ++ err = bpf_object__load(obj); ++ if (err) { ++ bpf_object__close(obj); ++ return -EINVAL; ++ } ++ ++ *pobj = obj; ++ *prog_fd = bpf_program__fd(prog); ++ return 0; ++} ++ ++int ++read_obj_file_into_buffer(char *fname, void **obj_buf, int *obj_buf_sz) ++{ ++ FILE *fileptr = NULL; ++ char *buf; ++ long file_size, readed; ++ int err; ++ ++ if(obj_buf == NULL || obj_buf_sz == NULL) { ++ LOGE("Invalid arguments for object file read\n"); ++ goto error; ++ } ++ ++ fileptr = fopen(fname, "rb"); ++ if(fileptr == NULL) { ++ ERROR("fopen"); ++ goto error; ++ } ++ err = fseek(fileptr, 0, SEEK_END); ++ if(err < 0) { ++ ERROR("fseek"); ++ goto error; ++ } ++ file_size = ftell(fileptr); ++ if(file_size < 0) { ++ ERROR("ftell"); ++ goto error; ++ } ++ rewind(fileptr); ++ ++ buf = (char *) malloc((file_size) * sizeof(char)); ++ if(buf == NULL) { ++ ERROR("malloc"); ++ goto error; ++ } ++ ++ readed = fread(buf, file_size, 1, fileptr); ++ if(readed != 1) { ++ ERROR("fread"); ++ goto cleanup; ++ } ++ ++ *obj_buf = (void *) buf; ++ *obj_buf_sz = file_size; ++ return 0; ++ ++cleanup: ++ free(buf); ++error: ++ if(fileptr != NULL) ++ fclose(fileptr); ++ return -1; ++} ++ ++int ++init_ebpf_object_buffers(obufs_t *progs) ++{ ++ char *buf; ++ int i, err; ++ ++ if(progs == NULL) { ++ printf("Invalid argument for object buffer initialization\n"); ++ goto error; ++ } ++ ++ err = read_obj_file_into_buffer(PARSE_PROG_FILENAME, ++ &progs->parse_prog, &progs->parse_prog_size); ++ if(err < 0) { ++ goto error; ++ } ++ ++ err = read_obj_file_into_buffer(VERDICT_PROG_FILENAME, ++ &progs->verdict_prog, &progs->verdict_prog_size); ++ if(err < 0) { ++ goto error; ++ } ++ ++ buf = (char *) progs->verdict_prog; ++ for(i = 0; i < progs->verdict_prog_size - 3; ++i) { ++ if(buf[i] == (char)0xfc && ++ buf[i+1] == (char)0xfc && ++ buf[i+2] == (char)0xfc && ++ buf[i+3] == (char)0xfc) { ++ ++ progs->verdict_port_p = (int *) &buf[i]; ++ } ++ } ++ ++ return 0; ++ ++error: ++ return -1; ++} ++ ++void ++free_ebpf_object_buffers(obufs_t *progs) ++{ ++ free(progs->parse_prog); ++ free(progs->verdict_prog); ++ progs->verdict_port_p = NULL; ++ progs->parse_prog_size = -1; ++ progs->verdict_prog_size = -1; ++} ++ ++int ++init_ebpf_connection(int local_sock, int remote_sock) ++{ ++ int pfd, vfd, sfd, err = -1; ++ int local_key, remote_key; ++ ebpf_conn_t *conn; ++ socklen_t sock_size; ++ struct sockaddr_in local_addr; ++ struct bpf_object *vobj, *pobj; ++ struct bpf_map *smap; ++ ++ local_key = 1; remote_key = 0; ++ ++ conn = (ebpf_conn_t *) malloc(sizeof(ebpf_conn_t)); ++ if(conn == NULL) { ++ ERROR("malloc"); ++ goto error; ++ } ++ ++ sock_size = sizeof(struct sockaddr); ++ err = getsockname(local_sock, (struct sockaddr *) &local_addr, &sock_size); ++ if(err < 0) { ++ ERROR("getsockname"); ++ goto error_cleanup; ++ } ++ //set the correct local port in the bytecode before load ++ if(obufs.verdict_port_p != NULL) ++ *obufs.verdict_port_p = ntohs(local_addr.sin_port); ++ else { ++ LOGE("Invalid memory address to save port\n."); ++ goto error_cleanup; ++ } ++ ++ err = bpf_prog_load_obj_buf(obufs.parse_prog, ++ obufs.parse_prog_size, ++ PARSE_PROG_FILENAME, BPF_PROG_TYPE_SK_SKB, ++ &pobj, &pfd); ++ if(err < 0) { ++ LOGE("Failed to load BPF object\n"); ++ goto error_cleanup; ++ } ++ ++ err = bpf_prog_load_obj_buf(obufs.verdict_prog, ++ obufs.verdict_prog_size, ++ VERDICT_PROG_FILENAME, BPF_PROG_TYPE_SK_SKB, ++ &vobj, &vfd); ++ if(err < 0) { ++ LOGE("Failed to load BPF object\n"); ++ goto error_cleanup; ++ } ++ ++ smap = bpf_object__find_map_by_name(vobj, "redirect_map"); ++ if(smap == NULL) { ++ LOGE("Cannot find sockmap. Malformed BPF object\n"); ++ goto error_cleanup; ++ } ++ ++ sfd = bpf_map__fd(smap); ++ if(sfd < 0) { ++ LOGE("Unable to get sockmap reference fd\n"); ++ goto error_cleanup; ++ } ++ ++ err = bpf_prog_attach(pfd, sfd, BPF_SK_SKB_STREAM_PARSER, 0); ++ if(err < 0) { ++ LOGE("Failed to attach stream parser to sockmap\n"); ++ goto error_cleanup; ++ } ++ ++ err = bpf_prog_attach(vfd, sfd, BPF_SK_SKB_STREAM_VERDICT, 0); ++ if(err < 0) { ++ LOGE("Failed to attach verdict program to sockmap\n"); ++ goto error_cleanup; ++ } ++ ++ err = bpf_map_update_elem(sfd, &local_key, &local_sock, BPF_ANY); ++ if(err < 0) { ++ LOGE("Failed to put socket into sockmap\n"); ++ goto error_cleanup; ++ } ++ ++ err = bpf_map_update_elem(sfd, &remote_key, &remote_sock, BPF_ANY); ++ if(err < 0) { ++ LOGE("Failed to put socket into sockmap\n"); ++ goto error_cleanup; ++ } ++ ++ conn->sockmap_fd = sfd; ++ conn->parse_prog_fd = pfd; ++ conn->verdict_prog_fd = vfd; ++ conn->local_sock = local_sock; ++ conn->remote_sock = remote_sock; ++ ++ connections[ntohs(local_addr.sin_port)] = conn; ++ ++ free(vobj); ++ free(pobj); ++ ++ return 0; ++ ++error_cleanup: ++ free(conn); ++ free(vobj); ++ free(pobj); ++error: ++ return -1; ++} ++ ++inline void ++free_ebpf_connection(ebpf_conn_t *conn) ++{ ++ close(conn->sockmap_fd); ++ close(conn->parse_prog_fd); ++ close(conn->verdict_prog_fd); ++ ++ if(is_open(conn->local_sock)) { ++ close(conn->local_sock); ++ } ++ ++ if(is_open(conn->remote_sock)) { ++ close(conn->remote_sock); ++ } ++ ++ conn = NULL; ++} ++ ++int ++send_ebpf_handshake(int remote_sock, struct sockaddr_storage *destaddr) ++{ ++ // send destaddr ++ buffer_t ss_addr_to_send; ++ buffer_t *abuf = &ss_addr_to_send; ++ balloc(abuf, BUF_SIZE); ++ ++ if (AF_INET6 == destaddr->ss_family) { // IPv6 ++ abuf->data[abuf->len++] = 4; // Type 4 is IPv6 address ++ ++ size_t in6_addr_len = sizeof(struct in6_addr); ++ memcpy(abuf->data + abuf->len, ++ &(((struct sockaddr_in6 *) destaddr)->sin6_addr), ++ in6_addr_len); ++ abuf->len += in6_addr_len; ++ memcpy(abuf->data + abuf->len, ++ &(((struct sockaddr_in6 *) destaddr)->sin6_port), ++ 2); ++ } else { // IPv4 ++ abuf->data[abuf->len++] = 1; // Type 1 is IPv4 address ++ ++ size_t in_addr_len = sizeof(struct in_addr); ++ memcpy(abuf->data + abuf->len, ++ &((struct sockaddr_in *) destaddr)->sin_addr, in_addr_len); ++ abuf->len += in_addr_len; ++ memcpy(abuf->data + abuf->len, ++ &((struct sockaddr_in *) destaddr)->sin_port, 2); ++ } ++ ++ abuf->len += 2; ++ ++ struct sockaddr_storage addr; ++ memset(&addr, 0, sizeof(struct sockaddr_storage)); ++ socklen_t len = sizeof addr; ++ int r, tries = 100; ++ do { ++ r = getpeername(remote_sock, (struct sockaddr *)&addr, &len); ++ } while(tries && r != 0); ++ if(r < 0) { ++ return -1; ++ } ++ ++ r = send(remote_sock, abuf->data, abuf->len, 0); ++ ++ bfree(abuf); ++ ++ return r; ++} ++ + static void + server_recv_cb(EV_P_ ev_io *w, int revents) + { +@@ -310,6 +642,8 @@ + // all sent out, wait for reading + server->buf->len = 0; + server->buf->idx = 0; ++ if(ebpf && remote->recv_ctx->connected && remote->send_ctx->connected) ++ init_ebpf_connection(server->fd, remote->fd); + ev_io_stop(EV_A_ & server_send_ctx->io); + ev_io_start(EV_A_ & remote->recv_ctx->io); + } +@@ -323,17 +657,24 @@ + delayed_connect_watcher); + remote_t *remote = server->remote; + +- int r = connect(remote->fd, remote->addr, +- get_sockaddr_len(remote->addr)); ++ if(!is_open(remote->fd)) { ++ int r = connect(remote->fd, remote->addr, ++ get_sockaddr_len(remote->addr)); + +- remote->addr = NULL; ++ remote->addr = NULL; + +- if (r == -1 && errno != CONNECT_IN_PROGRESS) { +- ERROR("connect"); +- close_and_free_remote(EV_A_ remote); +- close_and_free_server(EV_A_ server); +- return; +- } else { ++ if (r == -1 && errno != CONNECT_IN_PROGRESS) { ++ ERROR("connect"); ++ close_and_free_remote(EV_A_ remote); ++ close_and_free_server(EV_A_ server); ++ return; ++ } ++ else { ++ ev_io_start(EV_A_ & remote->send_ctx->io); ++ ev_timer_start(EV_A_ & remote->send_ctx->watcher); ++ } ++ } ++ else { + // listen to remote connected event + ev_io_start(EV_A_ & remote->send_ctx->io); + ev_timer_start(EV_A_ & remote->send_ctx->watcher); +@@ -788,6 +1129,27 @@ + } + } + ++ //Initialize eBPF dataplane if enabled ++ /*if (ebpf) { ++ int r = connect(remotefd, remote_addr, get_sockaddr_len(remote_addr)); ++ if (r == -1 && errno != CONNECT_IN_PROGRESS) { ++ ERROR("connect"); ++ return; ++ } ++ ++ int err = send_ebpf_handshake(remotefd, &destaddr); ++ if(err < 0) { ++ LOGE("eBPF handshake failed"); ++ } ++ ++ err = init_ebpf_connection(remotefd, serverfd); ++ if(err < 0) { ++ LOGE("eBPF connection initialization failed"); ++ } ++ ++ return; ++ }*/ ++ + server_t *server = new_server(serverfd); + remote_t *remote = new_remote(remotefd, listener->timeout); + server->remote = remote; +@@ -799,14 +1161,18 @@ + remote->addr = remote_addr; + ev_timer_start(EV_A_ & server->delayed_connect_watcher); + } else { +- int r = connect(remotefd, remote_addr, get_sockaddr_len(remote_addr)); + +- if (r == -1 && errno != CONNECT_IN_PROGRESS) { +- ERROR("connect"); +- close_and_free_remote(EV_A_ remote); +- close_and_free_server(EV_A_ server); +- return; ++ if(!is_open(remotefd)) { ++ int r = connect(remotefd, remote_addr, get_sockaddr_len(remote_addr)); ++ ++ if (r == -1 && errno != CONNECT_IN_PROGRESS) { ++ ERROR("connect"); ++ close_and_free_remote(EV_A_ remote); ++ close_and_free_server(EV_A_ server); ++ return; ++ } + } ++ + // listen to remote connected event + ev_io_start(EV_A_ & remote->send_ctx->io); + ev_timer_start(EV_A_ & remote->send_ctx->watcher); +@@ -874,6 +1240,7 @@ + { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, + { "mtu", required_argument, NULL, GETOPT_VAL_MTU }, + { "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP }, ++ { "ebpf", no_argument, NULL, GETOPT_VAL_EBPF }, + { "plugin", required_argument, NULL, GETOPT_VAL_PLUGIN }, + { "plugin-opts", required_argument, NULL, GETOPT_VAL_PLUGIN_OPTS }, + { "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT }, +@@ -902,6 +1269,10 @@ + mptcp = 1; + LOGI("enable multipath TCP"); + break; ++ case GETOPT_VAL_EBPF: ++ ebpf = 1; ++ LOGI("enable eBPF dataplane"); ++ break; + case GETOPT_VAL_NODELAY: + no_delay = 1; + LOGI("enable TCP no-delay"); +@@ -1042,6 +1413,9 @@ + if (mptcp == 0) { + mptcp = conf->mptcp; + } ++ if(ebpf == 0) { ++ ebpf = conf->ebpf; ++ } + if (no_delay == 0) { + no_delay = conf->no_delay; + } +@@ -1188,6 +1562,15 @@ + } + listen_ctx.timeout = atoi(timeout); + listen_ctx.mptcp = mptcp; ++ if(ebpf) { ++ int r = init_ebpf_object_buffers(&obufs); ++ if(r < 0) { ++ LOGE("Failed to load eBPF objects"); ++ } ++ else { ++ memset(connections, 0, 65536 * sizeof(ebpf_conn_t *)); ++ } ++ } + + struct ev_loop *loop = EV_DEFAULT; + +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/redir.h shadowsocks-libev-nocrypto-ebpf/src/redir.h +--- shadowsocks-libev-nocrypto-nocrypto/src/redir.h 2018-06-23 14:57:34.000000000 +0200 ++++ shadowsocks-libev-nocrypto-ebpf/src/redir.h 2018-08-14 13:45:18.000000000 +0200 +@@ -31,6 +31,24 @@ + #include "crypto.h" + #include "jconf.h" + ++typedef struct obufs ++{ ++ void *parse_prog; ++ void *verdict_prog; ++ int *verdict_port_p; ++ int parse_prog_size; ++ int verdict_prog_size; ++} obufs_t; ++ ++typedef struct ebpf_conn ++{ ++ int sockmap_fd; ++ int parse_prog_fd; ++ int verdict_prog_fd; ++ int local_sock; ++ int remote_sock; ++} ebpf_conn_t; ++ + typedef struct listen_ctx { + ev_io io; + int remote_num; +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/sockmap_parse.c shadowsocks-libev-nocrypto-ebpf/src/sockmap_parse.c +--- shadowsocks-libev-nocrypto-nocrypto/src/sockmap_parse.c 1970-01-01 01:00:00.000000000 +0100 ++++ shadowsocks-libev-nocrypto-ebpf/src/sockmap_parse.c 2018-08-14 13:45:18.000000000 +0200 +@@ -0,0 +1,19 @@ ++#include ++#include "bpf_helpers.h" ++ ++int _version SEC("version") = 1; ++ ++#define bpf_printk(fmt, ...) \ ++({ \ ++ char ____fmt[] = fmt; \ ++ bpf_trace_printk(____fmt, sizeof(____fmt), \ ++ ##__VA_ARGS__); \ ++}) ++ ++SEC("sk_skb_parse_prog") ++int sk_skb_parse(struct __sk_buff *skb) ++{ ++ return skb->len; ++} ++ ++char _license[] SEC("license") = "GPL"; +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/sockmap_parse.o.ebpf shadowsocks-libev-nocrypto-ebpf/src/sockmap_parse.o.ebpf +--- shadowsocks-libev-nocrypto-nocrypto/src/sockmap_parse.o.ebpf 1970-01-01 01:00:00.000000000 +0100 ++++ shadowsocks-libev-nocrypto-ebpf/src/sockmap_parse.o.ebpf 2018-08-14 13:45:18.000000000 +0200 +@@ -0,0 +1 @@ ++ELF@@a•GPL/".text_versionsk_skb_parse_progsk_skb_parse_license.strtab.symtab8¸H@@P0T@X` +\ Pas de fin de ligne à la fin du fichier +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/sockmap_verdict.c shadowsocks-libev-nocrypto-ebpf/src/sockmap_verdict.c +--- shadowsocks-libev-nocrypto-nocrypto/src/sockmap_verdict.c 1970-01-01 01:00:00.000000000 +0100 ++++ shadowsocks-libev-nocrypto-ebpf/src/sockmap_verdict.c 2018-08-14 13:45:18.000000000 +0200 +@@ -0,0 +1,22 @@ ++#include ++#include "bpf_helpers.h" ++ ++int _version SEC("version") = 1; ++ ++struct bpf_map_def SEC("maps") redirect_map = { ++ .type = BPF_MAP_TYPE_SOCKMAP, ++ .key_size = sizeof(int), ++ .value_size = sizeof(int), ++ .max_entries = 2, ++}; ++ ++SEC("sk_skb_verdict_prog") ++int sk_skb_verdict(struct __sk_buff *skb) ++{ ++ if(skb->local_port == 0xfcfcfcfc) ++ return bpf_sk_redirect_map(skb, &redirect_map, 0, 0); ++ ++ return bpf_sk_redirect_map(skb, &redirect_map, 1, 0); ++} ++ ++char _license[] SEC("license") = "GPL"; +diff -aurN shadowsocks-libev-nocrypto-nocrypto/src/sockmap_verdict.o.ebpf shadowsocks-libev-nocrypto-ebpf/src/sockmap_verdict.o.ebpf +--- shadowsocks-libev-nocrypto-nocrypto/src/sockmap_verdict.o.ebpf 1970-01-01 01:00:00.000000000 +0100 ++++ shadowsocks-libev-nocrypto-ebpf/src/sockmap_verdict.o.ebpf 2018-08-14 13:45:18.000000000 +0200 +@@ -0,0 +1 @@ ++ELF@@ aˆüüüü]2···…4•GPLi@bXI( @.textsk_skb_verdictmapsredirect_map_version.relsk_skb_verdict_prog_license.strtab.symtabLBB0_3LBB0_2R p@5@p1 € )°´JÐZب +\ Pas de fin de ligne à la fin du fichier