50 Commits
v0.16 ... v0.19

Author SHA1 Message Date
Fancy Zhang
45d883d66d Merge branch 'master' of github.com:springzfx/cgproxy into master 2020-08-16 15:33:34 +08:00
Fancy Zhang
ae781fc184 update man 2020-08-16 15:29:44 +08:00
Fancy Zhang
5da82723dd wait at most 60s in case of xtables lock 2020-08-16 15:16:01 +08:00
Fancy Zhang
70b6f29b72 update readme 2020-08-16 15:07:07 +08:00
sixg0000d
cf647c223d Fix some typos and Update toc (#15) 2020-08-12 00:08:08 +08:00
Fancy Zhang
b2d49eb6ca ignore process live too short error
If cgroup is empty, then the process is not exsit any more.
So can be ignored.
2020-08-11 19:19:27 +08:00
Fancy Zhang
46b0e4bc95 Merge branch 'master' of github.com:springzfx/cgproxy into master 2020-08-11 18:49:25 +08:00
Fancy Zhang
89619a3afc update readme 2020-08-11 18:49:18 +08:00
千魂剑
c417d5050c fix: v2ray.service 404 in readme.md (#14) 2020-08-11 18:47:41 +08:00
sixg0000d
55d67f4799 Add network-online.target to service (#12)
Add network-online.target to service

If Firewalld is enabled, Cgproxy should running after the network was up to online to avoid conflict from Firewalld.
2020-08-06 09:40:52 +08:00
Fancy Zhang
aa05c07c01 revert service install path
User is responsible to the not-standard service install path, if CMAKE_INSTALL_PREFIX is not "/usr"
2020-08-06 09:28:39 +08:00
Fancy Zhang
5f03c52923 add bridge network issue workaround in readme 2020-08-05 23:05:56 +08:00
Fancy Zhang
88e2bc09b3 Merge branch 'master' of github.com:springzfx/cgproxy into master 2020-08-05 22:49:33 +08:00
Fancy Zhang
49afaf2ad7 Merge branch 'karuboniru-master' into master 2020-08-05 22:48:43 +08:00
Fancy Zhang
2daa46c019 directly use CMAKE_INSTALL_FULL_XX
- CMAKE_INSTALL_XX and  CMAKE_INSTALL_FULL_XX result the same effect in intall(...), so why not use CMAKE_INSTALL_FULL_XX directly
- install systemd service to fixed /usr/lib/ path, according to the systemd unit doc
2020-08-05 22:47:52 +08:00
Qiyu Yan
655290bf97 Don't hardcode paths 2020-08-05 05:08:10 +08:00
Fancy
4b480c9eff Merge pull request #9 from karuboniru/master 2020-08-05 00:00:51 +08:00
Qiyu Yan
8c2072f3bf Always install systemd unit files in right place 2020-08-04 18:05:51 +08:00
Fancy Zhang
d6394f25f3 fix local packet loop 2020-07-21 14:05:42 +08:00
Fancy Zhang
0eca327785 logical organize tproxy iptables 2020-07-20 19:00:14 +08:00
Fancy
032b780e07 Merge pull request #7 from DuckSoft/patch-1
config.json: minor typo fix
2020-07-18 11:49:12 +08:00
DuckSoft
48780c749c config.json: minor typo fix 2020-07-18 11:34:19 +08:00
springzfx
538f1722cf add vscode settings 2020-07-15 01:02:10 +08:00
Fancy Zhang
cba7800e2b execsnoop-kernel can also be built with vmlinux.hwith vmlinux.h, execsnoop_kern.c can be built without kernel treeremove execsnoop-libbpfadd cross build instruction 2020-07-14 22:15:15 +08:00
Fancy Zhang
f397900adc support aarch64 2020-07-10 23:06:52 +08:00
Fancy Zhang
134caa9c78 update readme 2020-07-09 19:34:33 +08:00
Fancy Zhang
7123254ebb fix execsnoop interrupt when sleep 2020-07-09 19:23:20 +08:00
Fancy Zhang
892c6587dc bump version 2020-07-05 23:07:54 +08:00
Fancy Zhang
8ea4062384 test on deepin 15.11 2020-07-05 22:36:53 +08:00
Fancy Zhang
85e7bb3317 allow static build, mainly for deb and rpm pack 2020-07-05 22:30:36 +08:00
Fancy Zhang
9d2c26e765 customize cgroup mount point 2020-07-05 22:29:43 +08:00
Fancy Zhang
00cd293842 tested on ubuntu 18.04 2020-07-05 17:58:10 +08:00
Fancy Zhang
b8b70dcad5 build deb and rpm static prefered 2020-07-05 17:41:41 +08:00
Fancy Zhang
5398740bf3 add cmake option: build_execsnoop_dl, build_static 2020-07-05 16:57:00 +08:00
Fancy Zhang
f5dc84e34a bump version 2020-07-04 15:59:39 +08:00
Fancy Zhang
720806317b update readme 2020-07-04 15:54:31 +08:00
Fancy Zhang
5509be3926 add libbpf depency for deb and rpm 2020-07-04 15:34:20 +08:00
Fancy Zhang
ab98bae840 add warning output 2020-07-04 15:33:02 +08:00
Fancy Zhang
4753d2be68 filter cgroup that not exist 2020-07-04 15:32:36 +08:00
Fancy Zhang
9f4c980c3e minor adjustments 2020-07-04 14:37:28 +08:00
Fancy Zhang
e4437071c9 use execsnoop-kernel which btf is not requested 2020-07-04 13:23:43 +08:00
Fancy Zhang
3b5378a79d use bpftool-gen to generate skel 2020-07-04 11:07:56 +08:00
Fancy Zhang
7f132c5d63 add execsnoop-kernel which need to be built in kernel tree 2020-07-03 22:36:35 +08:00
Fancy Zhang
6cb169522c update install 2020-06-29 15:16:56 +08:00
Fancy Zhang
0d2725d00c expose table, fwmark, mark_newin to config 2020-06-28 00:12:38 +08:00
Fancy Zhang
a4628cfed8 use more uncommon route table and mark 2020-06-27 12:52:48 +08:00
Fancy Zhang
77b57247ea commit add .vscode things 2020-06-27 10:13:04 +08:00
Fancy Zhang
4618b8f475 fix #5: segfault when program not exist 2020-06-27 10:06:04 +08:00
Fancy Zhang
46fb9bae2b optimize cmake 2020-06-21 22:07:30 +08:00
Fancy Zhang
c223af9d71 updated readme 2020-06-18 15:54:17 +08:00
58 changed files with 1499 additions and 108833 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,5 @@
build
build*
.directory
.vscode
.clangd
v2ray_config/proxy
v2ray_config/06_outbounds_proxy.json

15
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [],
"defines": ["USE_VMLINUX"],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c18",
"cppStandard": "gnu++17",
"intelliSenseMode": "gcc-x64",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}

2
.vscode/gdb-root vendored Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
exec sudo -E /usr/bin/gdb "$@"

23
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "(gdb) cgproxyd",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/src/cgproxy",
"args": ["--daemon", "--debug", "--execsnoop"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "${workspaceFolder}/.vscode/gdb-root",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}]
}

7
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"cmake.configureOnOpen": true,
"cmake.configureArgs": [
"-Dbuild_static=OFF",
"-Dbuild_execsnoop_dl=OFF"
]
}

View File

