├── .github └── workflows │ └── build.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── assets ├── doc.go └── ebpf_probe.go ├── bin └── rtcagent ├── builder ├── Makefile.release ├── gen_android_nocore.sh ├── init_env.sh └── rpmBuild.spec ├── cli ├── cmd │ ├── freeswitch.go │ ├── global.go │ ├── kamailio.go │ ├── monitor.go │ ├── opensips.go │ ├── root.go │ └── tcprtt.go ├── cobrautl │ └── help.go └── main.go ├── dump.pcap ├── examples └── Dockerfile.kamailio ├── go.mod ├── go.sum ├── hepclient ├── hepclient.go └── hepsender │ └── hepsender.go ├── kern ├── bpf.old │ ├── arm64 │ │ ├── vmlinux.h │ │ └── vmlinux_510.h │ ├── bpf_core_read.h │ ├── bpf_endian.h │ ├── bpf_helper_defs.h │ ├── bpf_helpers.h │ ├── bpf_tracing.h │ └── x86 │ │ ├── .gitkeep │ │ └── vmlinux.h ├── bpf │ ├── arm64 │ │ ├── vmlinux.h │ │ └── vmlinux_510.h │ ├── bpf_core_read.h │ ├── bpf_endian.h │ ├── bpf_helper_defs.h │ ├── bpf_helpers.h │ ├── bpf_tracing.h │ └── x86 │ │ ├── .gitkeep │ │ └── vmlinux.h ├── common.h ├── common2.h ├── freeswitch.h ├── freeswitch_kern.c ├── kamailio.h ├── kamailio_kern.c ├── monitor_kern.c ├── opensips.h ├── opensips_kern.c ├── rtcagent.h └── tcprtt_kern.c ├── main.go ├── metric ├── definition.go ├── metric.go ├── prometheus.go └── reload.go ├── model └── metric.go ├── outdata └── hep │ ├── hep.pb.go │ ├── hep.proto │ └── marshal.go ├── pkg ├── event_processor │ ├── base_event.go │ ├── iparser.go │ ├── iworker.go │ └── processor.go ├── proc │ ├── go_elf │ │ ├── eprint.go │ │ └── gccgo.go │ ├── proc.go │ └── proc_test.go └── util │ ├── ebpf │ ├── bpf.go │ ├── bpf_androidgki.go │ ├── bpf_linux.go │ ├── config.gz │ └── parse.go │ ├── hkdf │ ├── hkdf.go │ └── hkdf_test.go │ └── kernel │ ├── kernel_version.go │ ├── kernel_version_unsupport.go │ └── version.go ├── rtcagent.service ├── rtcagent.yml ├── runagent.sh └── user ├── bytecode ├── .gitkeep ├── freeswitch_kern.d ├── freeswitch_kern.o ├── freeswitch_kern_less52.d ├── freeswitch_kern_less52.o ├── kamailio_kern.d ├── kamailio_kern.o ├── kamailio_kern_less52.d ├── kamailio_kern_less52.o ├── monitor_kern.d ├── monitor_kern.o ├── monitor_kern_less52.d ├── monitor_kern_less52.o ├── opensips_kern.d ├── opensips_kern.o ├── opensips_kern_less52.d ├── opensips_kern_less52.o ├── tcprtt_kern.d ├── tcprtt_kern.o ├── tcprtt_kern_less52.d └── tcprtt_kern_less52.o ├── config ├── common.go ├── common_androidgki.go ├── common_linux.go ├── config_freeswitch.go ├── config_kamailio.go ├── config_monitor.go ├── config_opensips.go ├── config_tcprtt.go ├── const.go └── iconfig.go ├── event ├── event_freeswitch.go ├── event_kamailio.go ├── event_monitor.go ├── event_opensips.go ├── event_tcprtt.go ├── ievent.go └── misc.go ├── module ├── const.go ├── iclose.go ├── imodule.go ├── probe_freeswitch.go ├── probe_kamailio.go ├── probe_monitor.go ├── probe_opensips.go ├── probe_tcpdrop.go └── register.go └── time └── monotonic.go /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: RTCAgent Builder 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [created] 7 | 8 | 9 | env: 10 | REGISTRY: ghcr.io 11 | IMAGE_NAME: ${{ github.repository }} 12 | 13 | jobs: 14 | 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up Go 20 | uses: actions/setup-go@v5.0.2 21 | with: 22 | go-version: 1.21 23 | - name: Setup Build Env 24 | run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com//sipcapture/rtcagent/master/builder/init_env.sh)" 25 | - name: Build RTCAgent 26 | run: make 27 | - name: Compress 28 | run: | 29 | strip bin/rtcagent 30 | upx bin/rtcagent 31 | - name: Check & Run 32 | run: | 33 | ls -alFh bin/rtcagent 34 | ./bin/rtcagent --help 35 | 36 | - name: Patch NFPM Version 37 | run: | 38 | sudo sed -i "s/0.0.0/${{ github.ref_name }}/g" rtcagent.yml 39 | - name: Create deb package 40 | id: nfpm-deb 41 | uses: burningalchemist/nfpm-action@v1 42 | env: 43 | VERSION: ${{ github.event.release.tag_name }} 44 | with: 45 | packager: deb 46 | config: rtcagent.yml 47 | target: rtcagent_${{ github.ref_name }}_all.deb 48 | - name: Create rpm package 49 | id: nfpm-rpm 50 | uses: burningalchemist/nfpm-action@v1 51 | env: 52 | VERSION: ${{ github.event.release.tag_name }} 53 | with: 54 | packager: rpm 55 | config: rtcagent.yml 56 | target: rtcagent_${{ github.ref_name }}.amd64.rpm 57 | 58 | - name: Upload Release 59 | if: github.event_name != 'pull_request' 60 | uses: boxpositron/upload-multiple-releases@1.0.7 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | with: 64 | release_config: | 65 | ./bin/rtcagent 66 | ./rtcagent_${{ github.ref_name }}_all.deb 67 | ./rtcagent_${{ github.ref_name }}.amd64.rpm 68 | tag_name: ${{ github.ref_name }} 69 | release_name: rtcagent_${{ github.ref_name }} 70 | draft: false 71 | prerelease: false 72 | overwrite: true 73 | 74 | - name: Log in to the Container registry 75 | uses: docker/login-action@v2.0.0 76 | with: 77 | registry: ${{ env.REGISTRY }} 78 | username: ${{ github.actor }} 79 | password: ${{ secrets.GITHUB_TOKEN }} 80 | 81 | - name: Docker Build and push 82 | uses: docker/build-push-action@v3.0.0 83 | with: 84 | context: . 85 | push: true 86 | tags: | 87 | ghcr.io/sipcapture/rtcagent:latest 88 | ghcr.io/sipcapture/rtcagent:${{ github.ref_name }} 89 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine as builder 2 | RUN apk add --allow-untrusted --update --no-cache curl ca-certificates 3 | WORKDIR / 4 | RUN curl -fsSL github.com/sipcapture/rtcagent/releases/latest/download/rtcagent -O && chmod +x rtcagent 5 | 6 | FROM scratch 7 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 8 | COPY --from=builder /rtcagent /rtcagent 9 | CMD ["/rtcagent"] 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | > RTCagent is an HEP/eBPF powered observability tool for VoIP/WebRTC Applications. 6 | 7 |
8 | 9 | ### About 10 | 11 | **RTCAgent** is a _next-generation **HEP Agent**_ developed using the latest **[eBPF](https://ebpf.io)** technologies. 12 | 13 | RTCAgent greatly differs from any other previous HEP Agent in several ways: 14 | 15 | - Unlike _native agents_, it does not require any code modifications or patches 16 | - Unlike _passive agents_, it does not require access to network interfaces and packets 17 | - Unlike _any other agent_, it traces functions used for _sending/receiving_ data 18 | 19 | _The result is a new, lightweight and portable HEP Agent able to mirror SIP packets through eBPF hooks
20 | from the core of supported applications bypassing manual code integrations, network encryption and complexity._ 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | ### Download 30 | Download an `amd64/x86` static build of `rtcagent` and use it immediately on modern kernels. 31 | ```bash 32 | curl -fsSL github.com/sipcapture/rtcagent/releases/latest/download/rtcagent -O && chmod +x rtcagent 33 | ``` 34 | 35 | Prefer using packages? Get the latest [deb and rpm](https://github.com/sipcapture/rtcagent/releases) releases for `amd64/x86` 36 | 37 | ### Usage 38 | 39 | ``` 40 | 41 | NAME: rtcagent - Capture and debug RTC Projects. 42 | USAGE: rtcagent [flags] 43 | 44 | COMMANDS: 45 | 46 | help Help about any command 47 | freeswitch capture SIP messages from freeswitch (libsofia): t_port, su_recv 48 | kamailio capture SIP messages from kamailio: recv_msg, udp_send, tcp_send. 49 | opensips capture SIP messages from v: recv_msg, udp_send, tcp_send. 50 | tcprtt show tcp rtt stats 51 | monitor show advanced monitor statistics 52 | 53 | 54 | DESCRIPTION: 55 | 56 | RTCAgent is a tool that can capture and trace SIP packets using eBPF hooks and HEP 57 | 58 | Usage: 59 | rtcagent -h 60 | 61 | OPTIONS: 62 | -d, --debug[=false] enable debug logging 63 | -h, --help[=false] help for rtcagent 64 | -P, --hep-port="9060" hep port - default 9060 65 | -S, --hep-server="" hep server to duplicate: i.e. 10.0.0.1 66 | -T, --hep-transport="udp" hep transport default udp. Can be udp, tcp, tls 67 | --hex[=false] print byte strings as hex encoded strings 68 | -l, --log-file="" -l save the packets to file 69 | --nosearch[=false] no lib search 70 | -p, --pid=0 if pid is 0 then we target all pids 71 | -u, --uid=0 if uid is 0 then we target all users 72 | -v, --version[=false] version for rtcagent 73 | 74 | ``` 75 | 76 |
77 | 78 | ### Build 79 | 80 | > Compatible with Linux/Android kernel versions >= **x86_64 5.x**, >= **aarch64 5.5**.
81 | > Linux only. Does not support Windows and macOS. 82 | 83 | #### Requirements 84 | * golang 1.18 or newer 85 | * clang 9.0 or newer 86 | * cmake 3.18.4 or newer 87 | * clang backend: llvm 9.0 or newer 88 | * kernel config:CONFIG_DEBUG_INFO_BTF=y 89 | 90 | #### Instructions 91 | 92 | ##### Ubuntu 93 | If you are using Ubuntu 20.04 or later versions, you can use a single command to complete the initialization of the compilation environment. 94 | ```shell 95 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com//sipcapture/rtcagent/master/builder/init_env.sh)" 96 | ``` 97 | ##### Any Linux 98 | In addition to the software listed in the 'Toolchain Version' section above, the following software is also required for the compilation environment. Please install before proceeding. 99 | 100 | * linux-tools-common 101 | * linux-tools-generic 102 | * pkgconf 103 | * libelf-dev 104 | 105 | **Clone the repository code and compile** 106 | ```shell 107 | git clone git@github.com:/sipcapture/rtcagent.git 108 | cd rtcagent 109 | make 110 | bin/rtcagent 111 | ``` 112 | #### compile without BTF 113 | RTCAgent support BTF disabled with command `make nocore` to compile at 2022/04/17 and can run on Linux systems that do not support BTF. 114 | ```shell 115 | make nocore 116 | bin/rtcagent --help 117 | ``` 118 | 119 |
120 | 121 | ### Usage 122 | Run `rtcagent` on a native host attaching to a local binary target 123 | ##### Kamailio 124 | ``` 125 | ./rtcagent kamailio -T udp -P 9060 -S $HOMER -m /usr/sbin/kamailio 126 | ``` 127 | ##### OpenSIPS 128 | ``` 129 | ./rtcagent opensips -T tcp -P 9061 -S $HOMER -m /usr/sbin/opensips 130 | ``` 131 | ##### FreeSwitch 132 | ``` 133 | ./rtcagent freeswitch -T udp -P 9060 -S $HOMER -m /usr/sbin/freeswitch 134 | ``` 135 | 136 | ### Docker 137 | #### Hypervisor Mode 138 | Run `rtcagent` as a priviledged container on your host attaching to a local binary target 139 | ``` 140 | rtcagent: 141 | privileged: true 142 | pid: host 143 | image: ghcr.io/sipcapture/rtcagent 144 | container_name: rtcagent 145 | restart: unless-stopped 146 | volumes: 147 | - /sys/fs/cgroup:/host/sys/fs/cgroup:ro 148 | - /sys/kernel/debug:/sys/kernel/debug:rw 149 | command: --cgroupfs-root=/host/sys/fs/cgroup 150 | ``` 151 | 152 | #### Cross-Container Mode 153 | Run `rtcagent` as a priviledged container attached to a target container via docker volume mounts 154 | ##### Kamailio 155 | ``` 156 | rtcagent: 157 | privileged: true 158 | image: ghcr.io/sipcapture/rtcagent 159 | container_name: rtcagent 160 | restart: unless-stopped 161 | volumes: 162 | - $(docker inspect --format="{{.GraphDriver.Data.MergedDir}}" kamailio)/usr/sbin/kamailio:/kamailio:ro 163 | command: /rtcagent kamailio -m /kamailio 164 | depends_on: 165 | - kamailio 166 | ``` 167 | ##### OpenSIPS 168 | ``` 169 | rtcagent: 170 | privileged: true 171 | image: ghcr.io/sipcapture/rtcagent 172 | container_name: rtcagent 173 | restart: unless-stopped 174 | volumes: 175 | - $(docker inspect --format="{{.GraphDriver.Data.MergedDir}}" opensips)/usr/sbin/opensips:/opensips:ro 176 | command: /rtcagent opensips -m /opensips 177 | depends_on: 178 | - opensips 179 | ``` 180 | 181 | 182 | ### Credits 183 | 184 | RTCAgent is inspired by Cilum, Odigos, eCapture and the many eBPF guides, libraries and implementations. 185 | -------------------------------------------------------------------------------- /assets/doc.go: -------------------------------------------------------------------------------- 1 | package assets 2 | -------------------------------------------------------------------------------- /bin/rtcagent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/bin/rtcagent -------------------------------------------------------------------------------- /builder/Makefile.release: -------------------------------------------------------------------------------- 1 | # 2 | # Responsible for creating rtcagent snapshots for testing and releasing 3 | # 4 | 5 | .PHONY: all 6 | all: help 7 | release: snapshot snapshot_android publish 8 | 9 | # 10 | # make 11 | # 12 | 13 | .ONESHELL: 14 | SHELL = /bin/sh 15 | 16 | MAKEFLAGS += --no-print-directory 17 | 18 | # 19 | # tools 20 | # 21 | 22 | CMD_CHECKSUM ?= sha256sum 23 | CMD_GITHUB ?= gh 24 | CMD_TAR ?= tar 25 | CMD_GIT ?= git 26 | CMD_RM ?= rm 27 | CMD_TOUCH ?= touch 28 | CMD_MKDIR ?= mkdir 29 | CMD_MV ?= mv 30 | CMD_CP ?= cp 31 | 32 | UNAME_M := $(shell uname -m) 33 | SNAPSHOT_VERSION ?= $(shell git rev-parse HEAD) 34 | 35 | .ONESHELL: 36 | .check_%: 37 | # 38 | @command -v $* >/dev/null 39 | if [ $$? -ne 0 ]; then 40 | echo "missing required tool $*" 41 | exit 1 42 | else 43 | touch $@ # avoid target rebuilds due to inexistent file 44 | fi 45 | 46 | # 47 | # environment 48 | # 49 | 50 | .PHONY: env 51 | env: 52 | @echo --------------------------------------- 53 | @echo "CMD_CHECKSUM $(CMD_CHECKSUM)" 54 | @echo "CMD_GIT $(CMD_GIT)" 55 | @echo "CMD_GITHUB $(CMD_GITHUB)" 56 | @echo "CMD_TAR $(CMD_TAR)" 57 | @echo "CMD_TOUCH $(CMD_TOUCH)" 58 | @echo "CMD_RM $(CMD_RM)" 59 | @echo "CMD_MKDIR $(CMD_MKDIR)" 60 | @echo --------------------------------------- 61 | @echo "SNAPSHOT_VERSION $(SNAPSHOT_VERSION)" 62 | @echo --------------------------------------- 63 | 64 | # 65 | # usage 66 | # 67 | 68 | .PHONY: help 69 | help: 70 | @echo "" 71 | @echo "Create rtcagent snapshots for testing and releasing" 72 | @echo "" 73 | @echo "To generate a release snapshot:" 74 | @echo "" 75 | @echo " $$ make -f builder/Makefile.release snapshot" 76 | @echo "" 77 | @echo " - Compiles rtcagent" 78 | @echo " - Creates an archive of build artifacts along with license" 79 | @echo " - Takes a checksum of the archive" 80 | @echo "" 81 | @echo " Example:" 82 | @echo "" 83 | @echo " To create build artifacts versioned by latest git SHA:" 84 | @echo "" 85 | @echo " $$ make -f builder/Makefile.release snapshot" 86 | @echo "" 87 | @echo " To create build artifacts with version v0.1.6:" 88 | @echo "" 89 | @echo " $$ SNAPSHOT_VERSION=v0.1.6 \ " 90 | @echo " make -f builder/Makefile.release snapshot" 91 | @echo "" 92 | @echo "To publish a release:" 93 | @echo "" 94 | @echo " $$ SNAPSHOT_VERSION=v0.1.6 \ " 95 | @echo " make -f builder/Makefile.release publish" 96 | @echo "" 97 | @echo "" 98 | @echo "Clean leftovers:" 99 | @echo "" 100 | @echo " $$ make -f builder/Makefile.release clean" 101 | @echo "" 102 | 103 | # 104 | # requirements 105 | # 106 | 107 | .PHONY: .check_tree 108 | .check_tree: 109 | # 110 | @if [ ! -d ./builder ]; then 111 | echo "you must be in the root directory" 112 | exit 1 113 | fi 114 | 115 | # 116 | # output dir 117 | # 118 | 119 | OUTPUT_DIR = ./bin 120 | TAR_DIR = rtcagent-$(SNAPSHOT_VERSION)-linux-$(UNAME_M) 121 | TAR_DIR_ANDROID = rtcagent-$(SNAPSHOT_VERSION)-android-$(UNAME_M) 122 | 123 | # from CLI args. 124 | RELEASE_NOTES ?= $(OUTPUT_DIR)/release_notes.txt 125 | 126 | $(OUTPUT_DIR): 127 | # 128 | $(CMD_MKDIR) -p $@ 129 | # $(CMD_TOUCH) $(RELEASE_NOTES) 130 | 131 | # 132 | # Create a release snapshot 133 | # 134 | 135 | OUT_ARCHIVE := $(OUTPUT_DIR)/$(TAR_DIR).tar.gz 136 | OUT_ARCHIVE_ANDROID := $(OUTPUT_DIR)/$(TAR_DIR_ANDROID).tar.gz 137 | OUT_CHECKSUMS := $(OUTPUT_DIR)/checksum-$(SNAPSHOT_VERSION).txt 138 | 139 | .PHONY: snapshot 140 | snapshot: \ 141 | $(OUTPUT_DIR) \ 142 | | .check_tree \ 143 | .check_$(CMD_TAR) \ 144 | .check_$(CMD_CHECKSUM) \ 145 | .check_$(CMD_GITHUB) 146 | 147 | # build binaries 148 | $(MAKE) clean 149 | $(MAKE) nocore 150 | # create the tar ball and checksum files 151 | $(CMD_MKDIR) -p $(TAR_DIR) 152 | $(CMD_CP) LICENSE $(TAR_DIR)/LICENSE 153 | $(CMD_CP) CHANGELOG.md $(TAR_DIR)/CHANGELOG.md 154 | $(CMD_CP) README.md $(TAR_DIR)/README.md 155 | $(CMD_CP) README_CN.md $(TAR_DIR)/README_CN.md 156 | $(CMD_MV) $(OUTPUT_DIR)/rtcagent $(TAR_DIR)/rtcagent 157 | $(CMD_CP) $(RELEASE_NOTES) $(OUTPUT_DIR)/release_notes.txt 158 | $(CMD_TAR) -czf $(OUT_ARCHIVE) $(TAR_DIR) 159 | cd $(OUTPUT_DIR) 160 | $(CMD_CHECKSUM) $(TAR_DIR).tar.gz > ./../$(OUT_CHECKSUMS) 161 | cd ../ 162 | 163 | .PHONY: snapshot_android 164 | snapshot_android: \ 165 | $(OUTPUT_DIR) \ 166 | | .check_tree \ 167 | .check_$(CMD_TAR) \ 168 | .check_$(CMD_CHECKSUM) \ 169 | .check_$(CMD_GITHUB) 170 | 171 | # build binaries 172 | $(MAKE) clean 173 | ANDROID=1 $(MAKE) nocore 174 | # create the tar ball and checksum files 175 | $(CMD_MKDIR) -p $(TAR_DIR_ANDROID) 176 | $(CMD_CP) LICENSE $(TAR_DIR_ANDROID)/LICENSE 177 | $(CMD_CP) CHANGELOG.md $(TAR_DIR_ANDROID)/CHANGELOG.md 178 | $(CMD_CP) README.md $(TAR_DIR_ANDROID)/README.md 179 | $(CMD_CP) README_CN.md $(TAR_DIR_ANDROID)/README_CN.md 180 | $(CMD_MV) $(OUTPUT_DIR)/rtcagent $(TAR_DIR_ANDROID)/rtcagent 181 | $(CMD_CP) $(RELEASE_NOTES) $(TAR_DIR_ANDROID)/release_notes.txt 182 | $(CMD_TAR) -czf $(OUT_ARCHIVE_ANDROID) $(TAR_DIR_ANDROID) 183 | cd $(OUTPUT_DIR) 184 | $(CMD_CHECKSUM) $(TAR_DIR_ANDROID).tar.gz >> ./../$(OUT_CHECKSUMS) 185 | cd ../ 186 | 187 | .PHONY: publish 188 | publish: \ 189 | $(OUTPUT_DIR) \ 190 | $(OUT_ARCHIVE) \ 191 | $(OUT_ARCHIVE_ANDROID) \ 192 | $(OUT_CHECKSUMS) \ 193 | | .check_tree \ 194 | .check_$(CMD_GITHUB) 195 | # 196 | # release it! 197 | $(CMD_GITHUB) release create $(SNAPSHOT_VERSION) $(OUT_ARCHIVE) $(OUT_ARCHIVE_ANDROID) $(OUT_CHECKSUMS) --title "RtcAgent $(SNAPSHOT_VERSION)" --notes-file $(RELEASE_NOTES) 198 | 199 | .PHONY: clean 200 | clean: 201 | # 202 | $(MAKE) clean 203 | -------------------------------------------------------------------------------- /builder/gen_android_nocore.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bash builder/gen_android_nocore.sh 1.0.0 3 | SHELL_GH=gh 4 | 5 | # Android nocore ubnutu 20.04 ARM 6 | UNAME_M=`uname -m` 7 | OUTPUT_DIR="./bin" 8 | SNAPSHOT_VERSION=v${1} 9 | export PATH=/usr/local/go/bin:$PATH 10 | ANDROID=1 make nocore 11 | TAR_DIR=rtcagent-android-${UNAME_M}_nocore-${SNAPSHOT_VERSION} 12 | 13 | # rtcagent-v0.4.8-android-x86_64.tar.gz 14 | OUT_ARCHIVE=${OUTPUT_DIR}/rtcagent-${SNAPSHOT_VERSION}-android-${UNAME_M}-nocore.tar.gz 15 | 16 | # add gobin into $PATH 17 | mkdir -p ${TAR_DIR} 18 | cp LICENSE ${TAR_DIR}/LICENSE 19 | cp CHANGELOG.md ${TAR_DIR}/CHANGELOG.md 20 | cp README.md ${TAR_DIR}/README.md 21 | cp README_CN.md ${TAR_DIR}/README_CN.md 22 | cp ${OUTPUT_DIR}/rtcagent ${TAR_DIR}/rtcagent 23 | tar -czf ${OUT_ARCHIVE} ${TAR_DIR} 24 | 25 | 26 | # upload to github 27 | ${SHELL_GH} release download ${SNAPSHOT_VERSION} -p "checksum-${SNAPSHOT_VERSION}.txt" 28 | sha256sum rtcagent-*.tar.gz >> checksum-${SNAPSHOT_VERSION}.txt 29 | files=($(ls rtcagent-*.tar.gz checksum-${SNAPSHOT_VERSION}.txt)) 30 | # shellcheck disable=SC2145 31 | echo "-------------------upload files: ${files[@]} -------------------" 32 | ${SHELL_GH} release upload ${SNAPSHOT_VERSION} "${files[@]}" --clobber 33 | -------------------------------------------------------------------------------- /builder/init_env.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com//sipcapture/rtcagent/master/builder/init_env.sh)" 4 | 5 | # check env 6 | release_num=$(lsb_release -r --short) 7 | if [ $? -ne 0 ]; then 8 | echo "command not found, supported ubuntu only." 9 | exit 10 | fi 11 | 12 | CLANG_NUM=12 13 | # shellcheck disable=SC2209 14 | MAKE_RTCAGENT=make 15 | if [ ${release_num} == "20.04" ]; then 16 | CLANG_NUM=9 17 | MAKE_RTCAGENT="make nocore" 18 | elif [ ${release_num} == "20.10" ]; then 19 | CLANG_NUM=10 20 | MAKE_RTCAGENT="make nocore" 21 | elif [ ${release_num} == "21.04" ]; then 22 | CLANG_NUM=11 23 | elif [ ${release_num} == "21.10" ]; then 24 | CLANG_NUM=12 25 | elif [ ${release_num} == "22.04" ]; then 26 | CLANG_NUM=12 27 | elif [ ${release_num} == "22.10" ]; then 28 | CLANG_NUM=12 29 | else 30 | echo "unsupported release version ${release_num}" && exit 31 | fi 32 | 33 | echo "CLANG_NUM=${CLANG_NUM}" 34 | 35 | UNAME_M=`uname -m` 36 | ARCH="amd64" 37 | if [[ ${UNAME_M} =~ "x86_64" ]];then 38 | ARCH="amd64" 39 | elif [[ ${UNAME_M} =~ "aarch64" ]]; then 40 | ARCH="arm64" 41 | else 42 | echo "unsupported arch ${UNAME_M}"; 43 | fi 44 | 45 | GOBIN_ZIP="go1.21.12.linux-${ARCH}.tar.gz" 46 | echo "GOBIN_ZIP:${GOBIN_ZIP}" 47 | 48 | cd ~ 49 | 50 | uname -a 51 | sudo apt-get update 52 | 53 | # install packages 54 | sudo apt-get install --yes build-essential pkgconf libelf-dev llvm-${CLANG_NUM} clang-${CLANG_NUM} linux-tools-common linux-tools-generic 55 | for tool in "clang" "llc" "llvm-strip" 56 | do 57 | sudo rm -f /usr/bin/$tool 58 | sudo ln -s /usr/bin/$tool-${CLANG_NUM} /usr/bin/$tool 59 | done 60 | 61 | clang --version 62 | 63 | 64 | if ! command -v go /dev/null 65 | then 66 | # install golang 67 | wget https://go.dev/dl/${GOBIN_ZIP} 68 | sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf ${GOBIN_ZIP} 69 | export PATH=/usr/local/go/bin:$PATH 70 | fi 71 | 72 | # clone repo 73 | git clone https://github.com//sipcapture/rtcagent.git 74 | cd ./rtcagent || exit 75 | go mod tidy 76 | ${MAKE_RTCAGENT} 77 | -------------------------------------------------------------------------------- /builder/rpmBuild.spec: -------------------------------------------------------------------------------- 1 | Name: rtcagent 2 | Version: 3 | Release: 4 | Summary: capture SIP traffic include TLS without certificate 5 | License: AGPL-3.0 6 | URL: https://www.qxip.net 7 | Source0: %{name}-%{version}.tar.gz 8 | 9 | %global _missing_build_ids_terminate_build 0 10 | %define debug_package %{nil} 11 | 12 | BuildRequires: make 13 | BuildRequires: clang 14 | 15 | %description 16 | SIP/TLS plaintext capture, 17 | 18 | supports kamailio, freeswitch, opensips 19 | 20 | %prep 21 | %setup -c 22 | 23 | %build 24 | make 25 | 26 | %install 27 | rm -rf %{buildroot} 28 | mkdir -p %{buildroot}/usr/local/bin/ 29 | install -m 755 bin/rtcagent %{buildroot}/usr/local/bin/rtcagent 30 | 31 | %files 32 | /usr/local/bin/rtcagent 33 | 34 | %changelog 35 | -------------------------------------------------------------------------------- /cli/cmd/freeswitch.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | LINK - http://github.com/sipcapture/rtcagent 6 | 7 | Copyright (C) 2023 QXIP B.V. 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Affero General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Affero General Public License for more details. 18 | 19 | You should have received a copy of the GNU Affero General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "log" 28 | "os" 29 | "os/signal" 30 | "rtcagent/hepclient" 31 | "rtcagent/hepclient/hepsender" 32 | "rtcagent/user/config" 33 | "rtcagent/user/module" 34 | "strings" 35 | "syscall" 36 | 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | var freeswitchConfig = config.NewFreeSwitchConfig() 41 | 42 | // freeswitchCmd represents the freeswitch command 43 | var freeswitchCmd = &cobra.Command{ 44 | Use: "freeswitch", 45 | Short: "capture SIP messages from freeswitch (libsofia): t_port, su_recv.", 46 | Long: ` Tested on freeswitch 1.x`, 47 | Run: freeswitchCommandFunc, 48 | } 49 | 50 | func init() { 51 | freeswitchCmd.PersistentFlags().StringVarP(&freeswitchConfig.FreeSwitchpath, "lib-sofia", "m", "/usr/local/lib/libsofia-sip-ua.so.0", "libsofia file path, use to hook") 52 | rootCmd.AddCommand(freeswitchCmd) 53 | } 54 | 55 | // freeswitchCommandFunc executes the "freeswitch" command. 56 | func freeswitchCommandFunc(command *cobra.Command, args []string) { 57 | stopper := make(chan os.Signal, 1) 58 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 59 | ctx, cancelFun := context.WithCancel(context.TODO()) 60 | 61 | mod := module.GetModuleByName(module.ModuleNameFreeSwitch) 62 | 63 | logger := log.New(os.Stdout, "freeswitch_", log.LstdFlags) 64 | logger.Printf("RTCAGENT :: version :%s", GitVersion) 65 | logger.Printf("RTCAGENT :: start to run %s module", mod.Name()) 66 | 67 | // save global config 68 | gConf, e := getGlobalConf(command) 69 | if e != nil { 70 | logger.Fatal(e) 71 | os.Exit(1) 72 | } 73 | freeswitchConfig.Pid = gConf.Pid 74 | freeswitchConfig.Debug = gConf.Debug 75 | freeswitchConfig.IsHex = gConf.IsHex 76 | 77 | if (gConf.HepServer != "" || len(strings.TrimSpace(gConf.HepServer)) > 0) && hepsender.Hepsender == nil { 78 | 79 | log.Println("HEP client will be started") 80 | 81 | var err error 82 | hepsender.Hepsender, err = hepclient.NewHepClient(gConf.HepServer, gConf.HepPort, gConf.HepTransport) 83 | if err != nil { 84 | log.Fatalf("HEP client couldn't be init: addr:%s, port: %s, transport: %s. Error: %s", gConf.HepServer, gConf.HepPort, gConf.HepTransport, err.Error()) 85 | os.Exit(1) 86 | } else { 87 | log.Println("HEP client started") 88 | } 89 | } 90 | 91 | log.Printf("RTCAGENT :: pid info :%d -%s", os.Getpid(), gConf.HepServer) 92 | //bc.Pid = globalFlags.Pid 93 | if e := freeswitchConfig.Check(); e != nil { 94 | logger.Fatal(e) 95 | os.Exit(1) 96 | } 97 | 98 | err := mod.Init(ctx, logger, freeswitchConfig) 99 | if err != nil { 100 | logger.Fatal(err) 101 | os.Exit(1) 102 | } 103 | 104 | go func(module module.IModule) { 105 | err := module.Run() 106 | if err != nil { 107 | logger.Fatalf("%v", err) 108 | } 109 | }(mod) 110 | <-stopper 111 | cancelFun() 112 | os.Exit(0) 113 | } 114 | -------------------------------------------------------------------------------- /cli/cmd/global.go: -------------------------------------------------------------------------------- 1 | /* 2 | LINK - http://github.com/sipcapture/rtcagent 3 | 4 | Copyright (C) 2023 QXIP B.V. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | package cmd 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | // GlobalFlags are flags that defined globally 26 | // and are inherited to all sub-commands. 27 | type GlobalFlags struct { 28 | IsHex bool 29 | Debug bool 30 | Pid uint64 // PID 31 | Uid uint64 // UID 32 | NoSearch bool // No lib search 33 | loggerFile string // save file 34 | HepServer string 35 | HepPort string 36 | HepTransport string 37 | HepEnable bool 38 | } 39 | 40 | func getGlobalConf(command *cobra.Command) (conf GlobalFlags, err error) { 41 | conf.Pid, err = command.Flags().GetUint64("pid") 42 | if err != nil { 43 | return 44 | } 45 | 46 | conf.Uid, err = command.Flags().GetUint64("uid") 47 | if err != nil { 48 | return 49 | } 50 | 51 | conf.Debug, err = command.Flags().GetBool("debug") 52 | if err != nil { 53 | return 54 | } 55 | 56 | conf.IsHex, err = command.Flags().GetBool("hex") 57 | if err != nil { 58 | return 59 | } 60 | 61 | conf.NoSearch, err = command.Flags().GetBool("nosearch") 62 | if err != nil { 63 | return 64 | } 65 | 66 | conf.loggerFile, err = command.Flags().GetString("log-file") 67 | if err != nil { 68 | return 69 | } 70 | 71 | conf.HepServer, err = command.Flags().GetString("hep-server") 72 | if err != nil { 73 | return 74 | } 75 | 76 | conf.HepPort, err = command.Flags().GetString("hep-port") 77 | if err != nil { 78 | return 79 | } 80 | 81 | conf.HepTransport, err = command.Flags().GetString("hep-transport") 82 | if err != nil { 83 | return 84 | } 85 | 86 | return 87 | } 88 | -------------------------------------------------------------------------------- /cli/cmd/kamailio.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | LINK - http://github.com/sipcapture/rtcagent 6 | 7 | Copyright (C) 2023 QXIP B.V. 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Affero General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Affero General Public License for more details. 18 | 19 | You should have received a copy of the GNU Affero General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "log" 28 | "os" 29 | "os/signal" 30 | "rtcagent/hepclient" 31 | "rtcagent/hepclient/hepsender" 32 | "rtcagent/user/config" 33 | "rtcagent/user/module" 34 | "strings" 35 | "syscall" 36 | 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | var kamailioConfig = config.NewKamailioConfig() 41 | 42 | // kamailioCmd represents the kamailio command 43 | var kamailioCmd = &cobra.Command{ 44 | Use: "kamailio", 45 | Short: "capture SIP messages from kamailio: recv_msg, udp_send, tcp_send.", 46 | Long: ` Tested on kamailio 5.x`, 47 | Run: kamailioCommandFunc, 48 | } 49 | 50 | func init() { 51 | kamailioCmd.PersistentFlags().StringVarP(&kamailioConfig.Kamailiopath, "kamailio", "m", "/usr/sbin/kamailio", "kamailio binary file path, use to hook") 52 | rootCmd.AddCommand(kamailioCmd) 53 | } 54 | 55 | // kamailioCommandFunc executes the "kamailio" command. 56 | func kamailioCommandFunc(command *cobra.Command, args []string) { 57 | stopper := make(chan os.Signal, 1) 58 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 59 | ctx, cancelFun := context.WithCancel(context.TODO()) 60 | 61 | mod := module.GetModuleByName(module.ModuleNameKamailio) 62 | 63 | logger := log.New(os.Stdout, "kamailio_", log.LstdFlags) 64 | logger.Printf("RTCAGENT :: version :%s", GitVersion) 65 | logger.Printf("RTCAGENT :: start to run %s module", mod.Name()) 66 | 67 | // save global config 68 | gConf, e := getGlobalConf(command) 69 | if e != nil { 70 | logger.Fatal(e) 71 | os.Exit(1) 72 | } 73 | kamailioConfig.Pid = gConf.Pid 74 | kamailioConfig.Debug = gConf.Debug 75 | kamailioConfig.IsHex = gConf.IsHex 76 | kamailioConfig.NoSearch = gConf.NoSearch 77 | 78 | logger.Printf("RTCAGENT :: kamailio - nosearch: %v", kamailioConfig.NoSearch) 79 | 80 | if (gConf.HepServer != "" || len(strings.TrimSpace(gConf.HepServer)) > 0) && hepsender.Hepsender == nil { 81 | 82 | log.Println("HEP client will be started") 83 | 84 | var err error 85 | hepsender.Hepsender, err = hepclient.NewHepClient(gConf.HepServer, gConf.HepPort, gConf.HepTransport) 86 | if err != nil { 87 | log.Fatalf("HEP client couldn't be init: addr:%s, port: %s, transport: %s. Error: %s", gConf.HepServer, gConf.HepPort, gConf.HepTransport, err.Error()) 88 | os.Exit(1) 89 | } else { 90 | log.Println("HEP client started") 91 | } 92 | } 93 | 94 | log.Printf("RTCAGENT :: pid info :%d -%s", os.Getpid(), gConf.HepServer) 95 | //bc.Pid = globalFlags.Pid 96 | if e := kamailioConfig.Check(); e != nil { 97 | logger.Fatal(e) 98 | os.Exit(1) 99 | } 100 | 101 | err := mod.Init(ctx, logger, kamailioConfig) 102 | if err != nil { 103 | logger.Fatal(err) 104 | os.Exit(1) 105 | } 106 | 107 | go func(module module.IModule) { 108 | err := module.Run() 109 | if err != nil { 110 | logger.Fatalf("%v", err) 111 | } 112 | }(mod) 113 | <-stopper 114 | cancelFun() 115 | os.Exit(0) 116 | } 117 | -------------------------------------------------------------------------------- /cli/cmd/monitor.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | LINK - http://github.com/sipcapture/rtcagent 6 | 7 | Copyright (C) 2023 QXIP B.V. 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Affero General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Affero General Public License for more details. 18 | 19 | You should have received a copy of the GNU Affero General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "log" 28 | "os" 29 | "os/signal" 30 | "rtcagent/hepclient" 31 | "rtcagent/hepclient/hepsender" 32 | "rtcagent/metric" 33 | "rtcagent/user/config" 34 | "rtcagent/user/module" 35 | "strings" 36 | "syscall" 37 | 38 | "github.com/spf13/cobra" 39 | ) 40 | 41 | var monitorConfig = config.NewMonitorConfig() 42 | 43 | // monitorCmd represents the monitor command 44 | var monitorCmd = &cobra.Command{ 45 | Use: "monitor", 46 | Short: "show stats", 47 | Long: ` Tested on linux`, 48 | Run: monitorCommandFunc, 49 | } 50 | 51 | func init() { 52 | monitorCmd.PersistentFlags().StringVarP(&monitorConfig.Monitorpath, "binary", "b", "", "monitor binary file path, use to hook") 53 | monitorCmd.PersistentFlags().BoolVarP(&monitorConfig.SysCall, "syscall", "", false, "monitor syscall") 54 | monitorCmd.PersistentFlags().BoolVarP(&monitorConfig.UserCall, "usercall", "", false, "monitor usercall") 55 | monitorCmd.PersistentFlags().BoolVarP(&monitorConfig.NetworkCall, "networkcall", "", false, "monitor networkcall") 56 | monitorCmd.PersistentFlags().StringSliceVarP(&monitorConfig.UserFunctions, "functions", "f", nil, "monitor user functions") 57 | monitorCmd.PersistentFlags().BoolVarP(&monitorConfig.ShowUserFunction, "show-user-functions", "", false, "show user functions") 58 | 59 | rootCmd.AddCommand(monitorCmd) 60 | } 61 | 62 | // monitorCommandFunc executes the "monitor" command. 63 | func monitorCommandFunc(command *cobra.Command, args []string) { 64 | stopper := make(chan os.Signal, 1) 65 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 66 | ctx, cancelFun := context.WithCancel(context.TODO()) 67 | 68 | mod := module.GetModuleByName(module.ModuleNameMonitor) 69 | 70 | logger := log.New(os.Stdout, "monitor_", log.LstdFlags) 71 | logger.Printf("RTCAGENT :: version :%s", GitVersion) 72 | logger.Printf("RTCAGENT :: start to run %s module", mod.Name()) 73 | 74 | // save global config 75 | gConf, e := getGlobalConf(command) 76 | if e != nil { 77 | logger.Fatal(e) 78 | os.Exit(1) 79 | } 80 | monitorConfig.Pid = gConf.Pid 81 | monitorConfig.Debug = gConf.Debug 82 | monitorConfig.IsHex = gConf.IsHex 83 | 84 | if (gConf.HepServer != "" || len(strings.TrimSpace(gConf.HepServer)) > 0) && hepsender.Hepsender == nil { 85 | 86 | log.Println("HEP client will be started") 87 | 88 | var err error 89 | hepsender.Hepsender, err = hepclient.NewHepClient(gConf.HepServer, gConf.HepPort, gConf.HepTransport) 90 | if err != nil { 91 | log.Fatalf("HEP client couldn't be init: addr:%s, port: %s, transport: %s. Error: %s", gConf.HepServer, gConf.HepPort, gConf.HepTransport, err.Error()) 92 | os.Exit(1) 93 | } else { 94 | log.Println("HEP client started") 95 | } 96 | } 97 | 98 | log.Printf("RTCAGENT :: pid info :%d -%s", os.Getpid(), gConf.HepServer) 99 | if e := monitorConfig.Check(); e != nil { 100 | logger.Fatal(e) 101 | os.Exit(1) 102 | } 103 | 104 | err := mod.Init(ctx, logger, monitorConfig) 105 | if err != nil { 106 | logger.Fatal(err) 107 | os.Exit(1) 108 | } 109 | 110 | m := metric.New("prometheus") 111 | m.Chan = monitorConfig.PromCh 112 | 113 | if err := m.Run(); err != nil { 114 | log.Printf("Error: %v", err) 115 | } 116 | defer m.End() 117 | 118 | go func(module module.IModule) { 119 | err := module.Run() 120 | if err != nil { 121 | logger.Fatalf("%v", err) 122 | } 123 | }(mod) 124 | <-stopper 125 | 126 | cancelFun() 127 | os.Exit(0) 128 | } 129 | -------------------------------------------------------------------------------- /cli/cmd/opensips.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | LINK - http://github.com/sipcapture/rtcagent 6 | 7 | Copyright (C) 2023 QXIP B.V. 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Affero General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Affero General Public License for more details. 18 | 19 | You should have received a copy of the GNU Affero General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "log" 28 | "os" 29 | "os/signal" 30 | "rtcagent/hepclient" 31 | "rtcagent/hepclient/hepsender" 32 | "rtcagent/user/config" 33 | "rtcagent/user/module" 34 | "strings" 35 | "syscall" 36 | 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | var opensipsConfig = config.NewOpensipsConfig() 41 | 42 | // opensipsCmd represents the opensips command 43 | var opensipsCmd = &cobra.Command{ 44 | Use: "opensips", 45 | Short: "capture SIP messages from opensips: recv_msg, udp_send, tcp_send.", 46 | Long: ` Tested on opensips 3.x`, 47 | Run: opensipsCommandFunc, 48 | } 49 | 50 | func init() { 51 | opensipsCmd.PersistentFlags().StringVarP(&opensipsConfig.Opensipspath, "opensips", "m", "/usr/sbin/opensips", "opensips binary file path, use to hook") 52 | rootCmd.AddCommand(opensipsCmd) 53 | } 54 | 55 | // opensipsCommandFunc executes the "opensips" command. 56 | func opensipsCommandFunc(command *cobra.Command, args []string) { 57 | stopper := make(chan os.Signal, 1) 58 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 59 | ctx, cancelFun := context.WithCancel(context.TODO()) 60 | 61 | mod := module.GetModuleByName(module.ModuleNameOpensips) 62 | 63 | logger := log.New(os.Stdout, "opensips_", log.LstdFlags) 64 | logger.Printf("RTCAGENT :: version :%s", GitVersion) 65 | logger.Printf("RTCAGENT :: start to run %s module", mod.Name()) 66 | 67 | // save global config 68 | gConf, e := getGlobalConf(command) 69 | if e != nil { 70 | logger.Fatal(e) 71 | os.Exit(1) 72 | } 73 | opensipsConfig.Pid = gConf.Pid 74 | opensipsConfig.Debug = gConf.Debug 75 | opensipsConfig.IsHex = gConf.IsHex 76 | opensipsConfig.NoSearch = gConf.NoSearch 77 | 78 | logger.Printf("RTCAGENT :: opensips - nosearch: %v", opensipsConfig.NoSearch) 79 | 80 | if (gConf.HepServer != "" || len(strings.TrimSpace(gConf.HepServer)) > 0) && hepsender.Hepsender == nil { 81 | 82 | log.Println("HEP client will be started") 83 | 84 | var err error 85 | hepsender.Hepsender, err = hepclient.NewHepClient(gConf.HepServer, gConf.HepPort, gConf.HepTransport) 86 | if err != nil { 87 | log.Fatalf("HEP client couldn't be init: addr:%s, port: %s, transport: %s. Error: %s", gConf.HepServer, gConf.HepPort, gConf.HepTransport, err.Error()) 88 | os.Exit(1) 89 | } else { 90 | log.Println("HEP client started") 91 | } 92 | } 93 | 94 | log.Printf("RTCAGENT :: pid info :%d -%s", os.Getpid(), gConf.HepServer) 95 | //bc.Pid = globalFlags.Pid 96 | if e := opensipsConfig.Check(); e != nil { 97 | logger.Fatal(e) 98 | os.Exit(1) 99 | } 100 | 101 | err := mod.Init(ctx, logger, opensipsConfig) 102 | if err != nil { 103 | logger.Fatal(err) 104 | os.Exit(1) 105 | } 106 | 107 | go func(module module.IModule) { 108 | err := module.Run() 109 | if err != nil { 110 | logger.Fatalf("%v", err) 111 | } 112 | }(mod) 113 | <-stopper 114 | cancelFun() 115 | os.Exit(0) 116 | } 117 | -------------------------------------------------------------------------------- /cli/cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | LINK - http://github.com/sipcapture/rtcagent 3 | 4 | Copyright (C) 2023 QXIP B.V. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | package cmd 21 | 22 | import ( 23 | "os" 24 | "rtcagent/cli/cobrautl" 25 | 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | const ( 30 | cliName = "rtcagent" 31 | cliDescription = "Capture and debug RTC Projects." 32 | ) 33 | 34 | var ( 35 | GitVersion = "v0.0.2" 36 | //ReleaseDate = "2022-03-16" 37 | ) 38 | 39 | const ( 40 | defaultPid uint64 = 0 41 | defaultUid uint64 = 0 42 | ) 43 | 44 | // rootCmd represents the base command when called without any subcommands 45 | var rootCmd = &cobra.Command{ 46 | Use: cliName, 47 | Short: cliDescription, 48 | SuggestFor: []string{"rtcagent"}, 49 | 50 | Long: `RTCAgent is a tool that can capture and trace SIP packets by hijacking application's function like kamailio, freeswitch, opensips, monitor 51 | it can also make tcprtt statics on the server. 52 | 53 | Repository: https://github.com//sipcapture/rtcagent 54 | HomePage: https://www.qxip.net 55 | 56 | Usage: 57 | rtcagent kamailio -h 58 | rtcagent freeswitch -h 59 | rtcagent opensips -h 60 | `, 61 | } 62 | 63 | func usageFunc(c *cobra.Command) error { 64 | return cobrautl.UsageFunc(c, GitVersion) 65 | } 66 | 67 | // Execute adds all child commands to the root command and sets flags appropriately. 68 | // This is called by main.main(). It only needs to happen once to the rootCmd. 69 | func Execute() { 70 | rootCmd.SetUsageFunc(usageFunc) 71 | rootCmd.SetHelpTemplate(`{{.UsageString}}`) 72 | rootCmd.CompletionOptions.DisableDefaultCmd = true 73 | rootCmd.Version = GitVersion 74 | rootCmd.SetVersionTemplate(`{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version:\t%s" .Version}} 75 | `) 76 | 77 | err := rootCmd.Execute() 78 | if err != nil { 79 | os.Exit(1) 80 | } 81 | 82 | } 83 | 84 | func init() { 85 | cobra.EnablePrefixMatching = true 86 | var globalFlags = GlobalFlags{} 87 | 88 | //rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 89 | rootCmd.PersistentFlags().BoolVarP(&globalFlags.Debug, "debug", "d", false, "enable debug logging") 90 | rootCmd.PersistentFlags().BoolVar(&globalFlags.IsHex, "hex", false, "print byte strings as hex encoded strings") 91 | rootCmd.PersistentFlags().BoolVar(&globalFlags.NoSearch, "nosearch", false, "no lib search") 92 | rootCmd.PersistentFlags().Uint64VarP(&globalFlags.Pid, "pid", "p", defaultPid, "if pid is 0 then we target all pids") 93 | rootCmd.PersistentFlags().Uint64VarP(&globalFlags.Uid, "uid", "u", defaultUid, "if uid is 0 then we target all users") 94 | rootCmd.PersistentFlags().StringVarP(&globalFlags.loggerFile, "log-file", "l", "", "-l save the packets to file") 95 | rootCmd.PersistentFlags().StringVarP(&globalFlags.HepServer, "hep-server", "S", "", "hep server to duplicate: i.e. 10.0.0.1") 96 | rootCmd.PersistentFlags().StringVarP(&globalFlags.HepPort, "hep-port", "P", "9060", "hep port - default 9060") 97 | rootCmd.PersistentFlags().StringVarP(&globalFlags.HepTransport, "hep-transport", "T", "udp", "hep transport default udp. Can be udp, tcp, tls") 98 | 99 | } 100 | -------------------------------------------------------------------------------- /cli/cmd/tcprtt.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | LINK - http://github.com/sipcapture/rtcagent 6 | 7 | Copyright (C) 2023 QXIP B.V. 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Affero General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Affero General Public License for more details. 18 | 19 | You should have received a copy of the GNU Affero General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "log" 28 | "os" 29 | "os/signal" 30 | "rtcagent/hepclient" 31 | "rtcagent/hepclient/hepsender" 32 | "rtcagent/user/config" 33 | "rtcagent/user/module" 34 | "strings" 35 | "syscall" 36 | 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | var tcprttConfig = config.NewTcprttConfig() 41 | 42 | // tcprttCmd represents the tcprtt command 43 | var tcprttCmd = &cobra.Command{ 44 | Use: "tcprtt", 45 | Short: "show tcp rtt", 46 | Long: ` Tested on linux`, 47 | Run: tcprttCommandFunc, 48 | } 49 | 50 | func init() { 51 | tcprttCmd.PersistentFlags().StringVarP(&tcprttConfig.Tcprttpath, "tcprtt", "m", "", "tcprtt binary file path, use to hook") 52 | rootCmd.AddCommand(tcprttCmd) 53 | } 54 | 55 | // tcprttCommandFunc executes the "tcprtt" command. 56 | func tcprttCommandFunc(command *cobra.Command, args []string) { 57 | stopper := make(chan os.Signal, 1) 58 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 59 | ctx, cancelFun := context.WithCancel(context.TODO()) 60 | 61 | mod := module.GetModuleByName(module.ModuleNameTcprtt) 62 | 63 | logger := log.New(os.Stdout, "tcprtt_", log.LstdFlags) 64 | logger.Printf("RTCAGENT :: version :%s", GitVersion) 65 | logger.Printf("RTCAGENT :: start to run %s module", mod.Name()) 66 | 67 | // save global config 68 | gConf, e := getGlobalConf(command) 69 | if e != nil { 70 | logger.Fatal(e) 71 | os.Exit(1) 72 | } 73 | tcprttConfig.Pid = gConf.Pid 74 | tcprttConfig.Debug = gConf.Debug 75 | tcprttConfig.IsHex = gConf.IsHex 76 | 77 | if (gConf.HepServer != "" || len(strings.TrimSpace(gConf.HepServer)) > 0) && hepsender.Hepsender == nil { 78 | 79 | log.Println("HEP client will be started") 80 | 81 | var err error 82 | hepsender.Hepsender, err = hepclient.NewHepClient(gConf.HepServer, gConf.HepPort, gConf.HepTransport) 83 | if err != nil { 84 | log.Fatalf("HEP client couldn't be init: addr:%s, port: %s, transport: %s. Error: %s", gConf.HepServer, gConf.HepPort, gConf.HepTransport, err.Error()) 85 | os.Exit(1) 86 | } else { 87 | log.Println("HEP client started") 88 | } 89 | } 90 | 91 | log.Printf("RTCAGENT :: pid info :%d -%s", os.Getpid(), gConf.HepServer) 92 | //bc.Pid = globalFlags.Pid 93 | if e := tcprttConfig.Check(); e != nil { 94 | logger.Fatal(e) 95 | os.Exit(1) 96 | } 97 | 98 | err := mod.Init(ctx, logger, tcprttConfig) 99 | if err != nil { 100 | logger.Fatal(err) 101 | os.Exit(1) 102 | } 103 | 104 | go func(module module.IModule) { 105 | err := module.Run() 106 | if err != nil { 107 | logger.Fatalf("%v", err) 108 | } 109 | }(mod) 110 | <-stopper 111 | cancelFun() 112 | os.Exit(0) 113 | } 114 | -------------------------------------------------------------------------------- /cli/cobrautl/help.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package cobrautl 23 | 24 | import ( 25 | "bytes" 26 | "fmt" 27 | "io" 28 | "os" 29 | "strings" 30 | "text/tabwriter" 31 | "text/template" 32 | 33 | "github.com/spf13/cobra" 34 | "github.com/spf13/pflag" 35 | ) 36 | 37 | var ( 38 | commandUsageTemplate *template.Template 39 | templFuncs = template.FuncMap{ 40 | "descToLines": func(s string) []string { 41 | // trim leading/trailing whitespace and split into slice of lines 42 | return strings.Split(strings.Trim(s, "\n\t "), "\n") 43 | }, 44 | "cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string { 45 | parts := []string{cmd.Name()} 46 | for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() { 47 | cmd = cmd.Parent() 48 | parts = append([]string{cmd.Name()}, parts...) 49 | } 50 | return strings.Join(parts, " ") 51 | }, 52 | } 53 | ) 54 | 55 | func init() { 56 | commandUsage := ` 57 | {{ $cmd := .Cmd }}\ 58 | {{ $cmdname := cmdName .Cmd .Cmd.Root }}\ 59 | NAME: 60 | {{ if not .Cmd.HasParent }}\ 61 | {{printf "\t%s - %s" .Cmd.Name .Cmd.Short}} 62 | {{else}}\ 63 | {{printf "\t%s - %s" $cmdname .Cmd.Short}} 64 | {{end}}\ 65 | 66 | USAGE: 67 | {{printf "\t%s" .Cmd.UseLine}} 68 | {{ if not .Cmd.HasParent }}\ 69 | 70 | VERSION: 71 | {{printf "\t%s" .Version}} 72 | {{end}}\ 73 | {{if .Cmd.HasSubCommands}}\ 74 | 75 | COMMANDS: 76 | {{range .SubCommands}}\ 77 | {{ $cmdname := cmdName . $cmd }}\ 78 | {{ if .Runnable }}\ 79 | {{printf "\t%s\t%s" $cmdname .Short}} 80 | {{end}}\ 81 | {{end}}\ 82 | {{end}}\ 83 | {{ if .Cmd.Long }}\ 84 | 85 | DESCRIPTION: 86 | {{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}} 87 | {{end}}\ 88 | {{end}}\ 89 | {{if .Cmd.HasLocalFlags}}\ 90 | 91 | OPTIONS: 92 | {{.LocalFlags}}\ 93 | {{end}}\ 94 | {{if .Cmd.HasInheritedFlags}}\ 95 | 96 | GLOBAL OPTIONS: 97 | {{.GlobalFlags}}\ 98 | {{end}} 99 | `[1:] 100 | 101 | commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1))) 102 | } 103 | 104 | func rtcagentFlagUsages(flagSet *pflag.FlagSet) string { 105 | x := new(bytes.Buffer) 106 | 107 | flagSet.VisitAll(func(flag *pflag.Flag) { 108 | if len(flag.Deprecated) > 0 { 109 | return 110 | } 111 | var format string 112 | if len(flag.Shorthand) > 0 { 113 | format = " -%s, --%s" 114 | } else { 115 | format = " %s --%s" 116 | } 117 | if len(flag.NoOptDefVal) > 0 { 118 | format = format + "[" 119 | } 120 | if flag.Value.Type() == "string" { 121 | // put quotes on the value 122 | format = format + "=%q" 123 | } else { 124 | format = format + "=%s" 125 | } 126 | if len(flag.NoOptDefVal) > 0 { 127 | format = format + "]" 128 | } 129 | format = format + "\t%s\n" 130 | shorthand := flag.Shorthand 131 | fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage) 132 | }) 133 | 134 | return x.String() 135 | } 136 | 137 | func getSubCommands(cmd *cobra.Command) []*cobra.Command { 138 | var subCommands []*cobra.Command 139 | for _, subCmd := range cmd.Commands() { 140 | subCommands = append(subCommands, subCmd) 141 | subCommands = append(subCommands, getSubCommands(subCmd)...) 142 | } 143 | return subCommands 144 | } 145 | 146 | func UsageFunc(cmd *cobra.Command, version string) error { 147 | subCommands := getSubCommands(cmd) 148 | tabOut := getTabOutWithWriter(os.Stdout) 149 | err := commandUsageTemplate.Execute(tabOut, struct { 150 | Cmd *cobra.Command 151 | LocalFlags string 152 | GlobalFlags string 153 | SubCommands []*cobra.Command 154 | Version string 155 | }{ 156 | cmd, 157 | rtcagentFlagUsages(cmd.LocalFlags()), 158 | rtcagentFlagUsages(cmd.InheritedFlags()), 159 | subCommands, 160 | version, 161 | }) 162 | if err != nil { 163 | return err 164 | } 165 | err = tabOut.Flush() 166 | return err 167 | } 168 | 169 | func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer { 170 | aTabOut := new(tabwriter.Writer) 171 | aTabOut.Init(writer, 0, 8, 1, '\t', 0) 172 | return aTabOut 173 | } 174 | -------------------------------------------------------------------------------- /cli/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package cli 23 | 24 | import ( 25 | "rtcagent/cli/cmd" 26 | ) 27 | 28 | func Start() { 29 | cmd.Execute() 30 | } 31 | -------------------------------------------------------------------------------- /dump.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/dump.pcap -------------------------------------------------------------------------------- /examples/Dockerfile.kamailio: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/kamailio/kamailio:5.8.0-bookworm 2 | ENV SHM_MEMORY=${SHM_MEMORY:-64} 3 | ENV PKG_MEMORY=${PKG_MEMORY:-8} 4 | RUN apt update && apt install wget \ 5 | && wget https://github.com/sipcapture/rtcagent/releases/latest/download/rtcagent \ 6 | && chmod +x /rtcagent \ 7 | && apt-get clean && rm -rf /var/lib/apt/lists/* 8 | 9 | ENTRYPOINT kamailio -DD -E -m ${SHM_MEMORY} -M ${PKG_MEMORY} & /rtcagent kamailio -m /usr/sbin/kamailio 10 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module rtcagent 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require ( 8 | github.com/VictoriaMetrics/fastcache v1.12.2 9 | github.com/adubovikov/ebpfmanager v0.4.7 10 | github.com/buger/goterm v1.0.4 11 | github.com/cilium/ebpf v0.12.3 12 | github.com/gogo/protobuf v1.3.2 13 | github.com/prometheus/client_golang v1.19.0 14 | github.com/shuLhan/go-bindata v4.0.0+incompatible 15 | github.com/spf13/cobra v1.4.0 16 | github.com/spf13/pflag v1.0.5 17 | golang.org/x/crypto v0.21.0 18 | golang.org/x/sys v0.18.0 19 | ) 20 | 21 | require ( 22 | github.com/avast/retry-go v3.0.0+incompatible // indirect 23 | github.com/beorn7/perks v1.0.1 // indirect 24 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 25 | github.com/florianl/go-tc v0.4.0 // indirect 26 | github.com/golang/snappy v0.0.4 // indirect 27 | github.com/google/go-cmp v0.6.0 // indirect 28 | github.com/hashicorp/errwrap v1.0.0 // indirect 29 | github.com/hashicorp/go-multierror v1.1.1 // indirect 30 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 31 | github.com/josharian/native v1.0.0 // indirect 32 | github.com/mdlayher/netlink v1.7.1 // indirect 33 | github.com/mdlayher/socket v0.4.0 // indirect 34 | github.com/prometheus/client_model v0.5.0 // indirect 35 | github.com/prometheus/common v0.48.0 // indirect 36 | github.com/prometheus/procfs v0.12.0 // indirect 37 | github.com/stretchr/testify v1.7.0 // indirect 38 | github.com/vishvananda/netlink v1.1.0 // indirect 39 | github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect 40 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect 41 | golang.org/x/net v0.23.0 // indirect 42 | golang.org/x/sync v0.3.0 // indirect 43 | google.golang.org/protobuf v1.33.0 // indirect 44 | ) 45 | 46 | replace github.com/google/gopacket v1.1.19 => github.com/cfc4n/gopacket v1.1.20 47 | -------------------------------------------------------------------------------- /hepclient/hepclient.go: -------------------------------------------------------------------------------- 1 | package hepclient 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "fmt" 7 | "log" 8 | "net" 9 | "strings" 10 | "unicode" 11 | ) 12 | 13 | type HEPConn struct { 14 | conn net.Conn 15 | writer *bufio.Writer 16 | errCnt uint 17 | } 18 | type HepClient struct { 19 | hepQueue chan []byte 20 | addr string 21 | transport string 22 | client HEPConn 23 | } 24 | 25 | func NewHepClient(addr, port, trans string) (*HepClient, error) { 26 | 27 | h := &HepClient{ 28 | addr: strings.ToLower(cutSpace(addr + ":" + port)), 29 | client: HEPConn{}, 30 | transport: trans, 31 | hepQueue: make(chan []byte, 20000), 32 | } 33 | 34 | if err := h.ConnectServer(); err != nil { 35 | log.Fatalf("Error: %s", err.Error()) 36 | return nil, fmt.Errorf("cannot establish a connection") 37 | } 38 | 39 | go h.Start() 40 | return h, nil 41 | } 42 | 43 | func (h *HepClient) Close() { 44 | if err := h.client.conn.Close(); err != nil { 45 | log.Fatalf("cannnot close connection to %s: %v", h.addr, err) 46 | } 47 | } 48 | 49 | func (h *HepClient) ReConnect() (err error) { 50 | if err = h.ConnectServer(); err != nil { 51 | return err 52 | } 53 | h.client.writer.Reset(h.client.conn) 54 | return err 55 | } 56 | 57 | func (h *HepClient) ConnectServer() (err error) { 58 | if h.transport == "udp" { 59 | 60 | if h.client.conn, err = net.Dial("udp", h.addr); err != nil { 61 | return fmt.Errorf("dial transport failed: %s", err.Error()) 62 | } 63 | } else if h.transport == "tcp" { 64 | 65 | if h.client.conn, err = net.Dial("tcp", h.addr); err != nil { 66 | return fmt.Errorf("dial transport failed: %s", err.Error()) 67 | } 68 | } else if h.transport == "tls" { 69 | if h.client.conn, err = tls.Dial("tcp", h.addr, &tls.Config{InsecureSkipVerify: true}); err != nil { 70 | return fmt.Errorf("dial transport failed: %s", err.Error()) 71 | } 72 | } else { 73 | return fmt.Errorf("unsupported transport: %s", h.transport) 74 | } 75 | 76 | h.client.writer = bufio.NewWriterSize(h.client.conn, 8192) 77 | return err 78 | } 79 | 80 | func (h *HepClient) Output(msg []byte) { 81 | h.hepQueue <- msg 82 | } 83 | 84 | func (h *HepClient) Send(msg []byte) { 85 | h.client.writer.Write(msg) 86 | err := h.client.writer.Flush() 87 | if err != nil { 88 | log.Fatal("%v", err) 89 | h.client.errCnt++ 90 | retry := true 91 | if retry { 92 | h.client.errCnt = 0 93 | if err = h.ReConnect(); err != nil { 94 | log.Fatalf("reconnect error: %v", err) 95 | return 96 | } 97 | } 98 | } 99 | } 100 | 101 | func (h *HepClient) Start() { 102 | for msg := range h.hepQueue { 103 | h.Send(msg) 104 | } 105 | } 106 | 107 | func cutSpace(str string) string { 108 | return strings.Map(func(r rune) rune { 109 | if unicode.IsSpace(r) { 110 | return -1 111 | } 112 | return r 113 | }, str) 114 | } 115 | -------------------------------------------------------------------------------- /hepclient/hepsender/hepsender.go: -------------------------------------------------------------------------------- 1 | package hepsender 2 | 3 | import "rtcagent/hepclient" 4 | 5 | var Hepsender *hepclient.HepClient 6 | -------------------------------------------------------------------------------- /kern/bpf.old/arm64/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_510.h -------------------------------------------------------------------------------- /kern/bpf.old/bpf_endian.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_ENDIAN__ 3 | #define __BPF_ENDIAN__ 4 | 5 | /* 6 | * Isolate byte #n and put it into byte #m, for __u##b type. 7 | * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64: 8 | * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 9 | * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000 10 | * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 11 | * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000 12 | */ 13 | #define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8)) 14 | 15 | #define ___bpf_swab16(x) ((__u16)( \ 16 | ___bpf_mvb(x, 16, 0, 1) | \ 17 | ___bpf_mvb(x, 16, 1, 0))) 18 | 19 | #define ___bpf_swab32(x) ((__u32)( \ 20 | ___bpf_mvb(x, 32, 0, 3) | \ 21 | ___bpf_mvb(x, 32, 1, 2) | \ 22 | ___bpf_mvb(x, 32, 2, 1) | \ 23 | ___bpf_mvb(x, 32, 3, 0))) 24 | 25 | #define ___bpf_swab64(x) ((__u64)( \ 26 | ___bpf_mvb(x, 64, 0, 7) | \ 27 | ___bpf_mvb(x, 64, 1, 6) | \ 28 | ___bpf_mvb(x, 64, 2, 5) | \ 29 | ___bpf_mvb(x, 64, 3, 4) | \ 30 | ___bpf_mvb(x, 64, 4, 3) | \ 31 | ___bpf_mvb(x, 64, 5, 2) | \ 32 | ___bpf_mvb(x, 64, 6, 1) | \ 33 | ___bpf_mvb(x, 64, 7, 0))) 34 | 35 | /* LLVM's BPF target selects the endianness of the CPU 36 | * it compiles on, or the user specifies (bpfel/bpfeb), 37 | * respectively. The used __BYTE_ORDER__ is defined by 38 | * the compiler, we cannot rely on __BYTE_ORDER from 39 | * libc headers, since it doesn't reflect the actual 40 | * requested byte order. 41 | * 42 | * Note, LLVM's BPF target has different __builtin_bswapX() 43 | * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE 44 | * in bpfel and bpfeb case, which means below, that we map 45 | * to cpu_to_be16(). We could use it unconditionally in BPF 46 | * case, but better not rely on it, so that this header here 47 | * can be used from application and BPF program side, which 48 | * use different targets. 49 | */ 50 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 51 | # define __bpf_ntohs(x) __builtin_bswap16(x) 52 | # define __bpf_htons(x) __builtin_bswap16(x) 53 | # define __bpf_constant_ntohs(x) ___bpf_swab16(x) 54 | # define __bpf_constant_htons(x) ___bpf_swab16(x) 55 | # define __bpf_ntohl(x) __builtin_bswap32(x) 56 | # define __bpf_htonl(x) __builtin_bswap32(x) 57 | # define __bpf_constant_ntohl(x) ___bpf_swab32(x) 58 | # define __bpf_constant_htonl(x) ___bpf_swab32(x) 59 | # define __bpf_be64_to_cpu(x) __builtin_bswap64(x) 60 | # define __bpf_cpu_to_be64(x) __builtin_bswap64(x) 61 | # define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x) 62 | # define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x) 63 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 64 | # define __bpf_ntohs(x) (x) 65 | # define __bpf_htons(x) (x) 66 | # define __bpf_constant_ntohs(x) (x) 67 | # define __bpf_constant_htons(x) (x) 68 | # define __bpf_ntohl(x) (x) 69 | # define __bpf_htonl(x) (x) 70 | # define __bpf_constant_ntohl(x) (x) 71 | # define __bpf_constant_htonl(x) (x) 72 | # define __bpf_be64_to_cpu(x) (x) 73 | # define __bpf_cpu_to_be64(x) (x) 74 | # define __bpf_constant_be64_to_cpu(x) (x) 75 | # define __bpf_constant_cpu_to_be64(x) (x) 76 | #else 77 | # error "Fix your compiler's __BYTE_ORDER__?!" 78 | #endif 79 | 80 | #define bpf_htons(x) \ 81 | (__builtin_constant_p(x) ? \ 82 | __bpf_constant_htons(x) : __bpf_htons(x)) 83 | #define bpf_ntohs(x) \ 84 | (__builtin_constant_p(x) ? \ 85 | __bpf_constant_ntohs(x) : __bpf_ntohs(x)) 86 | #define bpf_htonl(x) \ 87 | (__builtin_constant_p(x) ? \ 88 | __bpf_constant_htonl(x) : __bpf_htonl(x)) 89 | #define bpf_ntohl(x) \ 90 | (__builtin_constant_p(x) ? \ 91 | __bpf_constant_ntohl(x) : __bpf_ntohl(x)) 92 | #define bpf_cpu_to_be64(x) \ 93 | (__builtin_constant_p(x) ? \ 94 | __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x)) 95 | #define bpf_be64_to_cpu(x) \ 96 | (__builtin_constant_p(x) ? \ 97 | __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x)) 98 | 99 | #endif /* __BPF_ENDIAN__ */ 100 | -------------------------------------------------------------------------------- /kern/bpf.old/bpf_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_HELPERS__ 3 | #define __BPF_HELPERS__ 4 | 5 | /* 6 | * Note that bpf programs need to include either 7 | * vmlinux.h (auto-generated from BTF) or linux/types.h 8 | * in advance since bpf_helper_defs.h uses such types 9 | * as __u64. 10 | */ 11 | #include "bpf_helper_defs.h" 12 | 13 | #define __uint(name, val) int (*name)[val] 14 | #define __type(name, val) typeof(val) *name 15 | #define __array(name, val) typeof(val) *name[] 16 | 17 | /* Helper macro to print out debug messages */ 18 | #define bpf_printk(fmt, ...) \ 19 | ({ \ 20 | char ____fmt[] = fmt; \ 21 | bpf_trace_printk(____fmt, sizeof(____fmt), \ 22 | ##__VA_ARGS__); \ 23 | }) 24 | 25 | /* 26 | * Helper macro to place programs, maps, license in 27 | * different sections in elf_bpf file. Section names 28 | * are interpreted by libbpf depending on the context (BPF programs, BPF maps, 29 | * extern variables, etc). 30 | * To allow use of SEC() with externs (e.g., for extern .maps declarations), 31 | * make sure __attribute__((unused)) doesn't trigger compilation warning. 32 | */ 33 | #define SEC(name) \ 34 | _Pragma("GCC diagnostic push") \ 35 | _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ 36 | __attribute__((section(name), used)) \ 37 | _Pragma("GCC diagnostic pop") \ 38 | 39 | /* Avoid 'linux/stddef.h' definition of '__always_inline'. */ 40 | #undef __always_inline 41 | #define __always_inline inline __attribute__((always_inline)) 42 | 43 | #ifndef __noinline 44 | #define __noinline __attribute__((noinline)) 45 | #endif 46 | #ifndef __weak 47 | #define __weak __attribute__((weak)) 48 | #endif 49 | 50 | /* 51 | * Use __hidden attribute to mark a non-static BPF subprogram effectively 52 | * static for BPF verifier's verification algorithm purposes, allowing more 53 | * extensive and permissive BPF verification process, taking into account 54 | * subprogram's caller context. 55 | */ 56 | #define __hidden __attribute__((visibility("hidden"))) 57 | 58 | /* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include 59 | * any system-level headers (such as stddef.h, linux/version.h, etc), and 60 | * commonly-used macros like NULL and KERNEL_VERSION aren't available through 61 | * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define 62 | * them on their own. So as a convenience, provide such definitions here. 63 | */ 64 | #ifndef NULL 65 | #define NULL ((void *)0) 66 | #endif 67 | 68 | #ifndef KERNEL_VERSION 69 | #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) 70 | #endif 71 | 72 | /* 73 | * Helper macros to manipulate data structures 74 | */ 75 | #ifndef offsetof 76 | #define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) 77 | #endif 78 | #ifndef container_of 79 | #define container_of(ptr, type, member) \ 80 | ({ \ 81 | void *__mptr = (void *)(ptr); \ 82 | ((type *)(__mptr - offsetof(type, member))); \ 83 | }) 84 | #endif 85 | 86 | /* 87 | * Helper macro to throw a compilation error if __bpf_unreachable() gets 88 | * built into the resulting code. This works given BPF back end does not 89 | * implement __builtin_trap(). This is useful to assert that certain paths 90 | * of the program code are never used and hence eliminated by the compiler. 91 | * 92 | * For example, consider a switch statement that covers known cases used by 93 | * the program. __bpf_unreachable() can then reside in the default case. If 94 | * the program gets extended such that a case is not covered in the switch 95 | * statement, then it will throw a build error due to the default case not 96 | * being compiled out. 97 | */ 98 | #ifndef __bpf_unreachable 99 | # define __bpf_unreachable() __builtin_trap() 100 | #endif 101 | 102 | /* 103 | * Helper function to perform a tail call with a constant/immediate map slot. 104 | */ 105 | #if __clang_major__ >= 8 && defined(__bpf__) 106 | static __always_inline void 107 | bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) 108 | { 109 | if (!__builtin_constant_p(slot)) 110 | __bpf_unreachable(); 111 | 112 | /* 113 | * Provide a hard guarantee that LLVM won't optimize setting r2 (map 114 | * pointer) and r3 (constant map index) from _different paths_ ending 115 | * up at the _same_ call insn as otherwise we won't be able to use the 116 | * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel 117 | * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key 118 | * tracking for prog array pokes") for details on verifier tracking. 119 | * 120 | * Note on clobber list: we need to stay in-line with BPF calling 121 | * convention, so even if we don't end up using r0, r4, r5, we need 122 | * to mark them as clobber so that LLVM doesn't end up using them 123 | * before / after the call. 124 | */ 125 | asm volatile("r1 = %[ctx]\n\t" 126 | "r2 = %[map]\n\t" 127 | "r3 = %[slot]\n\t" 128 | "call 12" 129 | :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) 130 | : "r0", "r1", "r2", "r3", "r4", "r5"); 131 | } 132 | #endif 133 | 134 | /* 135 | * Helper structure used by eBPF C program 136 | * to describe BPF map attributes to libbpf loader 137 | */ 138 | struct bpf_map_def { 139 | unsigned int type; 140 | unsigned int key_size; 141 | unsigned int value_size; 142 | unsigned int max_entries; 143 | unsigned int map_flags; 144 | }; 145 | 146 | enum libbpf_pin_type { 147 | LIBBPF_PIN_NONE, 148 | /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ 149 | LIBBPF_PIN_BY_NAME, 150 | }; 151 | 152 | enum libbpf_tristate { 153 | TRI_NO = 0, 154 | TRI_YES = 1, 155 | TRI_MODULE = 2, 156 | }; 157 | 158 | #define __kconfig __attribute__((section(".kconfig"))) 159 | #define __ksym __attribute__((section(".ksyms"))) 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /kern/bpf.old/x86/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/kern/bpf.old/x86/.gitkeep -------------------------------------------------------------------------------- /kern/bpf/arm64/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_510.h -------------------------------------------------------------------------------- /kern/bpf/bpf_endian.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_ENDIAN__ 3 | #define __BPF_ENDIAN__ 4 | 5 | /* 6 | * Isolate byte #n and put it into byte #m, for __u##b type. 7 | * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64: 8 | * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 9 | * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000 10 | * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 11 | * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000 12 | */ 13 | #define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8)) 14 | 15 | #define ___bpf_swab16(x) ((__u16)( \ 16 | ___bpf_mvb(x, 16, 0, 1) | \ 17 | ___bpf_mvb(x, 16, 1, 0))) 18 | 19 | #define ___bpf_swab32(x) ((__u32)( \ 20 | ___bpf_mvb(x, 32, 0, 3) | \ 21 | ___bpf_mvb(x, 32, 1, 2) | \ 22 | ___bpf_mvb(x, 32, 2, 1) | \ 23 | ___bpf_mvb(x, 32, 3, 0))) 24 | 25 | #define ___bpf_swab64(x) ((__u64)( \ 26 | ___bpf_mvb(x, 64, 0, 7) | \ 27 | ___bpf_mvb(x, 64, 1, 6) | \ 28 | ___bpf_mvb(x, 64, 2, 5) | \ 29 | ___bpf_mvb(x, 64, 3, 4) | \ 30 | ___bpf_mvb(x, 64, 4, 3) | \ 31 | ___bpf_mvb(x, 64, 5, 2) | \ 32 | ___bpf_mvb(x, 64, 6, 1) | \ 33 | ___bpf_mvb(x, 64, 7, 0))) 34 | 35 | /* LLVM's BPF target selects the endianness of the CPU 36 | * it compiles on, or the user specifies (bpfel/bpfeb), 37 | * respectively. The used __BYTE_ORDER__ is defined by 38 | * the compiler, we cannot rely on __BYTE_ORDER from 39 | * libc headers, since it doesn't reflect the actual 40 | * requested byte order. 41 | * 42 | * Note, LLVM's BPF target has different __builtin_bswapX() 43 | * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE 44 | * in bpfel and bpfeb case, which means below, that we map 45 | * to cpu_to_be16(). We could use it unconditionally in BPF 46 | * case, but better not rely on it, so that this header here 47 | * can be used from application and BPF program side, which 48 | * use different targets. 49 | */ 50 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 51 | # define __bpf_ntohs(x) __builtin_bswap16(x) 52 | # define __bpf_htons(x) __builtin_bswap16(x) 53 | # define __bpf_constant_ntohs(x) ___bpf_swab16(x) 54 | # define __bpf_constant_htons(x) ___bpf_swab16(x) 55 | # define __bpf_ntohl(x) __builtin_bswap32(x) 56 | # define __bpf_htonl(x) __builtin_bswap32(x) 57 | # define __bpf_constant_ntohl(x) ___bpf_swab32(x) 58 | # define __bpf_constant_htonl(x) ___bpf_swab32(x) 59 | # define __bpf_be64_to_cpu(x) __builtin_bswap64(x) 60 | # define __bpf_cpu_to_be64(x) __builtin_bswap64(x) 61 | # define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x) 62 | # define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x) 63 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 64 | # define __bpf_ntohs(x) (x) 65 | # define __bpf_htons(x) (x) 66 | # define __bpf_constant_ntohs(x) (x) 67 | # define __bpf_constant_htons(x) (x) 68 | # define __bpf_ntohl(x) (x) 69 | # define __bpf_htonl(x) (x) 70 | # define __bpf_constant_ntohl(x) (x) 71 | # define __bpf_constant_htonl(x) (x) 72 | # define __bpf_be64_to_cpu(x) (x) 73 | # define __bpf_cpu_to_be64(x) (x) 74 | # define __bpf_constant_be64_to_cpu(x) (x) 75 | # define __bpf_constant_cpu_to_be64(x) (x) 76 | #else 77 | # error "Fix your compiler's __BYTE_ORDER__?!" 78 | #endif 79 | 80 | #define bpf_htons(x) \ 81 | (__builtin_constant_p(x) ? \ 82 | __bpf_constant_htons(x) : __bpf_htons(x)) 83 | #define bpf_ntohs(x) \ 84 | (__builtin_constant_p(x) ? \ 85 | __bpf_constant_ntohs(x) : __bpf_ntohs(x)) 86 | #define bpf_htonl(x) \ 87 | (__builtin_constant_p(x) ? \ 88 | __bpf_constant_htonl(x) : __bpf_htonl(x)) 89 | #define bpf_ntohl(x) \ 90 | (__builtin_constant_p(x) ? \ 91 | __bpf_constant_ntohl(x) : __bpf_ntohl(x)) 92 | #define bpf_cpu_to_be64(x) \ 93 | (__builtin_constant_p(x) ? \ 94 | __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x)) 95 | #define bpf_be64_to_cpu(x) \ 96 | (__builtin_constant_p(x) ? \ 97 | __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x)) 98 | 99 | #endif /* __BPF_ENDIAN__ */ 100 | -------------------------------------------------------------------------------- /kern/bpf/x86/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/kern/bpf/x86/.gitkeep -------------------------------------------------------------------------------- /kern/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | #ifndef RTCAGENT_COMMON_H 23 | #define RTCAGENT_COMMON_H 24 | 25 | #ifdef DEBUG_PRINT 26 | #define debug_bpf_printk(fmt, ...) \ 27 | do { \ 28 | char s[] = fmt; \ 29 | bpf_trace_printk(s, sizeof(s), ##__VA_ARGS__); \ 30 | } while (0) 31 | #else 32 | #define debug_bpf_printk(fmt, ...) 33 | #endif 34 | 35 | #define TASK_COMM_LEN 16 36 | #define MAX_DATA_SIZE_OPENSSL 1024 * 4 37 | #define MAX_DATA_SIZE_MYSQL 256 38 | #define MAX_DATA_SIZE_POSTGRES 256 39 | #define MAX_DATA_SIZE_BASH 256 40 | 41 | // enum_server_command, via 42 | #define COM_QUERY 3 43 | 44 | #define AF_INET 2 45 | #define AF_INET6 10 46 | #define SA_DATA_LEN 14 47 | #define BASH_ERRNO_DEFAULT 128 48 | 49 | ///////// for TC & XDP ebpf programs in tc.h 50 | #define TC_ACT_OK 0 51 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 52 | #define SKB_MAX_DATA_SIZE 2048 53 | 54 | #ifndef KERNEL_LESS_5_2 55 | // alawyse, we used it in tc.h 56 | const volatile u64 target_port = 443; 57 | 58 | // Optional Target PID and UID 59 | const volatile u64 target_pid = 0; 60 | const volatile u64 target_uid = 0; 61 | const volatile u64 target_errno = BASH_ERRNO_DEFAULT; 62 | #else 63 | #endif 64 | 65 | char __license[] SEC("license") = "Dual MIT/GPL"; 66 | __u32 _version SEC("version") = 0xFFFFFFFE; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /kern/common2.h: -------------------------------------------------------------------------------- 1 | // This is a compact version of `vmlinux.h` to be used in the examples using C code. 2 | 3 | #pragma once 4 | 5 | typedef unsigned char __u8; 6 | typedef short int __s16; 7 | typedef short unsigned int __u16; 8 | typedef int __s32; 9 | typedef unsigned int __u32; 10 | typedef long long int __s64; 11 | typedef long long unsigned int __u64; 12 | typedef __u8 u8; 13 | typedef __s16 s16; 14 | typedef __u16 u16; 15 | typedef __s32 s32; 16 | typedef __u32 u32; 17 | typedef __s64 s64; 18 | typedef __u64 u64; 19 | typedef __u16 __le16; 20 | typedef __u16 __be16; 21 | typedef __u32 __be32; 22 | typedef __u64 __be64; 23 | typedef __u32 __wsum; 24 | 25 | #include "bpf/bpf_helpers.h" 26 | 27 | enum bpf_map_type { 28 | BPF_MAP_TYPE_UNSPEC = 0, 29 | BPF_MAP_TYPE_HASH = 1, 30 | BPF_MAP_TYPE_ARRAY = 2, 31 | BPF_MAP_TYPE_PROG_ARRAY = 3, 32 | BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, 33 | BPF_MAP_TYPE_PERCPU_HASH = 5, 34 | BPF_MAP_TYPE_PERCPU_ARRAY = 6, 35 | BPF_MAP_TYPE_STACK_TRACE = 7, 36 | BPF_MAP_TYPE_CGROUP_ARRAY = 8, 37 | BPF_MAP_TYPE_LRU_HASH = 9, 38 | BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, 39 | BPF_MAP_TYPE_LPM_TRIE = 11, 40 | BPF_MAP_TYPE_ARRAY_OF_MAPS = 12, 41 | BPF_MAP_TYPE_HASH_OF_MAPS = 13, 42 | BPF_MAP_TYPE_DEVMAP = 14, 43 | BPF_MAP_TYPE_SOCKMAP = 15, 44 | BPF_MAP_TYPE_CPUMAP = 16, 45 | BPF_MAP_TYPE_XSKMAP = 17, 46 | BPF_MAP_TYPE_SOCKHASH = 18, 47 | BPF_MAP_TYPE_CGROUP_STORAGE = 19, 48 | BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20, 49 | BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21, 50 | BPF_MAP_TYPE_QUEUE = 22, 51 | BPF_MAP_TYPE_STACK = 23, 52 | BPF_MAP_TYPE_SK_STORAGE = 24, 53 | BPF_MAP_TYPE_DEVMAP_HASH = 25, 54 | BPF_MAP_TYPE_STRUCT_OPS = 26, 55 | BPF_MAP_TYPE_RINGBUF = 27, 56 | BPF_MAP_TYPE_INODE_STORAGE = 28, 57 | }; 58 | 59 | enum xdp_action { 60 | XDP_ABORTED = 0, 61 | XDP_DROP = 1, 62 | XDP_PASS = 2, 63 | XDP_TX = 3, 64 | XDP_REDIRECT = 4, 65 | }; 66 | 67 | struct xdp_md { 68 | __u32 data; 69 | __u32 data_end; 70 | __u32 data_meta; 71 | __u32 ingress_ifindex; 72 | __u32 rx_queue_index; 73 | __u32 egress_ifindex; 74 | }; 75 | 76 | typedef __u16 __sum16; 77 | 78 | #define ETH_P_IP 0x0800 79 | 80 | struct ethhdr { 81 | unsigned char h_dest[6]; 82 | unsigned char h_source[6]; 83 | __be16 h_proto; 84 | }; 85 | 86 | struct iphdr { 87 | __u8 ihl: 4; 88 | __u8 version: 4; 89 | __u8 tos; 90 | __be16 tot_len; 91 | __be16 id; 92 | __be16 frag_off; 93 | __u8 ttl; 94 | __u8 protocol; 95 | __sum16 check; 96 | __be32 saddr; 97 | __be32 daddr; 98 | }; 99 | 100 | enum { 101 | BPF_ANY = 0, 102 | BPF_NOEXIST = 1, 103 | BPF_EXIST = 2, 104 | BPF_F_LOCK = 4, 105 | }; 106 | 107 | /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and 108 | * BPF_FUNC_perf_event_read_value flags. 109 | */ 110 | #define BPF_F_INDEX_MASK 0xffffffffULL 111 | #define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK 112 | 113 | #if defined(__TARGET_ARCH_x86) 114 | struct pt_regs { 115 | /* 116 | * C ABI says these regs are callee-preserved. They aren't saved on kernel entry 117 | * unless syscall needs a complete, fully filled "struct pt_regs". 118 | */ 119 | unsigned long r15; 120 | unsigned long r14; 121 | unsigned long r13; 122 | unsigned long r12; 123 | unsigned long rbp; 124 | unsigned long rbx; 125 | /* These regs are callee-clobbered. Always saved on kernel entry. */ 126 | unsigned long r11; 127 | unsigned long r10; 128 | unsigned long r9; 129 | unsigned long r8; 130 | unsigned long rax; 131 | unsigned long rcx; 132 | unsigned long rdx; 133 | unsigned long rsi; 134 | unsigned long rdi; 135 | /* 136 | * On syscall entry, this is syscall#. On CPU exception, this is error code. 137 | * On hw interrupt, it's IRQ number: 138 | */ 139 | unsigned long orig_rax; 140 | /* Return frame for iretq */ 141 | unsigned long rip; 142 | unsigned long cs; 143 | unsigned long eflags; 144 | unsigned long rsp; 145 | unsigned long ss; 146 | /* top of stack page */ 147 | }; 148 | #endif /* __TARGET_ARCH_x86 */ 149 | -------------------------------------------------------------------------------- /kern/kamailio.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | 23 | #include "rtcagent.h" 24 | 25 | #define _SYS_SOCKET_H 1 26 | #define IP4_MAX_STR_SIZE 15 27 | #define IP6_MAX_STR_SIZE 45 28 | #define IP_ADDR_MAX_STR_SIZE 6 /* ip62ascii + \0*/ 29 | #define SS_MAXSIZE 128 /* Implementation specific max size */ 30 | #define MAX_DATA_SIZE_SIP 1024*4 31 | 32 | typedef unsigned short sa_family_t; 33 | 34 | typedef struct _str 35 | { 36 | char *s; 37 | int len; 38 | } str; 39 | 40 | struct sockaddr_storage 41 | { 42 | union 43 | { 44 | struct 45 | { 46 | sa_family_t ss_family; /* address family */ 47 | /* Following field(s) are implementation specific */ 48 | char __data[SS_MAXSIZE - sizeof(unsigned short)]; 49 | /* space to achieve desired size, */ 50 | /* _SS_MAXSIZE value minus size of ss_family */ 51 | }; 52 | void *__align; /* implementation specific desired alignment */ 53 | }; 54 | }; 55 | 56 | typedef struct ip_addr 57 | { 58 | unsigned int af; /* address family: AF_INET6 or AF_INET */ 59 | unsigned int len; /* address len, 16 or 4 */ 60 | 61 | /* 64 bits aligned address */ 62 | union 63 | { 64 | unsigned long addrl[16 / sizeof(long)]; /* long format*/ 65 | unsigned int addr32[4]; 66 | unsigned short addr16[8]; 67 | unsigned char addr[16]; 68 | } u; 69 | } ip_addr_t; 70 | 71 | typedef struct advertise_info 72 | { 73 | str name; /* name - eg.: foo.bar or 10.0.0.1 */ 74 | unsigned short port_no; /* port number */ 75 | short port_pad; /* padding field */ 76 | str port_no_str; /* port number converted to string -- optimization*/ 77 | str address_str; /*ip address converted to string -- optimization*/ 78 | struct ip_addr address; /* ip address */ 79 | str sock_str; /* Socket proto, ip, and port as string */ 80 | } advertise_info_t; 81 | 82 | typedef struct snd_flags 83 | { 84 | unsigned short f; /* snd flags */ 85 | unsigned short blst_imask; /* blocklist ignore mask */ 86 | } snd_flags_t; 87 | 88 | typedef enum si_flags 89 | { 90 | SI_NONE = 0, 91 | SI_IS_IP = (1 << 0), 92 | SI_IS_LO = (1 << 1), 93 | SI_IS_MCAST = (1 << 2), 94 | SI_IS_ANY = (1 << 3), 95 | SI_IS_MHOMED = (1 << 4), 96 | SI_IS_VIRTUAL = (1 << 5), 97 | } si_flags_t; 98 | 99 | typedef union sockaddr_union 100 | { 101 | struct sockaddr s; 102 | struct sockaddr_in sin; 103 | struct sockaddr_in6 sin6; 104 | struct sockaddr_storage sas; 105 | } sr_sockaddr_union_t; 106 | 107 | typedef struct socket_info 108 | { 109 | int socket; 110 | int gindex; /* global index in the lists of all sockets */ 111 | str name; /* name - eg.: foo.bar or 10.0.0.1 */ 112 | struct ip_addr address; /* ip address */ 113 | str address_str; /*ip address converted to string -- optimization*/ 114 | str port_no_str; /* port number converted to string -- optimization*/ 115 | enum si_flags flags; /* SI_IS_IP | SI_IS_LO | SI_IS_MCAST */ 116 | union sockaddr_union su; 117 | struct socket_info *next; 118 | struct socket_info *prev; 119 | unsigned short port_no; /* port number */ 120 | char proto; /* tcp or udp*/ 121 | char proto_pad0; /* padding field */ 122 | short proto_pad1; /* padding field */ 123 | str sock_str; /* Socket proto, ip, and port as string */ 124 | struct addr_info *addr_info_lst; /* extra addresses (e.g. SCTP mh) */ 125 | int workers; /* number of worker processes for this socket */ 126 | int workers_tcpidx; /* index of workers in tcp children array */ 127 | str sockname; /* socket name given in config listen value */ 128 | struct advertise_info useinfo; /* details to be used in SIP msg */ 129 | #ifdef USE_MCAST 130 | str mcast; /* name of interface that should join multicast group*/ 131 | #endif /* USE_MCAST */ 132 | } socket_info_t; 133 | 134 | typedef struct dest_info 135 | { 136 | struct socket_info *send_sock; 137 | union sockaddr_union to; 138 | int id; /* tcp stores the connection id here */ 139 | snd_flags_t send_flags; 140 | char proto; 141 | #ifdef USE_COMP 142 | char proto_pad0; /* padding field */ 143 | short comp; 144 | #else 145 | char proto_pad0; /* padding field */ 146 | short proto_pad1; /* padding field */ 147 | #endif 148 | } dest_info_t; 149 | 150 | /* inits an ip_addr pointer from a sockaddr_union ip address */ 151 | static inline void su2ip_addr(struct ip_addr *ip, const union sockaddr_union *su) 152 | { 153 | switch (su->s.sa_family) 154 | { 155 | case AF_INET: 156 | ip->af = AF_INET; 157 | ip->len = 4; 158 | __builtin_memcpy(ip->u.addr, &su->sin.sin_addr, 4); 159 | break; 160 | case AF_INET6: 161 | ip->af = AF_INET6; 162 | ip->len = 16; 163 | __builtin_memcpy(ip->u.addr, &su->sin6.sin6_addr, 16); 164 | break; 165 | default: 166 | __builtin_memset(ip, 0, sizeof(ip_addr_t)); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /kern/opensips.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | #include "rtcagent.h" 23 | 24 | #define _SYS_SOCKET_H 1 25 | #define IP4_MAX_STR_SIZE 15 26 | #define IP6_MAX_STR_SIZE 45 27 | #define IP_ADDR_MAX_STR_SIZE 6 /* ip62ascii + \0*/ 28 | #define SS_MAXSIZE 128 /* Implementation specific max size */ 29 | #define MAX_DATA_SIZE_SIP 1024 * 4 30 | 31 | typedef unsigned short sa_family_t; 32 | 33 | typedef struct _str 34 | { 35 | char *s; 36 | int len; 37 | } str; 38 | 39 | struct sockaddr_storage 40 | { 41 | union 42 | { 43 | struct 44 | { 45 | sa_family_t ss_family; /* address family */ 46 | /* Following field(s) are implementation specific */ 47 | char __data[SS_MAXSIZE - sizeof(unsigned short)]; 48 | /* space to achieve desired size, */ 49 | /* _SS_MAXSIZE value minus size of ss_family */ 50 | }; 51 | void *__align; /* implementation specific desired alignment */ 52 | }; 53 | }; 54 | 55 | typedef struct ip_addr 56 | { 57 | unsigned int af; /* address family: AF_INET6 or AF_INET */ 58 | unsigned int len; /* address len, 16 or 4 */ 59 | 60 | /* 64 bits aligned address */ 61 | union 62 | { 63 | unsigned long addrl[16 / sizeof(long)]; /* long format*/ 64 | unsigned int addr32[4]; 65 | unsigned short addr16[8]; 66 | unsigned char addr[16]; 67 | } u; 68 | } ip_addr_t; 69 | 70 | typedef struct advertise_info 71 | { 72 | str name; /* name - eg.: foo.bar or 10.0.0.1 */ 73 | unsigned short port_no; /* port number */ 74 | short port_pad; /* padding field */ 75 | str port_no_str; /* port number converted to string -- optimization*/ 76 | str address_str; /*ip address converted to string -- optimization*/ 77 | struct ip_addr address; /* ip address */ 78 | str sock_str; /* Socket proto, ip, and port as string */ 79 | } advertise_info_t; 80 | 81 | typedef struct snd_flags 82 | { 83 | unsigned short f; /* snd flags */ 84 | unsigned short blst_imask; /* blocklist ignore mask */ 85 | } snd_flags_t; 86 | 87 | typedef enum si_flags 88 | { 89 | SI_NONE = 0, 90 | SI_IS_IP = (1 << 0), 91 | SI_IS_LO = (1 << 1), 92 | SI_IS_MCAST = (1 << 2), 93 | SI_IS_ANY = (1 << 3), 94 | SI_IS_MHOMED = (1 << 4), 95 | SI_IS_VIRTUAL = (1 << 5), 96 | } si_flags_t; 97 | 98 | typedef union sockaddr_union 99 | { 100 | struct sockaddr s; 101 | struct sockaddr_in sin; 102 | struct sockaddr_in6 sin6; 103 | struct sockaddr_storage sas; 104 | } sr_sockaddr_union_t; 105 | 106 | typedef struct socket_info 107 | { 108 | int socket; 109 | str name; /*!< name - eg.: foo.bar or 10.0.0.1 */ 110 | str tag; /* the tag of the interface, use only in OpenSIPS ecosystem */ 111 | struct ip_addr address; /*!< ip address */ 112 | str address_str; /*!< ip address converted to string -- optimization*/ 113 | unsigned short port_no; /*!< port number */ 114 | str port_no_str; /*!< port number converted to string -- optimization*/ 115 | enum si_flags flags; /*!< SI_IS_IP | SI_IS_LO | SI_IS_MCAST | SI_IS_ANYCAST */ 116 | union sockaddr_union su; 117 | int proto; /*!< tcp or udp*/ 118 | str sock_str; 119 | str adv_sock_str; 120 | str tag_sock_str; 121 | str adv_name_str; /* Advertised name of this interface */ 122 | str adv_port_str; /* Advertised port of this interface */ 123 | struct ip_addr adv_address; /* Advertised address in ip_addr form (for find_si) */ 124 | unsigned short adv_port; /* optimization for grep_sock_info() */ 125 | unsigned short workers; 126 | } socket_info_t; 127 | 128 | /* 129 | typedef struct socket_info 130 | { 131 | int socket; 132 | int gindex; 133 | str name; 134 | struct ip_addr address; 135 | str address_str; 136 | str port_no_str; 137 | enum si_flags flags; 138 | union sockaddr_union su; 139 | struct socket_info *next; 140 | struct socket_info *prev; 141 | unsigned short port_no; 142 | char proto; 143 | char proto_pad0; 144 | short proto_pad1; 145 | str sock_str; 146 | struct addr_info *addr_info_lst; 147 | int workers; 148 | int workers_tcpidx; 149 | str sockname; 150 | struct advertise_info useinfo; 151 | } socket_info_t; 152 | */ 153 | 154 | typedef struct dest_info 155 | { 156 | struct socket_info *send_sock; 157 | union sockaddr_union to; 158 | int id; /* tcp stores the connection id here */ 159 | snd_flags_t send_flags; 160 | char proto; 161 | #ifdef USE_COMP 162 | char proto_pad0; /* padding field */ 163 | short comp; 164 | #else 165 | char proto_pad0; /* padding field */ 166 | short proto_pad1; /* padding field */ 167 | #endif 168 | } dest_info_t; 169 | 170 | /* inits an ip_addr pointer from a sockaddr_union ip address */ 171 | static inline void su2ip_addr(struct ip_addr *ip, const union sockaddr_union *su) 172 | { 173 | switch (su->s.sa_family) 174 | { 175 | case AF_INET: 176 | ip->af = AF_INET; 177 | ip->len = 4; 178 | __builtin_memcpy(ip->u.addr, &su->sin.sin_addr, 4); 179 | break; 180 | case AF_INET6: 181 | ip->af = AF_INET6; 182 | ip->len = 16; 183 | __builtin_memcpy(ip->u.addr, &su->sin6.sin6_addr, 16); 184 | break; 185 | default: 186 | __builtin_memset(ip, 0, sizeof(ip_addr_t)); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /kern/rtcagent.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 CFC4N . All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef RTCAGENT_H 16 | #define RTCAGENT_H 17 | 18 | #ifndef NOCORE 19 | //CO:RE is enabled 20 | #include "vmlinux.h" 21 | #include "bpf/bpf_core_read.h" 22 | #include "bpf/bpf_helpers.h" 23 | #include "bpf/bpf_tracing.h" 24 | #include "bpf/bpf_endian.h" 25 | 26 | 27 | #else 28 | //CO:RE is disabled 29 | #include 30 | 31 | // see https://github.com//sipcapture/rtcagent/issues/256 for more detail. 32 | /* 33 | * This will bring in asm_volatile_goto and asm_inline macro definitions 34 | * if enabled by compiler and config options. 35 | */ 36 | #include 37 | 38 | /* 39 | * asm_inline is defined as asm __inline in "include/linux/compiler_types.h" 40 | * if supported by the kernel's CC (i.e CONFIG_CC_HAS_ASM_INLINE) which is not 41 | * supported by CLANG. 42 | */ 43 | #ifdef asm_inline 44 | #undef asm_inline 45 | #define asm_inline asm 46 | #endif 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | struct tcphdr { 61 | __be16 source; 62 | __be16 dest; 63 | }; 64 | 65 | #endif 66 | 67 | #include "common.h" 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /kern/tcprtt_kern.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | #include "common2.h" 23 | #include "bpf/bpf_endian.h" 24 | #include "bpf/bpf_tracing.h" 25 | 26 | 27 | #define AF_INET 2 28 | 29 | char __license[] SEC("license") = "Dual MIT/GPL"; 30 | 31 | /** 32 | * For CO-RE relocatable eBPF programs, __attribute__((preserve_access_index)) 33 | * preserves the offset of the specified fields in the original kernel struct. 34 | * So here we don't need to include "vmlinux.h". Instead we only need to define 35 | * the kernel struct and their fields the eBPF program actually requires. 36 | * 37 | * Also note that BTF-enabled programs like fentry, fexit, fmod_ret, tp_btf, 38 | * lsm, etc. declared using the BPF_PROG macro can read kernel memory without 39 | * needing to call bpf_probe_read*(). 40 | */ 41 | 42 | struct sock_common { 43 | union { 44 | struct { 45 | // skc_daddr is destination IP address 46 | __be32 skc_daddr; 47 | // skc_rcv_saddr is the source IP address 48 | __be32 skc_rcv_saddr; 49 | }; 50 | }; 51 | union { 52 | struct { 53 | // skc_dport is the destination TCP/UDP port 54 | __be16 skc_dport; 55 | // skc_num is the source TCP/UDP port 56 | __u16 skc_num; 57 | }; 58 | }; 59 | // skc_family is the network address family (2 for IPV4) 60 | short unsigned int skc_family; 61 | } __attribute__((preserve_access_index)); 62 | 63 | /** 64 | * struct sock is the network layer representation of sockets. 65 | * This is a simplified copy of the kernel's struct sock. 66 | * This copy is needed only to access struct sock_common. 67 | */ 68 | struct sock { 69 | struct sock_common __sk_common; 70 | } __attribute__((preserve_access_index)); 71 | 72 | /** 73 | * struct tcp_sock is the Linux representation of a TCP socket. 74 | * This is a simplified copy of the kernel's struct tcp_sock. 75 | * For this example we only need srtt_us to read the smoothed RTT. 76 | */ 77 | struct tcp_sock { 78 | u32 srtt_us; 79 | } __attribute__((preserve_access_index)); 80 | 81 | struct { 82 | __uint(type, BPF_MAP_TYPE_RINGBUF); 83 | __uint(max_entries, 1 << 24); 84 | } events SEC(".maps"); 85 | 86 | /** 87 | * The sample submitted to userspace over a ring buffer. 88 | * Emit struct event's type info into the ELF's BTF so bpf2go 89 | * can generate a Go type from it. 90 | */ 91 | struct event { 92 | u16 sport; 93 | u16 dport; 94 | u32 saddr; 95 | u32 daddr; 96 | u32 srtt; 97 | }; 98 | struct event *unused_event __attribute__((unused)); 99 | 100 | SEC("fentry/tcp_close") 101 | int BPF_PROG(tcp_close, struct sock *sk) 102 | { 103 | 104 | //char fmt2[] = "TIMESTAMP: %d\n"; 105 | //bpf_trace_printk(fmt2, sizeof(fmt2), 1); 106 | 107 | if (sk->__sk_common.skc_family != AF_INET) 108 | { 109 | return 0; 110 | } 111 | 112 | // The input struct sock is actually a tcp_sock, so we can type-cast 113 | struct tcp_sock *ts = bpf_skc_to_tcp_sock(sk); 114 | if (!ts) 115 | { 116 | return 0; 117 | } 118 | 119 | struct event *tcp_info; 120 | tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0); 121 | if (!tcp_info) 122 | { 123 | return 0; 124 | } 125 | 126 | tcp_info->saddr = sk->__sk_common.skc_rcv_saddr; 127 | tcp_info->daddr = sk->__sk_common.skc_daddr; 128 | tcp_info->dport = bpf_ntohs(sk->__sk_common.skc_dport); 129 | tcp_info->sport = sk->__sk_common.skc_num; 130 | 131 | tcp_info->srtt = ts->srtt_us >> 3; 132 | tcp_info->srtt /= 1000; 133 | 134 | bpf_ringbuf_submit(tcp_info, 0); 135 | 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "rtcagent/cli" 6 | "rtcagent/pkg/util/ebpf" 7 | "rtcagent/pkg/util/kernel" 8 | "runtime" 9 | 10 | _ "github.com/shuLhan/go-bindata" // add for bindata in Makefile 11 | ) 12 | 13 | const ( 14 | BtfNotSupport = "You can compile a no BTF version by yourself with command `make nocore`,Please read Makefile for more info." 15 | ) 16 | 17 | var ( 18 | enableCORE = "true" 19 | ) 20 | 21 | func main() { 22 | 23 | kv, err := kernel.HostVersion() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | switch runtime.GOARCH { 28 | case "amd64": 29 | if kv < kernel.VersionCode(4, 18, 0) { 30 | log.Fatalf("Linux/Android Kernel (x86_64) version %v is not supported. Need > 4.18 .", kv) 31 | } 32 | case "arm64": 33 | if kv < kernel.VersionCode(5, 5, 0) { 34 | log.Fatalf("Linux/Android Kernel (aarch64) version %v is not supported. Need > 5.5 .", kv) 35 | } 36 | default: 37 | log.Fatalf("unsupported CPU arch:%v. ", runtime.GOARCH) 38 | } 39 | 40 | isContainer, err := ebpf.IsContainer() 41 | if err != nil { 42 | log.Fatal("Check container error:", err) 43 | } 44 | 45 | if isContainer { 46 | log.Printf("Your environment is a container. We will not detect the BTF config.") 47 | } else { 48 | enable, e := ebpf.IsEnableBPF() 49 | if e != nil { 50 | log.Fatalf("Kernel config read failed, error:%v", e) 51 | } 52 | 53 | if !enable { 54 | log.Fatalf("Kernel not support, error:%v", e) 55 | } 56 | 57 | if enableCORE == "true" { 58 | enable, e := ebpf.IsEnableBTF() 59 | if e != nil { 60 | log.Fatalf("Can't found BTF config with error:%v.\n"+BtfNotSupport, e) 61 | } 62 | if !enable { 63 | log.Fatal("BTF not support, please check it. shell: cat /boot/config-`uname -r` | grep CONFIG_DEBUG_INFO_BTF \n " + 64 | BtfNotSupport) 65 | } 66 | } 67 | } 68 | 69 | cli.Start() 70 | } 71 | -------------------------------------------------------------------------------- /metric/definition.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | "github.com/prometheus/client_golang/prometheus/promauto" 6 | ) 7 | 8 | var ( 9 | // TCP Latency 10 | latencyTCP = promauto.NewGaugeVec(prometheus.GaugeOpts{ 11 | Name: "tcp_latency", 12 | Help: "latency of tcp connection"}, 13 | []string{"node_id", "src_ip", "dst_ip", "src_port", "dst_port"}) 14 | ) 15 | -------------------------------------------------------------------------------- /metric/metric.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/signal" 7 | "rtcagent/model" 8 | "runtime" 9 | "syscall" 10 | ) 11 | 12 | type Metric struct { 13 | H MetricHandler 14 | Chan chan model.AggregatedMetricValue 15 | quit chan bool 16 | } 17 | 18 | type MetricHandler interface { 19 | setup() error 20 | reload() 21 | expose(chan model.AggregatedMetricValue) 22 | } 23 | 24 | func New(name string) *Metric { 25 | var register = map[string]MetricHandler{ 26 | "prometheus": new(Prometheus), 27 | } 28 | 29 | return &Metric{ 30 | H: register[name], 31 | quit: make(chan bool), 32 | } 33 | } 34 | 35 | func (m *Metric) Run() error { 36 | err := m.H.setup() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | for i := 0; i < runtime.NumCPU(); i++ { 42 | go func() { 43 | m.H.expose(m.Chan) 44 | }() 45 | } 46 | 47 | s := make(chan os.Signal, 1) 48 | signal.Notify(s, syscall.SIGHUP) 49 | go func() { 50 | for { 51 | select { 52 | case <-s: 53 | m.H.reload() 54 | case <-m.quit: 55 | m.quit <- true 56 | return 57 | } 58 | } 59 | }() 60 | 61 | return nil 62 | } 63 | 64 | func (m *Metric) End() { 65 | m.quit <- true 66 | <-m.quit 67 | close(m.Chan) 68 | log.Printf("close metric channel") 69 | } 70 | -------------------------------------------------------------------------------- /metric/prometheus.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "net/http" 7 | "rtcagent/model" 8 | "strings" 9 | "sync" 10 | "time" 11 | 12 | "github.com/VictoriaMetrics/fastcache" 13 | "github.com/prometheus/client_golang/prometheus/promhttp" 14 | ) 15 | 16 | const ( 17 | invite = "INVITE" 18 | register = "REGISTER" 19 | cacheSize = 60 * 1024 * 1024 20 | ) 21 | 22 | type Prometheus struct { 23 | TargetEmpty bool 24 | TargetIP []string 25 | TargetName []string 26 | TargetMap map[string]string 27 | TargetConf *sync.RWMutex 28 | cache *fastcache.Cache 29 | s *http.Server 30 | } 31 | 32 | func (p *Prometheus) setup() (err error) { 33 | p.TargetConf = new(sync.RWMutex) 34 | p.TargetIP = strings.Split("10.0.0.1", ",") 35 | p.TargetName = strings.Split("test", ",") 36 | p.cache = fastcache.New(cacheSize) 37 | 38 | /* 39 | err = prometheus.Register(version_collector.NewCollector("rtcagent")) 40 | if err != nil { 41 | log.Fatalf("Error registering version collector: %s", err) 42 | } 43 | 44 | p.exporter, err = NewExporter() 45 | if err != nil { 46 | log.Fatalf("Error creating exporter: %s", err) 47 | } 48 | 49 | err = prometheus.Register(p.exporter) 50 | if err != nil { 51 | log.Fatalf("Error registering exporter: %s", err) 52 | } 53 | */ 54 | 55 | metricsPath := "/metrics" 56 | listenAddress := ":9435" 57 | 58 | mux := http.NewServeMux() 59 | mux.Handle("/metrics", promhttp.Handler()) 60 | 61 | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 62 | _, err = w.Write([]byte(` 63 | eBPF Exporter 64 | 65 |