@@ -1,20 +1,24 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(cgproxy VERSION 0.16)
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter)
project(cgproxy VERSION 0.18)
include(GNUInstallDirs)
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter -Wl,--no-undefined)
# for clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
option(build_execsnoop_dl "build libexecsnoop.so which will be dynamic loaded, otherwise built directly into cgproxy" ON)
option(build_static "build with static link prefered" OFF)
option(build_tools OFF)
option(build_test OFF)
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
option(build_test "for develop" OFF)
add_subdirectory(src)
add_subdirectory(execsnoop-kernel)
add_subdirectory(pack)
if (build_tools)
add_subdirectory(tools)
@@ -23,23 +27,23 @@ if (build_test)
add_subdirectory(test)
endif()
install(FILES cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission})
install(FILES cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
install(FILES config.json DESTINATION /etc/cgproxy/)
install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/ PERMISSIONS ${basic_permission})
install(FILES readme.md DESTINATION /usr/share/doc/cgproxy/)
configure_file(cgnoproxy.cmake cgnoproxy)
configure_file(cgproxyd.cmake cgproxyd)
configure_file(cgproxy.service.cmake cgproxy.service)
# instal scripts and other things
install(PROGRAMS ${CMAKE_BINARY_DIR}/cgproxyd TYPE BIN)
install(PROGRAMS ${CMAKE_BINARY_DIR}/cgnoproxy TYPE BIN)
install(PROGRAMS cgroup-tproxy.sh DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/cgproxy/scripts)
install(FILES ${CMAKE_BINARY_DIR}/cgproxy.service DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/systemd/system)
install(FILES config.json DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/cgproxy)
install(FILES readme.md DESTINATION ${CMAKE_INSTALL_FULL_DOCDIR})
# man pages
set(man_gz
${PROJECT_BINARY_DIR}/cgproxyd.1.gz
${PROJECT_BINARY_DIR}/cgproxy.1.gz
${PROJECT_BINARY_DIR}/cgnoproxy.1.gz
)
add_custom_target(man
set(man_gz ${PROJECT_SOURCE_DIR}/man/cgproxyd.1.gz ${PROJECT_SOURCE_DIR}/man/cgproxy.1.gz ${PROJECT_SOURCE_DIR}/man/cgnoproxy.1.gz)
add_custom_command(OUTPUT ${man_gz}
COMMAND gzip -fk cgproxyd.1 cgproxy.1 cgnoproxy.1
COMMAND mv *.gz ${PROJECT_BINARY_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/man
)
add_dependencies(main man)
install(FILES ${man_gz} DESTINATION /usr/share/man/man1/)
add_custom_target(man ALL DEPENDS ${man_gz})
install(FILES ${man_gz} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/)

View File

@@ -1,2 +0,0 @@
#!/bin/sh
exec /usr/bin/cgproxy --noproxy $@

2
cgnoproxy.cmake Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
exec @CMAKE_INSTALL_FULL_BINDIR@/cgproxy --noproxy $@

View File

@@ -1,10 +0,0 @@
[Unit]
Description=cgproxy service
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/cgproxyd --execsnoop
[Install]
WantedBy=multi-user.target

10
cgproxy.service.cmake Normal file
View File

@@ -0,0 +1,10 @@
[Unit]
Description=cgproxy service
After=network.target network-online.target
[Service]
Type=simple
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/cgproxyd --execsnoop
[Install]
WantedBy=multi-user.target

View File

@@ -1,2 +0,0 @@
#!/bin/sh
exec /usr/bin/cgproxy --daemon $@

2
cgproxyd.cmake Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
exec @CMAKE_INSTALL_FULL_BINDIR@/cgproxy --daemon $@

366
cgroup-tproxy.sh Normal file → Executable file
View File

@@ -1,33 +1,35 @@
#!/bin/bash
### This script will proxy/noproxy anything running in specific cgroup
### need cgroup2 support, and iptables cgroup2 path match support
###
### script usage:
### cgroup-tproxy.sh [--help|--config|stop]
### options:
### --config=FILE load config from file
### --help show help info
### stop clean then stop. Variables may change when stopping, which should be avoid
### so always stop first in the last context before start new context
###
### available variables with default value:
### cgroup_noproxy="/noproxy.slice"
### cgroup_proxy="/proxy.slice"
### port=12345
### enable_dns=true
### enable_udp=true
### enable_tcp=true
### enable_ipv4=true
### enable_ipv6=true
### enable_gateway=false
### table=10007
### fwmark=0x9973
### cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET | head -n 1)
###
### semicolon to seperate multi cgroup:
### cgroup_noproxy="/noproxy1.slice:/noproxy2.slice"
### cgroup_proxy="/proxy1.slice:/proxy2.slice"
print_help(){
cat << 'DOC'
#############################################################################
#
# 1. This script need cgroup v2
#
# 2. Listening port is expected to accept iptables TPROXY, while REDIRECT
# will not work in this script, because REDIRECT only support tcp/ipv4
#
# 3. TPROXY need root or special capability whatever process is listening on port
# v2ray as example:
# sudo setcap "cap_net_bind_service=+ep cap_net_admin=+ep" /usr/lib/v2ray/v2ray
#
# 4. this script will proxy anything running in specific cgroup
#
# script usage:
# cgroup-tproxy.sh [--help|--config|stop]
# --config=FILE
# load config from file
# --help
# show help info
# stop
# clean then stop
#
# proxy usage:
# cgproxy <program>
#
#############################################################################
DOC
sed -rn 's/^### ?//;T;p' "$0"
}
## check root
@@ -47,56 +49,75 @@ else
IFS=':' read -r -a cgroup_noproxy <<< "$cgroup_noproxy"
fi
# allow as gateway for local network
[ -z ${enable_gateway+x} ] && enable_gateway=false
## some variables
## tproxy listening port
[ -z ${port+x} ] && port=12345
## some options
## controll options
[ -z ${enable_dns+x} ] && enable_dns=true
[ -z ${enable_tcp+x} ] && enable_tcp=true
[ -z ${enable_udp+x} ] && enable_udp=true
[ -z ${enable_tcp+x} ] && enable_tcp=true
[ -z ${enable_ipv4+x} ] && enable_ipv4=true
[ -z ${enable_ipv6+x} ] && enable_ipv6=true
[ -z ${enable_gateway+x} ] && enable_gateway=false
## do not modify this if you don't known what you are doing
table=100
fwmark=0x01
make_newin=0x02
## mark/route things
[ -z ${table+x} ] && table=10007
[ -z ${fwmark+x} ] && fwmark=0x9973
[ -z ${table_reroute+x} ] && table_reroute=$table
[ -z ${table_tproxy+x} ] && table_tproxy=$table
[ -z ${fwmark_reroute+x} ] && fwmark_reroute=$fwmark
[ -z ${fwmark_tproxy+x} ] && fwmark_tproxy=$fwmark
## cgroup things
cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET)
cgroup_type="cgroup2"
cgroup_procs_file="cgroup.procs"
## cgroup mount point things
[ -z ${cgroup_mount_point+x} ] && cgroup_mount_point=$(findmnt -t cgroup2 -n -o TARGET | head -n 1)
stop(){
iptables -t mangle -L TPROXY_PRE &> /dev/null || return
iptables -w 60 -t mangle -L TPROXY_ENT &> /dev/null || return
echo "iptables: cleaning tproxy iptables"
iptables -t mangle -D PREROUTING -j TPROXY_PRE
iptables -t mangle -D OUTPUT -j TPROXY_OUT
iptables -t mangle -F TPROXY_PRE
iptables -t mangle -F TPROXY_OUT
iptables -t mangle -F TPROXY_ENT
iptables -t mangle -X TPROXY_PRE
iptables -t mangle -X TPROXY_OUT
iptables -t mangle -X TPROXY_ENT
ip6tables -t mangle -D PREROUTING -j TPROXY_PRE
ip6tables -t mangle -D OUTPUT -j TPROXY_OUT
ip6tables -t mangle -F TPROXY_PRE
ip6tables -t mangle -F TPROXY_OUT
ip6tables -t mangle -F TPROXY_ENT
ip6tables -t mangle -X TPROXY_PRE
ip6tables -t mangle -X TPROXY_OUT
ip6tables -t mangle -X TPROXY_ENT
ip rule delete fwmark $fwmark lookup $table
ip route flush table $table
ip -6 rule delete fwmark $fwmark lookup $table
ip -6 route flush table $table
iptables -w 60 -t mangle -D PREROUTING -j TPROXY_PRE
iptables -w 60 -t mangle -D OUTPUT -j TPROXY_OUT
iptables -w 60 -t mangle -F TPROXY_PRE
iptables -w 60 -t mangle -F TPROXY_ENT
iptables -w 60 -t mangle -F TPROXY_OUT
iptables -w 60 -t mangle -F TPROXY_MARK
iptables -w 60 -t mangle -X TPROXY_PRE
iptables -w 60 -t mangle -X TPROXY_ENT
iptables -w 60 -t mangle -X TPROXY_OUT
iptables -w 60 -t mangle -X TPROXY_MARK
ip rule delete fwmark $fwmark_tproxy lookup $table_tproxy
ip rule delete fwmark $fwmark_reroute lookup $table_reroute &> /dev/null
ip route flush table $table_tproxy
ip route flush table $table_reroute &> /dev/null
ip6tables -w 60 -t mangle -D PREROUTING -j TPROXY_PRE
ip6tables -w 60 -t mangle -D OUTPUT -j TPROXY_OUT
ip6tables -w 60 -t mangle -F TPROXY_PRE
ip6tables -w 60 -t mangle -F TPROXY_OUT
ip6tables -w 60 -t mangle -F TPROXY_ENT
ip6tables -w 60 -t mangle -F TPROXY_MARK
ip6tables -w 60 -t mangle -X TPROXY_PRE
ip6tables -w 60 -t mangle -X TPROXY_OUT
ip6tables -w 60 -t mangle -X TPROXY_ENT
ip6tables -w 60 -t mangle -X TPROXY_MARK
ip -6 rule delete fwmark $fwmark_tproxy lookup $table_tproxy
ip -6 rule delete fwmark $fwmark_reroute lookup $table_reroute &> /dev/null
ip -6 route flush table $table_tproxy
ip -6 route flush table $table_reroute &> /dev/null
## may not exist, just ignore, and tracking their existence is not reliable
iptables -t nat -D POSTROUTING -m owner ! --socket-exists -j MASQUERADE &> /dev/null
ip6tables -t nat -D POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE &> /dev/null
iptables -w 60 -t nat -D POSTROUTING -m owner ! --socket-exists -j MASQUERADE &> /dev/null
ip6tables -w 60 -t nat -D POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE &> /dev/null
## unmount cgroup2
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" = "cgroup2" ] && umount $cgroup_mount_point
}
## parse parameter
@@ -115,120 +136,139 @@ case $i in
print_help
exit 0
;;
*)
print_help
exit 0
;;
esac
done
## TODO cgroup need to exists before using in iptables since 5.6.5, maybe it's bug
## check cgroup_mount_point, create and mount if necessary
[ -z $cgroup_mount_point ] && { >&2 echo "iptables: no cgroup2 mount point available"; exit -1; }
[ ! -d $cgroup_mount_point ] && mkdir -p $cgroup_mount_point
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && mount -t cgroup2 none $cgroup_mount_point
[ "$(findmnt -M $cgroup_mount_point -n -o FSTYPE)" != "cgroup2" ] && { >&2 echo "iptables: mount $cgroup_mount_point failed"; exit -1; }
## only create the first one in arrary
test -d $cgroup_mount_point$cgroup_proxy || mkdir $cgroup_mount_point$cgroup_proxy || exit -1;
test -d $cgroup_mount_point$cgroup_noproxy || mkdir $cgroup_mount_point$cgroup_noproxy || exit -1;
## filter cgroup that not exist
_cgroup_noproxy=()
for cg in ${cgroup_noproxy[@]}; do
test -d $cgroup_mount_point$cg && _cgroup_noproxy+=($cg) || { >&2 echo "iptables: $cg not exist, ignore";}
done
unset cgroup_noproxy && cgroup_noproxy=${_cgroup_noproxy[@]}
## filter cgroup that not exist
_cgroup_proxy=()
for cg in ${cgroup_proxy[@]}; do
test -d $cgroup_mount_point$cg && _cgroup_proxy+=($cg) || { >&2 echo "iptables: $cg not exist, ignore";}
done
unset cgroup_proxy && cgroup_proxy=${_cgroup_proxy[@]}
## ipv4 #########################################################################
echo "iptables: applying tproxy iptables"
## use TPROXY
#ipv4#
ip rule add fwmark $fwmark table $table
ip route add local default dev lo table $table
iptables -t mangle -N TPROXY_ENT
iptables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
iptables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
## mangle prerouting
ip rule add fwmark $fwmark_tproxy table $table_tproxy
ip route add local default dev lo table $table_tproxy
# core
iptables -w 60 -t mangle -N TPROXY_ENT
iptables -w 60 -t mangle -A TPROXY_ENT -m socket -j MARK --set-mark $fwmark_tproxy
iptables -w 60 -t mangle -A TPROXY_ENT -m socket -j ACCEPT
iptables -w 60 -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark_tproxy
iptables -w 60 -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip 127.0.0.1 --on-port $port --tproxy-mark $fwmark_tproxy
# filter
iptables -w 60 -t mangle -N TPROXY_PRE
iptables -w 60 -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
iptables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
$enable_gateway || iptables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
$enable_dns && iptables -w 60 -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
$enable_udp && iptables -w 60 -t mangle -A TPROXY_PRE -p udp -j TPROXY_ENT
$enable_tcp && iptables -w 60 -t mangle -A TPROXY_PRE -p tcp -j TPROXY_ENT
# hook
iptables -w 60 -t mangle -A PREROUTING -j TPROXY_PRE
iptables -t mangle -N TPROXY_PRE
iptables -t mangle -A TPROXY_PRE -m socket --transparent -j MARK --set-mark $fwmark
iptables -t mangle -A TPROXY_PRE -m socket --transparent -j RETURN
iptables -t mangle -A TPROXY_PRE -p icmp -j RETURN
iptables -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
iptables -t mangle -A TPROXY_PRE -p tcp --dport 53 -j TPROXY_ENT
iptables -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
iptables -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
iptables -t mangle -A TPROXY_PRE -j TPROXY_ENT
iptables -t mangle -A PREROUTING -j TPROXY_PRE
iptables -t mangle -N TPROXY_OUT
iptables -t mangle -A TPROXY_OUT -p icmp -j RETURN
iptables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN
iptables -t mangle -A TPROXY_OUT -m addrtype --dst-type LOCAL -j RETURN
iptables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
for cg in ${cgroup_noproxy[@]}; do
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN || { >&2 echo "iptables: $cg not exist, won't apply"; }
done
for cg in ${cgroup_proxy[@]}; do
iptables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark || { >&2 echo "iptables: $cg not exist, won't apply"; }
done
iptables -t mangle -A OUTPUT -j TPROXY_OUT
#ipv6#
ip -6 rule add fwmark $fwmark table $table
ip -6 route add local default dev lo table $table
ip6tables -t mangle -N TPROXY_ENT
ip6tables -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
ip6tables -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip localhost --on-port $port --tproxy-mark $fwmark
ip6tables -t mangle -N TPROXY_PRE
ip6tables -t mangle -A TPROXY_PRE -m socket --transparent -j MARK --set-mark $fwmark
ip6tables -t mangle -A TPROXY_PRE -m socket --transparent -j RETURN
ip6tables -t mangle -A TPROXY_PRE -p icmpv6 -j RETURN
ip6tables -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
ip6tables -t mangle -A TPROXY_PRE -p tcp --dport 53 -j TPROXY_ENT
ip6tables -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
ip6tables -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
ip6tables -t mangle -A TPROXY_PRE -j TPROXY_ENT
ip6tables -t mangle -A PREROUTING -j TPROXY_PRE
ip6tables -t mangle -N TPROXY_OUT
ip6tables -t mangle -A TPROXY_OUT -p icmpv6 -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m connmark --mark $make_newin -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m addrtype --dst-type LOCAL -j RETURN
ip6tables -t mangle -A TPROXY_OUT -m addrtype ! --dst-type UNICAST -j RETURN
for cg in ${cgroup_noproxy[@]}; do
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN || { >&2 echo "iptables: $cg not exist, won't apply"; }
done
for cg in ${cgroup_proxy[@]}; do
ip6tables -t mangle -A TPROXY_OUT -m cgroup --path $cg -j MARK --set-mark $fwmark || { >&2 echo "iptables: $cg not exist, won't apply"; }
done
ip6tables -t mangle -A OUTPUT -j TPROXY_OUT
## allow to disable, order is important
$enable_dns || iptables -t mangle -I TPROXY_OUT -p udp --dport 53 -j RETURN
$enable_dns || ip6tables -t mangle -I TPROXY_OUT -p udp --dport 53 -j RETURN
$enable_udp || iptables -t mangle -I TPROXY_OUT -p udp -j RETURN
$enable_udp || ip6tables -t mangle -I TPROXY_OUT -p udp -j RETURN
$enable_tcp || iptables -t mangle -I TPROXY_OUT -p tcp -j RETURN
$enable_tcp || ip6tables -t mangle -I TPROXY_OUT -p tcp -j RETURN
$enable_ipv4 || iptables -t mangle -I TPROXY_OUT -j RETURN
$enable_ipv6 || ip6tables -t mangle -I TPROXY_OUT -j RETURN
if $enable_gateway; then
$enable_dns || iptables -t mangle -I TPROXY_PRE -p udp --dport 53 -j RETURN
$enable_dns || ip6tables -t mangle -I TPROXY_PRE -p udp --dport 53 -j RETURN
$enable_udp || iptables -t mangle -I TPROXY_PRE -p udp -j RETURN
$enable_udp || ip6tables -t mangle -I TPROXY_PRE -p udp -j RETURN
$enable_tcp || iptables -t mangle -I TPROXY_PRE -p tcp -j RETURN
$enable_tcp || ip6tables -t mangle -I TPROXY_PRE -p tcp -j RETURN
$enable_ipv4 || iptables -t mangle -I TPROXY_PRE -j RETURN
$enable_ipv6 || ip6tables -t mangle -I TPROXY_PRE -j RETURN
## mangle output
if [ $fwmark_reroute != $fwmark_tproxy ]; then
ip rule add fwmark $fwmark_reroute table $table_reroute
ip route add local default dev lo table $table_reroute
fi
# filter
iptables -w 60 -t mangle -N TPROXY_MARK
iptables -w 60 -t mangle -A TPROXY_MARK -m addrtype ! --dst-type UNICAST -j RETURN
$enable_dns && iptables -w 60 -t mangle -A TPROXY_MARK -p udp --dport 53 -j MARK --set-mark $fwmark_reroute
$enable_udp && iptables -w 60 -t mangle -A TPROXY_MARK -p udp -j MARK --set-mark $fwmark_reroute
$enable_tcp && iptables -w 60 -t mangle -A TPROXY_MARK -p tcp -j MARK --set-mark $fwmark_reroute
# cgroup
iptables -w 60 -t mangle -N TPROXY_OUT
iptables -w 60 -t mangle -A TPROXY_OUT -m conntrack --ctdir REPLY -j RETURN
for cg in ${cgroup_noproxy[@]}; do
iptables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
done
for cg in ${cgroup_proxy[@]}; do
iptables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j TPROXY_MARK
done
# hook
$enable_ipv4 && iptables -w 60 -t mangle -A OUTPUT -j TPROXY_OUT
## do not handle local device connection through tproxy if gateway is not enabled
$enable_gateway || iptables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
$enable_gateway || ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
## ipv6 #########################################################################
## mangle prerouting
ip -6 rule add fwmark $fwmark_tproxy table $table_tproxy
ip -6 route add local default dev lo table $table_tproxy
# core
ip6tables -w 60 -t mangle -N TPROXY_ENT
ip6tables -w 60 -t mangle -A TPROXY_ENT -m socket -j MARK --set-mark $fwmark_tproxy
ip6tables -w 60 -t mangle -A TPROXY_ENT -m socket -j ACCEPT
ip6tables -w 60 -t mangle -A TPROXY_ENT -p tcp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark_tproxy
ip6tables -w 60 -t mangle -A TPROXY_ENT -p udp -j TPROXY --on-ip ::1 --on-port $port --tproxy-mark $fwmark_tproxy
# filter
ip6tables -w 60 -t mangle -N TPROXY_PRE
ip6tables -w 60 -t mangle -A TPROXY_PRE -m addrtype --dst-type LOCAL -j RETURN
ip6tables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --dst-type UNICAST -j RETURN
$enable_gateway || ip6tables -w 60 -t mangle -A TPROXY_PRE -m addrtype ! --src-type LOCAL -j RETURN
$enable_dns && ip6tables -w 60 -t mangle -A TPROXY_PRE -p udp --dport 53 -j TPROXY_ENT
$enable_udp && ip6tables -w 60 -t mangle -A TPROXY_PRE -p udp -j TPROXY_ENT
$enable_tcp && ip6tables -w 60 -t mangle -A TPROXY_PRE -p tcp -j TPROXY_ENT
# hook
ip6tables -w 60 -t mangle -A PREROUTING -j TPROXY_PRE
## make sure following rules are the first in chain TPROXY_PRE to mark new incoming connection or gateway proxy connection
## so must put at last to insert first
iptables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
ip6tables -t mangle -I TPROXY_PRE -m addrtype ! --src-type LOCAL -m conntrack --ctstate NEW -j CONNMARK --set-mark $make_newin
## mangle output
if [ $fwmark_reroute != $fwmark_tproxy ]; then
ip -6 rule add fwmark $fwmark_reroute table $table_reroute
ip -6 route add local default dev lo table $table_reroute
fi
# filter
ip6tables -w 60 -t mangle -N TPROXY_MARK
ip6tables -w 60 -t mangle -A TPROXY_MARK -m addrtype ! --dst-type UNICAST -j RETURN
$enable_dns && ip6tables -w 60 -t mangle -A TPROXY_MARK -p udp --dport 53 -j MARK --set-mark $fwmark_reroute
$enable_udp && ip6tables -w 60 -t mangle -A TPROXY_MARK -p udp -j MARK --set-mark $fwmark_reroute
$enable_tcp && ip6tables -w 60 -t mangle -A TPROXY_MARK -p tcp -j MARK --set-mark $fwmark_reroute
# cgroup
ip6tables -w 60 -t mangle -N TPROXY_OUT
ip6tables -w 60 -t mangle -A TPROXY_OUT -m conntrack --ctdir REPLY -j RETURN
for cg in ${cgroup_noproxy[@]}; do
ip6tables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j RETURN
done
for cg in ${cgroup_proxy[@]}; do
ip6tables -w 60 -t mangle -A TPROXY_OUT -m cgroup --path $cg -j TPROXY_MARK
done
# hook
$enable_ipv6 && ip6tables -w 60 -t mangle -A OUTPUT -j TPROXY_OUT
## forward #######################################################################
if $enable_gateway; then
iptables -t nat -A POSTROUTING -m owner ! --socket-exists -j MASQUERADE
ip6tables -w 60 -t nat -A POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE # only masquerade ipv6 private address
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
echo "iptables: gateway enabled"
fi
## message for user
cat << DOC
iptables: noproxy cgroup: ${cgroup_noproxy[@]}
iptables: proxied cgroup: ${cgroup_proxy[@]}
DOC
if $enable_gateway; then
iptables -t nat -A POSTROUTING -m owner ! --socket-exists -j MASQUERADE
ip6tables -t nat -A POSTROUTING -m owner ! --socket-exists -s fc00::/7 -j MASQUERADE # only masquerade ipv6 private address
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
echo "ipatbles: gateway enabled"
fi

View File

@@ -1,4 +1,6 @@
{
"comment":"For usage, see https://github.com/springzfx/cgproxy",
"port": 12345,
"program_noproxy": ["v2ray", "qv2ray"],
"program_proxy": [],
@@ -9,5 +11,7 @@
"enable_udp": true,
"enable_tcp": true,
"enable_ipv4": true,
"enable_ipv6": true
"enable_ipv6": true,
"table": 10007,
"fwmark": 39283
}

View File

@@ -3,4 +3,4 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_library(execsnoop MODULE execsnoop.cpp ../src/common.cpp)
target_link_libraries(execsnoop bcc)
install(TARGETS execsnoop DESTINATION /usr/lib/cgproxy/ PERMISSIONS ${basic_permission})
install(TARGETS execsnoop DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/cgproxy/ PERMISSIONS ${basic_permission})

3
execsnoop-bcc/readme.md Normal file
View File

@@ -0,0 +1,3 @@
- depend [bcc](https://github.com/iovisor/bcc)
- huge memory usage, at least 50M

View File

@@ -0,0 +1,15 @@
# find libbpf
if (build_static)
find_library(LIBBPF libbpf.a REQUIRED)
else()
find_library(LIBBPF bpf REQUIRED)
endif()
if (build_execsnoop_dl)
add_library(execsnoop MODULE execsnoop_share.cpp)
install(TARGETS execsnoop DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/cgproxy/)
target_link_libraries(execsnoop PRIVATE ${LIBBPF} -lelf -lz)
else()
add_library(execsnoop STATIC execsnoop_share.cpp)
target_link_libraries(execsnoop PRIVATE ${LIBBPF} -l:libelf.a -l:libz.a)
endif()

View File

@@ -0,0 +1,216 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* THIS FILE IS AUTOGENERATED! */
#ifndef __EXECSNOOP_KERN_SKEL_H__
#define __EXECSNOOP_KERN_SKEL_H__
#include <stdlib.h>
#include <bpf/libbpf.h>
struct execsnoop_kern {
struct bpf_object_skeleton *skeleton;
struct bpf_object *obj;
struct {
struct bpf_map *perf_events;
struct bpf_map *records;
} maps;
struct {
struct bpf_program *syscall_enter_execve;
struct bpf_program *syscall_exit_execve;
} progs;
struct {
struct bpf_link *syscall_enter_execve;
struct bpf_link *syscall_exit_execve;
} links;
};
static void
execsnoop_kern__destroy(struct execsnoop_kern *obj)
{
if (!obj)
return;
if (obj->skeleton)
bpf_object__destroy_skeleton(obj->skeleton);
free(obj);
}
static inline int
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj);
static inline struct execsnoop_kern *
execsnoop_kern__open_opts(const struct bpf_object_open_opts *opts)
{
struct execsnoop_kern *obj;
obj = (typeof(obj))calloc(1, sizeof(*obj));
if (!obj)
return NULL;
if (execsnoop_kern__create_skeleton(obj))
goto err;
if (bpf_object__open_skeleton(obj->skeleton, opts))
goto err;
return obj;
err:
execsnoop_kern__destroy(obj);
return NULL;
}
static inline struct execsnoop_kern *
execsnoop_kern__open(void)
{
return execsnoop_kern__open_opts(NULL);
}
static inline int
execsnoop_kern__load(struct execsnoop_kern *obj)
{
return bpf_object__load_skeleton(obj->skeleton);
}
static inline struct execsnoop_kern *
execsnoop_kern__open_and_load(void)
{
struct execsnoop_kern *obj;
obj = execsnoop_kern__open();
if (!obj)
return NULL;
if (execsnoop_kern__load(obj)) {
execsnoop_kern__destroy(obj);
return NULL;
}
return obj;
}
static inline int
execsnoop_kern__attach(struct execsnoop_kern *obj)
{
return bpf_object__attach_skeleton(obj->skeleton);
}
static inline void
execsnoop_kern__detach(struct execsnoop_kern *obj)
{
return bpf_object__detach_skeleton(obj->skeleton);
}
static inline int
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj)
{
struct bpf_object_skeleton *s;
s = (typeof(s))calloc(1, sizeof(*s));
if (!s)
return -1;
obj->skeleton = s;
s->sz = sizeof(*s);
s->name = "execsnoop_kern";
s->obj = &obj->obj;
/* maps */
s->map_cnt = 2;
s->map_skel_sz = sizeof(*s->maps);
s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
if (!s->maps)
goto err;
s->maps[0].name = "perf_events";
s->maps[0].map = &obj->maps.perf_events;
s->maps[1].name = "records";
s->maps[1].map = &obj->maps.records;
/* programs */
s->prog_cnt = 2;
s->prog_skel_sz = sizeof(*s->progs);
s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
if (!s->progs)
goto err;
s->progs[0].name = "syscall_enter_execve";
s->progs[0].prog = &obj->progs.syscall_enter_execve;
s->progs[0].link = &obj->links.syscall_enter_execve;
s->progs[1].name = "syscall_exit_execve";
s->progs[1].prog = &obj->progs.syscall_exit_execve;
s->progs[1].link = &obj->links.syscall_exit_execve;
s->data_sz = 2024;
s->data = (void *)"\
\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\x28\x05\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0b\0\
\x01\0\x85\0\0\0\x0e\0\0\0\xbf\x06\0\0\0\0\0\0\x63\x6a\xfc\xff\0\0\0\0\x85\0\0\
\0\x0f\0\0\0\xbf\x07\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\
\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\
\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\
\xd0\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x04\0\0\x01\0\0\0\x85\
\0\0\0\x02\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x19\0\0\0\0\0\
\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x08\0\0\0\0\0\0\x15\x08\x12\0\0\0\0\0\x77\x06\0\
\0\x20\0\0\0\x61\xa1\xfc\xff\0\0\0\0\x63\x78\x1c\0\0\0\0\0\x63\x68\x14\0\0\0\0\
\0\x63\x18\x10\0\0\0\0\0\x85\0\0\0\x23\0\0\0\x07\0\0\0\x78\x04\0\0\xbf\xa1\0\0\
\0\0\0\0\x07\x01\0\0\xf0\xff\xff\xff\xb7\x02\0\0\x08\0\0\0\xbf\x03\0\0\0\0\0\0\
\x85\0\0\0\x04\0\0\0\x07\x08\0\0\x18\0\0\0\x79\xa3\xf0\xff\0\0\0\0\x07\x03\0\0\
\x6c\x04\0\0\xbf\x81\0\0\0\0\0\0\xb7\x02\0\0\x04\0\0\0\x85\0\0\0\x04\0\0\0\xb7\
\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\xbf\x16\0\0\0\0\0\0\x85\0\0\0\x0e\0\0\0\x63\
\x0a\xfc\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\x15\x07\x13\
\0\0\0\0\0\x79\x61\x10\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x6d\x12\x0b\0\0\0\0\0\xbf\
\x71\0\0\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x85\0\0\0\x10\0\0\0\xbf\x61\0\0\0\0\0\0\
\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x03\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\
\0\xbf\x74\0\0\0\0\0\0\xb7\x05\0\0\x20\0\0\0\x85\0\0\0\x19\0\0\0\xbf\xa2\0\0\0\
\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\
\0\x03\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\x20\0\0\0\
\0\x28\0\0\0\0\0\0\x04\0\0\0\x04\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x47\x50\x4c\
\0\x06\x07\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\0\x04\
\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdb\0\0\0\0\0\x03\0\x70\x01\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x05\0\xc0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcd\
\0\0\0\0\0\x05\0\xe8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa3\0\0\0\x11\0\x08\0\0\0\0\
\0\0\0\0\0\x04\0\0\0\0\0\0\0\x20\0\0\0\x11\0\x09\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\
\0\0\0\x07\0\0\0\x11\0\x07\0\x14\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x18\0\0\0\x11\
\0\x07\0\0\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x8e\0\0\0\x12\0\x03\0\0\0\0\0\0\0\0\
\0\x80\x01\0\0\0\0\0\0\x51\0\0\0\x12\0\x05\0\0\0\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\
\x70\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\xb8\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\
\x28\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\x88\0\0\0\0\0\0\0\x01\0\0\0\x07\0\0\0\
\xd0\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\0\x2e\x74\x65\x78\x74\0\x70\x65\x72\x66\
\x5f\x65\x76\x65\x6e\x74\x73\0\x6d\x61\x70\x73\0\x72\x65\x63\x6f\x72\x64\x73\0\
\x5f\x76\x65\x72\x73\x69\x6f\x6e\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\x6f\
\x69\x6e\x74\x2f\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\x78\
\x69\x74\x5f\x65\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\x65\x78\
\x69\x74\x5f\x65\x78\x65\x63\x76\x65\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\
\x6f\x69\x6e\x74\x2f\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\
\x6e\x74\x65\x72\x5f\x65\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\
\x65\x6e\x74\x65\x72\x5f\x65\x78\x65\x63\x76\x65\0\x5f\x6c\x69\x63\x65\x6e\x73\
\x65\0\x65\x78\x65\x63\x73\x6e\x6f\x6f\x70\x5f\x6b\x65\x72\x6e\x2e\x63\0\x2e\
\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x4c\x42\x42\x31\x5f\
\x34\0\x4c\x42\x42\x31\x5f\x33\0\x4c\x42\x42\x30\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\x40\x04\0\0\0\0\0\0\xe2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\x69\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\
\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x65\0\
\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\x03\0\0\0\0\0\0\x20\0\0\0\0\
\0\0\0\x0a\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x2d\0\0\0\x01\0\
\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x01\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x29\0\0\0\x09\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\x10\x04\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x0a\0\0\0\x05\0\0\
\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x13\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\xb8\x02\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\xe0\x02\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\x21\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe4\x02\0\0\0\
\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc5\
\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe8\x02\0\0\0\0\0\0\x08\x01\0\
\0\0\0\0\0\x01\0\0\0\x05\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0";
return 0;
err:
bpf_object__destroy_skeleton(s);
return -1;
}
#endif /* __EXECSNOOP_KERN_SKEL_H__ */

82
execsnoop-kernel/arm64.md Normal file
View File

@@ -0,0 +1,82 @@
## Cross Compile
```bash
docker pull debian:buster
# in container
dpkg --add-architecture arm64
apt update
apt install gcc-8-aarch64-linux-gnu # cross compile toolchain
apt install libelf-dev:arm64 # target depency library
...
```
## Emulation
### Register qemu-user-static
- before register binfmt
```bash
docker run --rm -t arm64/ubuntu uname -m
```
- register binfmt
```bash
# through docker
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# or through systemd
pacman -S qemu-user-static binfmt-qemu-static
systemctl restart systemd-binfmt.service
# test
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
```
- after register binfmt
```bash
docker run --rm -t arm64/ubuntu uname -m
```
### M1: Docker
```bash
# start container background
docker run -dit --name arm64 -v /home/fancy/workspace-xps:/data arm64v8/ubuntu
# enter container
docker exec -it arm64 bash
```
### M2: Chroot
download image [ubuntu-base-20.04-base-arm64.tar.gz](http://cdimage.ubuntu.com/ubuntu-base/releases/20.04/release/ubuntu-base-20.04-base-arm64.tar.gz), extract and chroot to it.
```bash
sudo arch-chroot ubuntu-base-20.04-base-arm64
```
### Refer
- https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/
- https://github.com/junaruga/fedora-workshop-multiarch/blob/master/slides/Lets-add-Fedora-multiarch-to-CI.pdf
- https://wiki.debian.org/QemuUserEmulation
### Compile
ready some depencies.
```bash
# maybe repository: https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/
# install in container for kernel bpf build
apt install dialog apt-utils
apt install build-essential gcc clang llvm
apt install bison flex bc rsync libssl-dev binutils-dev libreadline-dev libelf-dev
apt install make cmake
# for cgproxy
apt install nlohmann-json3-dev rpm
```

View File

@@ -0,0 +1,97 @@
#ifdef USE_VMLINUX
#include "vmlinux.h"
#else
#include "linux/sched.h"
#include <linux/ptrace.h>
#include <uapi/linux/bpf.h>
#endif
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
/* /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/format */
struct syscalls_enter_execve_args {
__u64 unused;
int syscall_nr;
const char filename_ptr;
const char *const * argv;
const char *const * envp;
};
/* /sys/kernel/debug/tracing/events/syscalls/sys_exit_execve/format */
struct syscalls_exit_execve_args {
__u64 unused;
int syscall_nr;
long ret;
};
struct bpf_map_def SEC("maps") records = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(pid_t),
.value_size = sizeof(struct event),
.max_entries = 10240,
};
struct bpf_map_def SEC("maps") perf_events = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(u32),
.value_size = sizeof(u32),
.max_entries = 128,
};
SEC("tracepoint/syscalls/sys_enter_execve")
int syscall_enter_execve(struct syscalls_enter_execve_args *ctx){
pid_t pid, tgid; uid_t uid;
struct event *event;
struct task_struct *task, *task_p;
u64 id = bpf_get_current_pid_tgid();
pid = (pid_t)id;
tgid = id >> 32;
uid = (u32)bpf_get_current_uid_gid();
struct event empty_event={};
if (bpf_map_update_elem(&records, &pid, &empty_event, BPF_NOEXIST)!=0) return 0;
event = bpf_map_lookup_elem(&records, &pid);
if (!event) return 0;
event->pid = pid;
event->tgid = tgid;
event->uid = uid;
/* ppid is not reliable here, normal in arch, but become 0 in ubuntu 20.04 */
task = (struct task_struct*)bpf_get_current_task();
bpf_probe_read(&task_p, sizeof(struct task_struct*),&task->real_parent);
bpf_probe_read(&event->ppid,sizeof(pid_t),&task_p->tgid);
return 0;
}
SEC("tracepoint/syscalls/sys_exit_execve")
int syscall_exit_execve(struct syscalls_exit_execve_args *ctx){
pid_t pid;
struct event *event;
pid = (pid_t)bpf_get_current_pid_tgid();
event = bpf_map_lookup_elem(&records,&pid);
if (!event) return 0;
if (ctx->ret < 0) goto cleanup;
/* get comm */
bpf_get_current_comm(&event->comm,sizeof(event->comm));
bpf_perf_event_output(ctx, &perf_events, BPF_F_CURRENT_CPU, event, sizeof(struct event));
cleanup:
bpf_map_delete_elem(&records, &pid);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

View File

@@ -0,0 +1,96 @@
#include "execsnoop_share.h"
#include <errno.h>
#include <signal.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#if defined(__x86_64__)
#include "x86_64/execsnoop_kern_skel.h"
#elif defined(__aarch64__)
#include "aarch64/execsnoop_kern_skel.h"
#endif
namespace CGPROXY::EXECSNOOP {
#define PERF_BUFFER_PAGES 64
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
function<int(int)> callback = NULL;
promise<void> status;
static void handle_event(void *ctx, int cpu, void *data, __u32 size) {
auto e = static_cast<event*>(data);
if (callback) callback(e->pid);
}
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) {
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int bump_memlock_rlimit(void) {
struct rlimit rlim_new = { RLIM_INFINITY, RLIM_INFINITY };
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}
int execsnoop() {
struct perf_buffer_opts pb_opts = {};
struct perf_buffer *pb;
int err;
bool notified=false;
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
struct execsnoop_kern *obj=execsnoop_kern__open_and_load();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
err = execsnoop_kern__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
return err;
}
main_loop:
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts);
err = libbpf_get_error(pb);
if (err) {
printf("failed to setup perf_buffer: %d\n", err);
return 1;
}
// notify
if (!notified) {status.set_value(); notified=true;}
while ((err = perf_buffer__poll(pb, -1)) >= 0) {}
perf_buffer__free(pb);
/* handle Interrupted system call when sleep */
if (err == -EINTR) goto main_loop;
perror("perf_buffer__poll");
kill(0, SIGINT);
return err;
}
void startThread(function<int(int)> c, promise<void> _status) {
status = move(_status);
callback = c;
execsnoop();
}
}

View File

@@ -7,10 +7,14 @@
using namespace std;
namespace CGPROXY::EXECSNOOP {
extern "C" void startThread(function<int(int)> c, promise<void> _status);
// typedef void startThread_t(function<int(int)>, promise<void>);
#ifdef BUIlD_EXECSNOOP_DL
// only for dlsym()
using startThread_t=decltype(startThread);
startThread_t *_startThread; // only for dlsym()
startThread_t *_startThread;
#endif
} // namespace CGPROXY::EXECSNOOP
#endif

View File

@@ -0,0 +1,40 @@
#include <signal.h>
#include <bpf/libbpf.h>
#include "bpf_load.h"
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size) {
struct event *e=data;
printf("comm: %s, pid: %d, tgid: %d, ppid: %d, uid: %d\n",e->comm,e->pid,e->tgid,e->ppid,e->uid);
}
int main(int argc, char **argv) {
struct perf_buffer_opts pb_opts = {};
struct perf_buffer *pb;
int ret;
if (load_bpf_file("execsnoop_kern.o")!=0) {
printf("%s", bpf_log_buf);
return 1;
}
pb_opts.sample_cb = print_bpf_output;
pb = perf_buffer__new(map_fd[1], 8, &pb_opts);
ret = libbpf_get_error(pb);
if (ret) {
printf("failed to setup perf_buffer: %d\n", ret);
return 1;
}
while ((ret = perf_buffer__poll(pb, -1)) >= 0) {}
kill(0, SIGINT);
return ret;
}