eBPF Exporter

66 |

Metrics

67 | 68 | `)) 69 | if err != nil { 70 | log.Fatalf("Error sending response body: %s", err) 71 | } 72 | }) 73 | 74 | p.s = &http.Server{ 75 | Addr: listenAddress, 76 | Handler: mux, 77 | ReadTimeout: 0, // 1 * time.Minute, 78 | WriteTimeout: 30 * time.Minute, 79 | MaxHeaderBytes: 1 << 20, 80 | } 81 | 82 | go func() { 83 | 84 | l, err := net.Listen("tcp", listenAddress) 85 | 86 | if err != nil { 87 | log.Fatal(err) 88 | } 89 | 90 | //fmt.Println(`{"server_state":"listening"}`) 91 | log.Fatal(p.s.Serve(l)) 92 | }() 93 | 94 | return err 95 | } 96 | 97 | func (p *Prometheus) expose(hCh chan model.AggregatedMetricValue) { 98 | for pkt := range hCh { 99 | 100 | //fmt.Println(pkt.Name) 101 | if pkt.IntType == 2 { 102 | latencyTCP.WithLabelValues(pkt.Labels...).Set(pkt.Value) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /metric/reload.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | func cutSpace(str string) string { 11 | return strings.Map(func(r rune) rune { 12 | if unicode.IsSpace(r) { 13 | return -1 14 | } 15 | return r 16 | }, str) 17 | } 18 | 19 | func (p *Prometheus) reload() { 20 | var fsTargetIP []string 21 | var fsTargetName []string 22 | 23 | fb, err := os.ReadFile("prometheus.conf") 24 | if err != nil { 25 | log.Printf("%v", err) 26 | return 27 | } 28 | 29 | fs := cutSpace(string(fb)) 30 | 31 | if si := strings.Index(fs, "PromTargetIP=\""); si > -1 { 32 | s := si + len("PromTargetIP=\"") 33 | e := strings.Index(fs[s:], "\"") 34 | if e >= 7 { 35 | fsTargetIP = strings.Split(fs[s:s+e], ",") 36 | } 37 | } 38 | if si := strings.Index(fs, "PromTargetName=\""); si > -1 { 39 | s := si + len("PromTargetName=\"") 40 | e := strings.Index(fs[s:], "\"") 41 | if e > 0 { 42 | fsTargetName = strings.Split(fs[s:s+e], ",") 43 | } 44 | } 45 | 46 | if fsTargetIP != nil && fsTargetName != nil && len(fsTargetIP) == len(fsTargetName) { 47 | p.TargetConf.Lock() 48 | p.TargetIP = fsTargetIP 49 | p.TargetName = fsTargetName 50 | p.TargetEmpty = false 51 | p.TargetMap = make(map[string]string) 52 | for i := 0; i < len(p.TargetName); i++ { 53 | p.TargetMap[p.TargetIP[i]] = p.TargetName[i] 54 | } 55 | p.TargetConf.Unlock() 56 | log.Printf("successfully reloaded PromTargetIP: %#v", fsTargetIP) 57 | log.Printf("successfully reloaded PromTargetName: %#v", fsTargetName) 58 | } else { 59 | log.Printf("failed to reload PromTargetIP: %#v", fsTargetIP) 60 | log.Printf("failed to reload PromTargetName: %#v", fsTargetName) 61 | log.Printf("please give every PromTargetIP a unique IP and PromTargetName a unique name") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /model/metric.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | ) 6 | 7 | // aggregatedMetricValue is a value after aggregation of equal label sets 8 | type AggregatedMetricValue struct { 9 | // labels are decoded from the raw key 10 | Labels []string 11 | // value is the kernel map value 12 | Value float64 13 | //Internal type 14 | IntType int 15 | // 16 | Type prometheus.ValueType 17 | //name 18 | Name string 19 | } 20 | 21 | // aggregatedMetricValue is a value after aggregation of equal label sets 22 | type AggregatedTimeMetricValue struct { 23 | // labels are decoded from the raw key 24 | MapLabelsString map[string]string 25 | MapLabelsInt map[string]int 26 | // value is the kernel map value 27 | Value float64 28 | // 29 | Type prometheus.ValueType 30 | //name 31 | Name string 32 | // 33 | IntType int 34 | //time 35 | Time int64 36 | } 37 | 38 | type Label struct { 39 | Name string `yaml:"name"` 40 | Size uint `yaml:"size"` 41 | } 42 | -------------------------------------------------------------------------------- /outdata/hep/hep.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package publish; 3 | 4 | 5 | import "github.com/gogo/protobuf/gogoproto/gogo.proto"; 6 | 7 | option (gogoproto.gostring_all) = true; 8 | option (gogoproto.goproto_stringer_all) = false; 9 | option (gogoproto.stringer_all) = true; 10 | option (gogoproto.marshaler_all) = true; 11 | option (gogoproto.sizer_all) = true; 12 | option (gogoproto.unmarshaler_all) = true; 13 | 14 | // For tests 15 | option (gogoproto.testgen_all) = true; 16 | option (gogoproto.benchgen_all) = true; 17 | option (gogoproto.equal_all) = true; 18 | option (gogoproto.populate_all) = true; 19 | 20 | // HEP represents HEP packet 21 | message HEP { 22 | required uint32 Version = 1 [(gogoproto.nullable) = false]; 23 | required uint32 Protocol = 2 [(gogoproto.nullable) = false]; 24 | required string SrcIP = 3 [(gogoproto.nullable) = false]; 25 | required string DstIP = 4 [(gogoproto.nullable) = false]; 26 | required uint32 SrcPort = 5 [(gogoproto.nullable) = false]; 27 | required uint32 DstPort = 6 [(gogoproto.nullable) = false]; 28 | required uint32 Tsec = 7 [(gogoproto.nullable) = false]; 29 | required uint32 Tmsec = 8 [(gogoproto.nullable) = false]; 30 | required uint32 ProtoType = 9 [(gogoproto.nullable) = false]; 31 | required uint32 NodeID = 10 [(gogoproto.nullable) = false]; 32 | required string NodePW = 11 [(gogoproto.nullable) = false]; 33 | required string Payload = 12 [(gogoproto.nullable) = false]; 34 | required string CID = 13 [(gogoproto.nullable) = false]; 35 | required uint32 Vlan = 14 [(gogoproto.nullable) = false]; 36 | } 37 | -------------------------------------------------------------------------------- /pkg/event_processor/iparser.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package event_processor 23 | 24 | import ( 25 | "bytes" 26 | "encoding/hex" 27 | "fmt" 28 | ) 29 | 30 | type ProcessStatus uint8 31 | type PacketType uint8 32 | type ParserType uint8 33 | 34 | const ( 35 | ProcessStateInit ProcessStatus = iota 36 | ProcessStateProcessing 37 | ProcessStateDone 38 | ) 39 | 40 | const ( 41 | PacketTypeNull PacketType = iota 42 | PacketTypeUnknow 43 | PacketTypeGzip 44 | PacketTypeWebSocket 45 | ) 46 | 47 | const ( 48 | ParserTypeNull ParserType = iota 49 | ParserTypeHttpRequest 50 | ParserTypeHttp2Request 51 | ParserTypeHttpResponse 52 | ParserTypeHttp2Response 53 | ParserTypeWebSocket 54 | ) 55 | 56 | type IParser interface { 57 | detect(b []byte) error 58 | Write(b []byte) (int, error) 59 | ParserType() ParserType 60 | PacketType() PacketType 61 | //Body() []byte 62 | Name() string 63 | IsDone() bool 64 | Init() 65 | Display() []byte 66 | Reset() 67 | } 68 | 69 | var parsers = make(map[string]IParser) 70 | 71 | func Register(p IParser) { 72 | if p == nil { 73 | panic("Register Parser is nil") 74 | } 75 | name := p.Name() 76 | if _, dup := parsers[name]; dup { 77 | panic(fmt.Sprintf("Register called twice for Parser %s", name)) 78 | } 79 | parsers[name] = p 80 | } 81 | 82 | func GetAllModules() map[string]IParser { 83 | return parsers 84 | } 85 | 86 | func GetModuleByName(name string) IParser { 87 | return parsers[name] 88 | } 89 | 90 | func NewParser(payload []byte) IParser { 91 | if len(payload) > 0 { 92 | var newParser IParser 93 | for _, parser := range GetAllModules() { 94 | err := parser.detect(payload) 95 | if err == nil { 96 | break 97 | } 98 | } 99 | if newParser == nil { 100 | newParser = new(DefaultParser) 101 | } 102 | newParser.Init() 103 | return newParser 104 | } 105 | var np = &DefaultParser{} 106 | np.Init() 107 | return np 108 | } 109 | 110 | type DefaultParser struct { 111 | reader *bytes.Buffer 112 | isdone bool 113 | } 114 | 115 | func (dp *DefaultParser) ParserType() ParserType { 116 | return ParserTypeNull 117 | } 118 | 119 | func (dp *DefaultParser) PacketType() PacketType { 120 | return PacketTypeNull 121 | } 122 | 123 | func (dp *DefaultParser) Write(b []byte) (int, error) { 124 | dp.isdone = true 125 | return dp.reader.Write(b) 126 | } 127 | 128 | func (dp *DefaultParser) detect(b []byte) error { 129 | return nil 130 | } 131 | 132 | func (dp *DefaultParser) Name() string { 133 | return "DefaultParser" 134 | } 135 | 136 | func (dp *DefaultParser) IsDone() bool { 137 | return dp.isdone 138 | } 139 | 140 | func (dp *DefaultParser) Init() { 141 | dp.reader = bytes.NewBuffer(nil) 142 | } 143 | 144 | func (dp *DefaultParser) Display() []byte { 145 | b := dp.reader.Bytes() 146 | if len(b) <= 0 { 147 | return []byte{} 148 | } 149 | if b[0] < 32 || b[0] > 126 { 150 | return []byte(hex.Dump(b)) 151 | } 152 | return []byte(CToGoString(dp.reader.Bytes())) 153 | } 154 | 155 | func (dp *DefaultParser) Reset() { 156 | dp.isdone = false 157 | dp.reader.Reset() 158 | } 159 | -------------------------------------------------------------------------------- /pkg/event_processor/iworker.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package event_processor 23 | 24 | import ( 25 | "encoding/hex" 26 | "rtcagent/user/event" 27 | "time" 28 | ) 29 | 30 | type IWorker interface { 31 | Write(event.IEventStruct) error 32 | GetUUID() string 33 | } 34 | 35 | const ( 36 | MaxTickerCount = 10 // 1 Sencond/(eventWorker.ticker.C) = 10 37 | MaxChanLen = 16 38 | ) 39 | 40 | type eventWorker struct { 41 | incoming chan event.IEventStruct 42 | //events []user.IEventStruct 43 | status ProcessStatus 44 | packetType PacketType 45 | ticker *time.Ticker 46 | tickerCount uint8 47 | UUID string 48 | processor *EventProcessor 49 | parser IParser 50 | } 51 | 52 | func NewEventWorker(uuid string, processor *EventProcessor) IWorker { 53 | eWorker := &eventWorker{} 54 | eWorker.init(uuid, processor) 55 | go func() { 56 | eWorker.Run() 57 | }() 58 | return eWorker 59 | } 60 | 61 | func (ew *eventWorker) init(uuid string, processor *EventProcessor) { 62 | ew.ticker = time.NewTicker(time.Millisecond * 100) 63 | ew.incoming = make(chan event.IEventStruct, MaxChanLen) 64 | ew.status = ProcessStateInit 65 | ew.UUID = uuid 66 | ew.processor = processor 67 | } 68 | 69 | func (ew *eventWorker) GetUUID() string { 70 | return ew.UUID 71 | } 72 | 73 | func (ew *eventWorker) Write(e event.IEventStruct) error { 74 | ew.incoming <- e 75 | return nil 76 | } 77 | 78 | func (ew *eventWorker) Display() { 79 | 80 | //if ew.parser.ParserType() != ParserTypeHttpResponse { 81 | // return 82 | //} 83 | 84 | b := ew.parser.Display() 85 | 86 | if len(b) <= 0 { 87 | return 88 | } 89 | 90 | if ew.processor.isHex { 91 | b = []byte(hex.Dump(b)) 92 | } 93 | 94 | //ew.processor.GetLogger().Printf("UUID:%s, Name:%s, Type:%d, Length:%d", ew.UUID, ew.parser.Name(), ew.parser.ParserType(), len(b)) 95 | ew.processor.GetLogger().Println("\n===================================================" + string(b)) 96 | ew.parser.Reset() 97 | ew.status = ProcessStateDone 98 | ew.packetType = PacketTypeNull 99 | } 100 | 101 | func (ew *eventWorker) parserEvent(e event.IEventStruct) { 102 | 103 | if ew.status == ProcessStateInit { 104 | parser := NewParser(e.Payload()) 105 | ew.parser = parser 106 | } 107 | 108 | ew.status = ProcessStateProcessing 109 | 110 | _, err := ew.parser.Write(e.Payload()[:e.PayloadLen()]) 111 | if err != nil { 112 | ew.processor.GetLogger().Fatalf("eventWorker: detect packet type error, UUID:%s, error:%v", ew.UUID, err) 113 | } 114 | 115 | if ew.parser.IsDone() { 116 | 117 | ew.Display() 118 | } 119 | 120 | } 121 | 122 | func (ew *eventWorker) Run() { 123 | for { 124 | select { 125 | case _ = <-ew.ticker.C: 126 | // 127 | if ew.tickerCount > MaxTickerCount { 128 | ew.processor.GetLogger().Printf("eventWorker TickerCount > %d, event closed.", MaxTickerCount) 129 | ew.Close() 130 | return 131 | } 132 | ew.tickerCount++ 133 | case e := <-ew.incoming: 134 | // reset tickerCount 135 | ew.tickerCount = 0 136 | ew.parserEvent(e) 137 | } 138 | } 139 | 140 | } 141 | 142 | func (ew *eventWorker) Close() { 143 | ew.ticker.Stop() 144 | ew.Display() 145 | ew.tickerCount = 0 146 | ew.processor.delWorkerByUUID(ew) 147 | } 148 | -------------------------------------------------------------------------------- /pkg/event_processor/processor.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package event_processor 23 | 24 | import ( 25 | "fmt" 26 | "log" 27 | "rtcagent/hepclient/hepsender" 28 | "rtcagent/user/event" 29 | "sync" 30 | ) 31 | 32 | const ( 33 | MaxIncomingChanLen = 1024 34 | MaxParserQueueLen = 1024 35 | ) 36 | 37 | type EventProcessor struct { 38 | sync.Mutex 39 | incoming chan event.IEventStruct 40 | 41 | workerQueue map[string]IWorker 42 | 43 | logger *log.Logger 44 | 45 | // output model 46 | isHex bool 47 | } 48 | 49 | func (ep *EventProcessor) GetLogger() *log.Logger { 50 | return ep.logger 51 | } 52 | 53 | func (ep *EventProcessor) init() { 54 | ep.incoming = make(chan event.IEventStruct, MaxIncomingChanLen) 55 | ep.workerQueue = make(map[string]IWorker, MaxParserQueueLen) 56 | } 57 | 58 | // Write event 59 | func (ep *EventProcessor) Serve() { 60 | for { 61 | select { 62 | case e := <-ep.incoming: 63 | ep.dispatch(e) 64 | } 65 | } 66 | } 67 | 68 | func (ep *EventProcessor) dispatch(e event.IEventStruct) { 69 | ep.logger.Printf("event ==== ID:%s", e.String()) 70 | 71 | //fmt.Printf("event ==== ID:%s\n", e.String()) 72 | 73 | if e.SendHep() { 74 | ep.logger.Printf("LETS SEND HEP\n") 75 | data, err := e.GenerateHEP() 76 | if err == nil { 77 | if hepsender.Hepsender != nil { 78 | hepsender.Hepsender.Output(data) 79 | } 80 | 81 | } 82 | } 83 | 84 | //ep. 85 | //Worker will be reimplement later! 86 | /* 87 | var uuid string = e.GetUUID() 88 | found, eWorker := ep.getWorkerByUUID(uuid) 89 | if !found { 90 | // ADD a new eventWorker into queue 91 | eWorker = NewEventWorker(e.GetUUID(), ep) 92 | ep.addWorkerByUUID(eWorker) 93 | } 94 | 95 | err := eWorker.Write(e) 96 | if err != nil { 97 | //... 98 | ep.GetLogger().Fatalf("write event failed , error:%v", err) 99 | } 100 | */ 101 | } 102 | 103 | //func (ep *EventProcessor) Incoming() chan user.IEventStruct { 104 | // return ep.incoming 105 | //} 106 | 107 | func (ep *EventProcessor) getWorkerByUUID(uuid string) (bool, IWorker) { 108 | ep.Lock() 109 | defer ep.Unlock() 110 | var eWorker IWorker 111 | var found bool 112 | eWorker, found = ep.workerQueue[uuid] 113 | if !found { 114 | return false, eWorker 115 | } 116 | return true, eWorker 117 | } 118 | 119 | func (ep *EventProcessor) addWorkerByUUID(worker IWorker) { 120 | ep.Lock() 121 | defer ep.Unlock() 122 | ep.workerQueue[worker.GetUUID()] = worker 123 | } 124 | 125 | func (ep *EventProcessor) delWorkerByUUID(worker IWorker) { 126 | ep.Lock() 127 | defer ep.Unlock() 128 | delete(ep.workerQueue, worker.GetUUID()) 129 | } 130 | 131 | // Write event 132 | func (ep *EventProcessor) Write(e event.IEventStruct) { 133 | select { 134 | case ep.incoming <- e: 135 | return 136 | } 137 | } 138 | 139 | func (ep *EventProcessor) Close() error { 140 | if len(ep.workerQueue) > 0 { 141 | return fmt.Errorf("EventProcessor.Close(): workerQueue is not empty:%d", len(ep.workerQueue)) 142 | } 143 | return nil 144 | } 145 | 146 | func NewEventProcessor(logger *log.Logger, isHex bool) *EventProcessor { 147 | var ep *EventProcessor 148 | ep = &EventProcessor{} 149 | ep.logger = logger 150 | ep.isHex = isHex 151 | ep.init() 152 | return ep 153 | } 154 | -------------------------------------------------------------------------------- /pkg/proc/go_elf/eprint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "C" 4 | import "fmt" 5 | 6 | //export eprint 7 | func eprint(i C.int) { 8 | fmt.Printf("RtcAgent unit testing : i = %d\n", uint32(i)) 9 | } 10 | -------------------------------------------------------------------------------- /pkg/proc/go_elf/gccgo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | extern void eprint(int i); 5 | 6 | void foo(void) { 7 | int i; 8 | for (i=0;i<10;i++) { 9 | eprint(i); 10 | } 11 | } 12 | */ 13 | import "C" 14 | 15 | func Foo() { 16 | C.foo() 17 | } 18 | 19 | func main() { 20 | Foo() 21 | } 22 | -------------------------------------------------------------------------------- /pkg/proc/proc.go: -------------------------------------------------------------------------------- 1 | package proc 2 | 3 | import ( 4 | "debug/buildinfo" 5 | "errors" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | const ( 11 | goVersionPrefix = "Go cmd/compile " 12 | ) 13 | 14 | // ErrVersionNotFound is returned when we can't find Go version info from a binary 15 | var ErrVersionNotFound = errors.New("version info not found") 16 | 17 | // GoVersion represents Go toolchain version that a binary is built with. 18 | type GoVersion struct { 19 | major int 20 | minor int 21 | } 22 | 23 | // After returns true if it is greater than major.minor 24 | func (v *GoVersion) After(major, minor int) bool { 25 | if v.major > minor { 26 | return true 27 | } 28 | if v.major == major && v.minor > minor { 29 | return true 30 | } 31 | return false 32 | } 33 | 34 | // ExtraceGoVersion extracts Go version info from a binary that is built with Go toolchain 35 | func ExtraceGoVersion(path string) (*GoVersion, error) { 36 | bi, e := buildinfo.ReadFile(path) 37 | if e != nil { 38 | return nil, ErrVersionNotFound 39 | } 40 | gv, err := parseGoVersion(bi.GoVersion) 41 | if err != nil { 42 | return nil, err 43 | } 44 | return gv, nil 45 | } 46 | 47 | func parseGoVersion(r string) (*GoVersion, error) { 48 | ver := strings.TrimPrefix(r, goVersionPrefix) 49 | 50 | if strings.HasPrefix(ver, "go") { 51 | v := strings.SplitN(ver[2:], ".", 3) 52 | var major, minor int 53 | var err error 54 | 55 | major, err = strconv.Atoi(v[0]) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | if len(v) >= 2 { 61 | minor, err = strconv.Atoi(v[1]) 62 | if err != nil { 63 | return nil, err 64 | } 65 | } 66 | 67 | return &GoVersion{ 68 | major: major, 69 | minor: minor, 70 | }, nil 71 | } 72 | return nil, ErrVersionNotFound 73 | } 74 | -------------------------------------------------------------------------------- /pkg/proc/proc_test.go: -------------------------------------------------------------------------------- 1 | package proc 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "testing" 10 | ) 11 | 12 | const ELF_BUILD_BY_CGO = "go_elf" 13 | 14 | func TestExtraceGoVersion(t *testing.T) { 15 | path := fmt.Sprintf("/proc/%d/exe", os.Getppid()) 16 | ver, err := ExtraceGoVersion(path) 17 | if err != nil { 18 | t.Log(err) 19 | return 20 | } 21 | t.Log(ver) 22 | } 23 | 24 | // cd go_elf 25 | // CGO_ENABLED=1 go build . 26 | func TestExtraceGoVersionGccgo(t *testing.T) { 27 | e := os.Chdir("go_elf") 28 | if e != nil { 29 | t.Fatalf("chdir error:%v\n", e) 30 | } 31 | 32 | p, e := os.Getwd() 33 | if e != nil { 34 | t.Fatalf("Getwd error:%v", e) 35 | } 36 | t.Logf("pwd:%s", p) 37 | 38 | // go build go_elf 39 | pathEnv := os.Getenv("PATH") 40 | t.Logf("env $PATH:%s", pathEnv) 41 | 42 | // mkdir directories 43 | goBuildPath := filepath.Join(os.TempDir(), "go-build") 44 | goEnvPath := filepath.Join(os.TempDir(), "go-env") 45 | e = os.MkdirAll(goBuildPath, os.ModePerm) 46 | if e != nil { 47 | t.Fatal(e) 48 | } 49 | e = os.MkdirAll(goEnvPath, os.ModePerm) 50 | if e != nil { 51 | t.Fatal(e) 52 | } 53 | 54 | c := exec.Command("go", "build", "-v", ".") 55 | 56 | var outb, errb bytes.Buffer 57 | c.Stdout = &outb 58 | c.Stderr = &errb 59 | e = c.Run() 60 | t.Logf("output:%s, errput:%s", outb.String(), errb.String()) 61 | if e != nil { 62 | c.Stderr = os.Stderr 63 | t.Fatalf("go build failed:%v", e) 64 | } 65 | 66 | p1 := filepath.Join(p, ELF_BUILD_BY_CGO) 67 | ver, err := ExtraceGoVersion(p1) 68 | t.Logf("Extrace GoVersion from CGO ELF :%s", p1) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | t.Logf("version found :%v", ver) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/util/ebpf/bpf.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | // CONFIG CHECK ITEMS 11 | var ( 12 | configCheckItems = []string{ 13 | "CONFIG_BPF", 14 | "CONFIG_UPROBES", 15 | "CONFIG_ARCH_SUPPORTS_UPROBES", 16 | } 17 | ) 18 | 19 | type UnameInfo struct { 20 | SysName string 21 | Nodename string 22 | Release string 23 | Version string 24 | Machine string 25 | Domainname string 26 | } 27 | 28 | func getOSUnamer() (*UnameInfo, error) { 29 | u := unix.Utsname{} 30 | e := unix.Uname(&u) 31 | if e != nil { 32 | return nil, e 33 | } 34 | ui := UnameInfo{} 35 | ui.SysName = charsToString(u.Sysname) 36 | ui.Nodename = charsToString(u.Nodename) 37 | ui.Release = charsToString(u.Release) 38 | ui.Version = charsToString(u.Version) 39 | ui.Machine = charsToString(u.Machine) 40 | ui.Domainname = charsToString(u.Domainname) 41 | 42 | return &ui, nil 43 | } 44 | 45 | func charsToString(ca [65]byte) string { 46 | s := make([]byte, len(ca)) 47 | var lens int 48 | for ; lens < len(ca); lens++ { 49 | if ca[lens] == 0 { 50 | break 51 | } 52 | s[lens] = uint8(ca[lens]) 53 | } 54 | return string(s[0:lens]) 55 | } 56 | 57 | // from internal/btf/bpf.go 58 | // checkKernelBTF attempts to load the raw vmlinux BTF blob at 59 | // /sys/kernel/btf/vmlinux and falls back to scanning the file system 60 | // for vmlinux ELFs. 61 | 62 | func checkKernelBTF() (bool, error) { 63 | _, err := os.Stat(SysKernelBtfVmlinux) 64 | 65 | // if exist ,return true 66 | if err == nil { 67 | return true, nil 68 | } 69 | 70 | return findVMLinux() 71 | } 72 | 73 | // findVMLinux scans multiple well-known paths for vmlinux kernel images. 74 | func findVMLinux() (bool, error) { 75 | kv, err := getOSUnamer() 76 | if err != nil { 77 | return false, err 78 | } 79 | release := kv.Release 80 | 81 | for _, loc := range locations { 82 | _, err := os.Stat(fmt.Sprintf(loc, release)) 83 | if err != nil { 84 | continue 85 | } 86 | return true, nil 87 | } 88 | return false, err 89 | } 90 | 91 | func IsEnableBTF() (bool, error) { 92 | found, e := checkKernelBTF() 93 | if e == nil && found { 94 | return true, nil 95 | } 96 | 97 | var KernelConfig = make(map[string]string) 98 | 99 | KernelConfig, e = GetSystemConfig() 100 | if e != nil { 101 | return false, e 102 | } 103 | 104 | bc, found := KernelConfig[ConfigDebugInfoBtf] 105 | if !found { 106 | return false, nil 107 | } 108 | 109 | if bc != "y" { 110 | return false, nil 111 | } 112 | return true, nil 113 | } 114 | 115 | // check BPF CONFIG 116 | func IsEnableBPF() (bool, error) { 117 | var e error 118 | var KernelConfig = make(map[string]string) 119 | 120 | KernelConfig, e = GetSystemConfig() 121 | if e != nil { 122 | return false, e 123 | } 124 | 125 | for _, item := range configCheckItems { 126 | bc, found := KernelConfig[item] 127 | if !found { 128 | return false, fmt.Errorf("Config not found, item:%s.", item) 129 | } 130 | 131 | if bc != "y" { 132 | return false, fmt.Errorf("Config disabled, item :%s.", item) 133 | } 134 | } 135 | 136 | return true, nil 137 | } 138 | -------------------------------------------------------------------------------- /pkg/util/ebpf/bpf_androidgki.go: -------------------------------------------------------------------------------- 1 | //go:build androidgki 2 | // +build androidgki 3 | 4 | package ebpf 5 | 6 | import ( 7 | "bufio" 8 | "compress/gzip" 9 | "fmt" 10 | "os" 11 | ) 12 | 13 | const ( 14 | BootConfigPath = "/proc/config.gz" 15 | ConfigDebugInfoBtf = "CONFIG_DEBUG_INFO_BTF" 16 | SysKernelBtfVmlinux = "/sys/kernel/btf/vmlinux" 17 | ) 18 | 19 | var ( 20 | // use same list of locations as libbpf 21 | // https://android.googlesource.com/platform/external/libbpf/ 22 | 23 | locations = []string{ 24 | "/sys/kernel/btf/vmlinux", 25 | } 26 | ) 27 | 28 | func GetSystemConfig() (map[string]string, error) { 29 | return getAndroidConfig(BootConfigPath) 30 | } 31 | 32 | func getAndroidConfig(filename string) (map[string]string, error) { 33 | var KernelConfig = make(map[string]string) 34 | // Open file bootConf. 35 | f, err := os.Open(filename) 36 | if err != nil { 37 | return KernelConfig, err 38 | } 39 | defer f.Close() 40 | 41 | // check if the file is gzipped 42 | var magic []byte 43 | var i int 44 | magic = make([]byte, 2) 45 | i, err = f.Read(magic) 46 | if err != nil { 47 | return KernelConfig, err 48 | } 49 | if i != 2 { 50 | return KernelConfig, fmt.Errorf("read %d bytes, expected 2", i) 51 | } 52 | 53 | var s *bufio.Scanner 54 | _, err = f.Seek(0, 0) 55 | if err != nil { 56 | return KernelConfig, err 57 | } 58 | 59 | var reader *gzip.Reader 60 | //magic number for gzip is 0x1f8b 61 | if magic[0] == 0x1f && magic[1] == 0x8b { 62 | // gzip file 63 | reader, err = gzip.NewReader(f) 64 | if err != nil { 65 | return KernelConfig, err 66 | } 67 | s = bufio.NewScanner(reader) 68 | } else { 69 | // not gzip file 70 | s = bufio.NewScanner(f) 71 | } 72 | 73 | if err = parse(s, KernelConfig); err != nil { 74 | return KernelConfig, err 75 | } 76 | return KernelConfig, nil 77 | } 78 | 79 | // IsContainedInCgroup returns true if the process is running in a container. 80 | func IsContainer() (bool, error) { 81 | return false, nil 82 | } 83 | -------------------------------------------------------------------------------- /pkg/util/ebpf/bpf_linux.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | package ebpf 5 | 6 | import ( 7 | "bufio" 8 | "compress/gzip" 9 | "fmt" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | const ( 15 | SysKernelBtfVmlinux = "/sys/kernel/btf/vmlinux" 16 | ConfigDebugInfoBtf = "CONFIG_DEBUG_INFO_BTF" 17 | ProcContainerCgroupPath = "/proc/1/cgroup" 18 | ProcContainerSchedPath = "/proc/1/sched" 19 | ) 20 | 21 | var ( 22 | // use same list of locations as libbpf 23 | // https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122 24 | 25 | locations = []string{ 26 | "/boot/vmlinux-%s", 27 | "/lib/modules/%s/vmlinux-%[1]s", 28 | "/lib/modules/%s/build/vmlinux", 29 | "/usr/lib/modules/%s/kernel/vmlinux", 30 | "/usr/lib/debug/boot/vmlinux-%s", 31 | "/usr/lib/debug/boot/vmlinux-%s.debug", 32 | "/usr/lib/debug/lib/modules/%s/vmlinux", 33 | } 34 | 35 | configPaths = []string{ 36 | "/proc/config.gz", 37 | "/boot/config", 38 | "/boot/config-%s", 39 | } 40 | ) 41 | 42 | func GetSystemConfig() (map[string]string, error) { 43 | var KernelConfig = make(map[string]string) 44 | var found bool 45 | i, e := getOSUnamer() 46 | if e != nil { 47 | return KernelConfig, e 48 | } 49 | 50 | var err error 51 | for _, system_config_path := range configPaths { 52 | var bootConf = system_config_path 53 | if strings.Index(system_config_path, "%s") != -1 { 54 | bootConf = fmt.Sprintf(system_config_path, i.Release) 55 | } 56 | 57 | KernelConfig, e = getLinuxConfig(bootConf) 58 | if e != nil { 59 | err = e 60 | continue 61 | } 62 | 63 | if len(KernelConfig) > 0 { 64 | found = true 65 | break 66 | } 67 | } 68 | 69 | if !found { 70 | return nil, fmt.Errorf("KernelConfig not found. with error: %v", err) 71 | } 72 | return KernelConfig, nil 73 | } 74 | 75 | func getLinuxConfig(filename string) (map[string]string, error) { 76 | var KernelConfig = make(map[string]string) 77 | 78 | // Open file bootConf. 79 | f, err := os.Open(filename) 80 | if err != nil { 81 | return KernelConfig, err 82 | } 83 | defer f.Close() 84 | 85 | // check if the file is gzipped 86 | var magic []byte 87 | var i int 88 | magic = make([]byte, 2) 89 | i, err = f.Read(magic) 90 | if err != nil { 91 | return KernelConfig, err 92 | } 93 | if i != 2 { 94 | return KernelConfig, fmt.Errorf("read %d bytes, expected 2", i) 95 | } 96 | 97 | var s *bufio.Scanner 98 | _, err = f.Seek(0, 0) 99 | if err != nil { 100 | return KernelConfig, err 101 | } 102 | 103 | var reader *gzip.Reader 104 | //magic number for gzip is 0x1f8b 105 | if magic[0] == 0x1f && magic[1] == 0x8b { 106 | // gzip file 107 | reader, err = gzip.NewReader(f) 108 | if err != nil { 109 | return KernelConfig, err 110 | } 111 | s = bufio.NewScanner(reader) 112 | } else { 113 | // not gzip file 114 | s = bufio.NewScanner(f) 115 | } 116 | 117 | if err = parse(s, KernelConfig); err != nil { 118 | return KernelConfig, err 119 | } 120 | return KernelConfig, nil 121 | } 122 | 123 | // IsContainer returns true if the process is running in a container. 124 | func IsContainer() (bool, error) { 125 | b, e := isCOntainerCgroup() 126 | if e != nil { 127 | return false, e 128 | } 129 | 130 | // if b is true, it's a container 131 | if b { 132 | return true, nil 133 | } 134 | 135 | // if b is false, continue to check sched 136 | b, e = isCOntainerSched() 137 | if e != nil { 138 | return false, e 139 | } 140 | 141 | return b, nil 142 | } 143 | 144 | // isCOntainerCgroup returns true if the process is running in a container. 145 | // https://www.baeldung.com/linux/is-process-running-inside-container 146 | 147 | func isCOntainerCgroup() (bool, error) { 148 | var f *os.File 149 | var err error 150 | var i int 151 | f, err = os.Open(ProcContainerCgroupPath) 152 | if err != nil { 153 | return false, err 154 | } 155 | defer f.Close() 156 | b := make([]byte, 1024) 157 | i, err = f.Read(b) 158 | if err != nil { 159 | return false, err 160 | } 161 | switch { 162 | case strings.Contains(string(b[:i]), "cpuset:/docker"): 163 | // CGROUP V1 docker container 164 | return true, nil 165 | case strings.Contains(string(b[:i]), "cpuset:/kubepods"): 166 | // k8s container 167 | return true, nil 168 | case strings.Contains(string(b[:i]), "0::/\n"): 169 | // CGROUP V2 docker container 170 | return true, nil 171 | } 172 | 173 | return false, nil 174 | } 175 | 176 | // isCOntainerSched returns true if the process is running in a container. 177 | // https://man7.org/linux/man-pages/man7/sched.7.html 178 | func isCOntainerSched() (bool, error) { 179 | var f *os.File 180 | var err error 181 | var i int 182 | f, err = os.Open(ProcContainerSchedPath) 183 | if err != nil { 184 | return false, err 185 | } 186 | defer f.Close() 187 | b := make([]byte, 1024) 188 | i, err = f.Read(b) 189 | if err != nil { 190 | return false, err 191 | } 192 | switch { 193 | case strings.Contains(string(b[:i]), "bash (1, #threads"): 194 | return true, nil 195 | case strings.Contains(string(b[:i]), "run-on-arch-com (1, #threads"): 196 | return true, nil 197 | case strings.Contains(string(b[:i]), "init (1, #threads:"): 198 | return false, nil 199 | case strings.Contains(string(b[:i]), "systemd (1, #threads"): 200 | return false, nil 201 | } 202 | 203 | return false, nil 204 | } 205 | -------------------------------------------------------------------------------- /pkg/util/ebpf/config.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/pkg/util/ebpf/config.gz -------------------------------------------------------------------------------- /pkg/util/ebpf/parse.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | func parse(s *bufio.Scanner, p map[string]string) error { 11 | r, _ := regexp.Compile("^(?:# *)?(CONFIG_\\w*)(?:=| )(y|n|m|is not set|\\d+|0x.+|\".*\")$") 12 | 13 | for s.Scan() { 14 | 15 | t := s.Text() 16 | 17 | // Skip line if empty. 18 | if t == "" { 19 | continue 20 | } 21 | 22 | // 0 is the match of the entire expression, 23 | // 1 is the key, 2 is the value. 24 | m := r.FindStringSubmatch(t) 25 | if m == nil { 26 | continue 27 | } 28 | 29 | if len(m) != 3 { 30 | return fmt.Errorf("match is not 3 chars long: %v", m) 31 | } 32 | // Remove all leading and trailing double quotes from the value. 33 | if len(m[2]) > 1 { 34 | m[2] = strings.Trim(m[2], "\"") 35 | } 36 | 37 | // Insert entry into map. 38 | p[m[1]] = m[2] 39 | } 40 | 41 | if err := s.Err(); err != nil { 42 | return err 43 | } 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/util/hkdf/hkdf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 CFC4N . All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package hkdf 16 | 17 | // Copyright 2014 The Go Authors. All rights reserved. 18 | // Use of this source code is governed by a BSD-style 19 | // license that can be found in the LICENSE file. 20 | 21 | // Mostly derived from golang.org/x/crypto/hkdf, but with an exposed 22 | // Extract API. 23 | // 24 | // HKDF is a cryptographic key derivation function (KDF) with the goal of 25 | // expanding limited input keying material into one or more cryptographically 26 | // strong secret keys. 27 | // 28 | // RFC 5869: https://tools.ietf.org/html/rfc5869 29 | 30 | import ( 31 | "crypto" 32 | 33 | "golang.org/x/crypto/cryptobyte" 34 | "golang.org/x/crypto/hkdf" 35 | ) 36 | 37 | const ( 38 | ResumptionBinderLabel = "res binder" 39 | ClientHandshakeTrafficLabel = "c hs traffic" 40 | ServerHandshakeTrafficLabel = "s hs traffic" 41 | ClientApplicationTrafficLabel = "c ap traffic" 42 | ServerApplicationTrafficLabel = "s ap traffic" 43 | ExporterLabel = "exp master" 44 | ResumptionLabel = "res master" 45 | TrafficUpdateLabel = "traffic upd" 46 | ) 47 | 48 | const ( 49 | KeyLogLabelTLS12 = "CLIENT_RANDOM" 50 | KeyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET" 51 | KeyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET" 52 | KeyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0" 53 | KeyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0" 54 | KeyLogLabelExporterSecret = "EXPORTER_SECRET" 55 | KeyLogLabelClientEarlyTafficSecret = "CLIENT_EARLY_TRAFFIC_SECRET" 56 | ) 57 | 58 | // crypto/tls/cipher_suites.go line 678 59 | // TLS 1.3 cipher suites. 60 | const ( 61 | TlsAes128GcmSha256 uint16 = 0x1301 62 | TlsAes256GcmSha384 uint16 = 0x1302 63 | TlsChacha20Poly1305Sha256 uint16 = 0x1303 64 | ) 65 | 66 | // ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. 67 | func ExpandLabel(secret []byte, label string, context []byte, length int, transcript crypto.Hash) []byte { 68 | var hkdfLabel cryptobyte.Builder 69 | hkdfLabel.AddUint16(uint16(length)) 70 | hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { 71 | b.AddBytes([]byte("tls13 ")) 72 | b.AddBytes([]byte(label)) 73 | }) 74 | hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { 75 | b.AddBytes(context[:length]) 76 | }) 77 | out := make([]byte, length) 78 | 79 | n, err := hkdf.Expand(transcript.New, secret[:length], hkdfLabel.BytesOrPanic()).Read(out) 80 | if err != nil || n != length { 81 | panic("tls: HKDF-Expand-Label invocation failed unexpectedly") 82 | } 83 | return out 84 | } 85 | -------------------------------------------------------------------------------- /pkg/util/hkdf/hkdf_test.go: -------------------------------------------------------------------------------- 1 | package hkdf 2 | 3 | import ( 4 | "crypto" 5 | _ "crypto/sha256" 6 | _ "crypto/sha512" 7 | "testing" 8 | ) 9 | 10 | func TestHkdf(t *testing.T) { 11 | t.Log("TestHkdf") 12 | //TODO 13 | var length int 14 | var transcript crypto.Hash 15 | var cipherId uint32 = 50336513 16 | // test with different cipherID 17 | switch uint16(cipherId & 0x0000FFFF) { 18 | case TlsAes128GcmSha256, TlsChacha20Poly1305Sha256: 19 | length = 32 20 | transcript = crypto.SHA256 21 | case TlsAes256GcmSha384: 22 | length = 48 23 | transcript = crypto.SHA384 24 | default: 25 | t.Log("Unknown cipher") 26 | } 27 | 28 | // HandshakeSecret 29 | handshakeSecret := []byte{0x33, 0xad, 0x0a, 0x1c, 0x60, 0x7e, 0xc0, 0x3b, 0x09, 0xe6, 0xcd, 0x98, 0x93, 0x68, 0x0c, 0xe2, 0x10, 0xad, 0xf3, 0x00, 0xaa, 0x1f, 0x26, 0x60, 0xe1, 0xb2, 0x2e, 0x10, 0xf1, 0x70, 0xf9, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 30 | // MasterSecret 31 | masterSecret := []byte{0x95, 0x8a, 0x8a, 0xfc, 0xa1, 0x12, 0x34, 0xac, 0x64, 0xf5, 0x9b, 0x09, 0xb3, 0xd2, 0xaf, 0xbb, 0xd3, 0xe8, 0x46, 0x4d, 0xce, 0xa5, 0x27, 0x6d, 0x30, 0xd4, 0x26, 0x85, 0x5a, 0x7a, 0xde, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 32 | // HandshakeTrafficHash 33 | handshakeTrafficHash := []byte{0x89, 0xf6, 0x4d, 0x87, 0xef, 0xf1, 0x30, 0x91, 0xfb, 0xa9, 0x9f, 0x20, 0x20, 0xb5, 0x16, 0xf9, 0x3a, 0x4a, 0xc4, 0x51, 0x6a, 0xd2, 0x6a, 0x94, 0x62, 0x04, 0x27, 0xe4, 0xe9, 0xa1, 0xd5, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 34 | // ServerFinishedHash 35 | serverFinishedHash := []byte{0x75, 0x28, 0xcb, 0x64, 0x54, 0xfb, 0x4c, 0xab, 0xbc, 0x00, 0x6a, 0x64, 0x01, 0xaa, 0xc6, 0xf3, 0x7f, 0x7d, 0xa0, 0x82, 0x72, 0x66, 0xc0, 0x6c, 0x5b, 0xbd, 0x96, 0x91, 0x02, 0xac, 0xf6, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 36 | // ExporterMasterSecret 37 | exporterMasterSecret := []byte{0x7d, 0x43, 0x64, 0x4b, 0xfa, 0x10, 0x38, 0x66, 0x18, 0x54, 0xac, 0x12, 0xff, 0x72, 0x6c, 0xcf, 0x26, 0xbe, 0x8d, 0x80, 0x8c, 0xf2, 0x0b, 0x16, 0x05, 0x71, 0xb1, 0xd4, 0xd4, 0xab, 0x5e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 38 | 39 | clientHandshakeSecret := ExpandLabel(handshakeSecret[:length], 40 | ClientHandshakeTrafficLabel, handshakeTrafficHash[:length], length, transcript) 41 | t.Logf("%s: %x", KeyLogLabelClientHandshake, clientHandshakeSecret) 42 | 43 | serverHandshakeSecret := ExpandLabel(handshakeSecret[:length], 44 | ServerHandshakeTrafficLabel, handshakeTrafficHash[:length], length, transcript) 45 | t.Logf("%s: %x", KeyLogLabelServerHandshake, serverHandshakeSecret) 46 | 47 | clientTrafficSecret := ExpandLabel(masterSecret[:length], 48 | ClientApplicationTrafficLabel, serverFinishedHash[:length], length, transcript) 49 | t.Logf("%s: %x", KeyLogLabelClientTraffic, clientTrafficSecret) 50 | 51 | serverTrafficSecret := ExpandLabel(masterSecret[:length], 52 | ServerApplicationTrafficLabel, serverFinishedHash[:length], length, transcript) 53 | t.Logf("%s: %x", KeyLogLabelServerTraffic, serverTrafficSecret) 54 | 55 | t.Logf("%s: %x", KeyLogLabelExporterSecret, exporterMasterSecret[:length]) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/util/kernel/kernel_version.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | // Copyright 2016-2017 Kinvolk 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | package kernel 19 | 20 | import ( 21 | "fmt" 22 | "io/ioutil" 23 | "regexp" 24 | "strconv" 25 | "strings" 26 | "syscall" 27 | ) 28 | 29 | var versionRegex = regexp.MustCompile(`^(\d+)\.(\d+)(?:.(\d+))?.*$`) 30 | 31 | // KernelVersionFromReleaseString converts a release string with format 32 | // 4.4.2[-1] to a kernel version number in LINUX_VERSION_CODE format. 33 | // That is, for kernel "a.b.c", the version number will be (a<<16 + b<<8 + c) 34 | func KernelVersionFromReleaseString(releaseString string) (uint32, error) { 35 | versionParts := versionRegex.FindStringSubmatch(releaseString) 36 | if len(versionParts) < 3 { 37 | return 0, fmt.Errorf("got invalid release version %q (expected format '4.3.2-1')", releaseString) 38 | } 39 | var major, minor, patch uint64 40 | var err error 41 | major, err = strconv.ParseUint(versionParts[1], 10, 8) 42 | if err != nil { 43 | return 0, err 44 | } 45 | 46 | minor, err = strconv.ParseUint(versionParts[2], 10, 8) 47 | if err != nil { 48 | return 0, err 49 | } 50 | 51 | // patch is optional 52 | if len(versionParts) >= 4 { 53 | patch, _ = strconv.ParseUint(versionParts[3], 10, 8) 54 | } 55 | 56 | // clamp patch/sublevel to 255 EARLY in 4.14.252 because they merged this too early: 57 | // https://github.com/torvalds/linux/commit/e131e0e880f942f138c4b5e6af944c7ddcd7ec96 58 | if major == 4 && minor == 14 && patch >= 252 { 59 | patch = 255 60 | } 61 | 62 | out := major*256*256 + minor*256 + patch 63 | return uint32(out), nil 64 | } 65 | 66 | func currentVersionUname() (uint32, error) { 67 | var buf syscall.Utsname 68 | if err := syscall.Uname(&buf); err != nil { 69 | return 0, err 70 | } 71 | releaseString := strings.Trim(utsnameStr(buf.Release[:]), "\x00") 72 | return KernelVersionFromReleaseString(releaseString) 73 | } 74 | 75 | func currentVersionUbuntu() (uint32, error) { 76 | procVersion, err := ioutil.ReadFile("/proc/version_signature") 77 | if err != nil { 78 | return 0, err 79 | } 80 | return parseUbuntuVersion(string(procVersion)) 81 | } 82 | 83 | func parseUbuntuVersion(procVersion string) (uint32, error) { 84 | var u1, u2, releaseString string 85 | _, err := fmt.Sscanf(procVersion, "%s %s %s", &u1, &u2, &releaseString) 86 | if err != nil { 87 | return 0, err 88 | } 89 | return KernelVersionFromReleaseString(releaseString) 90 | } 91 | 92 | var debianVersionRegex = regexp.MustCompile(`.* SMP Debian (\d+\.\d+.\d+-\d+)(?:\+[[:alnum:]]*)?.*`) 93 | 94 | func parseDebianVersion(str string) (uint32, error) { 95 | match := debianVersionRegex.FindStringSubmatch(str) 96 | if len(match) != 2 { 97 | return 0, fmt.Errorf("failed to parse kernel version from /proc/version: %s", str) 98 | } 99 | return KernelVersionFromReleaseString(match[1]) 100 | } 101 | 102 | func currentVersionDebian() (uint32, error) { 103 | procVersion, err := ioutil.ReadFile("/proc/version") 104 | if err != nil { 105 | return 0, fmt.Errorf("error reading /proc/version: %s", err) 106 | } 107 | 108 | return parseDebianVersion(string(procVersion)) 109 | } 110 | 111 | // CurrentKernelVersion returns the current kernel version in 112 | // LINUX_VERSION_CODE format (see KernelVersionFromReleaseString()) 113 | func CurrentKernelVersion() (uint32, error) { 114 | // We need extra checks for Debian and Ubuntu as they modify 115 | // the kernel version patch number for compatibility with 116 | // out-of-tree modules. Linux perf tools do the same for Ubuntu 117 | // systems: https://github.com/torvalds/linux/commit/d18acd15c 118 | // 119 | // See also: 120 | // https://kernel-handbook.alioth.debian.org/ch-versions.html 121 | // https://wiki.ubuntu.com/Kernel/FAQ 122 | version, err := currentVersionUbuntu() 123 | if err == nil { 124 | return version, nil 125 | } 126 | version, err = currentVersionDebian() 127 | if err == nil { 128 | return version, nil 129 | } 130 | return currentVersionUname() 131 | } 132 | 133 | func utsnameStr(in []int8) string { 134 | out := make([]byte, len(in)) 135 | for i := 0; i < len(in); i++ { 136 | if in[i] == 0 { 137 | break 138 | } 139 | out = append(out, byte(in[i])) 140 | } 141 | return string(out) 142 | } 143 | -------------------------------------------------------------------------------- /pkg/util/kernel/kernel_version_unsupport.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | // +build !linux 3 | 4 | package kernel 5 | 6 | import ( 7 | "fmt" 8 | 9 | "runtime" 10 | ) 11 | 12 | var ErrNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) 13 | 14 | func KernelVersionFromReleaseString(releaseString string) (uint32, error) { 15 | return 0, ErrNonLinux 16 | } 17 | 18 | func CurrentKernelVersion() (uint32, error) { 19 | return 0, ErrNonLinux 20 | } 21 | -------------------------------------------------------------------------------- /pkg/util/kernel/version.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package kernel 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | // Version is a numerical representation of a kernel version 11 | type Version uint32 12 | 13 | var hostVersion Version 14 | 15 | // String returns a string representing the version in x.x.x format 16 | func (v Version) String() string { 17 | a, b, c := v>>16, v>>8&0xff, v&0xff 18 | return fmt.Sprintf("%d.%d.%d", a, b, c) 19 | } 20 | 21 | // HostVersion returns the running kernel version of the host 22 | func HostVersion() (Version, error) { 23 | if hostVersion != 0 { 24 | return hostVersion, nil 25 | } 26 | kv, err := CurrentKernelVersion() 27 | if err != nil { 28 | return 0, err 29 | } 30 | hostVersion = Version(kv) 31 | return hostVersion, nil 32 | } 33 | 34 | // ParseVersion parses a string in the format of x.x.x to a Version 35 | func ParseVersion(s string) Version { 36 | var a, b, c byte 37 | fmt.Sscanf(s, "%d.%d.%d", &a, &b, &c) 38 | return VersionCode(a, b, c) 39 | } 40 | 41 | // VersionCode returns a Version computed from the individual parts of a x.x.x version 42 | func VersionCode(major, minor, patch byte) Version { 43 | // KERNEL_VERSION(a,b,c) = (a << 16) + (b << 8) + (c) 44 | // Per https://github.com/torvalds/linux/blob/db7c953555388571a96ed8783ff6c5745ba18ab9/Makefile#L1250 45 | return Version((uint32(major) << 16) + (uint32(minor) << 8) + uint32(patch)) 46 | } 47 | -------------------------------------------------------------------------------- /rtcagent.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Captures packets with eBPF Filter and sends them to Homer 3 | Wants=network.target 4 | After=network.target 5 | 6 | [Service] 7 | #this service file uses Kamailio binary for FreeSWITCH change the command accordingly 8 | ExecStart=/usr/bin/rtcagent kamailio -m /opt/kamailio/sbin/kamailio --hep-server="127.0.0.1" --hep-port="9060" --hep-transport="tcp" --debug=false 9 | ExecStop=/bin/kill ${MAINPID} 10 | Restart=on-failure 11 | RestartSec=10s 12 | Type=simple 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /rtcagent.yml: -------------------------------------------------------------------------------- 1 | name: rtcagent 2 | arch: amd64 3 | platform: linux 4 | version: 0.0.0 5 | release: 1 6 | section: default 7 | priority: extra 8 | replaces: 9 | - rtcagent 10 | provides: 11 | - rtcagent 12 | maintainer: Alexandr Dubovikob 13 | description: RTCagent is a HEP/eBPF sniffer for VoiP/RTC Applications 14 | vendor: sipcapture.org 15 | homepage: http://sipcapture.org 16 | license: AGPLv3 17 | contents: 18 | - src: ./bin/rtcagent 19 | dst: /usr/bin/rtcagent 20 | -------------------------------------------------------------------------------- /runagent.sh: -------------------------------------------------------------------------------- 1 | ./bin/rtcagent kamailio --hep-port=9060 --hep-server=homer.null.qxip.net 2 | #./bin/rtcagent opensips --hep-port=9060 --hep-server=homer.null.qxip.net 3 | -------------------------------------------------------------------------------- /user/bytecode/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/.gitkeep -------------------------------------------------------------------------------- /user/bytecode/freeswitch_kern.d: -------------------------------------------------------------------------------- 1 | user/bytecode/freeswitch_kern.o: kern/freeswitch_kern.c kern/freeswitch.h \ 2 | kern/rtcagent.h kern/bpf/x86/vmlinux.h kern/bpf/bpf_core_read.h \ 3 | kern/bpf/bpf_helpers.h kern/bpf/bpf_helper_defs.h \ 4 | kern/bpf/bpf_tracing.h kern/bpf/bpf_endian.h kern/common.h 5 | 6 | kern/freeswitch.h: 7 | 8 | kern/rtcagent.h: 9 | 10 | kern/bpf/x86/vmlinux.h: 11 | 12 | kern/bpf/bpf_core_read.h: 13 | 14 | kern/bpf/bpf_helpers.h: 15 | 16 | kern/bpf/bpf_helper_defs.h: 17 | 18 | kern/bpf/bpf_tracing.h: 19 | 20 | kern/bpf/bpf_endian.h: 21 | 22 | kern/common.h: 23 | -------------------------------------------------------------------------------- /user/bytecode/freeswitch_kern.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/freeswitch_kern.o -------------------------------------------------------------------------------- /user/bytecode/freeswitch_kern_less52.d: -------------------------------------------------------------------------------- 1 | user/bytecode/freeswitch_kern_less52.o: kern/freeswitch_kern.c \ 2 | kern/freeswitch.h kern/rtcagent.h kern/bpf/x86/vmlinux.h \ 3 | kern/bpf/bpf_core_read.h kern/bpf/bpf_helpers.h \ 4 | kern/bpf/bpf_helper_defs.h kern/bpf/bpf_tracing.h \ 5 | kern/bpf/bpf_endian.h kern/common.h 6 | 7 | kern/freeswitch.h: 8 | 9 | kern/rtcagent.h: 10 | 11 | kern/bpf/x86/vmlinux.h: 12 | 13 | kern/bpf/bpf_core_read.h: 14 | 15 | kern/bpf/bpf_helpers.h: 16 | 17 | kern/bpf/bpf_helper_defs.h: 18 | 19 | kern/bpf/bpf_tracing.h: 20 | 21 | kern/bpf/bpf_endian.h: 22 | 23 | kern/common.h: 24 | -------------------------------------------------------------------------------- /user/bytecode/freeswitch_kern_less52.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/freeswitch_kern_less52.o -------------------------------------------------------------------------------- /user/bytecode/kamailio_kern.d: -------------------------------------------------------------------------------- 1 | user/bytecode/kamailio_kern.o: kern/kamailio_kern.c kern/kamailio.h \ 2 | kern/rtcagent.h kern/bpf/x86/vmlinux.h kern/bpf/bpf_core_read.h \ 3 | kern/bpf/bpf_helpers.h kern/bpf/bpf_helper_defs.h \ 4 | kern/bpf/bpf_tracing.h kern/bpf/bpf_endian.h kern/common.h 5 | 6 | kern/kamailio.h: 7 | 8 | kern/rtcagent.h: 9 | 10 | kern/bpf/x86/vmlinux.h: 11 | 12 | kern/bpf/bpf_core_read.h: 13 | 14 | kern/bpf/bpf_helpers.h: 15 | 16 | kern/bpf/bpf_helper_defs.h: 17 | 18 | kern/bpf/bpf_tracing.h: 19 | 20 | kern/bpf/bpf_endian.h: 21 | 22 | kern/common.h: 23 | -------------------------------------------------------------------------------- /user/bytecode/kamailio_kern.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/kamailio_kern.o -------------------------------------------------------------------------------- /user/bytecode/kamailio_kern_less52.d: -------------------------------------------------------------------------------- 1 | user/bytecode/kamailio_kern_less52.o: kern/kamailio_kern.c \ 2 | kern/kamailio.h kern/rtcagent.h kern/bpf/x86/vmlinux.h \ 3 | kern/bpf/bpf_core_read.h kern/bpf/bpf_helpers.h \ 4 | kern/bpf/bpf_helper_defs.h kern/bpf/bpf_tracing.h \ 5 | kern/bpf/bpf_endian.h kern/common.h 6 | 7 | kern/kamailio.h: 8 | 9 | kern/rtcagent.h: 10 | 11 | kern/bpf/x86/vmlinux.h: 12 | 13 | kern/bpf/bpf_core_read.h: 14 | 15 | kern/bpf/bpf_helpers.h: 16 | 17 | kern/bpf/bpf_helper_defs.h: 18 | 19 | kern/bpf/bpf_tracing.h: 20 | 21 | kern/bpf/bpf_endian.h: 22 | 23 | kern/common.h: 24 | -------------------------------------------------------------------------------- /user/bytecode/kamailio_kern_less52.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/kamailio_kern_less52.o -------------------------------------------------------------------------------- /user/bytecode/monitor_kern.d: -------------------------------------------------------------------------------- 1 | user/bytecode/monitor_kern.o: kern/monitor_kern.c kern/bpf/x86/vmlinux.h \ 2 | kern/bpf/bpf_helpers.h kern/bpf/bpf_helper_defs.h \ 3 | kern/bpf/bpf_endian.h kern/bpf/bpf_tracing.h kern/bpf/bpf_core_read.h 4 | 5 | kern/bpf/x86/vmlinux.h: 6 | 7 | kern/bpf/bpf_helpers.h: 8 | 9 | kern/bpf/bpf_helper_defs.h: 10 | 11 | kern/bpf/bpf_endian.h: 12 | 13 | kern/bpf/bpf_tracing.h: 14 | 15 | kern/bpf/bpf_core_read.h: 16 | -------------------------------------------------------------------------------- /user/bytecode/monitor_kern.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/monitor_kern.o -------------------------------------------------------------------------------- /user/bytecode/monitor_kern_less52.d: -------------------------------------------------------------------------------- 1 | user/bytecode/monitor_kern_less52.o: kern/monitor_kern.c \ 2 | kern/bpf/x86/vmlinux.h kern/bpf/bpf_helpers.h \ 3 | kern/bpf/bpf_helper_defs.h kern/bpf/bpf_endian.h \ 4 | kern/bpf/bpf_tracing.h kern/bpf/bpf_core_read.h 5 | 6 | kern/bpf/x86/vmlinux.h: 7 | 8 | kern/bpf/bpf_helpers.h: 9 | 10 | kern/bpf/bpf_helper_defs.h: 11 | 12 | kern/bpf/bpf_endian.h: 13 | 14 | kern/bpf/bpf_tracing.h: 15 | 16 | kern/bpf/bpf_core_read.h: 17 | -------------------------------------------------------------------------------- /user/bytecode/monitor_kern_less52.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/monitor_kern_less52.o -------------------------------------------------------------------------------- /user/bytecode/opensips_kern.d: -------------------------------------------------------------------------------- 1 | user/bytecode/opensips_kern.o: kern/opensips_kern.c kern/opensips.h \ 2 | kern/rtcagent.h kern/bpf/x86/vmlinux.h kern/bpf/bpf_core_read.h \ 3 | kern/bpf/bpf_helpers.h kern/bpf/bpf_helper_defs.h \ 4 | kern/bpf/bpf_tracing.h kern/bpf/bpf_endian.h kern/common.h 5 | 6 | kern/opensips.h: 7 | 8 | kern/rtcagent.h: 9 | 10 | kern/bpf/x86/vmlinux.h: 11 | 12 | kern/bpf/bpf_core_read.h: 13 | 14 | kern/bpf/bpf_helpers.h: 15 | 16 | kern/bpf/bpf_helper_defs.h: 17 | 18 | kern/bpf/bpf_tracing.h: 19 | 20 | kern/bpf/bpf_endian.h: 21 | 22 | kern/common.h: 23 | -------------------------------------------------------------------------------- /user/bytecode/opensips_kern.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/opensips_kern.o -------------------------------------------------------------------------------- /user/bytecode/opensips_kern_less52.d: -------------------------------------------------------------------------------- 1 | user/bytecode/opensips_kern_less52.o: kern/opensips_kern.c \ 2 | kern/opensips.h kern/rtcagent.h kern/bpf/x86/vmlinux.h \ 3 | kern/bpf/bpf_core_read.h kern/bpf/bpf_helpers.h \ 4 | kern/bpf/bpf_helper_defs.h kern/bpf/bpf_tracing.h \ 5 | kern/bpf/bpf_endian.h kern/common.h 6 | 7 | kern/opensips.h: 8 | 9 | kern/rtcagent.h: 10 | 11 | kern/bpf/x86/vmlinux.h: 12 | 13 | kern/bpf/bpf_core_read.h: 14 | 15 | kern/bpf/bpf_helpers.h: 16 | 17 | kern/bpf/bpf_helper_defs.h: 18 | 19 | kern/bpf/bpf_tracing.h: 20 | 21 | kern/bpf/bpf_endian.h: 22 | 23 | kern/common.h: 24 | -------------------------------------------------------------------------------- /user/bytecode/opensips_kern_less52.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/opensips_kern_less52.o -------------------------------------------------------------------------------- /user/bytecode/tcprtt_kern.d: -------------------------------------------------------------------------------- 1 | user/bytecode/tcprtt_kern.o: kern/tcprtt_kern.c kern/common2.h \ 2 | kern/bpf/bpf_helpers.h kern/bpf/bpf_helper_defs.h \ 3 | kern/bpf/bpf_endian.h kern/bpf/bpf_tracing.h 4 | 5 | kern/common2.h: 6 | 7 | kern/bpf/bpf_helpers.h: 8 | 9 | kern/bpf/bpf_helper_defs.h: 10 | 11 | kern/bpf/bpf_endian.h: 12 | 13 | kern/bpf/bpf_tracing.h: 14 | -------------------------------------------------------------------------------- /user/bytecode/tcprtt_kern.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/tcprtt_kern.o -------------------------------------------------------------------------------- /user/bytecode/tcprtt_kern_less52.d: -------------------------------------------------------------------------------- 1 | user/bytecode/tcprtt_kern_less52.o: kern/tcprtt_kern.c kern/common2.h \ 2 | kern/bpf/bpf_helpers.h kern/bpf/bpf_helper_defs.h \ 3 | kern/bpf/bpf_endian.h kern/bpf/bpf_tracing.h 4 | 5 | kern/common2.h: 6 | 7 | kern/bpf/bpf_helpers.h: 8 | 9 | kern/bpf/bpf_helper_defs.h: 10 | 11 | kern/bpf/bpf_endian.h: 12 | 13 | kern/bpf/bpf_tracing.h: 14 | -------------------------------------------------------------------------------- /user/bytecode/tcprtt_kern_less52.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipcapture/rtcagent/6e4bba460ef57079552fcd03e4b790b6a712d4c3/user/bytecode/tcprtt_kern_less52.o -------------------------------------------------------------------------------- /user/config/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package config 23 | 24 | import ( 25 | "bufio" 26 | "debug/elf" 27 | "fmt" 28 | "log" 29 | "os" 30 | "path/filepath" 31 | "strings" 32 | 33 | "errors" 34 | ) 35 | 36 | func GlobMany(targets []string, onErr func(string, error)) []string { 37 | rv := make([]string, 0, 20) 38 | addFile := func(path string, fi os.FileInfo, err error) error { 39 | if err != nil { 40 | log.Println(err.Error()) 41 | return err 42 | } 43 | rv = append(rv, path) 44 | return err 45 | } 46 | 47 | for _, p := range targets { 48 | // "p" is a wildcard pattern? expand it: 49 | if strings.Contains(p, "*") { 50 | matches, err := filepath.Glob(p) 51 | if err == nil { 52 | // walk each match: 53 | for _, p := range matches { 54 | e := filepath.Walk(p, addFile) 55 | if e != nil { 56 | continue 57 | } 58 | } 59 | } 60 | // path is not a wildcard, walk it: 61 | } else { 62 | e := filepath.Walk(p, addFile) 63 | if e != nil { 64 | return []string{} 65 | } 66 | } 67 | } 68 | return rv 69 | } 70 | 71 | // ParseDynLibConf reads/parses DL config files defined as a pattern 72 | // and returns a list of directories found in there (or an error). 73 | func ParseDynLibConf(pattern string) (dirs []string, err error) { 74 | files := GlobMany([]string{pattern}, nil) 75 | 76 | for _, configFile := range files { 77 | if strings.Contains(configFile, "lib32") { 78 | continue 79 | } 80 | fd, err := os.Open(configFile) 81 | if err != nil { 82 | return dirs, err 83 | } 84 | defer fd.Close() 85 | 86 | sc := bufio.NewScanner(fd) 87 | for sc.Scan() { 88 | line := strings.TrimSpace(sc.Text()) 89 | // ignore comments and empty lines 90 | if len(line) == 0 || line[0] == '#' || line[0] == ';' { 91 | continue 92 | } 93 | // found "include" directive? 94 | words := strings.Fields(line) 95 | if strings.ToLower(words[0]) == "include" { 96 | subdirs, err := ParseDynLibConf(words[1]) 97 | if err != nil && !os.IsNotExist(err) { 98 | return dirs, err 99 | } 100 | dirs = append(dirs, subdirs...) 101 | } else { 102 | dirs = append(dirs, line) 103 | } 104 | } 105 | } 106 | if len(dirs) <= 0 { 107 | err = errors.New(fmt.Sprintf("read keylogger :%s error .", pattern)) 108 | } 109 | return dirs, err 110 | } 111 | 112 | // getDynsFromElf get shared objects from ELF keylogger 113 | func getDynsFromElf(file string) ([]string, error) { 114 | f, e := elf.Open(file) 115 | if e != nil { 116 | return nil, e 117 | } 118 | neededs, err := f.DynString(elf.DT_NEEDED) 119 | return neededs, err 120 | } 121 | 122 | // getDynPathByElf found soPath by soName from elfName 123 | func getDynPathByElf(elfName, soName string) (string, error) { 124 | 125 | sos, e := getDynsFromElf(elfName) 126 | if e != nil { 127 | return "", e 128 | } 129 | 130 | // search dynamic library form ld.so.conf 131 | var searchPath = GetDynLibDirs() 132 | realSoName := recurseDynStrings(sos, searchPath, soName) 133 | 134 | // if not found soName from elfName 135 | if len(realSoName) == 0 { 136 | return "", errors.New(fmt.Sprintf("cant found so lib from %s", elfName)) 137 | } 138 | return realSoName, nil 139 | } 140 | 141 | func recurseDynStrings(dynSym []string, searchPath []string, soName string) string { 142 | var realSoName string 143 | for _, el := range dynSym { 144 | // check keylogger path here for library if it doesnot exists panic 145 | var fd *os.File 146 | for _, entry := range searchPath { 147 | path := filepath.Join(entry, el) 148 | if _, err := os.Stat(path); !os.IsNotExist(err) { 149 | fd, err = os.OpenFile(path, os.O_RDONLY, 0644) 150 | if err != nil { 151 | //log.Fatal(err) 152 | fmt.Printf("open keylogger:%s error:%v\n", path, err) 153 | continue 154 | } else { 155 | // found 156 | if strings.HasPrefix(filepath.Base(path), soName) { 157 | realSoName = path 158 | break 159 | } 160 | 161 | // not match ,will open it, and recurse it 162 | } 163 | } 164 | } 165 | 166 | if len(realSoName) > 0 { 167 | return realSoName 168 | } 169 | 170 | if fd == nil { 171 | log.Fatal(fmt.Sprintf("cant found lib so:%s in dirs:%v", el, searchPath)) 172 | } 173 | 174 | bint, err := elf.NewFile(fd) 175 | if err != nil { 176 | log.Fatal(err) 177 | } 178 | 179 | bDynSym, err := bint.DynString(elf.DT_NEEDED) 180 | if err != nil { 181 | log.Fatal(err) 182 | } 183 | 184 | realSoName = recurseDynStrings(bDynSym, searchPath, soName) 185 | if len(realSoName) > 0 { 186 | return realSoName 187 | } 188 | } 189 | // not found 190 | return "" 191 | } 192 | -------------------------------------------------------------------------------- /user/config/common_androidgki.go: -------------------------------------------------------------------------------- 1 | //go:build androidgki 2 | // +build androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | // https://source.android.com/devices/architecture/vndk/linker-namespace 28 | var ( 29 | default_so_paths = []string{ 30 | "/data/asan/system/lib64", 31 | "/apex/com.android.conscrypt/lib64", 32 | "/apex/com.android.runtime/lib64/bionic", 33 | } 34 | ) 35 | 36 | const ElfArchIsandroid = true 37 | 38 | func GetDynLibDirs() []string { 39 | return default_so_paths 40 | } 41 | -------------------------------------------------------------------------------- /user/config/common_linux.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | import "log" 28 | 29 | const ( 30 | LdLoadPath = "/etc/ld.so.conf" 31 | ElfArchIsandroid = false 32 | ) 33 | 34 | /* 35 | 1, the RPATH binary header (set at build-time) of the library causing the lookup (if any) 36 | 2, the RPATH binary header (set at build-time) of the executable 37 | 3, the LD_LIBRARY_PATH environment variable (set at run-time) 38 | 4, the RUNPATH binary header (set at build-time) of the executable 39 | 5, /etc/ld.so.cache 40 | 6, base library directories (/lib and /usr/lib) 41 | ref: http://blog.tremily.us/posts/rpath/ 42 | */ 43 | var ( 44 | default_so_paths = []string{ 45 | "/lib", 46 | "/usr/lib", 47 | "/usr/lib64", 48 | "/lib64", 49 | } 50 | ) 51 | 52 | func GetDynLibDirs() []string { 53 | dirs, err := ParseDynLibConf(LdLoadPath) 54 | if err != nil { 55 | log.Println(err.Error()) 56 | return default_so_paths 57 | } 58 | return append(dirs, "/lib64", "/usr/lib64") 59 | } 60 | -------------------------------------------------------------------------------- /user/config/config_freeswitch.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | import ( 28 | "bytes" 29 | "debug/elf" 30 | "errors" 31 | "os" 32 | "strings" 33 | ) 34 | 35 | type FreeSwitchType uint8 36 | 37 | const ( 38 | FreeSwitchTypeUnknow FreeSwitchType = iota 39 | FreeSwitchType5 40 | FreeSwitchType4 41 | FreeSwitchType3 42 | FreeSwitchType2 43 | ) 44 | 45 | // freeswitch 46 | type FreeSwitchConfig struct { 47 | eConfig 48 | FreeSwitchpath string `json:"freeswitchPath"` 49 | ElfType uint8 // 50 | Version FreeSwitchType // 51 | VersionInfo string // info 52 | } 53 | 54 | func NewFreeSwitchConfig() *FreeSwitchConfig { 55 | config := &FreeSwitchConfig{} 56 | return config 57 | } 58 | 59 | func (this *FreeSwitchConfig) Check() error { 60 | 61 | if this.FreeSwitchpath == "" || len(strings.TrimSpace(this.FreeSwitchpath)) <= 0 { 62 | return errors.New("FreeSwitch path cant be null.") 63 | } 64 | 65 | _, e := os.Stat(this.FreeSwitchpath) 66 | if e != nil { 67 | return e 68 | } 69 | this.ElfType = ElfTypeBin 70 | 71 | _elf, e := elf.Open(this.FreeSwitchpath) 72 | if e != nil { 73 | return e 74 | } 75 | 76 | //if funcName == "" { 77 | // return errors.New(fmt.Sprintf("cant match freeswitch 'receive_msg'function to hook with freeswitch file::%s", this.FreeSwitchpath)) 78 | //} 79 | 80 | this.Version = FreeSwitchType5 81 | this.VersionInfo = "freeswitch" 82 | 83 | found := strings.Contains("receive_msg", "COM_DATA") 84 | if found { 85 | roSection := _elf.Section(".rodata") 86 | var buf []byte 87 | buf, e = roSection.Data() 88 | var ver FreeSwitchType 89 | var verInfo string 90 | if e == nil { 91 | ver, verInfo = getFreeSwitchVer(buf) 92 | } 93 | this.Version = ver 94 | this.VersionInfo = verInfo 95 | } 96 | 97 | return nil 98 | } 99 | 100 | func getFreeSwitchVer(buf []byte) (FreeSwitchType, string) { 101 | 102 | var slice [][]byte 103 | 104 | if slice = bytes.Split(buf, []byte("\x00")); slice == nil { 105 | return FreeSwitchTypeUnknow, "" 106 | } 107 | 108 | length := len(slice) 109 | var offset int 110 | 111 | for i := 0; i < length; i++ { 112 | if len(slice[i]) == 0 { 113 | continue 114 | } 115 | 116 | l := len(slice[i]) 117 | if l > 15 || l < 8 { 118 | continue 119 | } 120 | 121 | freeswitchVer := string(slice[i]) 122 | if strings.Contains(freeswitchVer, "freeswitch 5.") { 123 | //fmt.Println(fmt.Sprintf("offset:%d, body:%s", offset, slice[i])) 124 | return FreeSwitchType5, freeswitchVer 125 | } else if strings.Contains(freeswitchVer, "freeswitch 4.") { 126 | return FreeSwitchType4, freeswitchVer 127 | } else if strings.Contains(freeswitchVer, "freeswitch 3.") { 128 | return FreeSwitchType3, freeswitchVer 129 | } 130 | offset += len(slice[i]) + 1 131 | } 132 | return FreeSwitchTypeUnknow, "" 133 | } 134 | -------------------------------------------------------------------------------- /user/config/config_kamailio.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | import ( 28 | "bytes" 29 | "debug/elf" 30 | "errors" 31 | "log" 32 | "os" 33 | "strings" 34 | ) 35 | 36 | type KamailioType uint8 37 | 38 | const ( 39 | KamailioTypeUnknow KamailioType = iota 40 | KamailioType5 41 | KamailioType4 42 | KamailioType3 43 | KamailioType2 44 | ) 45 | 46 | // kamailio 47 | type KamailioConfig struct { 48 | eConfig 49 | Kamailiopath string `json:"kamailioPath"` 50 | ElfType uint8 // 51 | Version KamailioType // 52 | VersionInfo string // info 53 | } 54 | 55 | func NewKamailioConfig() *KamailioConfig { 56 | config := &KamailioConfig{} 57 | return config 58 | } 59 | 60 | func (this *KamailioConfig) Check() error { 61 | 62 | if this.Kamailiopath == "" || len(strings.TrimSpace(this.Kamailiopath)) <= 0 { 63 | return errors.New("Kamailio path cant be null.") 64 | } 65 | 66 | if this.GetNoSearch() { 67 | log.Printf("RTCAGENT :: kamailio. No search") 68 | return nil 69 | } 70 | 71 | _, e := os.Stat(this.Kamailiopath) 72 | if e != nil { 73 | return e 74 | } 75 | this.ElfType = ElfTypeBin 76 | 77 | _elf, e := elf.Open(this.Kamailiopath) 78 | if e != nil { 79 | return e 80 | } 81 | 82 | //if funcName == "" { 83 | // return errors.New(fmt.Sprintf("cant match kamailio 'receive_msg'function to hook with kamailio file::%s", this.Kamailiopath)) 84 | //} 85 | 86 | this.Version = KamailioType5 87 | this.VersionInfo = "kamailio" 88 | 89 | found := strings.Contains("receive_msg", "COM_DATA") 90 | if found { 91 | roSection := _elf.Section(".rodata") 92 | var buf []byte 93 | buf, e = roSection.Data() 94 | var ver KamailioType 95 | var verInfo string 96 | if e == nil { 97 | ver, verInfo = getKamailioVer(buf) 98 | } 99 | this.Version = ver 100 | this.VersionInfo = verInfo 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func getKamailioVer(buf []byte) (KamailioType, string) { 107 | 108 | var slice [][]byte 109 | 110 | if slice = bytes.Split(buf, []byte("\x00")); slice == nil { 111 | return KamailioTypeUnknow, "" 112 | } 113 | 114 | length := len(slice) 115 | var offset int 116 | 117 | for i := 0; i < length; i++ { 118 | if len(slice[i]) == 0 { 119 | continue 120 | } 121 | 122 | l := len(slice[i]) 123 | if l > 15 || l < 8 { 124 | continue 125 | } 126 | 127 | kamailioVer := string(slice[i]) 128 | if strings.Contains(kamailioVer, "kamailio 5.") { 129 | //fmt.Println(fmt.Sprintf("offset:%d, body:%s", offset, slice[i])) 130 | return KamailioType5, kamailioVer 131 | } else if strings.Contains(kamailioVer, "kamailio 4.") { 132 | return KamailioType4, kamailioVer 133 | } else if strings.Contains(kamailioVer, "kamailio 3.") { 134 | return KamailioType3, kamailioVer 135 | } 136 | offset += len(slice[i]) + 1 137 | } 138 | return KamailioTypeUnknow, "" 139 | } 140 | -------------------------------------------------------------------------------- /user/config/config_monitor.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | import ( 28 | "errors" 29 | "log" 30 | "os" 31 | "rtcagent/model" 32 | "strings" 33 | ) 34 | 35 | type MonitorType uint8 36 | 37 | const ( 38 | MonitorTypeUnknow MonitorType = iota 39 | MonitorType5 40 | MonitorType4 41 | MonitorType3 42 | MonitorType2 43 | ) 44 | 45 | // tcprtt 46 | type MonitorConfig struct { 47 | eConfig 48 | Monitorpath string `json:"monitPath"` 49 | SysCall bool // 50 | UserCall bool // 51 | NetworkCall bool // 52 | ShowUserFunction bool // 53 | NetworkLatency bool // 54 | ElfType uint8 // 55 | Version MonitorType // 56 | VersionInfo string // info 57 | UserFunctions []string // user functions 58 | PromCh chan model.AggregatedMetricValue 59 | UiCh chan model.AggregatedTimeMetricValue 60 | } 61 | 62 | func NewMonitorConfig() *MonitorConfig { 63 | config := &MonitorConfig{} 64 | config.PromCh = make(chan model.AggregatedMetricValue, 500) 65 | config.UiCh = make(chan model.AggregatedTimeMetricValue, 500) 66 | 67 | return config 68 | } 69 | 70 | func (this *MonitorConfig) Check() error { 71 | 72 | if this.Monitorpath == "" || len(strings.TrimSpace(this.Monitorpath)) <= 0 { 73 | return errors.New("binary path cant be null.") 74 | } 75 | 76 | if this.GetNoSearch() { 77 | log.Printf("RTCAGENT :: binary. No search") 78 | return nil 79 | } 80 | 81 | _, e := os.Stat(this.Monitorpath) 82 | if e != nil { 83 | return e 84 | } 85 | this.ElfType = ElfTypeBin 86 | 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /user/config/config_opensips.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | import ( 28 | "bytes" 29 | "debug/elf" 30 | "errors" 31 | "log" 32 | "os" 33 | "strings" 34 | ) 35 | 36 | type OpensipsType uint8 37 | 38 | const ( 39 | OpensipsTypeUnknow OpensipsType = iota 40 | OpensipsType3 41 | OpensipsType2 42 | OpensipsType1 43 | ) 44 | 45 | // opensips 46 | type OpensipsConfig struct { 47 | eConfig 48 | Opensipspath string `json:"opensipsPath"` 49 | ElfType uint8 // 50 | Version OpensipsType // 51 | VersionInfo string // info 52 | } 53 | 54 | func NewOpensipsConfig() *OpensipsConfig { 55 | config := &OpensipsConfig{} 56 | return config 57 | } 58 | 59 | func (this *OpensipsConfig) Check() error { 60 | 61 | if this.Opensipspath == "" || len(strings.TrimSpace(this.Opensipspath)) <= 0 { 62 | return errors.New("Opensips path cant be null.") 63 | } 64 | 65 | if this.GetNoSearch() { 66 | log.Printf("RTCAGENT :: opensips. No search") 67 | return nil 68 | } 69 | 70 | _, e := os.Stat(this.Opensipspath) 71 | if e != nil { 72 | return e 73 | } 74 | this.ElfType = ElfTypeBin 75 | 76 | _elf, e := elf.Open(this.Opensipspath) 77 | if e != nil { 78 | return e 79 | } 80 | 81 | //if funcName == "" { 82 | // return errors.New(fmt.Sprintf("cant match opensips 'receive_msg'function to hook with opensips file::%s", this.Opensipspath)) 83 | //} 84 | 85 | this.Version = OpensipsType3 86 | this.VersionInfo = "opensips" 87 | 88 | found := strings.Contains("receive_msg", "COM_DATA") 89 | if found { 90 | roSection := _elf.Section(".rodata") 91 | var buf []byte 92 | buf, e = roSection.Data() 93 | var ver OpensipsType 94 | var verInfo string 95 | if e == nil { 96 | ver, verInfo = getOpensipsVer(buf) 97 | } 98 | this.Version = ver 99 | this.VersionInfo = verInfo 100 | } 101 | 102 | return nil 103 | } 104 | 105 | func getOpensipsVer(buf []byte) (OpensipsType, string) { 106 | 107 | var slice [][]byte 108 | 109 | if slice = bytes.Split(buf, []byte("\x00")); slice == nil { 110 | return OpensipsTypeUnknow, "" 111 | } 112 | 113 | length := len(slice) 114 | var offset int 115 | 116 | for i := 0; i < length; i++ { 117 | if len(slice[i]) == 0 { 118 | continue 119 | } 120 | 121 | l := len(slice[i]) 122 | if l > 15 || l < 8 { 123 | continue 124 | } 125 | 126 | opensipsVer := string(slice[i]) 127 | if strings.Contains(opensipsVer, "opensips 3.") { 128 | //fmt.Println(fmt.Sprintf("offset:%d, body:%s", offset, slice[i])) 129 | return OpensipsType3, opensipsVer 130 | } else if strings.Contains(opensipsVer, "opensips 2.") { 131 | return OpensipsType2, opensipsVer 132 | } else if strings.Contains(opensipsVer, "opensips 1.") { 133 | return OpensipsType1, opensipsVer 134 | } 135 | offset += len(slice[i]) + 1 136 | } 137 | return OpensipsTypeUnknow, "" 138 | } 139 | -------------------------------------------------------------------------------- /user/config/config_tcprtt.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package config 26 | 27 | type TcprttType uint8 28 | 29 | const ( 30 | TcprttTypeUnknow TcprttType = iota 31 | TcprttType5 32 | TcprttType4 33 | TcprttType3 34 | TcprttType2 35 | ) 36 | 37 | // tcprtt 38 | type TcprttConfig struct { 39 | eConfig 40 | Tcprttpath string `json:"tcprttPath"` 41 | ElfType uint8 // 42 | Version TcprttType // 43 | VersionInfo string // info 44 | } 45 | 46 | func NewTcprttConfig() *TcprttConfig { 47 | config := &TcprttConfig{} 48 | return config 49 | } 50 | 51 | func (this *TcprttConfig) Check() error { 52 | 53 | //if funcName == "" { 54 | // return errors.New(fmt.Sprintf("cant match tcprtt 'receive_msg'function to hook with tcprtt file::%s", this.Tcprttpath)) 55 | //} 56 | 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /user/config/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package config 23 | 24 | const ( 25 | ElfTypeBin uint8 = 1 26 | ElfTypeSo uint8 = 2 27 | ) 28 | 29 | const ( 30 | X86BinaryPrefix = "/lib/x86_64-linux-gnu" 31 | OthersBinaryPrefix = "/usr/lib" 32 | ) 33 | -------------------------------------------------------------------------------- /user/config/iconfig.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package config 23 | 24 | import "rtcagent/pkg/util/kernel" 25 | 26 | type IConfig interface { 27 | Check() error // 28 | GetPid() uint64 29 | GetUid() uint64 30 | GetHex() bool 31 | GetDebug() bool 32 | GetNoSearch() bool 33 | SetPid(uint64) 34 | SetUid(uint64) 35 | SetHex(bool) 36 | SetDebug(bool) 37 | SetNoSearch(bool) 38 | EnableGlobalVar() bool // 39 | } 40 | 41 | type eConfig struct { 42 | Pid uint64 43 | Uid uint64 44 | IsHex bool 45 | Debug bool 46 | NoSearch bool 47 | } 48 | 49 | func (this *eConfig) GetPid() uint64 { 50 | return this.Pid 51 | } 52 | 53 | func (this *eConfig) GetUid() uint64 { 54 | return this.Uid 55 | } 56 | 57 | func (this *eConfig) GetDebug() bool { 58 | return this.Debug 59 | } 60 | 61 | func (this *eConfig) GetHex() bool { 62 | return this.IsHex 63 | } 64 | 65 | func (this *eConfig) GetNoSearch() bool { 66 | return this.NoSearch 67 | } 68 | 69 | func (this *eConfig) SetPid(pid uint64) { 70 | this.Pid = pid 71 | } 72 | 73 | func (this *eConfig) SetUid(uid uint64) { 74 | this.Uid = uid 75 | } 76 | 77 | func (this *eConfig) SetDebug(b bool) { 78 | this.Debug = b 79 | } 80 | 81 | func (this *eConfig) SetHex(isHex bool) { 82 | this.IsHex = isHex 83 | } 84 | 85 | func (this *eConfig) SetNoSearch(noSearch bool) { 86 | this.NoSearch = noSearch 87 | } 88 | 89 | func (this *eConfig) EnableGlobalVar() bool { 90 | kv, err := kernel.HostVersion() 91 | if err != nil { 92 | //log.Fatal(err) 93 | return true 94 | } 95 | if kv < kernel.VersionCode(5, 2, 0) { 96 | return false 97 | } 98 | return true 99 | } 100 | -------------------------------------------------------------------------------- /user/event/event_tcprtt.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | LINK - http://github.com/sipcapture/rtcagent 6 | 7 | Copyright (C) 2023 QXIP B.V. 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Affero General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Affero General Public License for more details. 18 | 19 | You should have received a copy of the GNU Affero General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | package event 24 | 25 | import ( 26 | "bytes" 27 | "encoding/binary" 28 | "fmt" 29 | "net" 30 | "rtcagent/model" 31 | ) 32 | 33 | type TcprttEvent struct { 34 | Sport uint16 `json:"sport"` 35 | Dport uint16 `json:"dport"` 36 | Saddr uint32 `json:"saddr"` 37 | Daddr uint32 `json:"daddr"` 38 | SRTT uint32 `json:"srtt"` 39 | } 40 | 41 | func (tcpev *TcprttEvent) Decode(payload []byte) (err error) { 42 | 43 | buf := bytes.NewBuffer(payload) 44 | 45 | if err = binary.Read(buf, binary.LittleEndian, &tcpev.Sport); err != nil { 46 | return 47 | } 48 | 49 | if err = binary.Read(buf, binary.LittleEndian, &tcpev.Dport); err != nil { 50 | return 51 | } 52 | if err = binary.Read(buf, binary.LittleEndian, &tcpev.Saddr); err != nil { 53 | return 54 | } 55 | if err = binary.Read(buf, binary.LittleEndian, &tcpev.Daddr); err != nil { 56 | return 57 | } 58 | if err = binary.Read(buf, binary.LittleEndian, &tcpev.SRTT); err != nil { 59 | return 60 | } 61 | 62 | return nil 63 | } 64 | 65 | func (tcpev *TcprttEvent) GetUUID() string { 66 | return fmt.Sprintf("%d_%d", tcpev.Sport, tcpev.Dport) 67 | } 68 | 69 | func (tcpev *TcprttEvent) Payload() []byte { 70 | da := []byte{} 71 | return da 72 | } 73 | 74 | func (tcpev *TcprttEvent) PayloadLen() int { 75 | return 0 76 | } 77 | 78 | func (tcpev *TcprttEvent) StringHex() string { 79 | //var connInfo string 80 | //perfix = COLORGREEN 81 | //s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, %s, Version:%s, Mask: %d, Payload:\n%s", tcpev.Pid, CToGoString(tcpev.Comm[:]), 82 | // tcpev.Tid, connInfo, v, 1, b.String()) 83 | //s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, %s, Version:%s, SrcPort: %d, DstPort:%d, SrcIPv6: %d, DstIPv6: %d, Payload:\n%s", tcpev.Pid, CToGoString(tcpev.Comm[:]), 84 | // tcpev.Tid, connInfo, v.String(), tcpev.RcInfo.SrcPort, tcpev.RcInfo.DstPort, 1, 1, b.String()) 85 | return "a" 86 | } 87 | 88 | // intToIP converts IPv4 number to net.IP 89 | 90 | func intToIP(ipNum uint32) net.IP { 91 | ip := make(net.IP, 4) 92 | binary.LittleEndian.PutUint32(ip, ipNum) 93 | return ip 94 | } 95 | 96 | func (tcpev *TcprttEvent) String() string { 97 | //addr := tcpev.module.(*module.MOpenSSLProbe).GetConn(tcpev.Pid, tcpev.Fd) 98 | 99 | srcIP := intToIP(tcpev.Saddr) 100 | dstIP := intToIP(tcpev.Daddr) 101 | 102 | prefix := COLORGREEN 103 | 104 | s := fmt.Sprintf("%s%-15s %-6d -> %-15s %-6d %-6d%s", prefix, srcIP, tcpev.Sport, dstIP, tcpev.Dport, tcpev.SRTT, COLORRESET) 105 | return s 106 | } 107 | 108 | func (tcpev *TcprttEvent) SendHep() bool { 109 | //Lets allow to send HEP 110 | return false 111 | } 112 | 113 | func (tcpev *TcprttEvent) GenerateHEP() ([]byte, error) { 114 | 115 | return nil, fmt.Errorf("no data") 116 | 117 | } 118 | 119 | func (tcpev *TcprttEvent) Clone() IEventStruct { 120 | event := new(TcprttEvent) 121 | return event 122 | } 123 | 124 | func (tcpev *TcprttEvent) EventType() EventType { 125 | return 0 126 | } 127 | 128 | func (tcpev *TcprttEvent) DoCorrelation(userFunctionArray []string) bool { 129 | 130 | return false 131 | } 132 | 133 | func (tcpev *TcprttEvent) GenerateMetric() model.AggregatedMetricValue { 134 | //Lets allow to send HEP 135 | 136 | return model.AggregatedMetricValue{} 137 | } 138 | 139 | func (tcpev *TcprttEvent) GenerateTimeMetric() model.AggregatedTimeMetricValue { 140 | 141 | return model.AggregatedTimeMetricValue{} 142 | } 143 | -------------------------------------------------------------------------------- /user/event/ievent.go: -------------------------------------------------------------------------------- 1 | /* 2 | LINK - http://github.com/sipcapture/rtcagent 3 | 4 | Copyright (C) 2023 QXIP B.V. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | package event 21 | 22 | import ( 23 | "rtcagent/model" 24 | ) 25 | 26 | type EventType uint8 27 | 28 | const ( 29 | // EventTypeOutput upload to server or write to logfile. 30 | EventTypeOutput EventType = iota 31 | 32 | // EventTypeModuleData set as module cache data 33 | EventTypeModuleData 34 | 35 | // EventTypeEventProcessor display by event_processor. 36 | EventTypeEventProcessor 37 | ) 38 | 39 | type IEventStruct interface { 40 | Decode(payload []byte) (err error) 41 | Payload() []byte 42 | PayloadLen() int 43 | GenerateHEP() ([]byte, error) 44 | String() string 45 | DoCorrelation(userFunctionArray []string) bool 46 | SendHep() bool 47 | StringHex() string 48 | Clone() IEventStruct 49 | EventType() EventType 50 | GetUUID() string 51 | GenerateMetric() model.AggregatedMetricValue 52 | GenerateTimeMetric() model.AggregatedTimeMetricValue 53 | } 54 | -------------------------------------------------------------------------------- /user/event/misc.go: -------------------------------------------------------------------------------- 1 | /* 2 | LINK - http://github.com/sipcapture/rtcagent 3 | 4 | Copyright (C) 2023 QXIP B.V. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | package event 21 | 22 | import ( 23 | "bytes" 24 | "fmt" 25 | ) 26 | 27 | const ChunkSize = 16 28 | const ChunkSizeHalf = ChunkSize / 2 29 | 30 | const ( 31 | COLORRESET = "\033[0m" 32 | COLORRED = "\033[31m" 33 | COLORGREEN = "\033[32m" 34 | COLORYELLOW = "\033[33m" 35 | COLORBLUE = "\033[34m" 36 | COLORPURPLE = "\033[35m" 37 | COLORCYAN = "\033[36m" 38 | COLORWHITE = "\033[37m" 39 | ) 40 | 41 | func dumpByteSlice(b []byte, perfix string) *bytes.Buffer { 42 | var a [ChunkSize]byte 43 | bb := new(bytes.Buffer) 44 | n := (len(b) + (ChunkSize - 1)) &^ (ChunkSize - 1) 45 | 46 | for i := 0; i < n; i++ { 47 | 48 | // 49 | if i%ChunkSize == 0 { 50 | bb.WriteString(perfix) 51 | bb.WriteString(fmt.Sprintf("%04d", i)) 52 | } 53 | 54 | if i%ChunkSizeHalf == 0 { 55 | bb.WriteString(" ") 56 | } else if i%(ChunkSizeHalf/2) == 0 { 57 | bb.WriteString(" ") 58 | } 59 | 60 | if i < len(b) { 61 | bb.WriteString(fmt.Sprintf(" %02X", b[i])) 62 | } else { 63 | bb.WriteString(" ") 64 | } 65 | 66 | if i >= len(b) { 67 | a[i%ChunkSize] = ' ' 68 | } else if b[i] < 32 || b[i] > 126 { 69 | a[i%ChunkSize] = '.' 70 | } else { 71 | a[i%ChunkSize] = b[i] 72 | } 73 | 74 | if i%ChunkSize == (ChunkSize - 1) { 75 | bb.WriteString(fmt.Sprintf(" %s\n", string(a[:]))) 76 | } 77 | } 78 | return bb 79 | } 80 | 81 | func CToGoString(c []byte) string { 82 | n := -1 83 | for i, b := range c { 84 | if b == 0 { 85 | break 86 | } 87 | n = i 88 | } 89 | return string(c[:n+1]) 90 | } 91 | -------------------------------------------------------------------------------- /user/module/const.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package module 23 | 24 | const ( 25 | ProbeTypeUprobe = "uprobe" 26 | ProbeTypeKprobe = "kprobe" 27 | ProbeTypeTC = "TC" 28 | ProbeTypeTP = "tracepoint" 29 | ProbeTypeFentry = "fentry" 30 | ProbeTypeXDP = "XDP" 31 | ) 32 | 33 | const ( 34 | ModuleNameKamailio = "EBPFProbeKamailio" 35 | ModuleNameFreeSwitch = "EBPFProbeFreeSwitch" 36 | ModuleNameTcprtt = "EBPFProbeTcprtt" 37 | ModuleNameOpensips = "EBPFProbeOpensips" 38 | ModuleNameMonitor = "EBPFProbeMonitor" 39 | ) 40 | 41 | const ( 42 | BashErrnoDefault int = 128 43 | ) 44 | 45 | // buffer size times of ebpf perf map 46 | // buffer size = BufferSizeOfEbpfMap * os.pagesize 47 | const BufferSizeOfEbpfMap = 1024 48 | -------------------------------------------------------------------------------- /user/module/iclose.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package module 23 | 24 | type IClose interface { 25 | Close() error 26 | } 27 | -------------------------------------------------------------------------------- /user/module/probe_freeswitch.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package module 26 | 27 | import ( 28 | "bytes" 29 | "context" 30 | "errors" 31 | "fmt" 32 | "log" 33 | "math" 34 | "os" 35 | "rtcagent/assets" 36 | "rtcagent/user/config" 37 | "rtcagent/user/event" 38 | 39 | manager "github.com/adubovikov/ebpfmanager" 40 | "github.com/cilium/ebpf" 41 | "golang.org/x/sys/unix" 42 | ) 43 | 44 | type MFreeSwitchProbe struct { 45 | Module 46 | bpfManager *manager.Manager 47 | bpfManagerOptions manager.Options 48 | eventFuncMaps map[*ebpf.Map]event.IEventStruct 49 | eventMaps []*ebpf.Map 50 | } 51 | 52 | func (this *MFreeSwitchProbe) Init(ctx context.Context, logger *log.Logger, conf config.IConfig) error { 53 | this.Module.Init(ctx, logger, conf) 54 | this.conf = conf 55 | this.Module.SetChild(this) 56 | this.eventMaps = make([]*ebpf.Map, 0, 2) 57 | this.eventFuncMaps = make(map[*ebpf.Map]event.IEventStruct) 58 | return nil 59 | } 60 | 61 | func (this *MFreeSwitchProbe) Start() error { 62 | if err := this.start(); err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | func (this *MFreeSwitchProbe) MakeUI() error { 69 | 70 | return nil 71 | } 72 | 73 | func (this *MFreeSwitchProbe) start() error { 74 | 75 | // fetch ebpf assets 76 | var bpfFileName = this.geteBPFName("user/bytecode/freeswitch_kern.o") 77 | this.logger.Printf("%s\tBPF bytecode filename: [%s]\n", this.Name(), bpfFileName) 78 | 79 | byteBuf, err := assets.Asset(bpfFileName) 80 | if err != nil { 81 | return fmt.Errorf("couldn't find asset %v.", err) 82 | } 83 | 84 | // setup the managers 85 | err = this.setupManagers() 86 | if err != nil { 87 | return fmt.Errorf("freeswitch module couldn't find binPath %v.", err) 88 | } 89 | 90 | // initialize the bootstrap manager 91 | if err = this.bpfManager.InitWithOptions(bytes.NewReader(byteBuf), this.bpfManagerOptions); err != nil { 92 | return fmt.Errorf("couldn't init manager %v", err) 93 | } 94 | 95 | // start the bootstrap manager 96 | if err = this.bpfManager.Start(); err != nil { 97 | return fmt.Errorf("couldn't start bootstrap manager %v", err) 98 | } 99 | 100 | err = this.initDecodeFun() 101 | if err != nil { 102 | return err 103 | } 104 | 105 | return nil 106 | } 107 | 108 | func (this *MFreeSwitchProbe) Close() error { 109 | if err := this.bpfManager.Stop(manager.CleanAll); err != nil { 110 | return fmt.Errorf("couldn't stop manager %v", err) 111 | } 112 | return this.Module.Close() 113 | } 114 | 115 | func (this *MFreeSwitchProbe) setupManagers() error { 116 | var binaryPath string 117 | switch this.conf.(*config.FreeSwitchConfig).ElfType { 118 | case config.ElfTypeBin: 119 | binaryPath = this.conf.(*config.FreeSwitchConfig).FreeSwitchpath 120 | default: 121 | binaryPath = "/usr/local/lib/libsofia-sip-ua.so.0" 122 | } 123 | 124 | _, err := os.Stat(binaryPath) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | version := this.conf.(*config.FreeSwitchConfig).Version 130 | versionInfo := this.conf.(*config.FreeSwitchConfig).VersionInfo 131 | 132 | //objdump -T /usr/local/lib/libsofia-sip-ua.so.0 |grep receive_msg 133 | //0000000000174c30 g DF .text 000000000000541f Base receive_msg 134 | 135 | var probes = []*manager.Probe{ 136 | { 137 | Section: "uprobe/receive_msg", 138 | EbpfFuncName: "freeswitch_receive_msg", 139 | AttachToFuncName: "su_vrecv", 140 | BinaryPath: binaryPath, 141 | }, 142 | { 143 | Section: "uretprobe/receive_msg", 144 | EbpfFuncName: "freeswitch_ret_receive_msg", 145 | AttachToFuncName: "su_vrecv", 146 | BinaryPath: binaryPath, 147 | }, 148 | { 149 | Section: "uprobe/msg_send", 150 | EbpfFuncName: "msg_send", 151 | AttachToFuncName: "tport_vsend", 152 | BinaryPath: binaryPath, 153 | }, 154 | { 155 | Section: "uretprobe/msg_send", 156 | EbpfFuncName: "msg_ret_send", 157 | AttachToFuncName: "tport_vsend", 158 | BinaryPath: binaryPath, 159 | }, 160 | } 161 | 162 | this.bpfManager = &manager.Manager{ 163 | Probes: probes, 164 | Maps: []*manager.Map{ 165 | { 166 | Name: "events", 167 | }, 168 | }, 169 | } 170 | 171 | this.logger.Printf("%s\tFreeSwitch: %d, Version:%s, binrayPath:%s\n", this.Name(), version, versionInfo, binaryPath) 172 | 173 | this.bpfManagerOptions = manager.Options{ 174 | DefaultKProbeMaxActive: 512, 175 | 176 | VerifierOptions: ebpf.CollectionOptions{ 177 | Programs: ebpf.ProgramOptions{ 178 | LogSize: 2097152, 179 | }, 180 | }, 181 | 182 | RLimit: &unix.Rlimit{ 183 | Cur: math.MaxUint64, 184 | Max: math.MaxUint64, 185 | }, 186 | } 187 | return nil 188 | } 189 | 190 | func (this *MFreeSwitchProbe) DecodeFun(em *ebpf.Map) (event.IEventStruct, bool) { 191 | fun, found := this.eventFuncMaps[em] 192 | return fun, found 193 | } 194 | 195 | func (this *MFreeSwitchProbe) initDecodeFun() error { 196 | // freeswitchEventsMap 197 | freeswitchEventsMap, found, err := this.bpfManager.GetMap("events") 198 | 199 | this.logger.Printf("====> BPF bytecode filename: [%v]\n", found) 200 | 201 | if err != nil { 202 | this.logger.Printf("====> ERRROR BPF bytecode filename: [%]\n", err.Error()) 203 | return err 204 | } 205 | if !found { 206 | return errors.New("cant found map:events") 207 | } 208 | this.eventMaps = append(this.eventMaps, freeswitchEventsMap) 209 | this.eventFuncMaps[freeswitchEventsMap] = &event.FreeSwitchEvent{} 210 | 211 | ///Packet{payload: payload[:len(hep)], length: len(hep)} 212 | 213 | return nil 214 | } 215 | 216 | func (this *MFreeSwitchProbe) Events() []*ebpf.Map { 217 | return this.eventMaps 218 | } 219 | 220 | func init() { 221 | mod := &MFreeSwitchProbe{} 222 | mod.name = ModuleNameFreeSwitch 223 | mod.mType = ProbeTypeUprobe 224 | Register(mod) 225 | } 226 | -------------------------------------------------------------------------------- /user/module/probe_kamailio.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package module 26 | 27 | import ( 28 | "bytes" 29 | "context" 30 | "errors" 31 | "fmt" 32 | "log" 33 | "math" 34 | "os" 35 | "rtcagent/assets" 36 | "rtcagent/user/config" 37 | "rtcagent/user/event" 38 | 39 | manager "github.com/adubovikov/ebpfmanager" 40 | "github.com/cilium/ebpf" 41 | "golang.org/x/sys/unix" 42 | ) 43 | 44 | type MKamailioProbe struct { 45 | Module 46 | bpfManager *manager.Manager 47 | bpfManagerOptions manager.Options 48 | eventFuncMaps map[*ebpf.Map]event.IEventStruct 49 | eventMaps []*ebpf.Map 50 | } 51 | 52 | func (this *MKamailioProbe) Init(ctx context.Context, logger *log.Logger, conf config.IConfig) error { 53 | this.Module.Init(ctx, logger, conf) 54 | this.conf = conf 55 | this.Module.SetChild(this) 56 | this.eventMaps = make([]*ebpf.Map, 0, 2) 57 | this.eventFuncMaps = make(map[*ebpf.Map]event.IEventStruct) 58 | return nil 59 | } 60 | 61 | func (this *MKamailioProbe) Start() error { 62 | if err := this.start(); err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | func (this *MKamailioProbe) MakeUI() error { 69 | 70 | return nil 71 | } 72 | 73 | func (this *MKamailioProbe) start() error { 74 | 75 | // fetch ebpf assets 76 | var bpfFileName = this.geteBPFName("user/bytecode/kamailio_kern.o") 77 | this.logger.Printf("%s\tBPF bytecode filename: [%s]\n", this.Name(), bpfFileName) 78 | 79 | byteBuf, err := assets.Asset(bpfFileName) 80 | if err != nil { 81 | return fmt.Errorf("couldn't find asset %v.", err) 82 | } 83 | 84 | // setup the managers 85 | err = this.setupManagers() 86 | if err != nil { 87 | return fmt.Errorf("kamailio module couldn't find binPath %v.", err) 88 | } 89 | 90 | // initialize the bootstrap manager 91 | if err = this.bpfManager.InitWithOptions(bytes.NewReader(byteBuf), this.bpfManagerOptions); err != nil { 92 | return fmt.Errorf("couldn't init manager %v", err) 93 | } 94 | 95 | // start the bootstrap manager 96 | if err = this.bpfManager.Start(); err != nil { 97 | return fmt.Errorf("couldn't start bootstrap manager %v", err) 98 | } 99 | 100 | err = this.initDecodeFun() 101 | if err != nil { 102 | return err 103 | } 104 | 105 | return nil 106 | } 107 | 108 | func (this *MKamailioProbe) Close() error { 109 | if err := this.bpfManager.Stop(manager.CleanAll); err != nil { 110 | return fmt.Errorf("couldn't stop manager %v", err) 111 | } 112 | return this.Module.Close() 113 | } 114 | 115 | func (this *MKamailioProbe) setupManagers() error { 116 | var binaryPath string 117 | switch this.conf.(*config.KamailioConfig).ElfType { 118 | case config.ElfTypeBin: 119 | binaryPath = this.conf.(*config.KamailioConfig).Kamailiopath 120 | default: 121 | binaryPath = "/usr/sbin/kamailio" 122 | } 123 | 124 | if !this.conf.(*config.KamailioConfig).GetNoSearch() { 125 | _, err := os.Stat(binaryPath) 126 | if err != nil { 127 | return err 128 | } 129 | } 130 | 131 | version := this.conf.(*config.KamailioConfig).Version 132 | versionInfo := this.conf.(*config.KamailioConfig).VersionInfo 133 | 134 | //objdump -T /usr/sbin/kamailio |grep receive_msg 135 | //0000000000174c30 g DF .text 000000000000541f Base receive_msg 136 | 137 | var probes = []*manager.Probe{ 138 | { 139 | Section: "uprobe/receive_msg", 140 | EbpfFuncName: "kamailio_receive_msg", 141 | AttachToFuncName: "receive_msg", 142 | BinaryPath: binaryPath, 143 | }, 144 | { 145 | Section: "uretprobe/receive_msg", 146 | EbpfFuncName: "kamailio_ret_receive_msg", 147 | AttachToFuncName: "receive_msg", 148 | BinaryPath: binaryPath, 149 | }, 150 | { 151 | Section: "uprobe/msg_send_udp", 152 | EbpfFuncName: "msg_send_udp", 153 | AttachToFuncName: "udp_send", 154 | BinaryPath: binaryPath, 155 | }, 156 | { 157 | Section: "uretprobe/msg_send_udp", 158 | EbpfFuncName: "msg_ret_send_udp", 159 | AttachToFuncName: "udp_send", 160 | BinaryPath: binaryPath, 161 | }, 162 | { 163 | Section: "uprobe/msg_send_tcp", 164 | EbpfFuncName: "msg_send_tcp", 165 | AttachToFuncName: "tcp_send", 166 | BinaryPath: binaryPath, 167 | }, 168 | { 169 | Section: "uretprobe/msg_send_tcp", 170 | EbpfFuncName: "msg_ret_send_tcp", 171 | AttachToFuncName: "tcp_send", 172 | BinaryPath: binaryPath, 173 | }, 174 | } 175 | 176 | this.bpfManager = &manager.Manager{ 177 | Probes: probes, 178 | Maps: []*manager.Map{ 179 | { 180 | Name: "events", 181 | }, 182 | }, 183 | } 184 | 185 | this.logger.Printf("%s\tKamailio: %d, Version:%s, binrayPath:%s\n", this.Name(), version, versionInfo, binaryPath) 186 | 187 | this.bpfManagerOptions = manager.Options{ 188 | DefaultKProbeMaxActive: 512, 189 | 190 | VerifierOptions: ebpf.CollectionOptions{ 191 | Programs: ebpf.ProgramOptions{ 192 | LogSize: 2097152, 193 | }, 194 | }, 195 | 196 | RLimit: &unix.Rlimit{ 197 | Cur: math.MaxUint64, 198 | Max: math.MaxUint64, 199 | }, 200 | } 201 | return nil 202 | } 203 | 204 | func (this *MKamailioProbe) DecodeFun(em *ebpf.Map) (event.IEventStruct, bool) { 205 | fun, found := this.eventFuncMaps[em] 206 | return fun, found 207 | } 208 | 209 | func (this *MKamailioProbe) initDecodeFun() error { 210 | // kamailioEventsMap 211 | kamailioEventsMap, found, err := this.bpfManager.GetMap("events") 212 | 213 | this.logger.Printf("====> BPF bytecode filename: [%v]\n", found) 214 | 215 | if err != nil { 216 | this.logger.Printf("====> ERRROR BPF bytecode filename: [%]\n", err.Error()) 217 | return err 218 | } 219 | if !found { 220 | return errors.New("cant found map:events") 221 | } 222 | this.eventMaps = append(this.eventMaps, kamailioEventsMap) 223 | this.eventFuncMaps[kamailioEventsMap] = &event.KamailioEvent{} 224 | 225 | ///Packet{payload: payload[:len(hep)], length: len(hep)} 226 | 227 | return nil 228 | } 229 | 230 | func (this *MKamailioProbe) Events() []*ebpf.Map { 231 | return this.eventMaps 232 | } 233 | 234 | func init() { 235 | mod := &MKamailioProbe{} 236 | mod.name = ModuleNameKamailio 237 | mod.mType = ProbeTypeUprobe 238 | Register(mod) 239 | } 240 | -------------------------------------------------------------------------------- /user/module/probe_tcpdrop.go: -------------------------------------------------------------------------------- 1 | //go:build !androidgki 2 | // +build !androidgki 3 | 4 | /* 5 | 6 | LINK - http://github.com/sipcapture/rtcagent 7 | 8 | Copyright (C) 2023 QXIP B.V. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Affero General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Affero General Public License for more details. 19 | 20 | You should have received a copy of the GNU Affero General Public License 21 | along with this program. If not, see . 22 | 23 | */ 24 | 25 | package module 26 | 27 | import ( 28 | "bytes" 29 | "context" 30 | "fmt" 31 | "log" 32 | "math" 33 | "rtcagent/assets" 34 | "rtcagent/user/config" 35 | "rtcagent/user/event" 36 | 37 | manager "github.com/adubovikov/ebpfmanager" 38 | "github.com/cilium/ebpf" 39 | "github.com/cilium/ebpf/link" 40 | "github.com/cilium/ebpf/rlimit" 41 | "golang.org/x/sys/unix" 42 | ) 43 | 44 | type MTcprttProbe struct { 45 | Module 46 | bpfManager *manager.Manager 47 | bpfManagerOptions manager.Options 48 | eventFuncMaps map[*ebpf.Map]event.IEventStruct 49 | eventMaps []*ebpf.Map 50 | linkData link.Link 51 | } 52 | 53 | func (this *MTcprttProbe) Init(ctx context.Context, logger *log.Logger, conf config.IConfig) error { 54 | this.Module.Init(ctx, logger, conf) 55 | this.conf = conf 56 | this.Module.SetChild(this) 57 | this.eventMaps = make([]*ebpf.Map, 0, 2) 58 | this.eventFuncMaps = make(map[*ebpf.Map]event.IEventStruct) 59 | return nil 60 | } 61 | 62 | func (this *MTcprttProbe) Start() error { 63 | if err := this.start(); err != nil { 64 | return err 65 | } 66 | return nil 67 | } 68 | 69 | type bpfPrograms struct { 70 | TcpClose *ebpf.Program `ebpf:"tcp_close"` 71 | } 72 | 73 | type bpfMaps struct { 74 | Events *ebpf.Map `ebpf:"events"` 75 | } 76 | type bpfObjects struct { 77 | bpfPrograms 78 | bpfMaps 79 | } 80 | 81 | func (this *MTcprttProbe) start() error { 82 | 83 | // Allow the current process to lock memory for eBPF resources. 84 | if err := rlimit.RemoveMemlock(); err != nil { 85 | log.Fatal(err) 86 | } 87 | 88 | var bpfFileName = this.geteBPFName("user/bytecode/tcprtt_kern.o") 89 | this.logger.Printf("%s\tBPF bytecode filename: [%s]\n", this.Name(), bpfFileName) 90 | 91 | byteBuf, err := assets.Asset(bpfFileName) 92 | if err != nil { 93 | return fmt.Errorf("couldn't find asset %v.", err) 94 | } 95 | 96 | objs := bpfObjects{} 97 | 98 | reader := bytes.NewReader(byteBuf) 99 | spec, err := ebpf.LoadCollectionSpecFromReader(reader) 100 | if err != nil { 101 | return fmt.Errorf("can't load bpf: %w", err) 102 | } 103 | 104 | err = spec.LoadAndAssign(&objs, nil) 105 | if err != nil { 106 | return fmt.Errorf("couldn't find asset %v.", err) 107 | } 108 | 109 | this.linkData, err = link.AttachTracing(link.TracingOptions{ 110 | Program: objs.bpfPrograms.TcpClose, 111 | }) 112 | if err != nil { 113 | this.logger.Printf("%s\tBPF bytecode filename FATAL: [%s]\n", this.Name(), bpfFileName) 114 | log.Fatal(err) 115 | } 116 | 117 | this.eventMaps = append(this.eventMaps, objs.bpfMaps.Events) 118 | this.eventFuncMaps[objs.bpfMaps.Events] = &event.TcprttEvent{} 119 | 120 | err = this.initDecodeFun() 121 | if err != nil { 122 | return err 123 | } 124 | 125 | return nil 126 | } 127 | 128 | func (this *MTcprttProbe) MakeUI() error { 129 | 130 | return nil 131 | } 132 | 133 | func (this *MTcprttProbe) Close() error { 134 | 135 | this.linkData.Close() 136 | if err := this.bpfManager.Stop(manager.CleanAll); err != nil { 137 | return fmt.Errorf("couldn't stop manager %v", err) 138 | } 139 | return this.Module.Close() 140 | } 141 | 142 | func (this *MTcprttProbe) setupManagers() error { 143 | var binaryPath string 144 | 145 | version := this.conf.(*config.TcprttConfig).Version 146 | versionInfo := this.conf.(*config.TcprttConfig).VersionInfo 147 | 148 | var probes = []*manager.Probe{ 149 | { 150 | Section: "fentry/tcp_close", 151 | EbpfFuncName: `ebpf:"tcp_close"`, 152 | AttachToFuncName: `ebpf:"tcp_close"`, 153 | }, 154 | } 155 | 156 | this.bpfManager = &manager.Manager{ 157 | Probes: probes, 158 | Maps: []*manager.Map{ 159 | { 160 | Name: "events", 161 | }, 162 | }, 163 | } 164 | 165 | this.logger.Printf("%s\tTcprtt: %d, Version:%s, binrayPath:%s\n", this.Name(), version, versionInfo, binaryPath) 166 | 167 | this.bpfManagerOptions = manager.Options{ 168 | DefaultKProbeMaxActive: 512, 169 | 170 | VerifierOptions: ebpf.CollectionOptions{ 171 | Programs: ebpf.ProgramOptions{ 172 | LogSize: 2097152, 173 | }, 174 | }, 175 | 176 | RLimit: &unix.Rlimit{ 177 | Cur: math.MaxUint64, 178 | Max: math.MaxUint64, 179 | }, 180 | } 181 | return nil 182 | } 183 | 184 | func (this *MTcprttProbe) DecodeFun(em *ebpf.Map) (event.IEventStruct, bool) { 185 | fun, found := this.eventFuncMaps[em] 186 | return fun, found 187 | } 188 | 189 | func (this *MTcprttProbe) initDecodeFun() error { 190 | 191 | prefix := event.COLORCYAN 192 | 193 | this.logger.Printf("%s%-15s %-6s -> %-15s %-6s %-6s%s", prefix, "Src addr", "Port", "Dest addr", "Port", "RTT", event.COLORRESET) 194 | 195 | return nil 196 | } 197 | 198 | func (this *MTcprttProbe) Events() []*ebpf.Map { 199 | return this.eventMaps 200 | } 201 | 202 | func init() { 203 | mod := &MTcprttProbe{} 204 | mod.name = ModuleNameTcprtt 205 | mod.mType = ProbeTypeFentry 206 | Register(mod) 207 | } 208 | -------------------------------------------------------------------------------- /user/module/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | LINK - http://github.com/sipcapture/rtcagent 4 | 5 | Copyright (C) 2023 QXIP B.V. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | package module 23 | 24 | import ( 25 | "fmt" 26 | ) 27 | 28 | var modules = make(map[string]IModule) 29 | 30 | func Register(p IModule) { 31 | if p == nil { 32 | panic("Register probe is nil") 33 | } 34 | name := p.Name() 35 | if _, dup := modules[name]; dup { 36 | panic(fmt.Sprintf("Register called twice for probe %s", name)) 37 | } 38 | modules[name] = p 39 | } 40 | 41 | func GetAllModules() map[string]IModule { 42 | return modules 43 | } 44 | 45 | func GetModuleByName(modName string) IModule { 46 | m, f := modules[modName] 47 | if f { 48 | return m 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /user/time/monotonic.go: -------------------------------------------------------------------------------- 1 | package monotonic 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | /* 8 | #include 9 | static unsigned long long get_nsecs(void) 10 | { 11 | struct timespec ts; 12 | clock_gettime(CLOCK_MONOTONIC, &ts); 13 | return (unsigned long long)ts.tv_sec * 1000000000UL + ts.tv_nsec; 14 | } 15 | */ 16 | import "C" 17 | 18 | func GetTime() (uint64, int64) { 19 | monotonic := uint64(C.get_nsecs()) 20 | timestamp := time.Now().UTC().UnixNano() 21 | return monotonic, timestamp 22 | } 23 | 24 | func GetRealTime(capTime uint64) time.Time { 25 | monotonic := uint64(C.get_nsecs()) 26 | timestamp := time.Now().UTC().UnixNano() 27 | return time.Unix(0, (timestamp - (int64(monotonic-capTime) * 1000))) 28 | } 29 | --------------------------------------------------------------------------------