View File

@@ -0,0 +1,72 @@
#include <signal.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#if defined(__x86_64__)
#include "x86_64/execsnoop_kern_skel.h"
#elif defined(__aarch64__)
#include "aarch64/execsnoop_kern_skel.h"
#endif
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
#define PERF_BUFFER_PAGES 64
static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size) {
struct event *e=data;
printf("comm: %s, pid: %d, tgid: %d, ppid: %d, uid: %d\n",e->comm,e->pid,e->tgid,e->ppid,e->uid);
}
int bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}
int main(int argc, char **argv) {
struct perf_buffer_opts pb_opts = {};
struct perf_buffer *pb;
int err;
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
struct execsnoop_kern *obj=execsnoop_kern__open_and_load();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
err = execsnoop_kern__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
return err;
}
pb_opts.sample_cb = print_bpf_output;
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts);
err = libbpf_get_error(pb);
if (err) {
printf("failed to setup perf_buffer: %d\n", err);
return 1;
}
while ((err = perf_buffer__poll(pb, -1)) >= 0) {}
kill(0, SIGINT);
return err;
}

198
execsnoop-kernel/readme.md Normal file
View File

@@ -0,0 +1,198 @@
## Prons and cons
- use stable execve tracepoint, so build once and should work everywhere
- build in kernel tree or build with VMLINUX
## Build `execsnoop_kern.o`
### M1: Build in kernel tree
- download kernel source code
- ready and config kernel tree
```bash
# kernel config
#gunzip -c /proc/config.gz > .config
#make oldconfig && make prepare
make defconfig && make prepare
# install headers to ./usr/include
make headers_install -j8
# build samples/bpf
make M=samples/bpf -j8
# build bpftool
make tools/bpf -j8
```
- put or link `execsnoop_kern.c` and `execsnoop_user.c` to *samples/bpf/*
- edit *samples/bpf/makefile*
```makefile
# in samples/bpf/makefile
tprogs-y += execsnoop
execsnoop-objs := bpf_load.o execsnoop_user.o $(TRACE_HELPERS)
always-y += execsnoop_kern.o
```
- compile again
```
make M=samples/bpf -j8
```
- run test
```bash
cd samples/bpf
sudo bash -c "ulimit -l unlimited && ./execsnoop"
```
**Detail build command**
using `make V=1 M=samples/bpf | tee -a log.txt` to get and filter following command
- build `execsnoop_kern.o`
```bash
clang -nostdinc \
-isystem /usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/include \
-I./arch/x86/include \
-I./arch/x86/include/generated \
-I./include \
-I./arch/x86/include/uapi \
-I./arch/x86/include/generated/uapi \
-I./include/uapi \
-I./include/generated/uapi \
-include ./include/linux/kconfig.h \
-I./samples/bpf \
-I./tools/testing/selftests/bpf/ \
-I./tools/lib/ \
-include asm_goto_workaround.h \
-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
-D__TARGET_ARCH_x86 -Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-address-of-packed-member -Wno-tautological-compare \
-Wno-unknown-warning-option \
-fno-stack-protector \
-O2 -emit-llvm -c samples/bpf/execsnoop_kern.c \
-o - | llc -march=bpf -filetype=obj -o samples/bpf/execsnoop_kern.o
```
### M2: Build with VMLINUX
- get `vmlinux.h`
```
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
```
- compile
note `-g` is needed if with BPF CO-RE
```
clang -O2 -target bpf -DUSE_VMLINUX -c execsnoop_kern.c -o execsnoop_kern.o
```
## Generate `execsnoop_kern_skel.h`
- generate `execsnoop_kern_skel.h`
```
bpftool gen skeleton execsnoop_kern.o > execsnoop_kern_skel.h
```
- build execsnoop
```
gcc -Wall -O2 execsnoop_user_1.c -o execsnoop -lbpf
```
## Multiarch build
- Cross compile, fast, but library link can be mess
- aarch64-linux-gnu-gcc
- Emulation, the easist, but with perfomance cost
- qemu-user-static + binfmt-qemu-static + docker/chroot
- see `arm64.md` to see how to setup
```bash
# if cross compile
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export SYSROOT=/home/fancy/workspace-xps/linux/ArchLinuxARM-aarch64-latest
export C_INCLUDE_PATH=$SYSROOT/usr/include
```
- edit `tools/lib/bpf/makefile` #192 to:
```makefile
$(OUTPUT)libbpf.so.$(LIBBPF_VERSION): $(BPF_IN_SHARED)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS)
```
- make
```bash
# clean
make mrproper
make clean
make -C tools clean
make -C samples/bpf clean
# make
make defconfig && make prepare
make headers_install -j8
# build samples/bpf
make M=samples/bpf -j8
# build bpftool
make tools/bpf -j8
```
- detail build `execsnoop_kern.o`
```bash
clang -nostdinc \
-isystem /usr/lib/gcc/aarch64-linux-gnu/9/include \
-I./arch/arm64/include -I./arch/arm64/include/generated \
-I./include -I./arch/arm64/include/uapi \
-I./arch/arm64/include/generated/uapi \
-I./include/uapi \
-I./include/generated/uapi \
-include ./include/linux/kconfig.h \
-I./samples/bpf \
-I./tools/testing/selftests/bpf/ \
-I./tools/lib/ \
-include asm_goto_workaround.h \
-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
-D__TARGET_ARCH_arm64 -Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-address-of-packed-member -Wno-tautological-compare \
-Wno-unknown-warning-option \
-fno-stack-protector \
-O2 -emit-llvm -c samples/bpf/execsnoop_kern.c \
-o -| llc -march=bpf -filetype=obj -o samples/bpf/execsnoop_kern.o
```
- generate
```
bpftool gen skeleton execsnoop_kern.o > aarch64/execsnoop_kern_skel.h
```
## Refer
- [A thorough introduction to eBPF](https://lwn.net/Articles/740157/)

View File

@@ -0,0 +1,225 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* THIS FILE IS AUTOGENERATED! */
#ifndef __EXECSNOOP_KERN_SKEL_H__
#define __EXECSNOOP_KERN_SKEL_H__
#include <stdlib.h>
#include <bpf/libbpf.h>
struct execsnoop_kern {
struct bpf_object_skeleton *skeleton;
struct bpf_object *obj;
struct {
struct bpf_map *perf_events;
struct bpf_map *records;
} maps;
struct {
struct bpf_program *syscall_enter_execve;
struct bpf_program *syscall_exit_execve;
} progs;
struct {
struct bpf_link *syscall_enter_execve;
struct bpf_link *syscall_exit_execve;
} links;
};
static void
execsnoop_kern__destroy(struct execsnoop_kern *obj)
{
if (!obj)
return;
if (obj->skeleton)
bpf_object__destroy_skeleton(obj->skeleton);
free(obj);
}
static inline int
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj);
static inline struct execsnoop_kern *
execsnoop_kern__open_opts(const struct bpf_object_open_opts *opts)
{
struct execsnoop_kern *obj;
obj = (typeof(obj))calloc(1, sizeof(*obj));
if (!obj)
return NULL;
if (execsnoop_kern__create_skeleton(obj))
goto err;
if (bpf_object__open_skeleton(obj->skeleton, opts))
goto err;
return obj;
err:
execsnoop_kern__destroy(obj);
return NULL;
}
static inline struct execsnoop_kern *
execsnoop_kern__open(void)
{
return execsnoop_kern__open_opts(NULL);
}
static inline int
execsnoop_kern__load(struct execsnoop_kern *obj)
{
return bpf_object__load_skeleton(obj->skeleton);
}
static inline struct execsnoop_kern *
execsnoop_kern__open_and_load(void)
{
struct execsnoop_kern *obj;
obj = execsnoop_kern__open();
if (!obj)
return NULL;
if (execsnoop_kern__load(obj)) {
execsnoop_kern__destroy(obj);
return NULL;
}
return obj;
}
static inline int
execsnoop_kern__attach(struct execsnoop_kern *obj)
{
return bpf_object__attach_skeleton(obj->skeleton);
}
static inline void
execsnoop_kern__detach(struct execsnoop_kern *obj)
{
return bpf_object__detach_skeleton(obj->skeleton);
}
static inline int
execsnoop_kern__create_skeleton(struct execsnoop_kern *obj)
{
struct bpf_object_skeleton *s;
s = (typeof(s))calloc(1, sizeof(*s));
if (!s)
return -1;
obj->skeleton = s;
s->sz = sizeof(*s);
s->name = "execsnoop_kern";
s->obj = &obj->obj;
/* maps */
s->map_cnt = 2;
s->map_skel_sz = sizeof(*s->maps);
s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
if (!s->maps)
goto err;
s->maps[0].name = "perf_events";
s->maps[0].map = &obj->maps.perf_events;
s->maps[1].name = "records";
s->maps[1].map = &obj->maps.records;
/* programs */
s->prog_cnt = 2;
s->prog_skel_sz = sizeof(*s->progs);
s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
if (!s->progs)
goto err;
s->progs[0].name = "syscall_enter_execve";
s->progs[0].prog = &obj->progs.syscall_enter_execve;
s->progs[0].link = &obj->links.syscall_enter_execve;
s->progs[1].name = "syscall_exit_execve";
s->progs[1].prog = &obj->progs.syscall_exit_execve;
s->progs[1].link = &obj->links.syscall_exit_execve;
s->data_sz = 2320;
s->data = (void *)"\
\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\xd0\x05\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\
\x01\0\x85\0\0\0\x0e\0\0\0\xbf\x06\0\0\0\0\0\0\x63\x6a\xfc\xff\0\0\0\0\x85\0\0\
\0\x0f\0\0\0\xbf\x07\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\
\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\
\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\
\xd0\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x04\0\0\x01\0\0\0\x85\
\0\0\0\x02\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x19\0\0\0\0\0\
\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x08\0\0\0\0\0\0\x15\x08\x12\0\0\0\0\0\x77\x06\0\
\0\x20\0\0\0\x61\xa1\xfc\xff\0\0\0\0\x63\x78\x1c\0\0\0\0\0\x63\x68\x14\0\0\0\0\
\0\x63\x18\x10\0\0\0\0\0\x85\0\0\0\x23\0\0\0\x07\0\0\0\xa0\x04\0\0\xbf\xa1\0\0\
\0\0\0\0\x07\x01\0\0\xf0\xff\xff\xff\xb7\x02\0\0\x08\0\0\0\xbf\x03\0\0\0\0\0\0\
\x85\0\0\0\x04\0\0\0\x07\x08\0\0\x18\0\0\0\x79\xa3\xf0\xff\0\0\0\0\x07\x03\0\0\
\x94\x04\0\0\xbf\x81\0\0\0\0\0\0\xb7\x02\0\0\x04\0\0\0\x85\0\0\0\x04\0\0\0\xb7\
\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\xbf\x16\0\0\0\0\0\0\x85\0\0\0\x0e\0\0\0\x63\
\x0a\xfc\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\x15\x07\x13\
\0\0\0\0\0\x79\x61\x10\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x6d\x12\x0b\0\0\0\0\0\xbf\
\x71\0\0\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x85\0\0\0\x10\0\0\0\xbf\x61\0\0\0\0\0\0\
\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x03\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\
\0\xbf\x74\0\0\0\0\0\0\xb7\x05\0\0\x20\0\0\0\x85\0\0\0\x19\0\0\0\xbf\xa2\0\0\0\
\0\0\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\
\0\x03\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\x20\0\0\0\
\0\x28\0\0\0\0\0\0\x04\0\0\0\x04\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x47\x50\x4c\
\0\x06\x07\x05\0\x10\0\0\0\0\0\0\0\x01\x7a\x52\0\x08\x7c\x0b\x01\x0c\0\0\0\x18\
\0\0\0\x18\0\0\0\0\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x34\0\0\
\0\0\0\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\xba\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\xe9\0\0\0\0\0\x03\0\x70\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe2\0\0\0\0\0\x05\0\
\xc0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdb\0\0\0\0\0\x05\0\xe8\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\
\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa3\0\0\0\x11\0\x08\0\0\0\0\0\0\0\0\0\
\x04\0\0\0\0\0\0\0\x20\0\0\0\x11\0\x09\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\x07\
\0\0\0\x11\0\x07\0\x14\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x18\0\0\0\x11\0\x07\0\0\
\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x8e\0\0\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x80\x01\
\0\0\0\0\0\0\x51\0\0\0\x12\0\x05\0\0\0\0\0\0\0\0\0\xf8\0\0\0\0\0\0\0\x70\0\0\0\
\0\0\0\0\x01\0\0\0\x0a\0\0\0\xb8\0\0\0\0\0\0\0\x01\0\0\0\x0a\0\0\0\x28\0\0\0\0\
\0\0\0\x01\0\0\0\x0a\0\0\0\x88\0\0\0\0\0\0\0\x01\0\0\0\x09\0\0\0\xd0\0\0\0\0\0\
\0\0\x01\0\0\0\x0a\0\0\0\x1c\0\0\0\0\0\0\0\x01\0\0\0\x05\0\0\0\x38\0\0\0\0\0\0\
\0\x01\0\0\0\x06\0\0\0\0\x2e\x74\x65\x78\x74\0\x70\x65\x72\x66\x5f\x65\x76\x65\
\x6e\x74\x73\0\x6d\x61\x70\x73\0\x72\x65\x63\x6f\x72\x64\x73\0\x5f\x76\x65\x72\
\x73\x69\x6f\x6e\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\x6f\x69\x6e\x74\x2f\
\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\x78\x69\x74\x5f\x65\
\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\x65\x78\x69\x74\x5f\x65\
\x78\x65\x63\x76\x65\0\x2e\x72\x65\x6c\x74\x72\x61\x63\x65\x70\x6f\x69\x6e\x74\
\x2f\x73\x79\x73\x63\x61\x6c\x6c\x73\x2f\x73\x79\x73\x5f\x65\x6e\x74\x65\x72\
\x5f\x65\x78\x65\x63\x76\x65\0\x73\x79\x73\x63\x61\x6c\x6c\x5f\x65\x6e\x74\x65\
\x72\x5f\x65\x78\x65\x63\x76\x65\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x72\
\x65\x6c\x2e\x65\x68\x5f\x66\x72\x61\x6d\x65\0\x65\x78\x65\x63\x73\x6e\x6f\x6f\
\x70\x5f\x6b\x65\x72\x6e\x2e\x63\0\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\
\x6d\x74\x61\x62\0\x4c\x42\x42\x31\x5f\x34\0\x4c\x42\x42\x31\x5f\x33\0\x4c\x42\
\x42\x30\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcb\0\0\0\
\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe0\x04\0\0\0\0\0\0\xf0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x06\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x69\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\x40\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\x65\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\
\x04\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\
\0\0\0\0\0\0\x2d\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x01\0\0\
\0\0\0\0\xf8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\x29\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x04\0\0\0\0\0\0\x30\0\
\0\0\0\0\0\0\x0c\0\0\0\x05\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x13\0\0\0\
\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\x02\0\0\0\0\0\0\x28\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\x01\0\0\0\x03\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe0\x02\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x21\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\xe4\x02\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\xb0\0\0\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\xe8\x02\0\0\0\0\0\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\xac\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x04\0\0\0\
\0\0\0\x20\0\0\0\0\0\0\0\x0c\0\0\0\x0a\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\
\0\xd3\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x03\0\0\0\0\0\0\x38\
\x01\0\0\0\0\0\0\x01\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0";
return 0;
err:
bpf_object__destroy_skeleton(s);
return -1;
}
#endif /* __EXECSNOOP_KERN_SKEL_H__ */

View File

@@ -1,87 +0,0 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := $(abspath build)
CLANG ?= clang
CFLAGS ?= -g -O2 -Wall
LLVM_STRIP ?= llvm-strip
BPFTOOL ?= bpftool
LIBBPF_DIR := $(abspath libbpf/src)
LIBBPF_OBJ := $(OUTPUT)/usr/lib64/libbpf.a
LIBBPF_H := $(OUTPUT)/usr/include
INCLUDES := -I$(OUTPUT) -I$(LIBBPF_H)
ARCH := $(shell uname -m | sed 's/x86_64/x86/')
APPS = execsnoop
LIBSO = libexecsnoop.so
.PHONY: all
all: $(APPS) $(LIBSO)
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif
COMMON_OBJ = \
$(OUTPUT)/trace_helpers.o \
$(OUTPUT)/syscall_helpers.o \
$(OUTPUT)/errno_helpers.o \
.PHONY: install
install: $(LIBSO)
install -D $(LIBSO) -t $(DESTDIR)/usr/lib/cgproxy/
.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS) $(LIBSO)
$(OUTPUT) $(OUTPUT)/libbpf:
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) $(COMMON_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ -lelf -lz -o $@
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -fPIC -c $(filter %.c,$^) -o $@
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) vmlinux.h | $(OUTPUT)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) -c $(filter %.c,$^) -o $@ && \
$(LLVM_STRIP) -g $@
vmlinux.h:
$(Q)$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
$(LIBBPF_OBJ): patch
$(Q)cd $(LIBBPF_DIR) && make DESTDIR=${OUTPUT} prefix=$(OUTPUT) install install_headers
patch: | $(LIBBPF_DIR)/Makefile
@echo "patching libbpf-fPIC.patch"
$(Q)cd libbpf/src && patch -p1 --forward -i ../../libbpf-fPIC.patch || true
# build libexecsnoop.so
$(LIBSO): execsnoop_share.cpp $(OUTPUT)/execsnoop.skel.h $(LIBBPF_OBJ) $(COMMON_OBJ) | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)$(CXX) -std=c++17 $(CFLAGS) $(INCLUDES) -fPIC $< $(COMMON_OBJ) -shared -lelf -lz -Wl,--no-whole-archive,--no-undefined $(LIBBPF_OBJ) -o $@
# delete failed targets
.DELETE_ON_ERROR:
# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:

View File

@@ -1,232 +0,0 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Anton Protopopov
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#define warn(...) fprintf(stderr, __VA_ARGS__)
#ifdef __x86_64__
static int errno_by_name_x86_64(const char *errno_name)
{
#define strcase(X, N) if (!strcmp(errno_name, (X))) return N
strcase("EPERM", 1);
strcase("ENOENT", 2);
strcase("ESRCH", 3);
strcase("EINTR", 4);
strcase("EIO", 5);
strcase("ENXIO", 6);
strcase("E2BIG", 7);
strcase("ENOEXEC", 8);
strcase("EBADF", 9);
strcase("ECHILD", 10);
strcase("EAGAIN", 11);
strcase("EWOULDBLOCK", 11);
strcase("ENOMEM", 12);
strcase("EACCES", 13);
strcase("EFAULT", 14);
strcase("ENOTBLK", 15);
strcase("EBUSY", 16);
strcase("EEXIST", 17);
strcase("EXDEV", 18);
strcase("ENODEV", 19);
strcase("ENOTDIR", 20);
strcase("EISDIR", 21);
strcase("EINVAL", 22);
strcase("ENFILE", 23);
strcase("EMFILE", 24);
strcase("ENOTTY", 25);
strcase("ETXTBSY", 26);
strcase("EFBIG", 27);
strcase("ENOSPC", 28);
strcase("ESPIPE", 29);
strcase("EROFS", 30);
strcase("EMLINK", 31);
strcase("EPIPE", 32);
strcase("EDOM", 33);
strcase("ERANGE", 34);
strcase("EDEADLK", 35);
strcase("EDEADLOCK", 35);
strcase("ENAMETOOLONG", 36);
strcase("ENOLCK", 37);
strcase("ENOSYS", 38);
strcase("ENOTEMPTY", 39);
strcase("ELOOP", 40);
strcase("ENOMSG", 42);
strcase("EIDRM", 43);
strcase("ECHRNG", 44);
strcase("EL2NSYNC", 45);
strcase("EL3HLT", 46);
strcase("EL3RST", 47);
strcase("ELNRNG", 48);
strcase("EUNATCH", 49);
strcase("ENOCSI", 50);
strcase("EL2HLT", 51);
strcase("EBADE", 52);
strcase("EBADR", 53);
strcase("EXFULL", 54);
strcase("ENOANO", 55);
strcase("EBADRQC", 56);
strcase("EBADSLT", 57);
strcase("EBFONT", 59);
strcase("ENOSTR", 60);
strcase("ENODATA", 61);
strcase("ETIME", 62);
strcase("ENOSR", 63);
strcase("ENONET", 64);
strcase("ENOPKG", 65);
strcase("EREMOTE", 66);
strcase("ENOLINK", 67);
strcase("EADV", 68);
strcase("ESRMNT", 69);
strcase("ECOMM", 70);
strcase("EPROTO", 71);
strcase("EMULTIHOP", 72);
strcase("EDOTDOT", 73);
strcase("EBADMSG", 74);
strcase("EOVERFLOW", 75);
strcase("ENOTUNIQ", 76);
strcase("EBADFD", 77);
strcase("EREMCHG", 78);
strcase("ELIBACC", 79);
strcase("ELIBBAD", 80);
strcase("ELIBSCN", 81);
strcase("ELIBMAX", 82);
strcase("ELIBEXEC", 83);
strcase("EILSEQ", 84);
strcase("ERESTART", 85);
strcase("ESTRPIPE", 86);
strcase("EUSERS", 87);
strcase("ENOTSOCK", 88);
strcase("EDESTADDRREQ", 89);
strcase("EMSGSIZE", 90);
strcase("EPROTOTYPE", 91);
strcase("ENOPROTOOPT", 92);
strcase("EPROTONOSUPPORT", 93);
strcase("ESOCKTNOSUPPORT", 94);
strcase("ENOTSUP", 95);
strcase("EOPNOTSUPP", 95);
strcase("EPFNOSUPPORT", 96);
strcase("EAFNOSUPPORT", 97);
strcase("EADDRINUSE", 98);
strcase("EADDRNOTAVAIL", 99);
strcase("ENETDOWN", 100);
strcase("ENETUNREACH", 101);
strcase("ENETRESET", 102);
strcase("ECONNABORTED", 103);
strcase("ECONNRESET", 104);
strcase("ENOBUFS", 105);
strcase("EISCONN", 106);
strcase("ENOTCONN", 107);
strcase("ESHUTDOWN", 108);
strcase("ETOOMANYREFS", 109);
strcase("ETIMEDOUT", 110);
strcase("ECONNREFUSED", 111);
strcase("EHOSTDOWN", 112);
strcase("EHOSTUNREACH", 113);
strcase("EALREADY", 114);
strcase("EINPROGRESS", 115);
strcase("ESTALE", 116);
strcase("EUCLEAN", 117);
strcase("ENOTNAM", 118);
strcase("ENAVAIL", 119);
strcase("EISNAM", 120);
strcase("EREMOTEIO", 121);
strcase("EDQUOT", 122);
strcase("ENOMEDIUM", 123);
strcase("EMEDIUMTYPE", 124);
strcase("ECANCELED", 125);
strcase("ENOKEY", 126);
strcase("EKEYEXPIRED", 127);
strcase("EKEYREVOKED", 128);
strcase("EKEYREJECTED", 129);
strcase("EOWNERDEAD", 130);
strcase("ENOTRECOVERABLE", 131);
strcase("ERFKILL", 132);
strcase("EHWPOISON", 133);
#undef strcase
return -1;
}
#endif
/* Try to find the errno number using the errno(1) program */
static int errno_by_name_dynamic(const char *errno_name)
{
int len = strlen(errno_name);
int err, number = -1;
char buf[128];
char cmd[64];
char *end;
long val;
FILE *f;
/* sanity check to not call popen with random input */
for (int i = 0; i < len; i++) {
if (errno_name[i] < 'A' || errno_name[i] > 'Z') {
warn("errno_name contains invalid char 0x%02x: %s\n",
errno_name[i], errno_name);
return -1;
}
}
snprintf(cmd, sizeof(cmd), "errno %s", errno_name);
f = popen(cmd, "r");
if (!f) {
warn("popen: %s: %s\n", cmd, strerror(errno));
return -1;
}
if (!fgets(buf, sizeof(buf), f)) {
goto close;
} else if (ferror(f)) {
warn("fgets: %s\n", strerror(errno));
goto close;
}
// expecting "<name> <number> <description>"
if (strncmp(errno_name, buf, len) || strlen(buf) < len+2) {
warn("expected '%s': %s\n", errno_name, buf);
goto close;
}
errno = 0;
val = strtol(buf+len+2, &end, 10);
if (errno || end == (buf+len+2) || number < 0 || number > INT_MAX) {
warn("can't parse the second column, expected int: %s\n", buf);
goto close;
}
number = val;
close:
err = pclose(f);
if (err < 0)
warn("pclose: %s\n", strerror(errno));
#ifndef __x86_64__
/* Ignore the error for x86_64 where we have a table compiled in */
else if (err && WEXITSTATUS(err) == 127) {
warn("errno(1) required for errno name/number mapping\n");
} else if (err) {
warn("errno(1) exit status (see wait(2)): 0x%x\n", err);
}
#endif
return number;
}
int errno_by_name(const char *errno_name)
{
#ifdef __x86_64__
int err;
err = errno_by_name_x86_64(errno_name);
if (err >= 0)
return err;
#endif
return errno_by_name_dynamic(errno_name);
}

View File

@@ -1,7 +0,0 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __ERRNO_HELPERS_H
#define __ERRNO_HELPERS_H
int errno_by_name(const char *errno_name);
#endif /* __ERRNO_HELPERS_H */

View File

@@ -1,128 +0,0 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include "execsnoop.h"
const volatile bool ignore_failed = true;
const volatile uid_t targ_uid = INVALID_UID;
const volatile int max_args = DEFAULT_MAXARGS;
static const struct event empty_event = {};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10240);
__type(key, pid_t);
__type(value, struct event);
} execs SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} events SEC(".maps");
static __always_inline bool valid_uid(uid_t uid) {
return uid != INVALID_UID;
}
SEC("tracepoint/syscalls/sys_enter_execve")
int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx)
{
u64 id;
pid_t pid, tgid;
unsigned int ret;
struct event *event;
struct task_struct *task;
const char **args = (const char **)(ctx->args[1]);
const char *argp;
uid_t uid = (u32)bpf_get_current_uid_gid();
if (valid_uid(targ_uid) && targ_uid != uid)
return 0;
id = bpf_get_current_pid_tgid();
pid = (pid_t)id;
tgid = id >> 32;
if (bpf_map_update_elem(&execs, &pid, &empty_event, BPF_NOEXIST))
return 0;
event = bpf_map_lookup_elem(&execs, &pid);
if (!event)
return 0;
event->pid = pid;
event->tgid = tgid;
event->uid = uid;
task = (struct task_struct*)bpf_get_current_task();
event->ppid = (pid_t)BPF_CORE_READ(task, real_parent, tgid);
event->args_count = 0;
event->args_size = 0;
ret = bpf_probe_read_str(event->args, ARGSIZE, (const char*)ctx->args[0]);
if (ret <= ARGSIZE) {
event->args_size += ret;
} else {
/* write an empty string */
event->args[0] = '\0';
event->args_size++;
}
event->args_count++;
#pragma unroll
for (int i = 1; i < TOTAL_MAX_ARGS && i < max_args; i++) {
bpf_probe_read(&argp, sizeof(argp), &args[i]);
if (!argp)
return 0;
if (event->args_size > LAST_ARG)
return 0;
ret = bpf_probe_read_str(&event->args[event->args_size], ARGSIZE, argp);
if (ret > ARGSIZE)
return 0;
event->args_count++;
event->args_size += ret;
}
/* try to read one more argument to check if there is one */
bpf_probe_read(&argp, sizeof(argp), &args[max_args]);
if (!argp)
return 0;
/* pointer to max_args+1 isn't null, asume we have more arguments */
event->args_count++;
return 0;
}
SEC("tracepoint/syscalls/sys_exit_execve")
int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit* ctx)
{
u64 id;
pid_t pid;
int ret;
struct event *event;
u32 uid = (u32)bpf_get_current_uid_gid();
if (valid_uid(targ_uid) && targ_uid != uid)
return 0;
id = bpf_get_current_pid_tgid();
pid = (pid_t)id;
event = bpf_map_lookup_elem(&execs, &pid);
if (!event)
return 0;
ret = ctx->ret;
if (ignore_failed && ret < 0)
goto cleanup;
event->retval = ret;
bpf_get_current_comm(&event->comm, sizeof(event->comm));
size_t len = EVENT_SIZE(event);
if (len <= sizeof(*event))
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, len);
cleanup:
bpf_map_delete_elem(&execs, &pid);
return 0;
}
char LICENSE[] SEC("license") = "GPL";

View File

@@ -1,329 +0,0 @@
// Based on execsnoop(8) from BCC by Brendan Gregg and others.
//
#include <argp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "execsnoop.h"
#include "execsnoop.skel.h"
#include "trace_helpers.h"
#define PERF_BUFFER_PAGES 64
#define NSEC_PRECISION (NSEC_PER_SEC / 1000)
#define MAX_ARGS_KEY 259
static struct env {
bool time;
bool timestamp;
bool fails;
uid_t uid;
bool quote;
const char *name;
const char *line;
bool print_uid;
bool verbose;
int max_args;
} env = {
.max_args = DEFAULT_MAXARGS,
.uid = INVALID_UID
};
static struct timespec start_time;
const char *argp_program_version = "execsnoop 0.1";
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
const char argp_program_doc[] =
"Trace open family syscalls\n"
"\n"
"USAGE: execsnoop [-h] [-T] [-t] [-x] [-u UID] [-q] [-n NAME] [-l LINE] [-U]\n"
" [--max-args MAX_ARGS]\n"
"\n"
"EXAMPLES:\n"
" ./execsnoop # trace all exec() syscalls\n"
" ./execsnoop -x # include failed exec()s\n"
" ./execsnoop -T # include time (HH:MM:SS)\n"
" ./execsnoop -U # include UID\n"
" ./execsnoop -u 1000 # only trace UID 1000\n"
" ./execsnoop -t # include timestamps\n"
" ./execsnoop -q # add \"quotemarks\" around arguments\n"
" ./execsnoop -n main # only print command lines containing \"main\"\n"
" ./execsnoop -l tpkg # only print command where arguments contains \"tpkg\"";
static const struct argp_option opts[] = {
{ "time", 'T', NULL, 0, "include time column on output (HH:MM:SS)"},
{ "timestamp", 't', NULL, 0, "include timestamp on output"},
{ "fails", 'x', NULL, 0, "include failed exec()s"},
{ "uid", 'u', "UID", 0, "trace this UID only"},
{ "quote", 'q', NULL, 0, "Add quotemarks (\") around arguments"},
{ "name", 'n', "NAME", 0, "only print commands matching this name, any arg"},
{ "line", 'l', "LINE", 0, "only print commands where arg contains this line"},
{ "print-uid", 'U', NULL, 0, "print UID column"},
{ "max-args", MAX_ARGS_KEY, "MAX_ARGS", 0,
"maximum number of arguments parsed and displayed, defaults to 20"},
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{},
};
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
long int uid, max_args;
switch (key) {
case 'h':
argp_usage(state);
break;
case 'T':
env.time = true;
break;
case 't':
env.timestamp = true;
break;
case 'x':
env.fails = true;
break;
case 'u':
errno = 0;
uid = strtol(arg, NULL, 10);
if (errno || uid < 0 || uid >= INVALID_UID) {
fprintf(stderr, "Invalid UID %s\n", arg);
argp_usage(state);
}
env.uid = uid;
break;
case 'q':
env.quote = true;
break;
case 'n':
env.name = arg;
break;
case 'l':
env.line = arg;
break;
case 'U':
env.print_uid = true;
break;
case 'v':
env.verbose = true;
break;
case MAX_ARGS_KEY:
errno = 0;
max_args = strtol(arg, NULL, 10);
if (errno || max_args < 1 || max_args > TOTAL_MAX_ARGS) {
fprintf(stderr, "Invalid MAX_ARGS %s, should be in [1, %d] range\n",
arg, TOTAL_MAX_ARGS);
argp_usage(state);
}
env.max_args = max_args;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
if (level == LIBBPF_DEBUG && !env.verbose)
return 0;
return vfprintf(stderr, format, args);
}
static void time_since_start()
{
long nsec, sec;
static struct timespec cur_time;
double time_diff;
clock_gettime(CLOCK_MONOTONIC, &cur_time);
nsec = cur_time.tv_nsec - start_time.tv_nsec;
sec = cur_time.tv_sec - start_time.tv_sec;
if (nsec < 0) {
nsec += NSEC_PER_SEC;
sec--;
}
time_diff = sec + (double)nsec / NSEC_PER_SEC;
printf("%-8.3f", time_diff);
}
static void inline quoted_symbol(char c) {
switch(c) {
case '"':
putchar('\\');
putchar('"');
break;
case '\t':
putchar('\\');
putchar('t');
break;
case '\n':
putchar('\\');
putchar('n');
break;
default:
putchar(c);
break;
}
}
static void print_args(const struct event *e, bool quote)
{
int args_counter = 0;
if (env.quote)
putchar('"');
for (int i = 0; i < e->args_size && args_counter < e->args_count; i++) {
char c = e->args[i];
if (env.quote) {
if (c == '\0') {
args_counter++;
putchar('"');
putchar(' ');
if (args_counter < e->args_count) {
putchar('"');
}
} else {
quoted_symbol(c);
}
} else {
if (c == '\0') {
args_counter++;
putchar(' ');
} else {
putchar(c);
}
}
}
if (e->args_count == env.max_args + 1) {
fputs(" ...", stdout);
}
}
void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
{
const struct event *e = data;
time_t t;
struct tm *tm;
char ts[32];
/* TODO: use pcre lib */
if (env.name && strstr(e->comm, env.name) == NULL)
return;
/* TODO: use pcre lib */
if (env.line && strstr(e->comm, env.line) == NULL)
return;
time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
if (env.time) {
printf("%-8s ", ts);
}
if (env.timestamp) {
time_since_start();
}
if (env.print_uid)
printf("%-6d", e->uid);
printf("%-16s %-6d %-6d %3d ", e->comm, e->pid, e->ppid, e->retval);
print_args(e, env.quote);
putchar('\n');
}
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
{
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int main(int argc, char **argv)
{
static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
struct perf_buffer_opts pb_opts;
struct perf_buffer *pb = NULL;
struct execsnoop_bpf *obj;
int err;
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;
libbpf_set_print(libbpf_print_fn);
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
obj = execsnoop_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
/* initialize global data (filtering options) */
obj->rodata->ignore_failed = !env.fails;
obj->rodata->targ_uid = env.uid;
obj->rodata->max_args = env.max_args;
err = execsnoop_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object: %d\n", err);
goto cleanup;
}
clock_gettime(CLOCK_MONOTONIC, &start_time);
err = execsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}
/* print headers */
if (env.time) {
printf("%-9s", "TIME");
}
if (env.timestamp) {
printf("%-8s ", "TIME(s)");
}
if (env.print_uid) {
printf("%-6s ", "UID");
}
printf("%-16s %-6s %-6s %3s %s\n", "PCOMM", "PID", "PPID", "RET", "ARGS");
/* setup event callbacks */
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &pb_opts);
err = libbpf_get_error(pb);
if (err) {
pb = NULL;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
goto cleanup;
}
/* main: poll */
while ((err = perf_buffer__poll(pb, 100)) >= 0)
;
printf("Error polling perf buffer: %d\n", err);
cleanup:
perf_buffer__free(pb);
execsnoop_bpf__destroy(obj);
return err != 0;
}

View File

@@ -1,27 +0,0 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __EXECSNOOP_H
#define __EXECSNOOP_H
#define ARGSIZE 128
#define TASK_COMM_LEN 16
#define TOTAL_MAX_ARGS 60
#define DEFAULT_MAXARGS 20
#define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE)
#define INVALID_UID ((uid_t)-1)
#define BASE_EVENT_SIZE (size_t)(&((struct event*)0)->args)
#define EVENT_SIZE(e) (BASE_EVENT_SIZE + e->args_size)
#define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE)
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
int retval;
int args_count;
unsigned int args_size;
char args[FULL_MAX_ARGS_ARR];
};
#endif /* __EXECSNOOP_H */

View File

@@ -1,101 +0,0 @@
// Based on execsnoop(8) from BCC by Brendan Gregg and others.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern "C" {
#define typeof(x) decltype(x)
#include "execsnoop.h"
#include "execsnoop.skel.h"
#include "trace_helpers.h"
}
#include "execsnoop_share.h"
#define PERF_BUFFER_PAGES 64
#define NSEC_PRECISION (NSEC_PER_SEC / 1000)
#define MAX_ARGS_KEY 259
namespace CGPROXY::EXECSNOOP {
function<int(int)> callback = NULL;
promise<void> status;
void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) {
auto e = static_cast<event*>(data);
int pid = e->pid;
if (callback) callback(pid);
}
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) {
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int execsnoop() {
struct perf_buffer_opts pb_opts;
struct perf_buffer *pb = NULL;
struct execsnoop_bpf *obj;
int err;
// libbpf_set_print(libbpf_print_fn);
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
obj = execsnoop_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
/* initialize global data (filtering options) */
obj->rodata->ignore_failed = true;
err = execsnoop_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object: %d\n", err);
goto cleanup;
}
err = execsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}
/* setup event callbacks */
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &pb_opts);
err = libbpf_get_error(pb);
if (err) {
pb = NULL;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
goto cleanup;
}
status.set_value();
/* main: poll */
while ((err = perf_buffer__poll(pb, 100)) >= 0);
printf("Error polling perf buffer: %d\n", err);
cleanup:
perf_buffer__free(pb);
execsnoop_bpf__destroy(obj);
return err != 0;
}
void startThread(function<int(int)> c, promise<void> _status) {
status = move(_status);
callback = c;
execsnoop();
}
}

View File

@@ -1,21 +0,0 @@
diff --git src/Makefile src/Makefile
index d0308c3..fcc3b6f 100644
--- src/Makefile
+++ src/Makefile
@@ -15,6 +15,7 @@ ifneq ($(FEATURE_REALLOCARRAY),)
ALL_CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
endif
+STATIC_CFLAGS += -fPIC
SHARED_CFLAGS += -fPIC -fvisibility=hidden -DSHARED
CFLAGS ?= -g -O2 -Werror -Wall
@@ -99,7 +100,7 @@ $(SHARED_OBJDIR):
mkdir -p $(SHARED_OBJDIR)
$(STATIC_OBJDIR)/%.o: %.c | $(STATIC_OBJDIR)
- $(CC) $(ALL_CFLAGS) $(CPPFLAGS) -c $< -o $@
+ $(CC) $(ALL_CFLAGS) $(STATIC_CFLAGS) $(CPPFLAGS) -c $< -o $@
$(SHARED_OBJDIR)/%.o: %.c | $(SHARED_OBJDIR)
$(CC) $(ALL_CFLAGS) $(SHARED_CFLAGS) $(CPPFLAGS) -c $< -o $@

View File

@@ -1,11 +0,0 @@
## Depency
- libbpf
- /usr/lib/libbpf.a
- some head file
- bpf
- `/usr/bin/bpftool` to generate skeleton and dump btf
## Refer
- https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html

View File

@@ -1,526 +0,0 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Anton Protopopov
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
static const char **syscall_names;
static size_t syscall_names_size;
#define warn(...) fprintf(stderr, __VA_ARGS__)
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
static const char *parse_syscall(const char *buf, int *number)
{
char *end;
long x;
errno = 0;
x = strtol(buf, &end, 10);
if (errno) {
warn("strtol(%s): %s\n", buf, strerror(errno));
return NULL;
} else if (end == buf) {
warn("strtol(%s): no digits found\n", buf);
return NULL;
} else if (x < 0 || x > INT_MAX) {
warn("strtol(%s): bad syscall number: %ld\n", buf, x);
return NULL;
}
if (*end != '\t') {
warn("bad input: %s (expected <num>\t<name>)\n", buf);
return NULL;
}
*number = x;
return ++end;
}
void init_syscall_names(void)
{
size_t old_size, size = 1024;
const char *name;
char buf[64];
int number;
int err;
FILE *f;
f = popen("ausyscall --dump 2>/dev/null", "r");
if (!f) {
warn("popen: ausyscall --dump: %s\n", strerror(errno));
return;
}
syscall_names = calloc(size, sizeof(char *));
if (!syscall_names) {
warn("calloc: %s\n", strerror(errno));
goto close;
}
/* skip the header */
fgets(buf, sizeof(buf), f);
while (fgets(buf, sizeof(buf), f)) {
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
name = parse_syscall(buf, &number);
if (!name || !name[0])
goto close;
/* In a rare case when syscall number is > than initial 1024 */
if (number >= size) {
old_size = size;
size = 1024 * (1 + number / 1024);
syscall_names = realloc(syscall_names,
size * sizeof(char *));
if (!syscall_names) {
warn("realloc: %s\n", strerror(errno));
goto close;
}
memset(syscall_names+old_size, 0,
(size - old_size) * sizeof(char *));
}
if (syscall_names[number]) {
warn("duplicate number: %d (stored: %s)",
number, syscall_names[number]);
goto close;
}
syscall_names[number] = strdup(name);
if (!syscall_names[number]) {
warn("strdup: %s\n", strerror(errno));
goto close;
}
syscall_names_size = MAX(number+1, syscall_names_size);
}
if (ferror(f))
warn("fgets: %s\n", strerror(errno));
close:
err = pclose(f);
if (err < 0)
warn("pclose: %s\n", strerror(errno));
#ifndef __x86_64__
/* Ignore the error for x86_64 where we have a table compiled in */
else if (err && WEXITSTATUS(err) == 127) {
warn("ausyscall required for syscalls number/name mapping\n");
} else if (err) {
warn("ausyscall exit status (see wait(2)): 0x%x\n", err);
}
#endif
}
void free_syscall_names(void)
{
for (size_t i = 0; i < syscall_names_size; i++)
free((void *) syscall_names[i]);
free(syscall_names);
}
/*
* Syscall table for Linux x86_64.
*
* Semi-automatically generated from strace/linux/x86_64/syscallent.h and
* linux/syscallent-common.h using the following commands:
*
* awk -F\" '/SEN/{printf("%d %s\n", substr($0,2,3), $(NF-1));}' syscallent.h
* awk '/SEN/ { printf("%d %s\n", $3, $9); }' syscallent-common.h
*
* (The idea is taken from src/python/bcc/syscall.py.)
*/
#ifdef __x86_64__
static const char *syscall_names_x86_64[] = {
[0] = "read",
[1] = "write",
[2] = "open",
[3] = "close",
[4] = "stat",
[5] = "fstat",
[6] = "lstat",
[7] = "poll",
[8] = "lseek",
[9] = "mmap",
[10] = "mprotect",
[11] = "munmap",
[12] = "brk",
[13] = "rt_sigaction",
[14] = "rt_sigprocmask",
[15] = "rt_sigreturn",
[16] = "ioctl",
[17] = "pread64",
[18] = "pwrite64",
[19] = "readv",
[20] = "writev",
[21] = "access",
[22] = "pipe",
[23] = "select",
[24] = "sched_yield",
[25] = "mremap",
[26] = "msync",
[27] = "mincore",
[28] = "madvise",
[29] = "shmget",
[30] = "shmat",
[31] = "shmctl",
[32] = "dup",
[33] = "dup2",
[34] = "pause",
[35] = "nanosleep",
[36] = "getitimer",
[37] = "alarm",
[38] = "setitimer",
[39] = "getpid",
[40] = "sendfile",
[41] = "socket",
[42] = "connect",
[43] = "accept",
[44] = "sendto",
[45] = "recvfrom",
[46] = "sendmsg",
[47] = "recvmsg",
[48] = "shutdown",
[49] = "bind",
[50] = "listen",
[51] = "getsockname",
[52] = "getpeername",
[53] = "socketpair",
[54] = "setsockopt",
[55] = "getsockopt",
[56] = "clone",
[57] = "fork",
[58] = "vfork",
[59] = "execve",
[60] = "exit",
[61] = "wait4",
[62] = "kill",
[63] = "uname",
[64] = "semget",
[65] = "semop",
[66] = "semctl",
[67] = "shmdt",
[68] = "msgget",
[69] = "msgsnd",
[70] = "msgrcv",
[71] = "msgctl",
[72] = "fcntl",
[73] = "flock",
[74] = "fsync",
[75] = "fdatasync",
[76] = "truncate",
[77] = "ftruncate",
[78] = "getdents",
[79] = "getcwd",
[80] = "chdir",
[81] = "fchdir",
[82] = "rename",
[83] = "mkdir",
[84] = "rmdir",
[85] = "creat",
[86] = "link",
[87] = "unlink",
[88] = "symlink",
[89] = "readlink",
[90] = "chmod",
[91] = "fchmod",
[92] = "chown",
[93] = "fchown",
[94] = "lchown",
[95] = "umask",
[96] = "gettimeofday",
[97] = "getrlimit",
[98] = "getrusage",
[99] = "sysinfo",
[100] = "times",
[101] = "ptrace",
[102] = "getuid",
[103] = "syslog",
[104] = "getgid",
[105] = "setuid",
[106] = "setgid",
[107] = "geteuid",
[108] = "getegid",
[109] = "setpgid",
[110] = "getppid",
[111] = "getpgrp",
[112] = "setsid",
[113] = "setreuid",
[114] = "setregid",
[115] = "getgroups",
[116] = "setgroups",
[117] = "setresuid",
[118] = "getresuid",
[119] = "setresgid",
[120] = "getresgid",
[121] = "getpgid",
[122] = "setfsuid",
[123] = "setfsgid",
[124] = "getsid",
[125] = "capget",
[126] = "capset",
[127] = "rt_sigpending",
[128] = "rt_sigtimedwait",
[129] = "rt_sigqueueinfo",
[130] = "rt_sigsuspend",
[131] = "sigaltstack",
[132] = "utime",
[133] = "mknod",
[134] = "uselib",
[135] = "personality",
[136] = "ustat",
[137] = "statfs",
[138] = "fstatfs",
[139] = "sysfs",
[140] = "getpriority",
[141] = "setpriority",
[142] = "sched_setparam",
[143] = "sched_getparam",
[144] = "sched_setscheduler",
[145] = "sched_getscheduler",
[146] = "sched_get_priority_max",
[147] = "sched_get_priority_min",
[148] = "sched_rr_get_interval",
[149] = "mlock",
[150] = "munlock",
[151] = "mlockall",
[152] = "munlockall",
[153] = "vhangup",
[154] = "modify_ldt",
[155] = "pivot_root",
[156] = "_sysctl",
[157] = "prctl",
[158] = "arch_prctl",
[159] = "adjtimex",
[160] = "setrlimit",
[161] = "chroot",
[162] = "sync",
[163] = "acct",
[164] = "settimeofday",
[165] = "mount",
[166] = "umount2",
[167] = "swapon",
[168] = "swapoff",
[169] = "reboot",
[170] = "sethostname",
[171] = "setdomainname",
[172] = "iopl",
[173] = "ioperm",
[174] = "create_module",
[175] = "init_module",
[176] = "delete_module",
[177] = "get_kernel_syms",
[178] = "query_module",
[179] = "quotactl",
[180] = "nfsservctl",
[181] = "getpmsg",
[182] = "putpmsg",
[183] = "afs_syscall",
[184] = "tuxcall",
[185] = "security",
[186] = "gettid",
[187] = "readahead",
[188] = "setxattr",
[189] = "lsetxattr",
[190] = "fsetxattr",
[191] = "getxattr",
[192] = "lgetxattr",
[193] = "fgetxattr",
[194] = "listxattr",
[195] = "llistxattr",
[196] = "flistxattr",
[197] = "removexattr",
[198] = "lremovexattr",
[199] = "fremovexattr",
[200] = "tkill",
[201] = "time",
[202] = "futex",
[203] = "sched_setaffinity",
[204] = "sched_getaffinity",
[205] = "set_thread_area",
[206] = "io_setup",
[207] = "io_destroy",
[208] = "io_getevents",
[209] = "io_submit",
[210] = "io_cancel",
[211] = "get_thread_area",
[212] = "lookup_dcookie",
[213] = "epoll_create",
[214] = "epoll_ctl_old",
[215] = "epoll_wait_old",
[216] = "remap_file_pages",
[217] = "getdents64",
[218] = "set_tid_address",
[219] = "restart_syscall",
[220] = "semtimedop",
[221] = "fadvise64",
[222] = "timer_create",
[223] = "timer_settime",
[224] = "timer_gettime",
[225] = "timer_getoverrun",
[226] = "timer_delete",
[227] = "clock_settime",
[228] = "clock_gettime",
[229] = "clock_getres",
[230] = "clock_nanosleep",
[231] = "exit_group",
[232] = "epoll_wait",
[233] = "epoll_ctl",
[234] = "tgkill",
[235] = "utimes",
[236] = "vserver",
[237] = "mbind",
[238] = "set_mempolicy",
[239] = "get_mempolicy",
[240] = "mq_open",
[241] = "mq_unlink",
[242] = "mq_timedsend",
[243] = "mq_timedreceive",
[244] = "mq_notify",
[245] = "mq_getsetattr",
[246] = "kexec_load",
[247] = "waitid",
[248] = "add_key",
[249] = "request_key",
[250] = "keyctl",
[251] = "ioprio_set",
[252] = "ioprio_get",
[253] = "inotify_init",
[254] = "inotify_add_watch",
[255] = "inotify_rm_watch",
[256] = "migrate_pages",
[257] = "openat",
[258] = "mkdirat",
[259] = "mknodat",
[260] = "fchownat",
[261] = "futimesat",
[262] = "newfstatat",
[263] = "unlinkat",
[264] = "renameat",
[265] = "linkat",
[266] = "symlinkat",
[267] = "readlinkat",
[268] = "fchmodat",
[269] = "faccessat",
[270] = "pselect6",
[271] = "ppoll",
[272] = "unshare",
[273] = "set_robust_list",
[274] = "get_robust_list",
[275] = "splice",
[276] = "tee",
[277] = "sync_file_range",
[278] = "vmsplice",
[279] = "move_pages",
[280] = "utimensat",
[281] = "epoll_pwait",
[282] = "signalfd",
[283] = "timerfd_create",
[284] = "eventfd",
[285] = "fallocate",
[286] = "timerfd_settime",
[287] = "timerfd_gettime",
[288] = "accept4",
[289] = "signalfd4",
[290] = "eventfd2",
[291] = "epoll_create1",
[292] = "dup3",
[293] = "pipe2",
[294] = "inotify_init1",
[295] = "preadv",
[296] = "pwritev",
[297] = "rt_tgsigqueueinfo",
[298] = "perf_event_open",
[299] = "recvmmsg",
[300] = "fanotify_init",
[301] = "fanotify_mark",
[302] = "prlimit64",
[303] = "name_to_handle_at",
[304] = "open_by_handle_at",
[305] = "clock_adjtime",
[306] = "syncfs",
[307] = "sendmmsg",
[308] = "setns",
[309] = "getcpu",
[310] = "process_vm_readv",
[311] = "process_vm_writev",
[312] = "kcmp",
[313] = "finit_module",
[314] = "sched_setattr",
[315] = "sched_getattr",
[316] = "renameat2",
[317] = "seccomp",
[318] = "getrandom",
[319] = "memfd_create",
[320] = "kexec_file_load",
[321] = "bpf",
[322] = "execveat",
[323] = "userfaultfd",
[324] = "membarrier",
[325] = "mlock2",
[326] = "copy_file_range",
[327] = "preadv2",
[328] = "pwritev2",
[329] = "pkey_mprotect",
[330] = "pkey_alloc",
[331] = "pkey_free",
[332] = "statx",
[333] = "io_pgetevents",
[334] = "rseq",
[424] = "pidfd_send_signal",
[425] = "io_uring_setup",
[426] = "io_uring_enter",
[427] = "io_uring_register",
[428] = "open_tree",
[429] = "move_mount",
[430] = "fsopen",
[431] = "fsconfig",
[432] = "fsmount",
[433] = "fspick",
[434] = "pidfd_open",
[435] = "clone3",
[437] = "openat2",
[438] = "pidfd_getfd",
};
size_t syscall_names_x86_64_size = sizeof(syscall_names_x86_64)/sizeof(char*);
#endif
void syscall_name(unsigned n, char *buf, size_t size)
{
const char *name = NULL;
if (n < syscall_names_size)
name = syscall_names[n];
#ifdef __x86_64__
else if (n < syscall_names_x86_64_size)
name = syscall_names_x86_64[n];
#endif
if (name)
strncpy(buf, name, size-1);
else
snprintf(buf, size, "[unknown: %u]", n);
}
int list_syscalls(void)
{
const char **list = syscall_names;
size_t size = syscall_names_size;
#ifdef __x86_64__
if (!size) {
size = syscall_names_x86_64_size;
list = syscall_names_x86_64;
}
#endif
for (size_t i = 0; i < size; i++) {
if (list[i])
printf("%3zd: %s\n", i, list[i]);
}
return (!list || !size);
}

View File

@@ -1,12 +0,0 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __SYSCALL_HELPERS_H
#define __SYSCALL_HELPERS_H
#include <stddef.h>
void init_syscall_names(void);
void free_syscall_names(void);
void list_syscalls(void);
void syscall_name(unsigned n, char *buf, size_t size);
#endif /* __SYSCALL_HELPERS_H */

View File

@@ -1,234 +0,0 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/resource.h>
#include <time.h>
#include "trace_helpers.h"
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
struct ksyms {
struct ksym *syms;
int syms_sz;
int syms_cap;
char *strs;
int strs_sz;
int strs_cap;
};
static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned long addr)
{
size_t new_cap, name_len = strlen(name) + 1;
struct ksym *ksym;
void *tmp;
if (ksyms->strs_sz + name_len > ksyms->strs_cap) {
new_cap = ksyms->strs_cap * 4 / 3;
if (new_cap < ksyms->strs_sz + name_len)
new_cap = ksyms->strs_sz + name_len;
if (new_cap < 1024)
new_cap = 1024;
tmp = realloc(ksyms->strs, new_cap);
if (!tmp)
return -1;
ksyms->strs = tmp;
ksyms->strs_cap = new_cap;
}
if (ksyms->syms_sz + 1 > ksyms->syms_cap) {
new_cap = ksyms->syms_cap * 4 / 3;
if (new_cap < 1024)
new_cap = 1024;
tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap);
if (!tmp)
return -1;
ksyms->syms = tmp;
ksyms->syms_cap = new_cap;
}
ksym = &ksyms->syms[ksyms->syms_sz];
/* while constructing, re-use pointer as just a plain offset */
ksym->name = (void *)(unsigned long)ksyms->strs_sz;
ksym->addr = addr;
memcpy(ksyms->strs + ksyms->strs_sz, name, name_len);
ksyms->strs_sz += name_len;
ksyms->syms_sz++;
return 0;
}
static int ksym_cmp(const void *p1, const void *p2)
{
const struct ksym *s1 = p1, *s2 = p2;
if (s1->addr == s2->addr)
return strcmp(s1->name, s2->name);
return s1->addr < s2->addr ? -1 : 1;
}
struct ksyms *ksyms__load(void)
{
char sym_type, sym_name[256];
struct ksyms *ksyms;
unsigned long sym_addr;
int i, ret;
FILE *f;
f = fopen("/proc/kallsyms", "r");
if (!f)
return NULL;
ksyms = calloc(1, sizeof(*ksyms));
if (!ksyms)
goto err_out;
while (true) {
ret = fscanf(f, "%lx %c %s%*[^\n]\n",
&sym_addr, &sym_type, sym_name);
if (ret == EOF && feof(f))
break;
if (ret != 3)
goto err_out;
if (ksyms__add_symbol(ksyms, sym_name, sym_addr))
goto err_out;
}
/* now when strings are finalized, adjust pointers properly */
for (i = 0; i < ksyms->syms_sz; i++)
ksyms->syms[i].name += (unsigned long)ksyms->strs;
qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp);
fclose(f);
return ksyms;
err_out:
ksyms__free(ksyms);
fclose(f);
return NULL;
}
void ksyms__free(struct ksyms *ksyms)
{
if (!ksyms)
return;
free(ksyms->syms);
free(ksyms->strs);
free(ksyms);
}
const struct ksym *ksyms__map_addr(const struct ksyms *ksyms,
unsigned long addr)
{
int start = 0, end = ksyms->syms_sz - 1, mid;
unsigned long sym_addr;
/* find largest sym_addr <= addr using binary search */
while (start < end) {
mid = start + (end - start + 1) / 2;
sym_addr = ksyms->syms[mid].addr;
if (sym_addr <= addr)
start = mid;
else
end = mid - 1;
}
if (start == end && ksyms->syms[start].addr <= addr)
return &ksyms->syms[start];
return NULL;
}
const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms,
const char *name)
{
int i;
for (i = 0; i < ksyms->syms_sz; i++) {
if (strcmp(ksyms->syms[i].name, name) == 0)
return &ksyms->syms[i];
}
return NULL;
}
static void print_stars(unsigned int val, unsigned int val_max, int width)
{
int num_stars, num_spaces, i;
bool need_plus;
num_stars = min(val, val_max) * width / val_max;
num_spaces = width - num_stars;
need_plus = val > val_max;
for (i = 0; i < num_stars; i++)
printf("*");
for (i = 0; i < num_spaces; i++)
printf(" ");
if (need_plus)
printf("+");
}
void print_log2_hist(unsigned int *vals, int vals_size, char *val_type)
{
int stars_max = 40, idx_max = -1;
unsigned int val, val_max = 0;
unsigned long long low, high;
int stars, width, i;
for (i = 0; i < vals_size; i++) {
val = vals[i];
if (val > 0)
idx_max = i;
if (val > val_max)
val_max = val;
}
if (idx_max < 0)
return;
printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "",
idx_max <= 32 ? 19 : 29, val_type);
if (idx_max <= 32)
stars = stars_max;
else
stars = stars_max / 2;
for (i = 0; i <= idx_max; i++) {
low = (1ULL << (i + 1)) >> 1;
high = (1ULL << (i + 1)) - 1;
if (low == high)
low -= 1;
val = vals[i];
width = idx_max <= 32 ? 10 : 20;
printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val);
print_stars(val, val_max, stars);
printf("|\n");
}
}
unsigned long long get_ktime_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
int bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}

View File

@@ -1,26 +0,0 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __TRACE_HELPERS_H
#define __TRACE_HELPERS_H
#define NSEC_PER_SEC 1000000000ULL
struct ksym {
const char *name;
unsigned long addr;
};
struct ksyms;
struct ksyms *ksyms__load(void);
void ksyms__free(struct ksyms *ksyms);
const struct ksym *ksyms__map_addr(const struct ksyms *ksyms,
unsigned long addr);
const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms,
const char *name);
void print_log2_hist(unsigned int *vals, int vals_size, char *val_type);
unsigned long long get_ktime_ns(void);
int bump_memlock_rlimit(void);
#endif /* __TRACE_HELPERS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,9 @@
cgnoproxy \- Run program without proxy
.SH SYNOPSIS
cgnoproxy --help
.br
cgnoproxy [--debug] <CMD>
.br
cgnoproxy [--debug] --pid <PID>
.SH ALIAS
cgnoproxy = cgproxy --noproxy

View File

@@ -4,7 +4,9 @@
cgproxy \- Run program with proxy
.SH SYNOPSIS
cgproxy --help
.br
cgproxy [--debug] <CMD>
.br
cgproxy [--debug] --pid <PID>
.SH DESCRIPTION
cgproxy send current running process pid or specified pid to cgproxyd through unix socket, then pid is attached to proxied cgroup

View File

@@ -15,7 +15,7 @@ enable execsnoop to support program level proxy, need bcc installed to actually
.B port
tproxy listenning port
.br
program level proxy controll, need `bcc` installed to work:
program level proxy controll, only work when execsnoop enabled:
.br
.RS
.B program_proxy

View File

@@ -4,8 +4,12 @@ set(CPACK_PACKAGE_NAME "cgproxy")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything running in specific cgroup.It aslo supports global transparent proxy and gateway proxy")
## deb pack
execute_process(COMMAND dpkg --print-architecture
OUTPUT_VARIABLE DEBIAN_ARCH
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CPACK_DEBIAN_FILE_NAME ${CPACK_PACKAGE_NAME}_${CMAKE_PROJECT_VERSION}_${DEBIAN_ARCH}.deb)
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "x86_64")
# set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
@@ -14,7 +18,11 @@ set(CPACK_DEBIAN_PACKAGE_MAINTAINER "springzfx@gmail.com")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/postinst;${CMAKE_CURRENT_SOURCE_DIR}/prerm")
## rpm pack
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
execute_process(COMMAND uname -m
OUTPUT_VARIABLE RPM_ARCH
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CPACK_RPM_FILE_NAME ${CPACK_PACKAGE_NAME}_${CMAKE_PROJECT_VERSION}_${RPM_ARCH}.rpm)
# set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
set(CPACK_RPM_PACKAGE_GROUP "network")
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")

View File

@@ -1,6 +1,6 @@
# Transparent Proxy powered with cgroup v2
# Transparent Proxy powered by cgroup v2
@@ -16,11 +16,11 @@ Main feature:
## Contents
<!--ts-->
* [Transparent Proxy with cgroup v2](#transparent-proxy-with-cgroup-v2)
* [Transparent Proxy powered by cgroup v2](#transparent-proxy-powered-by-cgroup-v2)
* [Introduction](#introduction)
* [Contents](#contents)
* [Prerequest](#prerequest)
* [How to install](#how-to-install)
* [How to build and install](#how-to-build-and-install)
* [Default usage](#default-usage)
* [Configuration](#configuration)
* [Global transparent proxy](#global-transparent-proxy)
@@ -29,8 +29,9 @@ Main feature:
* [NOTES](#notes)
* [TIPS](#tips)
* [Licences](#licences)
* [Known Issues](#known-issues)
<!-- Added by: fancy, at: Sat 16 May 2020 03:12:07 PM HKT -->
<!-- Added by: fancy, at: Sat 04 Jul 2020 03:52:07 PM CST -->
<!--te-->
@@ -52,17 +53,35 @@ Main feature:
ubuntu 16.04, debian 9, fedora 27 and later are desired
## How to install
## How to build and install
### distro install
- For debian and redhat series, download from [Release page](https://github.com/springzfx/cgproxy/releases)
- For archlinux series, already in archlinuxcn repo, or see [archlinux AUR](https://aur.archlinux.org/packages/?K=cgproxy)
- **Tested on archlinux, fedora 32, ubuntu 18.04, ubuntu 20.04, deepin 15.11, deepin v20 beta**
### build
- before build, install depencies: clang(if to build bpf obj from scratch), nlohmann-json, libbpf
- then cmake standard build
```bash
cd execsnoop-libbpf && make libexecsnoop.so
mkdir build && cd build && cmake .. && make install
# ready build dir
mkdir build
cd build
# generate
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-Dbuild_execsnoop_dl=ON \
-Dbuild_static=OFF \
..
# compile
make
```
- It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/?K=cgproxy).
- DEB and RPM are packaged in [release page](https://github.com/springzfx/cgproxy/releases).
## Default usage
- First enable and start service
@@ -96,21 +115,24 @@ Config file: **/etc/cgproxy/config.json**
{
"port": 12345,
"program_noproxy": ["v2ray", "qv2ray"],
"program_proxy": [ ],
"program_proxy": [],
"cgroup_noproxy": ["/system.slice/v2ray.service"],
"cgroup_proxy": [ ],
"cgroup_proxy": [],
"enable_gateway": false,
"enable_dns": true,
"enable_udp": true,
"enable_tcp": true,
"enable_ipv4": true,
"enable_ipv6": true
"enable_ipv6": true,
"table": 10007,
"fwmark": 39283
}
```
- **port** tproxy listenning port
- program level proxy control:
- program level proxy control, need execsnoop enabled:
- **program_proxy** program need to be proxied
- **program_noproxy** program that won't be proxied
@@ -132,11 +154,14 @@ Config file: **/etc/cgproxy/config.json**
- **enable_ipv6**
- **table**, **fwmark** you can specify iptables and route table related parameter in case conflict.
- options priority
```
program_noproxy > program_proxy > cgroup_noproxy > cgroup_proxy
enable_ipv6 > enable_ipv4 > enable_tcp > enable_udp > enable_dns
enable_ipv6 = enable_ipv4 > enable_dns > enable_tcp = enable_udp
command cgproxy and cgnoproxy always have highest priority
```
**Note**: cgroup in configuration need to be exist, otherwise ignored
@@ -186,7 +211,7 @@ sudo systemctl restart cgproxy.service
## NOTES
- v2ray TPROXY need root or special permission, use [service](https://github.com/springzfx/cgproxy/blob/v3.x/v2ray_config/v2ray.service) or
- v2ray TPROXY need root or special permission, use [service](/v2ray_config/v2ray.service) or
```bash
sudo setcap "cap_net_admin,cap_net_bind_service=ep" /usr/lib/v2ray/v2ray
@@ -208,6 +233,16 @@ sudo systemctl restart cgproxy.service
cgproxy is licenced under [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://www.gnu.org/licenses/gpl-2.0)
## Known Issus
## Known Issues
- docker breaks cgroup path match, add kernel parameter `cgroup_no_v1=net_cls,net_prio` to resolve, see [issue #3](https://github.com/springzfx/cgproxy/issues/3) for detail
- docker breaks cgroup v2 path match, add kernel parameter `cgroup_no_v1=net_cls,net_prio` to resolve, see [issue #3](https://github.com/springzfx/cgproxy/issues/3) for detail
- docker load `br_netfilter` module due to [hairpin nat](https://wiki.mikrotik.com/wiki/Hairpin_NAT), which is not a big deal, see [commit](https://github.com/moby/moby/pull/13162).
It enables data link layer packet to go through iptables and only once. However TPROXY do not accept this kind of packets. So to get it working, set following parameter to disable this behavior or unload br_netfilter module manualy. see [issue #10](https://github.com/springzfx/cgproxy/issues/10) for detail.
```
sudo sysctl -w net.bridge.bridge-nf-call-iptables=0
sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=0
sudo sysctl -w net.bridge.bridge-nf-call-arptables = 0
```

View File

@@ -1,15 +1,26 @@
find_package(Threads REQUIRED)
find_package(nlohmann_json REQUIRED)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/execsnoop-libbpf/)
include_directories(${PROJECT_SOURCE_DIR}/execsnoop-kernel/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(main main.cpp
common.cpp config.cpp cgroup_attach.cpp
socket_client.cpp socket_server.cpp)
target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json Threads::Threads ${CMAKE_DL_LIBS})
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
configure_file(common.h.cmake common.h)
if (build_execsnoop_dl)
add_definitions(-DBUIlD_EXECSNOOP_DL)
set(DL_LIB "-ldl")
set(EXECSNOOP_LIB "")
else()
set(EXECSNOOP_LIB "execsnoop")
endif()
add_executable(main main.cpp common.cpp config.cpp cgroup_attach.cpp socket_client.cpp socket_server.cpp)
target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json ${DL_LIB} ${EXECSNOOP_LIB})
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
install(TARGETS main RUNTIME)
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
install(FILES ../execsnoop-libbpf/libexecsnoop.so DESTINATION /usr/lib/cgproxy/ PERMISSIONS ${basic_permission})
if (build_static)
target_link_libraries(main PRIVATE -static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive)
else()
target_link_libraries(main PRIVATE -lpthread)
endif()

View File

@@ -79,6 +79,7 @@ int main(int argc, char *argv[]) {
if (attach_pid) return 0;
string s = join2str(argc - shift, argv + shift, ' ');
debug("executing: %s", s.c_str());
return system(s.c_str());
}
} // namespace CGPROXY::CGPROXY

View File

@@ -26,6 +26,7 @@ using namespace ::CGPROXY::CONFIG;
using namespace ::CGPROXY::CGROUP;
// using namespace ::CGPROXY::EXECSNOOP;
#ifdef BUIlD_EXECSNOOP_DL
namespace CGPROXY::EXECSNOOP {
bool loadExecsnoopLib() {
try {
@@ -48,6 +49,7 @@ bool loadExecsnoopLib() {
}
}
} // namespace CGPROXY::EXECSNOOP
#endif
namespace CGPROXY::CGPROXYD {
@@ -232,14 +234,21 @@ class cgproxyd {
}
void startExecsnoopThread() {
#ifdef BUIlD_EXECSNOOP_DL
if (!EXECSNOOP::loadExecsnoopLib() || EXECSNOOP::_startThread == NULL) {
error("execsnoop not ready to start, maybe bcc not installed");
error("execsnoop not ready to start, maybe missing libbpf");
return;
}
#endif
promise<void> status;
future<void> status_f = status.get_future();
#ifdef BUIlD_EXECSNOOP_DL
thread th(EXECSNOOP::_startThread, handle_pid_static, move(status));
#else
thread th(EXECSNOOP::startThread, handle_pid_static, move(status));
#endif
execsnoop_thread = move(th);
future_status fstatus = status_f.wait_for(chrono::seconds(THREAD_TIMEOUT));
@@ -262,7 +271,7 @@ class cgproxyd {
}
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
debug("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
info("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
continue;
}
if (!belongToCgroup(cg, config.cgroup_noproxy)) {
@@ -279,7 +288,7 @@ class cgproxyd {
}
if (belongToCgroup(cg, config.cgroup_proxy_preserved) ||
belongToCgroup(cg, config.cgroup_noproxy_preserved)) {
debug("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
info("already in preserverd cgroup, leave alone: %d %s", pid, path.c_str());
continue;
}
if (!belongToCgroup(cg, config.cgroup_proxy)) {
@@ -300,7 +309,10 @@ public:
assignStaticInstance();
config.loadFromFile(DEFAULT_CONFIG_FILE);
if (config.loadFromFile(DEFAULT_CONFIG_FILE)!=SUCCESS) {
error("load config file failed");
return -1;
}
applyConfig();
if (enable_socketserver) startSocketListeningThread();
@@ -311,6 +323,7 @@ public:
return 0;
}
int applyConfig() {
system(TPROXY_IPTABLS_CLEAN);
config.print_summary();

View File

@@ -14,19 +14,8 @@
namespace CGPROXY::CGROUP {
string cgroup2_mount_point = get_cgroup2_mount_point();
string cgroup2_mount_point = CGROUP2_MOUNT_POINT;
string get_cgroup2_mount_point() {
stringstream buffer;
unique_ptr<FILE, decltype(&pclose)> fp(popen("findmnt -t cgroup2 -n -o TARGET", "r"),
&pclose);
if (!fp) return "";
char buf[READ_SIZE_MAX];
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
string s = buffer.str();
s.pop_back(); // remove newline character
return s;
}
bool validate(string pid, string cgroup) {
bool pid_v = validPid(pid);
@@ -63,7 +52,11 @@ int attach(const string pid, const string cgroup_target) {
// return_error
}
if (getCgroup(pid) == cgroup_target) {
string cg;
cg = getCgroup(pid);
if (cg.empty()) return_success;
if (cg == cgroup_target) {
debug("%s already in %s", pid.c_str(), cgroup_target.c_str());
return_success;
}
@@ -73,7 +66,9 @@ int attach(const string pid, const string cgroup_target) {
// wait for small period and check again
this_thread::sleep_for(std::chrono::milliseconds(100));
if (getCgroup(pid) != cgroup_target && write2procs(pid, cgroup_target_procs) != 0)
cg = getCgroup(pid);
if (cg.empty()) return_success;
if (cg != cgroup_target && write2procs(pid, cgroup_target_procs) != 0)
return_error;
return_success;
}
@@ -89,7 +84,7 @@ int write2procs(string pid, string procspath) {
// maybe there some write error, for example process pid may not exist
if (!procs) {
error("write %s to %s failed, maybe process %s not exist", pid.c_str(),
error("write %s to %s failed, maybe process %s live too short", pid.c_str(),
procspath.c_str(), pid.c_str());
return_error;
}

View File

@@ -8,7 +8,6 @@ using namespace std;
namespace CGPROXY::CGROUP {
extern string cgroup2_mount_point;
bool validate(string pid, string cgroup);
string get_cgroup2_mount_point();
int attach(const string pid, const string cgroup_target);
int attach(const int pid, const string cgroup_target);
int write2procs(string pid, string procspath);

View File

@@ -67,7 +67,7 @@ string bash_which(const string &name) {
char buf[READ_SIZE_MAX];
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
string s = buffer.str();
s.pop_back(); // remove newline character
if (!s.empty()) s.pop_back(); // remove newline character
return s;
}
@@ -79,7 +79,7 @@ string bash_readlink(const string &path) {
char buf[READ_SIZE_MAX];
while (fgets(buf, READ_SIZE_MAX, fp.get()) != NULL) { buffer << buf; }
string s = buffer.str();
s.pop_back(); // remove newline character
if (!s.empty()) s.pop_back(); // remove newline character
return s;
}

View File

@@ -7,14 +7,15 @@
#include <vector>
using namespace std;
#define TPROXY_IPTABLS_START "/usr/share/cgproxy/scripts/cgroup-tproxy.sh"
#define TPROXY_IPTABLS_CLEAN "/usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
#define TPROXY_IPTABLS_START "@CMAKE_INSTALL_FULL_DATADIR@/cgproxy/scripts/cgroup-tproxy.sh"
#define TPROXY_IPTABLS_CLEAN "@CMAKE_INSTALL_FULL_DATADIR@/cgproxy/scripts/cgroup-tproxy.sh stop"
#define LIBEXECSNOOP_SO "/usr/lib/cgproxy/libexecsnoop.so"
#define LIBEXECSNOOP_SO "@CMAKE_INSTALL_FULL_LIBDIR@/cgproxy/libexecsnoop.so"
#define CGROUP2_MOUNT_POINT "/var/run/cgproxy/cgroup2"
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
#define LISTEN_BACKLOG 64
#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json"
#define DEFAULT_CONFIG_FILE "@CMAKE_INSTALL_FULL_SYSCONFDIR@/cgproxy/config.json"
#define READ_SIZE_MAX 128
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
@@ -49,6 +50,14 @@ extern bool enable_info;
fflush(stderr); \
}
#define warning(...) \
{ \
fprintf(stderr, "warning: "); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
fflush(stderr); \
}
#define debug(...) \
if (enable_debug) { \
fprintf(stdout, "debug: "); \

View File

@@ -21,6 +21,7 @@ using json = nlohmann::json;
namespace CGPROXY::CONFIG {
void Config::toEnv() {
setenv("cgroup_mount_point", CGROUP2_MOUNT_POINT, 1);
setenv("program_proxy", join2str(program_proxy, ':').c_str(), 1);
setenv("program_noproxy", join2str(program_noproxy, ':').c_str(), 1);
setenv("cgroup_proxy", join2str(cgroup_proxy, ':').c_str(), 1);
@@ -32,6 +33,9 @@ void Config::toEnv() {
setenv("enable_udp", to_str(enable_udp).c_str(), 1);
setenv("enable_ipv4", to_str(enable_ipv4).c_str(), 1);
setenv("enable_ipv6", to_str(enable_ipv6).c_str(), 1);
setenv("table", to_str(table).c_str(), 1);
setenv("fwmark", to_str(fwmark).c_str(), 1);
setenv("mark_newin", to_str(mark_newin).c_str(), 1);
}
int Config::saveToFile(const string f) {
@@ -56,6 +60,9 @@ string Config::toJsonStr() {
add2json(enable_udp);
add2json(enable_ipv4);
add2json(enable_ipv6);
add2json(table);
add2json(fwmark);
add2json(mark_newin);
return j.dump();
}
@@ -89,6 +96,9 @@ int Config::loadFromJsonStr(const string js) {
tryassign(enable_udp);
tryassign(enable_ipv4);
tryassign(enable_ipv6);
tryassign(table);
tryassign(fwmark);
tryassign(mark_newin);
// e.g. v2ray -> /usr/bin/v2ray -> /usr/lib/v2ray/v2ray
toRealProgramPath(program_noproxy);
@@ -109,7 +119,7 @@ bool Config::validateJsonStr(const string js) {
bool status = true;
const set<string> boolset = {"enable_gateway", "enable_dns", "enable_tcp",
"enable_udp", "enable_ipv4", "enable_ipv6"};
const set<string> allowset = {"program_proxy", "program_noproxy"};
const set<string> allowset = {"program_proxy", "program_noproxy", "comment", "table", "fwmark", "mark_newin"};
for (auto &[key, value] : j.items()) {
if (key == "cgroup_proxy" || key == "cgroup_noproxy") {
if (value.is_string() && !validCgroup((string)value)) status = false;
@@ -139,6 +149,7 @@ void Config::print_summary() {
info("proxied program: %s", join2str(program_proxy).c_str());
info("noproxy cgroup: %s", join2str(cgroup_noproxy).c_str());
info("proxied cgroup: %s", join2str(cgroup_proxy).c_str());
info("table: %d, fwmark: %d, mark_newin: %d", table, fwmark, mark_newin);
}
void Config::toRealProgramPath(vector<string> &v) {
@@ -147,7 +158,7 @@ void Config::toRealProgramPath(vector<string> &v) {
auto rpath = getRealExistPath(p);
if (!rpath.empty()) tmp.push_back(rpath);
else
error("%s not exist or broken link", p.c_str());
warning("%s not exist or broken link", p.c_str());
}
v = tmp;
}

View File

@@ -25,6 +25,11 @@ public:
bool enable_ipv4 = true;
bool enable_ipv6 = true;
// for iptables
int table=10007;
int fwmark=0x9973;
int mark_newin=0x9967;
void toEnv();
int saveToFile(const string f);
string toJsonStr();

View File

@@ -2,11 +2,4 @@ include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/src)
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
if (with_execsnoop)
add_executable(execsnoop_exec execsnoop.cpp ../src/common.cpp ../src/execsnoop.cpp)
set_target_properties(execsnoop_exec PROPERTIES OUTPUT_NAME execsnoop)
target_link_libraries(execsnoop_exec bcc)
install(TARGETS execsnoop_exec DESTINATION /usr/bin PERMISSIONS ${basic_permission})
endif()
install(TARGETS cgattach DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} PERMISSIONS ${basic_permission})

View File

@@ -1,24 +0,0 @@
#include "execsnoop.h"
#include "common.h"
#include <unistd.h>
using namespace std;
using namespace CGPROXY::EXECSNOOP;
#define PATH_MAX_LEN 128
int handle_pid(int pid) {
char path[PATH_MAX_LEN];
auto size = readlink(to_str("/proc/", pid, "/exe").c_str(), path, PATH_MAX_LEN);
if (size == -1) error("readlink: %s", to_str("/proc/", pid, "/exe").c_str());
path[size] = '\0';
info("%d %s", pid, path);
return 0;
}
int main() {
enable_debug = true;
enable_info = true;
callback = handle_pid;
execsnoop();
return 0;
}

View File

@@ -5,6 +5,7 @@
"port": 1080,
"protocol": "socks",
"settings": {
"udp": true,
"auth": "noauth",
"userLevel": 0
},