├── .devcontainer └── devcontainer.json ├── .github └── workflows │ ├── codeql.yml │ └── go.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE.txt ├── Makefile ├── README.md ├── as ├── asnumbersIPv4.go ├── asnumbersIPv4_test.go ├── asnumbersIPv6.go └── asnumbersIPv6_test.go ├── bin └── .gitkeep ├── conv ├── af.go ├── af_test.go ├── ip.go └── ip_test.go ├── go.mod ├── go.sum ├── headers ├── bpf_endian.h ├── bpf_helper_defs.h ├── bpf_helpers.h ├── bpf_tracing.h ├── update.sh ├── vmlinux_compact_amd64.h ├── vmlinux_compact_arm64.h └── vmlinux_compact_common.h ├── linux ├── process.go └── process_test.go ├── main.go ├── output.go ├── samples ├── socket-connect-bpf-big.gif ├── socket-connect-bpf-example.txt └── socket-connect-bpf.gif ├── securitySocketConnectSrc.c └── updateASData.sh /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/go 3 | { 4 | "name": "Go", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/go:1-1-bookworm", 7 | // Features to add to the dev container. More info: https://containers.dev/features. 8 | // "features": {}, 9 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 10 | // "forwardPorts": [], 11 | // Use 'postCreateCommand' to run commands after the container is created. 12 | "postCreateCommand": "sudo apt update && sudo apt install -y clang-16", 13 | "runArgs": ["--privileged"], 14 | // Configure tool-specific properties. 15 | "customizations": { 16 | // Configure properties specific to VS Code. 17 | "vscode": { 18 | // Add the IDs of extensions you want installed when the container is created. 19 | "extensions": [ 20 | "golang.Go" 21 | ] 22 | } 23 | } 24 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 25 | // "remoteUser": "root" 26 | } -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | paths-ignore: 9 | - '**/*.md' 10 | - '**/*.txt' 11 | schedule: 12 | - cron: '21 23 * * 6' 13 | 14 | jobs: 15 | analyze: 16 | name: Analyze 17 | runs-on: ubuntu-latest 18 | permissions: 19 | actions: read 20 | contents: read 21 | security-events: write 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | language: [ 'go' ] 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v3 31 | 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v2 34 | with: 35 | languages: ${{ matrix.language }} 36 | queries: +security-and-quality 37 | 38 | - name: Autobuild 39 | uses: github/codeql-action/autobuild@v2 40 | 41 | - name: Perform CodeQL Analysis 42 | uses: github/codeql-action/analyze@v2 43 | with: 44 | category: "/language:${{matrix.language}}" 45 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push] 3 | jobs: 4 | 5 | build: 6 | name: Build 7 | runs-on: "ubuntu-24.04" 8 | steps: 9 | 10 | - name: Set up Go 11 | uses: actions/setup-go@v5 12 | with: 13 | go-version: '1.23' 14 | id: go 15 | 16 | - name: Check out code into the Go module directory 17 | uses: actions/checkout@v4 18 | 19 | - name: Update AS Data 20 | run: ./updateASData.sh 21 | 22 | - name: Make all (go generate, build + test) 23 | run: make all 24 | 25 | - name: Smoke Test 26 | run: sudo timeout --preserve-status --signal=INT 10s ./bin/amd64/socket-connect-bpf -a 27 | 28 | - name: Create Build Artifact 29 | run: | 30 | mkdir -p bin/amd64/as 31 | mkdir -p bin/arm64/as 32 | cp ./README.md bin/amd64/ 33 | cp ./README.md bin/arm64/ 34 | cp as/*.tsv bin/amd64/as/ 35 | cp as/*.tsv bin/arm64/as/ 36 | mkdir artifacts 37 | tar czf artifacts/socket-connect-bpf-linux-amd64.tar.gz --directory=bin/amd64/ . 38 | tar czf artifacts/socket-connect-bpf-linux-arm64.tar.gz --directory=bin/arm64/ . 39 | 40 | - name: Upload Artifact 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: socket-connect-bpf 44 | path: artifacts 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | socket-connect-bpf 2 | TODO.md 3 | bin/**/socket-connect-bpf 4 | 5 | # generated files 6 | *.o 7 | bpf_*_bpfeb.go 8 | bpf_*_bpfel.go 9 | 10 | *.tsv -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.h": "c", 4 | } 5 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINARY_NAME=socket-connect-bpf 2 | 3 | all: build test 4 | 5 | build: 6 | go generate 7 | mkdir bin/amd64/ 8 | GOOS=linux GOARCH=amd64 go build -o bin/amd64/${BINARY_NAME} 9 | mkdir bin/arm64/ 10 | GOOS=linux GOARCH=arm64 go build -o bin/arm64/${BINARY_NAME} 11 | 12 | test: 13 | go test ./... 14 | 15 | clean: 16 | go clean 17 | rm -f bpf_bpfel_*.go 18 | rm -f bin/amd64/${BINARY_NAME} 19 | rm -f bin/arm64/${BINARY_NAME} 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # socket-connect-bpf 2 | 3 | socket-connect-bpf is a Linux command line utility that writes human-readable information about each application that makes new (network) connections to the standard output. 4 | 5 | ![socket-connect-bpf while making a request with curl](samples/socket-connect-bpf.gif) 6 | 7 | More [sample output](samples/socket-connect-bpf-example.txt). 8 | 9 | ## Details 10 | socket-connect-bpf is a BPF/eBPF prototype with a kernel probe attached to [`security_socket_connect`](https://github.com/torvalds/linux/blob/master/include/linux/security.h). Connections to AF_UNSPEC and AF_UNIX are explicitly excluded. 11 | 12 | Following information about each request is displayed if possible: 13 | 14 | | Name | Description | Sample | 15 | | --------------|----------------------------------------------------------|--------------------| 16 | | Time | Time at which the connection event was received. | `17:15:58` | 17 | | AF | Address family | `AF_INET` | 18 | | PID | Process ID of the process making the request. | `8549` | 19 | | Process | Process path/args of the process making the request. | `/usr/bin/curl` | 20 | | User | Username under which the process is executed. | `root` | 21 | | Destination | IP address and port of the destination. | `127.0.0.1 53` | 22 | | AS-Info | Info about the autonomous system (AS) of the IP address. | `AS36459 (GITHUB)` | 23 | 24 | ## Use cases 25 | 26 | You might want to try `socket-connect-bpf` for the following use cases: 27 | 28 | * Check if an application contains analytics. 29 | * Check if your trusted dependencies communicate with the outside world. 30 | * As a less invasive alternative to Kernel modules that provide the same functionality. 31 | 32 | ## License 33 | The socket-connect-bpf Go code is licensed under the Apache License. The BPF code is licensed under GPL as some [BPF-helpers are GPL-only](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers). 34 | 35 | ## System requirements 36 | * x64/amd64 or AArch64/arm64 CPU 37 | * Linux Kernel: 4.18 or later 38 | 39 | ## Installation 40 | 41 | ### Install binaries (Version 0.4.0 or later) 42 | Tested on following architecures: 43 | 44 | * amd64 (Intel x64 CPU) 45 | * arm64 (AWS Graviton2/Arm Neoverse-N1) 46 | 47 | Instructions tested on Debian Bookworm with Linux Kernel 6.5: 48 | 49 | * Extract the corresponding `socket-connect-bpf-*.tar.gz` [release](https://github.com/p-/socket-connect-bpf/releases). 50 | 51 | ### Verify binaries (Version 0.4.0 or later) 52 | Tarballs can be verified with [minisign](https://github.com/jedisct1/minisign) and following public key: 53 | 54 | `RWRUqB/iFRENms4B2LbOrNGizwXbStkIPE8sUq01r63cXJP8kzHp+ITv` 55 | 56 | ## Running: 57 | 58 | sudo ./socket-connect-bpf 59 | 60 | ### Print all 61 | Print all `-a` also prints the process arguments and the AS information. 62 | 63 | sudo ./socket-connect-bpf -a 64 | 65 | ### Autonomous System (AS) Information 66 | 67 | Information about an autonomous system (AS) that an IP address belongs to is not displayed by default. 68 | It can be turned on with the print all flag `-a`. 69 | 70 | sudo ./socket-connect-bpf -a 71 | 72 | #### AS data 73 | AS data of [IPtoASN](https://iptoasn.com/) is used. 74 | The local AS-Number lookup will require some more RAM. 75 | 76 | To update the AS data used while developing run: 77 | 78 | ./updateASData.sh 79 | 80 | ## Development 81 | 82 | ### Build code from repository 83 | Step-by-Step instructions for Debian Bookworm with Linux Kernel 6.5.0. 84 | 85 | # Install Go 1.23 or later (if not already installed) 86 | sudo snap install --classic go 87 | 88 | # Install Clang 16 (for compiling the BPF sources) 89 | sudo apt install clang-16 90 | 91 | # Change into a folder of your choice and clone socket-connect-bpf 92 | git clone https://github.com/p-/socket-connect-bpf.git 93 | 94 | cd socket-connect-bpf 95 | 96 | go generate 97 | go build 98 | 99 | ### Tests 100 | Run tests: 101 | 102 | go test ./... 103 | 104 | ### IDE 105 | [VS Code](https://code.visualstudio.com/) or any other Go Lang IDE can be used for development. 106 | -------------------------------------------------------------------------------- /as/asnumbersIPv4.go: -------------------------------------------------------------------------------- 1 | package as 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/csv" 6 | "fmt" 7 | "net" 8 | "os" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/p-/socket-connect-bpf/conv" 13 | ) 14 | 15 | var asMap = make(map[uint8][]ASInfo) 16 | 17 | // ParseASNumbersIPv4 parses the autonomous system (AS) Numbers and IPv4 ranges from a .tsv file 18 | func ParseASNumbersIPv4(asTsvFile string) { 19 | csvFile, err := os.Open(asTsvFile) 20 | 21 | if err != nil { 22 | fmt.Println("Could not read AS Number file") 23 | fmt.Println(err) 24 | return 25 | } 26 | 27 | defer csvFile.Close() 28 | 29 | reader := csv.NewReader(csvFile) 30 | 31 | reader.Comma = '\t' 32 | reader.LazyQuotes = true 33 | 34 | reader.FieldsPerRecord = -1 35 | 36 | csvData, err := reader.ReadAll() 37 | if err != nil { 38 | fmt.Println("Could not read AS Number file") 39 | fmt.Println(err) 40 | return 41 | } 42 | 43 | for _, each := range csvData { 44 | 45 | startAddr, _ := strconv.ParseUint(each[0], 10, 32) // Could be cast to uint32 46 | 47 | bs := make([]byte, 4) 48 | binary.BigEndian.PutUint32(bs, uint32(startAddr)) 49 | 50 | endAddr, _ := strconv.ParseUint(each[1], 10, 32) // Could be cast to uint32 51 | asNumber, _ := strconv.ParseUint(each[2], 10, 32) 52 | 53 | if asNumber != 0 { 54 | asName := getNameOnly(each[4]) 55 | bucket := bs[0] 56 | entry := ASInfo{StartIP: uint32(startAddr), EndIP: uint32(endAddr), AsNumber: uint32(asNumber), Name: asName} 57 | val, ok := asMap[bucket] 58 | 59 | if !ok { 60 | asMap[bucket] = []ASInfo{entry} 61 | } else { 62 | asMap[bucket] = append(val, entry) 63 | } 64 | } 65 | } 66 | } 67 | 68 | func toBigIP4(addr uint32) net.IP { 69 | ip := make(net.IP, 4) 70 | binary.BigEndian.PutUint32(ip, addr) 71 | return ip 72 | } 73 | 74 | func getNameOnly(desc string) string { 75 | return strings.Fields(desc)[0] 76 | } 77 | 78 | // GetASInfo returns information about an autonomous system (AS) of which the given IP is part of. 79 | func GetASInfoIPv4(ip net.IP) ASInfo { 80 | ipAddr := conv.ToUint(ip) 81 | bs := make([]byte, 4) 82 | binary.BigEndian.PutUint32(bs, ipAddr) 83 | bucket := bs[0] 84 | values := asMap[bucket] 85 | for _, asInfo := range values { 86 | inRange := checkRange(&asInfo, ipAddr) 87 | if inRange { 88 | return asInfo 89 | } 90 | } 91 | var empty ASInfo 92 | return empty 93 | } 94 | 95 | func checkRange(ips *ASInfo, ipAddr uint32) bool { 96 | if ipAddr < ips.StartIP { 97 | return false 98 | } 99 | if ipAddr > ips.EndIP { 100 | return false 101 | } 102 | return true 103 | 104 | } 105 | 106 | // ASInfo contains information about an autonomous system (AS) 107 | type ASInfo struct { 108 | StartIP uint32 109 | EndIP uint32 110 | AsNumber uint32 111 | Name string 112 | } 113 | -------------------------------------------------------------------------------- /as/asnumbersIPv4_test.go: -------------------------------------------------------------------------------- 1 | package as 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func TestIP4ToAsRange(t *testing.T) { 9 | ParseASNumbersIPv4("./ip2asn-v4-u32.tsv") 10 | ip := "82.197.176.1" 11 | got := GetASInfoIPv4(net.ParseIP(ip)) 12 | wantName := "INIT7" 13 | if got.Name != wantName { 14 | t.Errorf("GetASInfo(%s) = %s; want %s", ip, got.Name, wantName) 15 | } 16 | wantAsNumber := uint32(13030) 17 | if got.AsNumber != wantAsNumber { 18 | t.Errorf("GetASInfo(%s) = %d; want %d", ip, got.AsNumber, wantAsNumber) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /as/asnumbersIPv6.go: -------------------------------------------------------------------------------- 1 | package as 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "net" 8 | "os" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | var asList []ASInfoIPv6 14 | 15 | // ParseASNumbersIPv6 parses the autonomous system (AS) Numbers and IPv6 ranges from a .tsv file 16 | func ParseASNumbersIPv6(asTsvFile string) { 17 | csvFile, err := os.Open(asTsvFile) 18 | 19 | if err != nil { 20 | fmt.Println("Could not read AS Number file") 21 | fmt.Println(err) 22 | return 23 | } 24 | 25 | defer csvFile.Close() 26 | 27 | scanner := bufio.NewScanner(csvFile) 28 | for scanner.Scan() { 29 | fields := strings.Fields(scanner.Text()) 30 | if len(fields) != 5 { 31 | continue // Skip invalid lines 32 | } 33 | 34 | start := net.ParseIP(fields[0]) 35 | end := net.ParseIP(fields[1]) 36 | if start == nil || end == nil { 37 | continue // Skip invalid IP ranges 38 | } 39 | 40 | asNumber, _ := strconv.ParseUint(fields[2], 10, 32) 41 | 42 | asList = append(asList, ASInfoIPv6{ 43 | StartIP: start, 44 | EndIP: end, 45 | AsNumber: uint32(asNumber), 46 | Name: fields[4], 47 | }) 48 | } 49 | 50 | if err := scanner.Err(); err != nil { 51 | fmt.Println("Could not read AS Number file") 52 | fmt.Println(err) 53 | return 54 | } 55 | } 56 | 57 | // GetASInfoIPv6 returns information about an autonomous system (AS) of which the given IP is part of. 58 | func GetASInfoIPv6(ip net.IP) ASInfoIPv6 { 59 | for _, r := range asList { 60 | if bytes.Compare(ip, r.StartIP) >= 0 && bytes.Compare(ip, r.EndIP) <= 0 { 61 | return r 62 | } 63 | } 64 | var empty ASInfoIPv6 65 | return empty 66 | } 67 | 68 | // ASInfoIPv6 contains information about an autonomous system (AS) 69 | type ASInfoIPv6 struct { 70 | StartIP net.IP 71 | EndIP net.IP 72 | AsNumber uint32 73 | Name string 74 | } 75 | -------------------------------------------------------------------------------- /as/asnumbersIPv6_test.go: -------------------------------------------------------------------------------- 1 | package as 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func TestIP6ToAsRange(t *testing.T) { 9 | ParseASNumbersIPv6("./ip2asn-v6.tsv") 10 | ip := "2620:2d:4000:1::1" 11 | got := GetASInfoIPv6(net.ParseIP(ip)) 12 | wantName := "CANONICAL-AS" 13 | if got.Name != wantName { 14 | t.Errorf("GetASInfo(%s) = %s; want %s", ip, got.Name, wantName) 15 | } 16 | wantAsNumber := uint32(41231) 17 | if got.AsNumber != wantAsNumber { 18 | t.Errorf("GetASInfo(%s) = %d; want %d", ip, got.AsNumber, wantAsNumber) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p-/socket-connect-bpf/e04a8d0fbe9b5cc14145a1e1fb1c9f04b81b0afa/bin/.gitkeep -------------------------------------------------------------------------------- /conv/af.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import "strconv" 4 | 5 | const AF_INET = 2 6 | const AF_INET6 = 10 7 | 8 | // ToAddressFamily converts an address family number to a string 9 | // e.g. 10 to AF_INET6 10 | func ToAddressFamily(afNum int) string { 11 | if af, ok := addressFamilies[afNum]; ok { 12 | return af 13 | } 14 | return strconv.Itoa(int(afNum)) 15 | } 16 | 17 | var addressFamilies = map[int]string{ 18 | 0: "AF_UNSPEC", 19 | 1: "AF_UNIX", 20 | 2: "AF_INET", 21 | 3: "AF_AX25", 22 | 4: "AF_IPX", 23 | 5: "AF_APPLETALK", 24 | 6: "AF_NETROM", 25 | 7: "AF_BRIDGE", 26 | 8: "AF_ATMPVC", 27 | 9: "AF_X25", 28 | 10: "AF_INET6", 29 | 11: "AF_ROSE", 30 | 12: "AF_DECnet", 31 | 13: "AF_NETBEUI", 32 | 14: "AF_SECURITY", 33 | 15: "AF_KEY", 34 | 16: "AF_NETLINK", 35 | 17: "AF_PACKET", 36 | 18: "AF_ASH", 37 | 19: "AF_ECONET", 38 | 20: "AF_ATMSVC", 39 | 21: "AF_RDS", 40 | 22: "AF_SNA", 41 | 23: "AF_IRDA", 42 | 24: "AF_PPPOX", 43 | 25: "AF_WANPIPE", 44 | 26: "AF_LLC", 45 | 27: "AF_IB", 46 | 28: "AF_MPLS", 47 | 29: "AF_CAN", 48 | 30: "AF_TIPC", 49 | 31: "AF_BLUETOOTH", 50 | 32: "AF_IUCV", 51 | 33: "AF_RXRPC", 52 | 34: "AF_ISDN", 53 | 35: "AF_PHONET", 54 | 36: "AF_IEEE802154", 55 | 37: "AF_CAIF", 56 | 38: "AF_ALG", 57 | 39: "AF_NFC", 58 | 40: "AF_VSOCK", 59 | 41: "AF_KCM", 60 | 42: "AF_QIPCRTR", 61 | 43: "AF_SMC", 62 | 44: "AF_XDP", 63 | 45: "AF_MAX", 64 | } 65 | -------------------------------------------------------------------------------- /conv/af_test.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestExistingAfConversion(t *testing.T) { 8 | got := ToAddressFamily(10) 9 | want := "AF_INET6" 10 | if got != want { 11 | t.Errorf("ToAddressFamily(10) = %s; want %s", got, want) 12 | } 13 | } 14 | func TestNotExistingAfConversion(t *testing.T) { 15 | got := ToAddressFamily(55) 16 | want := "55" 17 | if got != want { 18 | t.Errorf("ToAddressFamily(55) = %s; want %s", got, want) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /conv/ip.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import ( 4 | "encoding/binary" 5 | "net" 6 | ) 7 | 8 | // ToIP4 converts an IPv4 address in network byte order to a net.IP 9 | func ToIP4(addr uint32) net.IP { 10 | ip := make(net.IP, 4) 11 | binary.LittleEndian.PutUint32(ip, addr) 12 | return ip 13 | } 14 | 15 | // ToIP6 converts an IPv6 address in network byte order to a net.IP 16 | func ToIP6(ipPart1 uint64, ipPart2 uint64) net.IP { 17 | ip := make(net.IP, 16) 18 | 19 | binary.LittleEndian.PutUint32(ip, uint32(ipPart1)) 20 | binary.LittleEndian.PutUint32(ip[4:], uint32(ipPart1>>32)) 21 | binary.LittleEndian.PutUint32(ip[8:], uint32(ipPart2)) 22 | binary.LittleEndian.PutUint32(ip[12:], uint32(ipPart2>>32)) 23 | return ip 24 | } 25 | 26 | // ToUint converts an IP to an uint32 27 | func ToUint(ip net.IP) uint32 { 28 | if len(ip) == 16 { 29 | return binary.BigEndian.Uint32(ip[12:16]) 30 | } 31 | return binary.BigEndian.Uint32(ip) 32 | } 33 | 34 | // IP4ToUint converts an IPv4 address to an uint32 (Little Endian) 35 | func IP4ToUint(ip net.IP) uint32 { 36 | return binary.LittleEndian.Uint32(ip.To4()) 37 | } 38 | -------------------------------------------------------------------------------- /conv/ip_test.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func TestIp4Conversion(t *testing.T) { 9 | got := ToIP4(251789322) 10 | want := "10.0.2.15" 11 | if !got.Equal(net.ParseIP(want)) { 12 | t.Errorf("ToIP4(251789322) = %s; want %s", got, want) 13 | } 14 | } 15 | 16 | func TestIp4ConversionWithLocalhost(t *testing.T) { 17 | got := ToIP4(16777343) 18 | want := "127.0.0.1" 19 | if !got.Equal(net.ParseIP(want)) { 20 | t.Errorf("ToIP4(16777343) = %s; want %s", got, want) 21 | } 22 | } 23 | 24 | func TestIp6ConversionWithLocalhost(t *testing.T) { 25 | got := ToIP6(0, 72057594037927936) 26 | want := "::1" 27 | if !got.Equal(net.ParseIP(want)) { 28 | t.Errorf("ToIP6(0, 72057594037927936) = %s; want %s", got, want) 29 | } 30 | } 31 | 32 | func TestIp6ConversionWith6to4Address(t *testing.T) { 33 | got := ToIP6(18305688338976, 0) 34 | want := "2002:d20:a610::" 35 | if !got.Equal(net.ParseIP(want)) { 36 | t.Errorf("ToIP6(18305688338976, 0) = %s; want %s", got, want) 37 | } 38 | } 39 | 40 | func TestIP4ToUintConversionWithIPv4Address(t *testing.T) { 41 | got := IP4ToUint(net.ParseIP("172.217.168.35")) 42 | want := uint32(598268332) 43 | if got != want { 44 | t.Errorf("IP4ToUint(net.ParseIP(\"172.217.168.35\")) = %d; want %d", got, want) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/p-/socket-connect-bpf 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.17.2 7 | golang.org/x/sys v0.29.0 8 | ) 9 | 10 | require golang.org/x/sync v0.10.0 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.17.2 h1:IQTaTVu0vKA8WTemFuBnxW9YbAwMkJVKHsNHW4lHv/g= 2 | github.com/cilium/ebpf v0.17.2/go.mod h1:9X5VAsIOck/nCAp0+nCSVzub1Q7x+zKXXItTMYfNE+E= 3 | github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= 4 | github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= 5 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 6 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 7 | github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= 8 | github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 9 | github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= 10 | github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= 11 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 12 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 13 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 14 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 15 | github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= 16 | github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= 17 | github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= 18 | github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= 19 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 20 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 21 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 22 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 23 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 24 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 25 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 26 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 27 | -------------------------------------------------------------------------------- /headers/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 | -------------------------------------------------------------------------------- /headers/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 | #define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name 17 | 18 | /* 19 | * Helper macro to place programs, maps, license in 20 | * different sections in elf_bpf file. Section names 21 | * are interpreted by libbpf depending on the context (BPF programs, BPF maps, 22 | * extern variables, etc). 23 | * To allow use of SEC() with externs (e.g., for extern .maps declarations), 24 | * make sure __attribute__((unused)) doesn't trigger compilation warning. 25 | */ 26 | #if __GNUC__ && !__clang__ 27 | 28 | /* 29 | * Pragma macros are broken on GCC 30 | * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 31 | * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90400 32 | */ 33 | #define SEC(name) __attribute__((section(name), used)) 34 | 35 | #else 36 | 37 | #define SEC(name) \ 38 | _Pragma("GCC diagnostic push") \ 39 | _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ 40 | __attribute__((section(name), used)) \ 41 | _Pragma("GCC diagnostic pop") \ 42 | 43 | #endif 44 | 45 | /* Avoid 'linux/stddef.h' definition of '__always_inline'. */ 46 | #undef __always_inline 47 | #define __always_inline inline __attribute__((always_inline)) 48 | 49 | #ifndef __noinline 50 | #define __noinline __attribute__((noinline)) 51 | #endif 52 | #ifndef __weak 53 | #define __weak __attribute__((weak)) 54 | #endif 55 | 56 | /* 57 | * Use __hidden attribute to mark a non-static BPF subprogram effectively 58 | * static for BPF verifier's verification algorithm purposes, allowing more 59 | * extensive and permissive BPF verification process, taking into account 60 | * subprogram's caller context. 61 | */ 62 | #define __hidden __attribute__((visibility("hidden"))) 63 | 64 | /* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include 65 | * any system-level headers (such as stddef.h, linux/version.h, etc), and 66 | * commonly-used macros like NULL and KERNEL_VERSION aren't available through 67 | * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define 68 | * them on their own. So as a convenience, provide such definitions here. 69 | */ 70 | #ifndef NULL 71 | #define NULL ((void *)0) 72 | #endif 73 | 74 | #ifndef KERNEL_VERSION 75 | #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) 76 | #endif 77 | 78 | /* 79 | * Helper macros to manipulate data structures 80 | */ 81 | 82 | /* offsetof() definition that uses __builtin_offset() might not preserve field 83 | * offset CO-RE relocation properly, so force-redefine offsetof() using 84 | * old-school approach which works with CO-RE correctly 85 | */ 86 | #undef offsetof 87 | #define offsetof(type, member) ((unsigned long)&((type *)0)->member) 88 | 89 | /* redefined container_of() to ensure we use the above offsetof() macro */ 90 | #undef container_of 91 | #define container_of(ptr, type, member) \ 92 | ({ \ 93 | void *__mptr = (void *)(ptr); \ 94 | ((type *)(__mptr - offsetof(type, member))); \ 95 | }) 96 | 97 | /* 98 | * Compiler (optimization) barrier. 99 | */ 100 | #ifndef barrier 101 | #define barrier() asm volatile("" ::: "memory") 102 | #endif 103 | 104 | /* Variable-specific compiler (optimization) barrier. It's a no-op which makes 105 | * compiler believe that there is some black box modification of a given 106 | * variable and thus prevents compiler from making extra assumption about its 107 | * value and potential simplifications and optimizations on this variable. 108 | * 109 | * E.g., compiler might often delay or even omit 32-bit to 64-bit casting of 110 | * a variable, making some code patterns unverifiable. Putting barrier_var() 111 | * in place will ensure that cast is performed before the barrier_var() 112 | * invocation, because compiler has to pessimistically assume that embedded 113 | * asm section might perform some extra operations on that variable. 114 | * 115 | * This is a variable-specific variant of more global barrier(). 116 | */ 117 | #ifndef barrier_var 118 | #define barrier_var(var) asm volatile("" : "+r"(var)) 119 | #endif 120 | 121 | /* 122 | * Helper macro to throw a compilation error if __bpf_unreachable() gets 123 | * built into the resulting code. This works given BPF back end does not 124 | * implement __builtin_trap(). This is useful to assert that certain paths 125 | * of the program code are never used and hence eliminated by the compiler. 126 | * 127 | * For example, consider a switch statement that covers known cases used by 128 | * the program. __bpf_unreachable() can then reside in the default case. If 129 | * the program gets extended such that a case is not covered in the switch 130 | * statement, then it will throw a build error due to the default case not 131 | * being compiled out. 132 | */ 133 | #ifndef __bpf_unreachable 134 | # define __bpf_unreachable() __builtin_trap() 135 | #endif 136 | 137 | /* 138 | * Helper function to perform a tail call with a constant/immediate map slot. 139 | */ 140 | #if (defined(__clang__) && __clang_major__ >= 8) || (!defined(__clang__) && __GNUC__ > 12) 141 | #if defined(__bpf__) 142 | static __always_inline void 143 | bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) 144 | { 145 | if (!__builtin_constant_p(slot)) 146 | __bpf_unreachable(); 147 | 148 | /* 149 | * Provide a hard guarantee that LLVM won't optimize setting r2 (map 150 | * pointer) and r3 (constant map index) from _different paths_ ending 151 | * up at the _same_ call insn as otherwise we won't be able to use the 152 | * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel 153 | * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key 154 | * tracking for prog array pokes") for details on verifier tracking. 155 | * 156 | * Note on clobber list: we need to stay in-line with BPF calling 157 | * convention, so even if we don't end up using r0, r4, r5, we need 158 | * to mark them as clobber so that LLVM doesn't end up using them 159 | * before / after the call. 160 | */ 161 | asm volatile("r1 = %[ctx]\n\t" 162 | "r2 = %[map]\n\t" 163 | "r3 = %[slot]\n\t" 164 | "call 12" 165 | :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) 166 | : "r0", "r1", "r2", "r3", "r4", "r5"); 167 | } 168 | #endif 169 | #endif 170 | 171 | enum libbpf_pin_type { 172 | LIBBPF_PIN_NONE, 173 | /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ 174 | LIBBPF_PIN_BY_NAME, 175 | }; 176 | 177 | enum libbpf_tristate { 178 | TRI_NO = 0, 179 | TRI_YES = 1, 180 | TRI_MODULE = 2, 181 | }; 182 | 183 | #define __kconfig __attribute__((section(".kconfig"))) 184 | #define __ksym __attribute__((section(".ksyms"))) 185 | #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted"))) 186 | #define __kptr __attribute__((btf_type_tag("kptr"))) 187 | #define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr"))) 188 | #define __uptr __attribute__((btf_type_tag("uptr"))) 189 | 190 | #if defined (__clang__) 191 | #define bpf_ksym_exists(sym) ({ \ 192 | _Static_assert(!__builtin_constant_p(!!sym), \ 193 | #sym " should be marked as __weak"); \ 194 | !!sym; \ 195 | }) 196 | #elif __GNUC__ > 8 197 | #define bpf_ksym_exists(sym) ({ \ 198 | _Static_assert(__builtin_has_attribute (*sym, __weak__), \ 199 | #sym " should be marked as __weak"); \ 200 | !!sym; \ 201 | }) 202 | #else 203 | #define bpf_ksym_exists(sym) !!sym 204 | #endif 205 | 206 | #define __arg_ctx __attribute__((btf_decl_tag("arg:ctx"))) 207 | #define __arg_nonnull __attribute((btf_decl_tag("arg:nonnull"))) 208 | #define __arg_nullable __attribute((btf_decl_tag("arg:nullable"))) 209 | #define __arg_trusted __attribute((btf_decl_tag("arg:trusted"))) 210 | #define __arg_arena __attribute((btf_decl_tag("arg:arena"))) 211 | 212 | #ifndef ___bpf_concat 213 | #define ___bpf_concat(a, b) a ## b 214 | #endif 215 | #ifndef ___bpf_apply 216 | #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) 217 | #endif 218 | #ifndef ___bpf_nth 219 | #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N 220 | #endif 221 | #ifndef ___bpf_narg 222 | #define ___bpf_narg(...) \ 223 | ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 224 | #endif 225 | 226 | #define ___bpf_fill0(arr, p, x) do {} while (0) 227 | #define ___bpf_fill1(arr, p, x) arr[p] = x 228 | #define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) 229 | #define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) 230 | #define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) 231 | #define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) 232 | #define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) 233 | #define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) 234 | #define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) 235 | #define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) 236 | #define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) 237 | #define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) 238 | #define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) 239 | #define ___bpf_fill(arr, args...) \ 240 | ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) 241 | 242 | /* 243 | * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values 244 | * in a structure. 245 | */ 246 | #define BPF_SEQ_PRINTF(seq, fmt, args...) \ 247 | ({ \ 248 | static const char ___fmt[] = fmt; \ 249 | unsigned long long ___param[___bpf_narg(args)]; \ 250 | \ 251 | _Pragma("GCC diagnostic push") \ 252 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 253 | ___bpf_fill(___param, args); \ 254 | _Pragma("GCC diagnostic pop") \ 255 | \ 256 | bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ 257 | ___param, sizeof(___param)); \ 258 | }) 259 | 260 | /* 261 | * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of 262 | * an array of u64. 263 | */ 264 | #define BPF_SNPRINTF(out, out_size, fmt, args...) \ 265 | ({ \ 266 | static const char ___fmt[] = fmt; \ 267 | unsigned long long ___param[___bpf_narg(args)]; \ 268 | \ 269 | _Pragma("GCC diagnostic push") \ 270 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 271 | ___bpf_fill(___param, args); \ 272 | _Pragma("GCC diagnostic pop") \ 273 | \ 274 | bpf_snprintf(out, out_size, ___fmt, \ 275 | ___param, sizeof(___param)); \ 276 | }) 277 | 278 | #ifdef BPF_NO_GLOBAL_DATA 279 | #define BPF_PRINTK_FMT_MOD 280 | #else 281 | #define BPF_PRINTK_FMT_MOD static const 282 | #endif 283 | 284 | #define __bpf_printk(fmt, ...) \ 285 | ({ \ 286 | BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \ 287 | bpf_trace_printk(____fmt, sizeof(____fmt), \ 288 | ##__VA_ARGS__); \ 289 | }) 290 | 291 | /* 292 | * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments 293 | * instead of an array of u64. 294 | */ 295 | #define __bpf_vprintk(fmt, args...) \ 296 | ({ \ 297 | static const char ___fmt[] = fmt; \ 298 | unsigned long long ___param[___bpf_narg(args)]; \ 299 | \ 300 | _Pragma("GCC diagnostic push") \ 301 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 302 | ___bpf_fill(___param, args); \ 303 | _Pragma("GCC diagnostic pop") \ 304 | \ 305 | bpf_trace_vprintk(___fmt, sizeof(___fmt), \ 306 | ___param, sizeof(___param)); \ 307 | }) 308 | 309 | /* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args 310 | * Otherwise use __bpf_vprintk 311 | */ 312 | #define ___bpf_pick_printk(...) \ 313 | ___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ 314 | __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ 315 | __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\ 316 | __bpf_printk /*1*/, __bpf_printk /*0*/) 317 | 318 | /* Helper macro to print out debug messages */ 319 | #define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args) 320 | 321 | struct bpf_iter_num; 322 | 323 | extern int bpf_iter_num_new(struct bpf_iter_num *it, int start, int end) __weak __ksym; 324 | extern int *bpf_iter_num_next(struct bpf_iter_num *it) __weak __ksym; 325 | extern void bpf_iter_num_destroy(struct bpf_iter_num *it) __weak __ksym; 326 | 327 | #ifndef bpf_for_each 328 | /* bpf_for_each(iter_type, cur_elem, args...) provides generic construct for 329 | * using BPF open-coded iterators without having to write mundane explicit 330 | * low-level loop logic. Instead, it provides for()-like generic construct 331 | * that can be used pretty naturally. E.g., for some hypothetical cgroup 332 | * iterator, you'd write: 333 | * 334 | * struct cgroup *cg, *parent_cg = <...>; 335 | * 336 | * bpf_for_each(cgroup, cg, parent_cg, CG_ITER_CHILDREN) { 337 | * bpf_printk("Child cgroup id = %d", cg->cgroup_id); 338 | * if (cg->cgroup_id == 123) 339 | * break; 340 | * } 341 | * 342 | * I.e., it looks almost like high-level for each loop in other languages, 343 | * supports continue/break, and is verifiable by BPF verifier. 344 | * 345 | * For iterating integers, the difference between bpf_for_each(num, i, N, M) 346 | * and bpf_for(i, N, M) is in that bpf_for() provides additional proof to 347 | * verifier that i is in [N, M) range, and in bpf_for_each() case i is `int 348 | * *`, not just `int`. So for integers bpf_for() is more convenient. 349 | * 350 | * Note: this macro relies on C99 feature of allowing to declare variables 351 | * inside for() loop, bound to for() loop lifetime. It also utilizes GCC 352 | * extension: __attribute__((cleanup())), supported by both GCC and 353 | * Clang. 354 | */ 355 | #define bpf_for_each(type, cur, args...) for ( \ 356 | /* initialize and define destructor */ \ 357 | struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */, \ 358 | cleanup(bpf_iter_##type##_destroy))), \ 359 | /* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */ \ 360 | *___p __attribute__((unused)) = ( \ 361 | bpf_iter_##type##_new(&___it, ##args), \ 362 | /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \ 363 | /* for bpf_iter_##type##_destroy() when used from cleanup() attribute */ \ 364 | (void)bpf_iter_##type##_destroy, (void *)0); \ 365 | /* iteration and termination check */ \ 366 | (((cur) = bpf_iter_##type##_next(&___it))); \ 367 | ) 368 | #endif /* bpf_for_each */ 369 | 370 | #ifndef bpf_for 371 | /* bpf_for(i, start, end) implements a for()-like looping construct that sets 372 | * provided integer variable *i* to values starting from *start* through, 373 | * but not including, *end*. It also proves to BPF verifier that *i* belongs 374 | * to range [start, end), so this can be used for accessing arrays without 375 | * extra checks. 376 | * 377 | * Note: *start* and *end* are assumed to be expressions with no side effects 378 | * and whose values do not change throughout bpf_for() loop execution. They do 379 | * not have to be statically known or constant, though. 380 | * 381 | * Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for() 382 | * loop bound variables and cleanup attribute, supported by GCC and Clang. 383 | */ 384 | #define bpf_for(i, start, end) for ( \ 385 | /* initialize and define destructor */ \ 386 | struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \ 387 | cleanup(bpf_iter_num_destroy))), \ 388 | /* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \ 389 | *___p __attribute__((unused)) = ( \ 390 | bpf_iter_num_new(&___it, (start), (end)), \ 391 | /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \ 392 | /* for bpf_iter_num_destroy() when used from cleanup() attribute */ \ 393 | (void)bpf_iter_num_destroy, (void *)0); \ 394 | ({ \ 395 | /* iteration step */ \ 396 | int *___t = bpf_iter_num_next(&___it); \ 397 | /* termination and bounds check */ \ 398 | (___t && ((i) = *___t, (i) >= (start) && (i) < (end))); \ 399 | }); \ 400 | ) 401 | #endif /* bpf_for */ 402 | 403 | #ifndef bpf_repeat 404 | /* bpf_repeat(N) performs N iterations without exposing iteration number 405 | * 406 | * Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for() 407 | * loop bound variables and cleanup attribute, supported by GCC and Clang. 408 | */ 409 | #define bpf_repeat(N) for ( \ 410 | /* initialize and define destructor */ \ 411 | struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \ 412 | cleanup(bpf_iter_num_destroy))), \ 413 | /* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \ 414 | *___p __attribute__((unused)) = ( \ 415 | bpf_iter_num_new(&___it, 0, (N)), \ 416 | /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \ 417 | /* for bpf_iter_num_destroy() when used from cleanup() attribute */ \ 418 | (void)bpf_iter_num_destroy, (void *)0); \ 419 | bpf_iter_num_next(&___it); \ 420 | /* nothing here */ \ 421 | ) 422 | #endif /* bpf_repeat */ 423 | 424 | #endif 425 | -------------------------------------------------------------------------------- /headers/bpf_tracing.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_TRACING_H__ 3 | #define __BPF_TRACING_H__ 4 | 5 | #include "bpf_helpers.h" 6 | 7 | /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ 8 | #if defined(__TARGET_ARCH_x86) 9 | #define bpf_target_x86 10 | #define bpf_target_defined 11 | #elif defined(__TARGET_ARCH_s390) 12 | #define bpf_target_s390 13 | #define bpf_target_defined 14 | #elif defined(__TARGET_ARCH_arm) 15 | #define bpf_target_arm 16 | #define bpf_target_defined 17 | #elif defined(__TARGET_ARCH_arm64) 18 | #define bpf_target_arm64 19 | #define bpf_target_defined 20 | #elif defined(__TARGET_ARCH_mips) 21 | #define bpf_target_mips 22 | #define bpf_target_defined 23 | #elif defined(__TARGET_ARCH_powerpc) 24 | #define bpf_target_powerpc 25 | #define bpf_target_defined 26 | #elif defined(__TARGET_ARCH_sparc) 27 | #define bpf_target_sparc 28 | #define bpf_target_defined 29 | #elif defined(__TARGET_ARCH_riscv) 30 | #define bpf_target_riscv 31 | #define bpf_target_defined 32 | #elif defined(__TARGET_ARCH_arc) 33 | #define bpf_target_arc 34 | #define bpf_target_defined 35 | #elif defined(__TARGET_ARCH_loongarch) 36 | #define bpf_target_loongarch 37 | #define bpf_target_defined 38 | #else 39 | 40 | /* Fall back to what the compiler says */ 41 | #if defined(__x86_64__) 42 | #define bpf_target_x86 43 | #define bpf_target_defined 44 | #elif defined(__s390__) 45 | #define bpf_target_s390 46 | #define bpf_target_defined 47 | #elif defined(__arm__) 48 | #define bpf_target_arm 49 | #define bpf_target_defined 50 | #elif defined(__aarch64__) 51 | #define bpf_target_arm64 52 | #define bpf_target_defined 53 | #elif defined(__mips__) 54 | #define bpf_target_mips 55 | #define bpf_target_defined 56 | #elif defined(__powerpc__) 57 | #define bpf_target_powerpc 58 | #define bpf_target_defined 59 | #elif defined(__sparc__) 60 | #define bpf_target_sparc 61 | #define bpf_target_defined 62 | #elif defined(__riscv) && __riscv_xlen == 64 63 | #define bpf_target_riscv 64 | #define bpf_target_defined 65 | #elif defined(__arc__) 66 | #define bpf_target_arc 67 | #define bpf_target_defined 68 | #elif defined(__loongarch__) 69 | #define bpf_target_loongarch 70 | #define bpf_target_defined 71 | #endif /* no compiler target */ 72 | 73 | #endif 74 | 75 | #ifndef __BPF_TARGET_MISSING 76 | #define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\"" 77 | #endif 78 | 79 | #if defined(bpf_target_x86) 80 | 81 | /* 82 | * https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI 83 | */ 84 | 85 | #if defined(__KERNEL__) || defined(__VMLINUX_H__) 86 | 87 | #define __PT_PARM1_REG di 88 | #define __PT_PARM2_REG si 89 | #define __PT_PARM3_REG dx 90 | #define __PT_PARM4_REG cx 91 | #define __PT_PARM5_REG r8 92 | #define __PT_PARM6_REG r9 93 | /* 94 | * Syscall uses r10 for PARM4. See arch/x86/entry/entry_64.S:entry_SYSCALL_64 95 | * comments in Linux sources. And refer to syscall(2) manpage. 96 | */ 97 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 98 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 99 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 100 | #define __PT_PARM4_SYSCALL_REG r10 101 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 102 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 103 | 104 | #define __PT_RET_REG sp 105 | #define __PT_FP_REG bp 106 | #define __PT_RC_REG ax 107 | #define __PT_SP_REG sp 108 | #define __PT_IP_REG ip 109 | 110 | #else 111 | 112 | #ifdef __i386__ 113 | 114 | /* i386 kernel is built with -mregparm=3 */ 115 | #define __PT_PARM1_REG eax 116 | #define __PT_PARM2_REG edx 117 | #define __PT_PARM3_REG ecx 118 | /* i386 syscall ABI is very different, refer to syscall(2) manpage */ 119 | #define __PT_PARM1_SYSCALL_REG ebx 120 | #define __PT_PARM2_SYSCALL_REG ecx 121 | #define __PT_PARM3_SYSCALL_REG edx 122 | #define __PT_PARM4_SYSCALL_REG esi 123 | #define __PT_PARM5_SYSCALL_REG edi 124 | #define __PT_PARM6_SYSCALL_REG ebp 125 | 126 | #define __PT_RET_REG esp 127 | #define __PT_FP_REG ebp 128 | #define __PT_RC_REG eax 129 | #define __PT_SP_REG esp 130 | #define __PT_IP_REG eip 131 | 132 | #else /* __i386__ */ 133 | 134 | #define __PT_PARM1_REG rdi 135 | #define __PT_PARM2_REG rsi 136 | #define __PT_PARM3_REG rdx 137 | #define __PT_PARM4_REG rcx 138 | #define __PT_PARM5_REG r8 139 | #define __PT_PARM6_REG r9 140 | 141 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 142 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 143 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 144 | #define __PT_PARM4_SYSCALL_REG r10 145 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 146 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 147 | 148 | #define __PT_RET_REG rsp 149 | #define __PT_FP_REG rbp 150 | #define __PT_RC_REG rax 151 | #define __PT_SP_REG rsp 152 | #define __PT_IP_REG rip 153 | 154 | #endif /* __i386__ */ 155 | 156 | #endif /* __KERNEL__ || __VMLINUX_H__ */ 157 | 158 | #elif defined(bpf_target_s390) 159 | 160 | /* 161 | * https://github.com/IBM/s390x-abi/releases/download/v1.6/lzsabi_s390x.pdf 162 | */ 163 | 164 | struct pt_regs___s390 { 165 | unsigned long orig_gpr2; 166 | } __attribute__((preserve_access_index)); 167 | 168 | /* s390 provides user_pt_regs instead of struct pt_regs to userspace */ 169 | #define __PT_REGS_CAST(x) ((const user_pt_regs *)(x)) 170 | #define __PT_PARM1_REG gprs[2] 171 | #define __PT_PARM2_REG gprs[3] 172 | #define __PT_PARM3_REG gprs[4] 173 | #define __PT_PARM4_REG gprs[5] 174 | #define __PT_PARM5_REG gprs[6] 175 | 176 | #define __PT_PARM1_SYSCALL_REG orig_gpr2 177 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 178 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 179 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 180 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 181 | #define __PT_PARM6_SYSCALL_REG gprs[7] 182 | #define PT_REGS_PARM1_SYSCALL(x) (((const struct pt_regs___s390 *)(x))->__PT_PARM1_SYSCALL_REG) 183 | #define PT_REGS_PARM1_CORE_SYSCALL(x) \ 184 | BPF_CORE_READ((const struct pt_regs___s390 *)(x), __PT_PARM1_SYSCALL_REG) 185 | 186 | #define __PT_RET_REG gprs[14] 187 | #define __PT_FP_REG gprs[11] /* Works only with CONFIG_FRAME_POINTER */ 188 | #define __PT_RC_REG gprs[2] 189 | #define __PT_SP_REG gprs[15] 190 | #define __PT_IP_REG psw.addr 191 | 192 | #elif defined(bpf_target_arm) 193 | 194 | /* 195 | * https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#machine-registers 196 | */ 197 | 198 | #define __PT_PARM1_REG uregs[0] 199 | #define __PT_PARM2_REG uregs[1] 200 | #define __PT_PARM3_REG uregs[2] 201 | #define __PT_PARM4_REG uregs[3] 202 | 203 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 204 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 205 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 206 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 207 | #define __PT_PARM5_SYSCALL_REG uregs[4] 208 | #define __PT_PARM6_SYSCALL_REG uregs[5] 209 | #define __PT_PARM7_SYSCALL_REG uregs[6] 210 | 211 | #define __PT_RET_REG uregs[14] 212 | #define __PT_FP_REG uregs[11] /* Works only with CONFIG_FRAME_POINTER */ 213 | #define __PT_RC_REG uregs[0] 214 | #define __PT_SP_REG uregs[13] 215 | #define __PT_IP_REG uregs[12] 216 | 217 | #elif defined(bpf_target_arm64) 218 | 219 | /* 220 | * https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#machine-registers 221 | */ 222 | 223 | struct pt_regs___arm64 { 224 | unsigned long orig_x0; 225 | } __attribute__((preserve_access_index)); 226 | 227 | /* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */ 228 | #define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) 229 | #define __PT_PARM1_REG regs[0] 230 | #define __PT_PARM2_REG regs[1] 231 | #define __PT_PARM3_REG regs[2] 232 | #define __PT_PARM4_REG regs[3] 233 | #define __PT_PARM5_REG regs[4] 234 | #define __PT_PARM6_REG regs[5] 235 | #define __PT_PARM7_REG regs[6] 236 | #define __PT_PARM8_REG regs[7] 237 | 238 | #define __PT_PARM1_SYSCALL_REG orig_x0 239 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 240 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 241 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 242 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 243 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 244 | #define PT_REGS_PARM1_SYSCALL(x) (((const struct pt_regs___arm64 *)(x))->__PT_PARM1_SYSCALL_REG) 245 | #define PT_REGS_PARM1_CORE_SYSCALL(x) \ 246 | BPF_CORE_READ((const struct pt_regs___arm64 *)(x), __PT_PARM1_SYSCALL_REG) 247 | 248 | #define __PT_RET_REG regs[30] 249 | #define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */ 250 | #define __PT_RC_REG regs[0] 251 | #define __PT_SP_REG sp 252 | #define __PT_IP_REG pc 253 | 254 | #elif defined(bpf_target_mips) 255 | 256 | /* 257 | * N64 ABI is assumed right now. 258 | * https://en.wikipedia.org/wiki/MIPS_architecture#Calling_conventions 259 | */ 260 | 261 | #define __PT_PARM1_REG regs[4] 262 | #define __PT_PARM2_REG regs[5] 263 | #define __PT_PARM3_REG regs[6] 264 | #define __PT_PARM4_REG regs[7] 265 | #define __PT_PARM5_REG regs[8] 266 | #define __PT_PARM6_REG regs[9] 267 | #define __PT_PARM7_REG regs[10] 268 | #define __PT_PARM8_REG regs[11] 269 | 270 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 271 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 272 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 273 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 274 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG /* only N32/N64 */ 275 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG /* only N32/N64 */ 276 | 277 | #define __PT_RET_REG regs[31] 278 | #define __PT_FP_REG regs[30] /* Works only with CONFIG_FRAME_POINTER */ 279 | #define __PT_RC_REG regs[2] 280 | #define __PT_SP_REG regs[29] 281 | #define __PT_IP_REG cp0_epc 282 | 283 | #elif defined(bpf_target_powerpc) 284 | 285 | /* 286 | * http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf (page 3-14, 287 | * section "Function Calling Sequence") 288 | */ 289 | 290 | #define __PT_PARM1_REG gpr[3] 291 | #define __PT_PARM2_REG gpr[4] 292 | #define __PT_PARM3_REG gpr[5] 293 | #define __PT_PARM4_REG gpr[6] 294 | #define __PT_PARM5_REG gpr[7] 295 | #define __PT_PARM6_REG gpr[8] 296 | #define __PT_PARM7_REG gpr[9] 297 | #define __PT_PARM8_REG gpr[10] 298 | 299 | /* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER. */ 300 | #define PT_REGS_SYSCALL_REGS(ctx) ctx 301 | #define __PT_PARM1_SYSCALL_REG orig_gpr3 302 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 303 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 304 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 305 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 306 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 307 | #if !defined(__arch64__) 308 | #define __PT_PARM7_SYSCALL_REG __PT_PARM7_REG /* only powerpc (not powerpc64) */ 309 | #endif 310 | 311 | #define __PT_RET_REG regs[31] 312 | #define __PT_FP_REG __unsupported__ 313 | #define __PT_RC_REG gpr[3] 314 | #define __PT_SP_REG sp 315 | #define __PT_IP_REG nip 316 | 317 | #elif defined(bpf_target_sparc) 318 | 319 | /* 320 | * https://en.wikipedia.org/wiki/Calling_convention#SPARC 321 | */ 322 | 323 | #define __PT_PARM1_REG u_regs[UREG_I0] 324 | #define __PT_PARM2_REG u_regs[UREG_I1] 325 | #define __PT_PARM3_REG u_regs[UREG_I2] 326 | #define __PT_PARM4_REG u_regs[UREG_I3] 327 | #define __PT_PARM5_REG u_regs[UREG_I4] 328 | #define __PT_PARM6_REG u_regs[UREG_I5] 329 | 330 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 331 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 332 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 333 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 334 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 335 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 336 | 337 | #define __PT_RET_REG u_regs[UREG_I7] 338 | #define __PT_FP_REG __unsupported__ 339 | #define __PT_RC_REG u_regs[UREG_I0] 340 | #define __PT_SP_REG u_regs[UREG_FP] 341 | /* Should this also be a bpf_target check for the sparc case? */ 342 | #if defined(__arch64__) 343 | #define __PT_IP_REG tpc 344 | #else 345 | #define __PT_IP_REG pc 346 | #endif 347 | 348 | #elif defined(bpf_target_riscv) 349 | 350 | /* 351 | * https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#risc-v-calling-conventions 352 | */ 353 | 354 | struct pt_regs___riscv { 355 | unsigned long orig_a0; 356 | } __attribute__((preserve_access_index)); 357 | 358 | /* riscv provides struct user_regs_struct instead of struct pt_regs to userspace */ 359 | #define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) 360 | #define __PT_PARM1_REG a0 361 | #define __PT_PARM2_REG a1 362 | #define __PT_PARM3_REG a2 363 | #define __PT_PARM4_REG a3 364 | #define __PT_PARM5_REG a4 365 | #define __PT_PARM6_REG a5 366 | #define __PT_PARM7_REG a6 367 | #define __PT_PARM8_REG a7 368 | 369 | #define __PT_PARM1_SYSCALL_REG orig_a0 370 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 371 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 372 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 373 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 374 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 375 | #define PT_REGS_PARM1_SYSCALL(x) (((const struct pt_regs___riscv *)(x))->__PT_PARM1_SYSCALL_REG) 376 | #define PT_REGS_PARM1_CORE_SYSCALL(x) \ 377 | BPF_CORE_READ((const struct pt_regs___riscv *)(x), __PT_PARM1_SYSCALL_REG) 378 | 379 | #define __PT_RET_REG ra 380 | #define __PT_FP_REG s0 381 | #define __PT_RC_REG a0 382 | #define __PT_SP_REG sp 383 | #define __PT_IP_REG pc 384 | 385 | #elif defined(bpf_target_arc) 386 | 387 | /* 388 | * Section "Function Calling Sequence" (page 24): 389 | * https://raw.githubusercontent.com/wiki/foss-for-synopsys-dwc-arc-processors/toolchain/files/ARCv2_ABI.pdf 390 | */ 391 | 392 | /* arc provides struct user_regs_struct instead of struct pt_regs to userspace */ 393 | #define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) 394 | #define __PT_PARM1_REG scratch.r0 395 | #define __PT_PARM2_REG scratch.r1 396 | #define __PT_PARM3_REG scratch.r2 397 | #define __PT_PARM4_REG scratch.r3 398 | #define __PT_PARM5_REG scratch.r4 399 | #define __PT_PARM6_REG scratch.r5 400 | #define __PT_PARM7_REG scratch.r6 401 | #define __PT_PARM8_REG scratch.r7 402 | 403 | /* arc does not select ARCH_HAS_SYSCALL_WRAPPER. */ 404 | #define PT_REGS_SYSCALL_REGS(ctx) ctx 405 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 406 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 407 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 408 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 409 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 410 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 411 | 412 | #define __PT_RET_REG scratch.blink 413 | #define __PT_FP_REG scratch.fp 414 | #define __PT_RC_REG scratch.r0 415 | #define __PT_SP_REG scratch.sp 416 | #define __PT_IP_REG scratch.ret 417 | 418 | #elif defined(bpf_target_loongarch) 419 | 420 | /* 421 | * https://docs.kernel.org/loongarch/introduction.html 422 | * https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html 423 | */ 424 | 425 | /* loongarch provides struct user_pt_regs instead of struct pt_regs to userspace */ 426 | #define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) 427 | #define __PT_PARM1_REG regs[4] 428 | #define __PT_PARM2_REG regs[5] 429 | #define __PT_PARM3_REG regs[6] 430 | #define __PT_PARM4_REG regs[7] 431 | #define __PT_PARM5_REG regs[8] 432 | #define __PT_PARM6_REG regs[9] 433 | #define __PT_PARM7_REG regs[10] 434 | #define __PT_PARM8_REG regs[11] 435 | 436 | /* loongarch does not select ARCH_HAS_SYSCALL_WRAPPER. */ 437 | #define PT_REGS_SYSCALL_REGS(ctx) ctx 438 | #define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG 439 | #define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG 440 | #define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG 441 | #define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG 442 | #define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG 443 | #define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG 444 | 445 | #define __PT_RET_REG regs[1] 446 | #define __PT_FP_REG regs[22] 447 | #define __PT_RC_REG regs[4] 448 | #define __PT_SP_REG regs[3] 449 | #define __PT_IP_REG csr_era 450 | 451 | #endif 452 | 453 | #if defined(bpf_target_defined) 454 | 455 | struct pt_regs; 456 | 457 | /* allow some architectures to override `struct pt_regs` */ 458 | #ifndef __PT_REGS_CAST 459 | #define __PT_REGS_CAST(x) (x) 460 | #endif 461 | 462 | /* 463 | * Different architectures support different number of arguments passed 464 | * through registers. i386 supports just 3, some arches support up to 8. 465 | */ 466 | #ifndef __PT_PARM4_REG 467 | #define __PT_PARM4_REG __unsupported__ 468 | #endif 469 | #ifndef __PT_PARM5_REG 470 | #define __PT_PARM5_REG __unsupported__ 471 | #endif 472 | #ifndef __PT_PARM6_REG 473 | #define __PT_PARM6_REG __unsupported__ 474 | #endif 475 | #ifndef __PT_PARM7_REG 476 | #define __PT_PARM7_REG __unsupported__ 477 | #endif 478 | #ifndef __PT_PARM8_REG 479 | #define __PT_PARM8_REG __unsupported__ 480 | #endif 481 | /* 482 | * Similarly, syscall-specific conventions might differ between function call 483 | * conventions within each architecture. All supported architectures pass 484 | * either 6 or 7 syscall arguments in registers. 485 | * 486 | * See syscall(2) manpage for succinct table with information on each arch. 487 | */ 488 | #ifndef __PT_PARM7_SYSCALL_REG 489 | #define __PT_PARM7_SYSCALL_REG __unsupported__ 490 | #endif 491 | 492 | #define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG) 493 | #define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) 494 | #define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG) 495 | #define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG) 496 | #define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG) 497 | #define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG) 498 | #define PT_REGS_PARM7(x) (__PT_REGS_CAST(x)->__PT_PARM7_REG) 499 | #define PT_REGS_PARM8(x) (__PT_REGS_CAST(x)->__PT_PARM8_REG) 500 | #define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG) 501 | #define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG) 502 | #define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG) 503 | #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) 504 | #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) 505 | 506 | #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_REG) 507 | #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_REG) 508 | #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_REG) 509 | #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_REG) 510 | #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_REG) 511 | #define PT_REGS_PARM6_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM6_REG) 512 | #define PT_REGS_PARM7_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM7_REG) 513 | #define PT_REGS_PARM8_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM8_REG) 514 | #define PT_REGS_RET_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RET_REG) 515 | #define PT_REGS_FP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_FP_REG) 516 | #define PT_REGS_RC_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RC_REG) 517 | #define PT_REGS_SP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_SP_REG) 518 | #define PT_REGS_IP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_IP_REG) 519 | 520 | #if defined(bpf_target_powerpc) 521 | 522 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) 523 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 524 | 525 | #elif defined(bpf_target_sparc) || defined(bpf_target_arm64) 526 | 527 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) 528 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 529 | 530 | #else 531 | 532 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) \ 533 | ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) 534 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \ 535 | ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) 536 | 537 | #endif 538 | 539 | #ifndef PT_REGS_PARM1_SYSCALL 540 | #define PT_REGS_PARM1_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM1_SYSCALL_REG) 541 | #define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_SYSCALL_REG) 542 | #endif 543 | #ifndef PT_REGS_PARM2_SYSCALL 544 | #define PT_REGS_PARM2_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM2_SYSCALL_REG) 545 | #define PT_REGS_PARM2_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_SYSCALL_REG) 546 | #endif 547 | #ifndef PT_REGS_PARM3_SYSCALL 548 | #define PT_REGS_PARM3_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM3_SYSCALL_REG) 549 | #define PT_REGS_PARM3_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_SYSCALL_REG) 550 | #endif 551 | #ifndef PT_REGS_PARM4_SYSCALL 552 | #define PT_REGS_PARM4_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM4_SYSCALL_REG) 553 | #define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_SYSCALL_REG) 554 | #endif 555 | #ifndef PT_REGS_PARM5_SYSCALL 556 | #define PT_REGS_PARM5_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM5_SYSCALL_REG) 557 | #define PT_REGS_PARM5_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_SYSCALL_REG) 558 | #endif 559 | #ifndef PT_REGS_PARM6_SYSCALL 560 | #define PT_REGS_PARM6_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM6_SYSCALL_REG) 561 | #define PT_REGS_PARM6_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM6_SYSCALL_REG) 562 | #endif 563 | #ifndef PT_REGS_PARM7_SYSCALL 564 | #define PT_REGS_PARM7_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM7_SYSCALL_REG) 565 | #define PT_REGS_PARM7_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM7_SYSCALL_REG) 566 | #endif 567 | 568 | #else /* defined(bpf_target_defined) */ 569 | 570 | #define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 571 | #define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 572 | #define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 573 | #define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 574 | #define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 575 | #define PT_REGS_PARM6(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 576 | #define PT_REGS_PARM7(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 577 | #define PT_REGS_PARM8(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 578 | #define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 579 | #define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 580 | #define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 581 | #define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 582 | #define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 583 | 584 | #define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 585 | #define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 586 | #define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 587 | #define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 588 | #define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 589 | #define PT_REGS_PARM6_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 590 | #define PT_REGS_PARM7_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 591 | #define PT_REGS_PARM8_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 592 | #define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 593 | #define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 594 | #define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 595 | #define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 596 | #define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 597 | 598 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 599 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 600 | 601 | #define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 602 | #define PT_REGS_PARM2_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 603 | #define PT_REGS_PARM3_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 604 | #define PT_REGS_PARM4_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 605 | #define PT_REGS_PARM5_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 606 | #define PT_REGS_PARM6_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 607 | #define PT_REGS_PARM7_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 608 | 609 | #define PT_REGS_PARM1_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 610 | #define PT_REGS_PARM2_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 611 | #define PT_REGS_PARM3_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 612 | #define PT_REGS_PARM4_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 613 | #define PT_REGS_PARM5_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 614 | #define PT_REGS_PARM6_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 615 | #define PT_REGS_PARM7_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) 616 | 617 | #endif /* defined(bpf_target_defined) */ 618 | 619 | /* 620 | * When invoked from a syscall handler kprobe, returns a pointer to a 621 | * struct pt_regs containing syscall arguments and suitable for passing to 622 | * PT_REGS_PARMn_SYSCALL() and PT_REGS_PARMn_CORE_SYSCALL(). 623 | */ 624 | #ifndef PT_REGS_SYSCALL_REGS 625 | /* By default, assume that the arch selects ARCH_HAS_SYSCALL_WRAPPER. */ 626 | #define PT_REGS_SYSCALL_REGS(ctx) ((struct pt_regs *)PT_REGS_PARM1(ctx)) 627 | #endif 628 | 629 | #ifndef ___bpf_concat 630 | #define ___bpf_concat(a, b) a ## b 631 | #endif 632 | #ifndef ___bpf_apply 633 | #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) 634 | #endif 635 | #ifndef ___bpf_nth 636 | #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N 637 | #endif 638 | #ifndef ___bpf_narg 639 | #define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 640 | #endif 641 | 642 | #define ___bpf_ctx_cast0() ctx 643 | #define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), ctx[0] 644 | #define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), ctx[1] 645 | #define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), ctx[2] 646 | #define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), ctx[3] 647 | #define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), ctx[4] 648 | #define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), ctx[5] 649 | #define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), ctx[6] 650 | #define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), ctx[7] 651 | #define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), ctx[8] 652 | #define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), ctx[9] 653 | #define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), ctx[10] 654 | #define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), ctx[11] 655 | #define ___bpf_ctx_cast(args...) ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args) 656 | 657 | /* 658 | * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and 659 | * similar kinds of BPF programs, that accept input arguments as a single 660 | * pointer to untyped u64 array, where each u64 can actually be a typed 661 | * pointer or integer of different size. Instead of requiring user to write 662 | * manual casts and work with array elements by index, BPF_PROG macro 663 | * allows user to declare a list of named and typed input arguments in the 664 | * same syntax as for normal C function. All the casting is hidden and 665 | * performed transparently, while user code can just assume working with 666 | * function arguments of specified type and name. 667 | * 668 | * Original raw context argument is preserved as well as 'ctx' argument. 669 | * This is useful when using BPF helpers that expect original context 670 | * as one of the parameters (e.g., for bpf_perf_event_output()). 671 | */ 672 | #define BPF_PROG(name, args...) \ 673 | name(unsigned long long *ctx); \ 674 | static __always_inline typeof(name(0)) \ 675 | ____##name(unsigned long long *ctx, ##args); \ 676 | typeof(name(0)) name(unsigned long long *ctx) \ 677 | { \ 678 | _Pragma("GCC diagnostic push") \ 679 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 680 | return ____##name(___bpf_ctx_cast(args)); \ 681 | _Pragma("GCC diagnostic pop") \ 682 | } \ 683 | static __always_inline typeof(name(0)) \ 684 | ____##name(unsigned long long *ctx, ##args) 685 | 686 | #ifndef ___bpf_nth2 687 | #define ___bpf_nth2(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \ 688 | _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, N, ...) N 689 | #endif 690 | #ifndef ___bpf_narg2 691 | #define ___bpf_narg2(...) \ 692 | ___bpf_nth2(_, ##__VA_ARGS__, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, \ 693 | 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0) 694 | #endif 695 | 696 | #define ___bpf_treg_cnt(t) \ 697 | __builtin_choose_expr(sizeof(t) == 1, 1, \ 698 | __builtin_choose_expr(sizeof(t) == 2, 1, \ 699 | __builtin_choose_expr(sizeof(t) == 4, 1, \ 700 | __builtin_choose_expr(sizeof(t) == 8, 1, \ 701 | __builtin_choose_expr(sizeof(t) == 16, 2, \ 702 | (void)0))))) 703 | 704 | #define ___bpf_reg_cnt0() (0) 705 | #define ___bpf_reg_cnt1(t, x) (___bpf_reg_cnt0() + ___bpf_treg_cnt(t)) 706 | #define ___bpf_reg_cnt2(t, x, args...) (___bpf_reg_cnt1(args) + ___bpf_treg_cnt(t)) 707 | #define ___bpf_reg_cnt3(t, x, args...) (___bpf_reg_cnt2(args) + ___bpf_treg_cnt(t)) 708 | #define ___bpf_reg_cnt4(t, x, args...) (___bpf_reg_cnt3(args) + ___bpf_treg_cnt(t)) 709 | #define ___bpf_reg_cnt5(t, x, args...) (___bpf_reg_cnt4(args) + ___bpf_treg_cnt(t)) 710 | #define ___bpf_reg_cnt6(t, x, args...) (___bpf_reg_cnt5(args) + ___bpf_treg_cnt(t)) 711 | #define ___bpf_reg_cnt7(t, x, args...) (___bpf_reg_cnt6(args) + ___bpf_treg_cnt(t)) 712 | #define ___bpf_reg_cnt8(t, x, args...) (___bpf_reg_cnt7(args) + ___bpf_treg_cnt(t)) 713 | #define ___bpf_reg_cnt9(t, x, args...) (___bpf_reg_cnt8(args) + ___bpf_treg_cnt(t)) 714 | #define ___bpf_reg_cnt10(t, x, args...) (___bpf_reg_cnt9(args) + ___bpf_treg_cnt(t)) 715 | #define ___bpf_reg_cnt11(t, x, args...) (___bpf_reg_cnt10(args) + ___bpf_treg_cnt(t)) 716 | #define ___bpf_reg_cnt12(t, x, args...) (___bpf_reg_cnt11(args) + ___bpf_treg_cnt(t)) 717 | #define ___bpf_reg_cnt(args...) ___bpf_apply(___bpf_reg_cnt, ___bpf_narg2(args))(args) 718 | 719 | #define ___bpf_union_arg(t, x, n) \ 720 | __builtin_choose_expr(sizeof(t) == 1, ({ union { __u8 z[1]; t x; } ___t = { .z = {ctx[n]}}; ___t.x; }), \ 721 | __builtin_choose_expr(sizeof(t) == 2, ({ union { __u16 z[1]; t x; } ___t = { .z = {ctx[n]} }; ___t.x; }), \ 722 | __builtin_choose_expr(sizeof(t) == 4, ({ union { __u32 z[1]; t x; } ___t = { .z = {ctx[n]} }; ___t.x; }), \ 723 | __builtin_choose_expr(sizeof(t) == 8, ({ union { __u64 z[1]; t x; } ___t = {.z = {ctx[n]} }; ___t.x; }), \ 724 | __builtin_choose_expr(sizeof(t) == 16, ({ union { __u64 z[2]; t x; } ___t = {.z = {ctx[n], ctx[n + 1]} }; ___t.x; }), \ 725 | (void)0))))) 726 | 727 | #define ___bpf_ctx_arg0(n, args...) 728 | #define ___bpf_ctx_arg1(n, t, x) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt1(t, x)) 729 | #define ___bpf_ctx_arg2(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt2(t, x, args)) ___bpf_ctx_arg1(n, args) 730 | #define ___bpf_ctx_arg3(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt3(t, x, args)) ___bpf_ctx_arg2(n, args) 731 | #define ___bpf_ctx_arg4(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt4(t, x, args)) ___bpf_ctx_arg3(n, args) 732 | #define ___bpf_ctx_arg5(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt5(t, x, args)) ___bpf_ctx_arg4(n, args) 733 | #define ___bpf_ctx_arg6(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt6(t, x, args)) ___bpf_ctx_arg5(n, args) 734 | #define ___bpf_ctx_arg7(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt7(t, x, args)) ___bpf_ctx_arg6(n, args) 735 | #define ___bpf_ctx_arg8(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt8(t, x, args)) ___bpf_ctx_arg7(n, args) 736 | #define ___bpf_ctx_arg9(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt9(t, x, args)) ___bpf_ctx_arg8(n, args) 737 | #define ___bpf_ctx_arg10(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt10(t, x, args)) ___bpf_ctx_arg9(n, args) 738 | #define ___bpf_ctx_arg11(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt11(t, x, args)) ___bpf_ctx_arg10(n, args) 739 | #define ___bpf_ctx_arg12(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt12(t, x, args)) ___bpf_ctx_arg11(n, args) 740 | #define ___bpf_ctx_arg(args...) ___bpf_apply(___bpf_ctx_arg, ___bpf_narg2(args))(___bpf_reg_cnt(args), args) 741 | 742 | #define ___bpf_ctx_decl0() 743 | #define ___bpf_ctx_decl1(t, x) , t x 744 | #define ___bpf_ctx_decl2(t, x, args...) , t x ___bpf_ctx_decl1(args) 745 | #define ___bpf_ctx_decl3(t, x, args...) , t x ___bpf_ctx_decl2(args) 746 | #define ___bpf_ctx_decl4(t, x, args...) , t x ___bpf_ctx_decl3(args) 747 | #define ___bpf_ctx_decl5(t, x, args...) , t x ___bpf_ctx_decl4(args) 748 | #define ___bpf_ctx_decl6(t, x, args...) , t x ___bpf_ctx_decl5(args) 749 | #define ___bpf_ctx_decl7(t, x, args...) , t x ___bpf_ctx_decl6(args) 750 | #define ___bpf_ctx_decl8(t, x, args...) , t x ___bpf_ctx_decl7(args) 751 | #define ___bpf_ctx_decl9(t, x, args...) , t x ___bpf_ctx_decl8(args) 752 | #define ___bpf_ctx_decl10(t, x, args...) , t x ___bpf_ctx_decl9(args) 753 | #define ___bpf_ctx_decl11(t, x, args...) , t x ___bpf_ctx_decl10(args) 754 | #define ___bpf_ctx_decl12(t, x, args...) , t x ___bpf_ctx_decl11(args) 755 | #define ___bpf_ctx_decl(args...) ___bpf_apply(___bpf_ctx_decl, ___bpf_narg2(args))(args) 756 | 757 | /* 758 | * BPF_PROG2 is an enhanced version of BPF_PROG in order to handle struct 759 | * arguments. Since each struct argument might take one or two u64 values 760 | * in the trampoline stack, argument type size is needed to place proper number 761 | * of u64 values for each argument. Therefore, BPF_PROG2 has different 762 | * syntax from BPF_PROG. For example, for the following BPF_PROG syntax: 763 | * 764 | * int BPF_PROG(test2, int a, int b) { ... } 765 | * 766 | * the corresponding BPF_PROG2 syntax is: 767 | * 768 | * int BPF_PROG2(test2, int, a, int, b) { ... } 769 | * 770 | * where type and the corresponding argument name are separated by comma. 771 | * 772 | * Use BPF_PROG2 macro if one of the arguments might be a struct/union larger 773 | * than 8 bytes: 774 | * 775 | * int BPF_PROG2(test_struct_arg, struct bpf_testmod_struct_arg_1, a, int, b, 776 | * int, c, int, d, struct bpf_testmod_struct_arg_2, e, int, ret) 777 | * { 778 | * // access a, b, c, d, e, and ret directly 779 | * ... 780 | * } 781 | */ 782 | #define BPF_PROG2(name, args...) \ 783 | name(unsigned long long *ctx); \ 784 | static __always_inline typeof(name(0)) \ 785 | ____##name(unsigned long long *ctx ___bpf_ctx_decl(args)); \ 786 | typeof(name(0)) name(unsigned long long *ctx) \ 787 | { \ 788 | return ____##name(ctx ___bpf_ctx_arg(args)); \ 789 | } \ 790 | static __always_inline typeof(name(0)) \ 791 | ____##name(unsigned long long *ctx ___bpf_ctx_decl(args)) 792 | 793 | struct pt_regs; 794 | 795 | #define ___bpf_kprobe_args0() ctx 796 | #define ___bpf_kprobe_args1(x) ___bpf_kprobe_args0(), (unsigned long long)PT_REGS_PARM1(ctx) 797 | #define ___bpf_kprobe_args2(x, args...) ___bpf_kprobe_args1(args), (unsigned long long)PT_REGS_PARM2(ctx) 798 | #define ___bpf_kprobe_args3(x, args...) ___bpf_kprobe_args2(args), (unsigned long long)PT_REGS_PARM3(ctx) 799 | #define ___bpf_kprobe_args4(x, args...) ___bpf_kprobe_args3(args), (unsigned long long)PT_REGS_PARM4(ctx) 800 | #define ___bpf_kprobe_args5(x, args...) ___bpf_kprobe_args4(args), (unsigned long long)PT_REGS_PARM5(ctx) 801 | #define ___bpf_kprobe_args6(x, args...) ___bpf_kprobe_args5(args), (unsigned long long)PT_REGS_PARM6(ctx) 802 | #define ___bpf_kprobe_args7(x, args...) ___bpf_kprobe_args6(args), (unsigned long long)PT_REGS_PARM7(ctx) 803 | #define ___bpf_kprobe_args8(x, args...) ___bpf_kprobe_args7(args), (unsigned long long)PT_REGS_PARM8(ctx) 804 | #define ___bpf_kprobe_args(args...) ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args) 805 | 806 | /* 807 | * BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for 808 | * tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific 809 | * low-level way of getting kprobe input arguments from struct pt_regs, and 810 | * provides a familiar typed and named function arguments syntax and 811 | * semantics of accessing kprobe input parameters. 812 | * 813 | * Original struct pt_regs* context is preserved as 'ctx' argument. This might 814 | * be necessary when using BPF helpers like bpf_perf_event_output(). 815 | */ 816 | #define BPF_KPROBE(name, args...) \ 817 | name(struct pt_regs *ctx); \ 818 | static __always_inline typeof(name(0)) \ 819 | ____##name(struct pt_regs *ctx, ##args); \ 820 | typeof(name(0)) name(struct pt_regs *ctx) \ 821 | { \ 822 | _Pragma("GCC diagnostic push") \ 823 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 824 | return ____##name(___bpf_kprobe_args(args)); \ 825 | _Pragma("GCC diagnostic pop") \ 826 | } \ 827 | static __always_inline typeof(name(0)) \ 828 | ____##name(struct pt_regs *ctx, ##args) 829 | 830 | #define ___bpf_kretprobe_args0() ctx 831 | #define ___bpf_kretprobe_args1(x) ___bpf_kretprobe_args0(), (unsigned long long)PT_REGS_RC(ctx) 832 | #define ___bpf_kretprobe_args(args...) ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args) 833 | 834 | /* 835 | * BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional 836 | * return value (in addition to `struct pt_regs *ctx`), but no input 837 | * arguments, because they will be clobbered by the time probed function 838 | * returns. 839 | */ 840 | #define BPF_KRETPROBE(name, args...) \ 841 | name(struct pt_regs *ctx); \ 842 | static __always_inline typeof(name(0)) \ 843 | ____##name(struct pt_regs *ctx, ##args); \ 844 | typeof(name(0)) name(struct pt_regs *ctx) \ 845 | { \ 846 | _Pragma("GCC diagnostic push") \ 847 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 848 | return ____##name(___bpf_kretprobe_args(args)); \ 849 | _Pragma("GCC diagnostic pop") \ 850 | } \ 851 | static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args) 852 | 853 | /* If kernel has CONFIG_ARCH_HAS_SYSCALL_WRAPPER, read pt_regs directly */ 854 | #define ___bpf_syscall_args0() ctx 855 | #define ___bpf_syscall_args1(x) ___bpf_syscall_args0(), (unsigned long long)PT_REGS_PARM1_SYSCALL(regs) 856 | #define ___bpf_syscall_args2(x, args...) ___bpf_syscall_args1(args), (unsigned long long)PT_REGS_PARM2_SYSCALL(regs) 857 | #define ___bpf_syscall_args3(x, args...) ___bpf_syscall_args2(args), (unsigned long long)PT_REGS_PARM3_SYSCALL(regs) 858 | #define ___bpf_syscall_args4(x, args...) ___bpf_syscall_args3(args), (unsigned long long)PT_REGS_PARM4_SYSCALL(regs) 859 | #define ___bpf_syscall_args5(x, args...) ___bpf_syscall_args4(args), (unsigned long long)PT_REGS_PARM5_SYSCALL(regs) 860 | #define ___bpf_syscall_args6(x, args...) ___bpf_syscall_args5(args), (unsigned long long)PT_REGS_PARM6_SYSCALL(regs) 861 | #define ___bpf_syscall_args7(x, args...) ___bpf_syscall_args6(args), (unsigned long long)PT_REGS_PARM7_SYSCALL(regs) 862 | #define ___bpf_syscall_args(args...) ___bpf_apply(___bpf_syscall_args, ___bpf_narg(args))(args) 863 | 864 | /* If kernel doesn't have CONFIG_ARCH_HAS_SYSCALL_WRAPPER, we have to BPF_CORE_READ from pt_regs */ 865 | #define ___bpf_syswrap_args0() ctx 866 | #define ___bpf_syswrap_args1(x) ___bpf_syswrap_args0(), (unsigned long long)PT_REGS_PARM1_CORE_SYSCALL(regs) 867 | #define ___bpf_syswrap_args2(x, args...) ___bpf_syswrap_args1(args), (unsigned long long)PT_REGS_PARM2_CORE_SYSCALL(regs) 868 | #define ___bpf_syswrap_args3(x, args...) ___bpf_syswrap_args2(args), (unsigned long long)PT_REGS_PARM3_CORE_SYSCALL(regs) 869 | #define ___bpf_syswrap_args4(x, args...) ___bpf_syswrap_args3(args), (unsigned long long)PT_REGS_PARM4_CORE_SYSCALL(regs) 870 | #define ___bpf_syswrap_args5(x, args...) ___bpf_syswrap_args4(args), (unsigned long long)PT_REGS_PARM5_CORE_SYSCALL(regs) 871 | #define ___bpf_syswrap_args6(x, args...) ___bpf_syswrap_args5(args), (unsigned long long)PT_REGS_PARM6_CORE_SYSCALL(regs) 872 | #define ___bpf_syswrap_args7(x, args...) ___bpf_syswrap_args6(args), (unsigned long long)PT_REGS_PARM7_CORE_SYSCALL(regs) 873 | #define ___bpf_syswrap_args(args...) ___bpf_apply(___bpf_syswrap_args, ___bpf_narg(args))(args) 874 | 875 | /* 876 | * BPF_KSYSCALL is a variant of BPF_KPROBE, which is intended for 877 | * tracing syscall functions, like __x64_sys_close. It hides the underlying 878 | * platform-specific low-level way of getting syscall input arguments from 879 | * struct pt_regs, and provides a familiar typed and named function arguments 880 | * syntax and semantics of accessing syscall input parameters. 881 | * 882 | * Original struct pt_regs * context is preserved as 'ctx' argument. This might 883 | * be necessary when using BPF helpers like bpf_perf_event_output(). 884 | * 885 | * At the moment BPF_KSYSCALL does not transparently handle all the calling 886 | * convention quirks for the following syscalls: 887 | * 888 | * - mmap(): __ARCH_WANT_SYS_OLD_MMAP. 889 | * - clone(): CONFIG_CLONE_BACKWARDS, CONFIG_CLONE_BACKWARDS2 and 890 | * CONFIG_CLONE_BACKWARDS3. 891 | * - socket-related syscalls: __ARCH_WANT_SYS_SOCKETCALL. 892 | * - compat syscalls. 893 | * 894 | * This may or may not change in the future. User needs to take extra measures 895 | * to handle such quirks explicitly, if necessary. 896 | * 897 | * This macro relies on BPF CO-RE support and virtual __kconfig externs. 898 | */ 899 | #define BPF_KSYSCALL(name, args...) \ 900 | name(struct pt_regs *ctx); \ 901 | extern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig; \ 902 | static __always_inline typeof(name(0)) \ 903 | ____##name(struct pt_regs *ctx, ##args); \ 904 | typeof(name(0)) name(struct pt_regs *ctx) \ 905 | { \ 906 | struct pt_regs *regs = LINUX_HAS_SYSCALL_WRAPPER \ 907 | ? (struct pt_regs *)PT_REGS_PARM1(ctx) \ 908 | : ctx; \ 909 | _Pragma("GCC diagnostic push") \ 910 | _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ 911 | if (LINUX_HAS_SYSCALL_WRAPPER) \ 912 | return ____##name(___bpf_syswrap_args(args)); \ 913 | else \ 914 | return ____##name(___bpf_syscall_args(args)); \ 915 | _Pragma("GCC diagnostic pop") \ 916 | } \ 917 | static __always_inline typeof(name(0)) \ 918 | ____##name(struct pt_regs *ctx, ##args) 919 | 920 | #define BPF_KPROBE_SYSCALL BPF_KSYSCALL 921 | 922 | /* BPF_UPROBE and BPF_URETPROBE are identical to BPF_KPROBE and BPF_KRETPROBE, 923 | * but are named way less confusingly for SEC("uprobe") and SEC("uretprobe") 924 | * use cases. 925 | */ 926 | #define BPF_UPROBE(name, args...) BPF_KPROBE(name, ##args) 927 | #define BPF_URETPROBE(name, args...) BPF_KRETPROBE(name, ##args) 928 | 929 | #endif 930 | -------------------------------------------------------------------------------- /headers/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Version of libbpf to fetch headers from 4 | LIBBPF_VERSION=1.5.0 5 | 6 | # The headers we want 7 | prefix=libbpf-"$LIBBPF_VERSION" 8 | headers=( 9 | "$prefix"/src/bpf_helper_defs.h 10 | "$prefix"/src/bpf_helpers.h 11 | "$prefix"/src/bpf_tracing.h 12 | "$prefix"/src/bpf_endian.h 13 | ) 14 | 15 | # Fetch libbpf release and extract the desired headers 16 | curl -sL "https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz" | \ 17 | tar -xz --xform='s#.*/##' "${headers[@]}" 18 | -------------------------------------------------------------------------------- /headers/vmlinux_compact_amd64.h: -------------------------------------------------------------------------------- 1 | // This is a PoC of a compact version of `vmlinux.h` for amd64. 2 | 3 | struct pt_regs { 4 | long unsigned int r15; 5 | long unsigned int r14; 6 | long unsigned int r13; 7 | long unsigned int r12; 8 | long unsigned int bp; 9 | long unsigned int bx; 10 | long unsigned int r11; 11 | long unsigned int r10; 12 | long unsigned int r9; 13 | long unsigned int r8; 14 | long unsigned int ax; 15 | long unsigned int cx; 16 | long unsigned int dx; 17 | long unsigned int si; 18 | long unsigned int di; 19 | long unsigned int orig_ax; 20 | long unsigned int ip; 21 | long unsigned int cs; 22 | long unsigned int flags; 23 | long unsigned int sp; 24 | long unsigned int ss; 25 | }; 26 | -------------------------------------------------------------------------------- /headers/vmlinux_compact_arm64.h: -------------------------------------------------------------------------------- 1 | // This is a PoC of a compact version of `vmlinux.h` for arm64/aarch64. 2 | 3 | struct user_pt_regs { 4 | __u64 regs[31]; 5 | __u64 sp; 6 | __u64 pc; 7 | __u64 pstate; 8 | }; 9 | 10 | struct pt_regs { 11 | union { 12 | struct user_pt_regs user_regs; 13 | struct { 14 | u64 regs[31]; 15 | u64 sp; 16 | u64 pc; 17 | u64 pstate; 18 | }; 19 | }; 20 | u64 orig_x0; 21 | s32 syscallno; 22 | u32 unused2; 23 | u64 sdei_ttbr1; 24 | u64 pmr_save; 25 | u64 stackframe[2]; 26 | u64 lockdep_hardirqs; 27 | u64 exit_rcu; 28 | }; 29 | -------------------------------------------------------------------------------- /headers/vmlinux_compact_common.h: -------------------------------------------------------------------------------- 1 | // This is a PoC of a compact version of `vmlinux.h`. 2 | 3 | #ifndef __VMLINUX_H__ 4 | #define __VMLINUX_H__ 5 | 6 | typedef unsigned char __u8; 7 | typedef short int __s16; 8 | typedef short unsigned int __u16; 9 | typedef int __s32; 10 | typedef unsigned int __u32; 11 | typedef long long int __s64; 12 | typedef long long unsigned int __u64; 13 | typedef __u8 u8; 14 | typedef __s16 s16; 15 | typedef __u16 u16; 16 | typedef __s32 s32; 17 | typedef __u32 u32; 18 | typedef __s64 s64; 19 | typedef __u64 u64; 20 | typedef __u16 __le16; 21 | typedef __u16 __be16; 22 | typedef __u32 __be32; 23 | typedef __u64 __be64; 24 | typedef __u32 __wsum; 25 | 26 | enum bpf_map_type { 27 | BPF_MAP_TYPE_UNSPEC = 0, 28 | BPF_MAP_TYPE_HASH = 1, 29 | BPF_MAP_TYPE_ARRAY = 2, 30 | BPF_MAP_TYPE_PROG_ARRAY = 3, 31 | BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, 32 | BPF_MAP_TYPE_PERCPU_HASH = 5, 33 | BPF_MAP_TYPE_PERCPU_ARRAY = 6, 34 | BPF_MAP_TYPE_STACK_TRACE = 7, 35 | BPF_MAP_TYPE_CGROUP_ARRAY = 8, 36 | BPF_MAP_TYPE_LRU_HASH = 9, 37 | BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, 38 | BPF_MAP_TYPE_LPM_TRIE = 11, 39 | BPF_MAP_TYPE_ARRAY_OF_MAPS = 12, 40 | BPF_MAP_TYPE_HASH_OF_MAPS = 13, 41 | BPF_MAP_TYPE_DEVMAP = 14, 42 | BPF_MAP_TYPE_SOCKMAP = 15, 43 | BPF_MAP_TYPE_CPUMAP = 16, 44 | BPF_MAP_TYPE_XSKMAP = 17, 45 | BPF_MAP_TYPE_SOCKHASH = 18, 46 | BPF_MAP_TYPE_CGROUP_STORAGE = 19, 47 | BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20, 48 | BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21, 49 | BPF_MAP_TYPE_QUEUE = 22, 50 | BPF_MAP_TYPE_STACK = 23, 51 | BPF_MAP_TYPE_SK_STORAGE = 24, 52 | BPF_MAP_TYPE_DEVMAP_HASH = 25, 53 | BPF_MAP_TYPE_STRUCT_OPS = 26, 54 | BPF_MAP_TYPE_RINGBUF = 27, 55 | BPF_MAP_TYPE_INODE_STORAGE = 28, 56 | }; 57 | 58 | enum { 59 | BPF_ANY = 0, 60 | BPF_NOEXIST = 1, 61 | BPF_EXIST = 2, 62 | BPF_F_LOCK = 4, 63 | }; 64 | 65 | /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and 66 | * BPF_FUNC_perf_event_read_value flags. 67 | */ 68 | #define BPF_F_INDEX_MASK 0xffffffffULL 69 | #define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK 70 | 71 | typedef short unsigned int __kernel_sa_family_t; 72 | 73 | typedef __kernel_sa_family_t sa_family_t; 74 | 75 | struct sockaddr { 76 | sa_family_t sa_family; 77 | char sa_data[14]; 78 | }; 79 | 80 | struct in6_addr { 81 | union { 82 | __u8 u6_addr8[16]; 83 | __be16 u6_addr16[8]; 84 | __be32 u6_addr32[4]; 85 | } in6_u; 86 | }; 87 | 88 | struct sockaddr_in6 { 89 | short unsigned int sin6_family; 90 | __be16 sin6_port; 91 | __be32 sin6_flowinfo; 92 | struct in6_addr sin6_addr; 93 | __u32 sin6_scope_id; 94 | }; 95 | 96 | struct in_addr { 97 | __be32 s_addr; 98 | }; 99 | 100 | struct sockaddr_in { 101 | __kernel_sa_family_t sin_family; 102 | __be16 sin_port; 103 | struct in_addr sin_addr; 104 | unsigned char __pad[8]; 105 | }; 106 | 107 | #endif /* __VMLINUX_H__ */ 108 | -------------------------------------------------------------------------------- /linux/process.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // PathForPid should retrieve the Process Path for a given PID. 14 | // TODO return error 15 | func ProcessPathForPid(pid int) string { 16 | readPath := "/proc/" + strconv.Itoa(pid) + "/exe" 17 | 18 | resolved, err := filepath.EvalSymlinks(readPath) 19 | if err != nil { 20 | log.Printf("Pid %d: Could not resolve path for %s", pid, readPath) 21 | return "" 22 | } 23 | 24 | if _, err := os.Stat(resolved); err != nil { 25 | log.Printf("Pid %d: File %s does not exist", pid, resolved) 26 | return "" 27 | } 28 | 29 | return resolved 30 | } 31 | 32 | // ProcessArgsForPid should retrieve the Process Arguments for a given PID. 33 | // TODO maybe combine with method above and only make one call 34 | func ProcessArgsForPid(pid int) string { 35 | readPath := "/proc/" + strconv.Itoa(pid) + "/cmdline" 36 | file, err := os.Open(readPath) 37 | if err != nil { 38 | log.Printf("Pid %d: could not open File %s", pid, readPath) 39 | return "" 40 | } 41 | defer file.Close() 42 | 43 | data, err := ioutil.ReadAll(file) 44 | if err != nil { 45 | log.Printf("Pid %d: could not read File %s", pid, readPath) 46 | return "" 47 | } 48 | 49 | if len(data) < 1 { 50 | return "" 51 | } 52 | 53 | parts := strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))) 54 | 55 | return strings.Join(parts[1:], " ") 56 | } 57 | -------------------------------------------------------------------------------- /linux/process_test.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import "testing" 4 | 5 | func TestProcessPathForPidForNotExistingPid(t *testing.T) { 6 | got := ProcessPathForPid(32769) // There should be no such PID, default MAX PID is 32768 7 | want := "" 8 | if got != want { 9 | t.Errorf("ProcessPathForPid(32769) = %s; want %s", got, want) 10 | } 11 | } 12 | 13 | func TestProcessArgsForPidForNotExistingPid(t *testing.T) { 14 | got := ProcessArgsForPid(32769) // There should be no such PID, default MAX PID is 32768 15 | want := "" 16 | if got != want { 17 | t.Errorf("ProcessArgsForPid(32769) = %s; want %s", got, want) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Peter Stöckli 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 | //go:build linux 16 | // +build linux 17 | 18 | package main 19 | 20 | import ( 21 | "bytes" 22 | "encoding/binary" 23 | "errors" 24 | "flag" 25 | "log" 26 | "net" 27 | "os" 28 | "os/signal" 29 | "os/user" 30 | "strconv" 31 | "syscall" 32 | "time" 33 | 34 | "github.com/cilium/ebpf/link" 35 | "github.com/cilium/ebpf/perf" 36 | "github.com/cilium/ebpf/rlimit" 37 | "github.com/p-/socket-connect-bpf/as" 38 | "github.com/p-/socket-connect-bpf/conv" 39 | "github.com/p-/socket-connect-bpf/linux" 40 | "golang.org/x/sys/unix" 41 | ) 42 | 43 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-16 -cflags "-O2 -g -Wall -Werror" -target amd64,arm64 bpf securitySocketConnectSrc.c -- -Iheaders/ 44 | 45 | var out output 46 | 47 | func main() { 48 | setupOutput() 49 | setupWorkers() 50 | } 51 | 52 | func setupOutput() { 53 | printAll := flag.Bool("a", false, "print all (AS numbers and process arguments in output") 54 | flag.Parse() 55 | if *printAll { 56 | as.ParseASNumbersIPv4("./as/ip2asn-v4-u32.tsv") 57 | as.ParseASNumbersIPv6("./as/ip2asn-v6.tsv") 58 | } 59 | out = newOutput(*printAll) 60 | } 61 | 62 | func setupWorkers() { 63 | fn := "security_socket_connect" 64 | 65 | stopper := make(chan os.Signal, 1) 66 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 67 | 68 | // Allow the current process to lock memory for eBPF resources. 69 | if err := rlimit.RemoveMemlock(); err != nil { 70 | log.Fatal(err) 71 | } 72 | 73 | // Load pre-compiled programs and maps into the kernel. 74 | objs := bpfObjects{} 75 | if err := loadBpfObjects(&objs, nil); err != nil { 76 | log.Fatalf("loading objects: %v", err) 77 | } 78 | defer objs.Close() 79 | 80 | kp, err := link.Kprobe(fn, objs.KprobeSecuritySocketConnect, nil) 81 | if err != nil { 82 | log.Fatalf("opening kprobe: %s", err) 83 | } 84 | defer kp.Close() 85 | 86 | rd4, err := perf.NewReader(objs.Ipv4Events, os.Getpagesize()) 87 | if err != nil { 88 | log.Fatalf("creating perf event reader: %s", err) 89 | } 90 | defer rd4.Close() 91 | 92 | rd6, err := perf.NewReader(objs.Ipv6Events, os.Getpagesize()) 93 | if err != nil { 94 | log.Fatalf("creating perf event reader: %s", err) 95 | } 96 | defer rd6.Close() 97 | 98 | rdOther, err := perf.NewReader(objs.OtherSocketEvents, os.Getpagesize()) 99 | if err != nil { 100 | log.Fatalf("creating perf event reader: %s", err) 101 | } 102 | defer rdOther.Close() 103 | 104 | go func() { 105 | <-stopper 106 | log.Println("Received signal, exiting program..") 107 | 108 | if err := rd4.Close(); err != nil { 109 | log.Fatalf("closing perf event reader: %s", err) 110 | } 111 | 112 | if err := rd6.Close(); err != nil { 113 | log.Fatalf("closing perf event reader: %s", err) 114 | } 115 | 116 | if err := rdOther.Close(); err != nil { 117 | log.Fatalf("closing perf event reader: %s", err) 118 | } 119 | }() 120 | 121 | out.PrintHeader() 122 | 123 | sig := make(chan os.Signal, 1) 124 | signal.Notify(sig, os.Interrupt, os.Kill) 125 | 126 | go (func() { 127 | for { 128 | if !readIP4Events(rd4) { 129 | return 130 | } 131 | } 132 | })() 133 | 134 | go (func() { 135 | for { 136 | if !readIP6Events(rd6) { 137 | return 138 | } 139 | } 140 | })() 141 | 142 | go (func() { 143 | if !readOtherEvents(rdOther) { 144 | return 145 | } 146 | })() 147 | 148 | <-sig 149 | } 150 | 151 | func readIP4Events(rd *perf.Reader) bool { 152 | var event IP4Event 153 | record, err := rd.Read() 154 | if err != nil { 155 | if errors.Is(err, perf.ErrClosed) { 156 | return false 157 | } 158 | log.Printf("reading from perf event reader: %s", err) 159 | return true 160 | } 161 | 162 | if record.LostSamples != 0 { 163 | log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples) 164 | return true 165 | } 166 | 167 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 168 | log.Printf("parsing perf event: %s", err) 169 | return true 170 | } 171 | 172 | eventPayload := newGenericEventPayload(&event.Event) 173 | eventPayload.DestIP = conv.ToIP4(event.Daddr) 174 | eventPayload.DestPort = event.Dport 175 | asInfo := as.GetASInfoIPv4(eventPayload.DestIP) 176 | eventPayload.ASNameInfo = ASNameInfo{Name: asInfo.Name, AsNumber: asInfo.AsNumber} 177 | out.PrintLine(eventPayload) 178 | return true 179 | } 180 | 181 | func readIP6Events(rd *perf.Reader) bool { 182 | var event IP6Event 183 | record, err := rd.Read() 184 | if err != nil { 185 | if errors.Is(err, perf.ErrClosed) { 186 | return false 187 | } 188 | log.Printf("reading from perf event reader: %s", err) 189 | return true 190 | } 191 | 192 | if record.LostSamples != 0 { 193 | log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples) 194 | return true 195 | } 196 | 197 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 198 | log.Printf("parsing perf event: %s", err) 199 | return true 200 | } 201 | 202 | eventPayload := newGenericEventPayload(&event.Event) 203 | eventPayload.DestIP = conv.ToIP6(event.Daddr1, event.Daddr2) 204 | eventPayload.DestPort = event.Dport 205 | asInfo := as.GetASInfoIPv6(eventPayload.DestIP) 206 | eventPayload.ASNameInfo = ASNameInfo{Name: asInfo.Name, AsNumber: asInfo.AsNumber} 207 | out.PrintLine(eventPayload) 208 | return true 209 | } 210 | 211 | func readOtherEvents(rd *perf.Reader) bool { 212 | var event OtherSocketEvent 213 | record, err := rd.Read() 214 | if err != nil { 215 | if errors.Is(err, perf.ErrClosed) { 216 | return false 217 | } 218 | log.Printf("reading from perf event reader: %s", err) 219 | return true 220 | } 221 | 222 | if record.LostSamples != 0 { 223 | log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples) 224 | return true 225 | } 226 | 227 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 228 | log.Printf("parsing perf event: %s", err) 229 | return true 230 | } 231 | 232 | eventPayload := newGenericEventPayload(&event.Event) 233 | out.PrintLine(eventPayload) 234 | return true 235 | } 236 | 237 | func newGenericEventPayload(event *Event) eventPayload { 238 | username := strconv.Itoa(int(event.UID)) 239 | user, err := user.LookupId(username) 240 | if err != nil { 241 | log.Printf("Could not lookup user with id: %d", event.UID) 242 | } else { 243 | username = user.Username 244 | } 245 | 246 | pid := int(event.Pid) 247 | payload := eventPayload{ 248 | KernelTime: strconv.Itoa(int(event.TsUs)), 249 | GoTime: time.Now(), 250 | AddressFamily: conv.ToAddressFamily(int(event.Af)), 251 | Pid: event.Pid, 252 | ProcessPath: linux.ProcessPathForPid(pid), 253 | ProcessArgs: linux.ProcessArgsForPid(pid), 254 | User: username, 255 | Comm: unix.ByteSliceToString(event.Task[:]), 256 | } 257 | return payload 258 | } 259 | 260 | // Event is a common event interface 261 | type Event struct { 262 | TsUs uint64 263 | Pid uint32 264 | UID uint32 265 | Af uint16 // Address Family 266 | Task [16]byte 267 | } 268 | 269 | // IP4Event represents a socket connect event from AF_INET(4) 270 | type IP4Event struct { 271 | Event 272 | Daddr uint32 273 | Dport uint16 274 | } 275 | 276 | // IP6Event represents a socket connect event from AF_INET6 277 | type IP6Event struct { 278 | Event 279 | Daddr1 uint64 280 | Daddr2 uint64 281 | Dport uint16 282 | } 283 | 284 | // OtherSocketEvent represents the socket connects that are not AF_INET, AF_INET6 or AF_UNIX 285 | type OtherSocketEvent struct { 286 | Event 287 | } 288 | 289 | type eventPayload struct { 290 | KernelTime string 291 | GoTime time.Time 292 | AddressFamily string 293 | Pid uint32 294 | ProcessPath string 295 | ProcessArgs string 296 | User string 297 | Comm string 298 | Host string 299 | DestIP net.IP 300 | DestPort uint16 301 | ASNameInfo ASNameInfo 302 | } 303 | 304 | // ASNameInfo contains the name and number of an autonomous system (AS) 305 | type ASNameInfo struct { 306 | AsNumber uint32 307 | Name string 308 | } 309 | -------------------------------------------------------------------------------- /output.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type output interface { 9 | PrintHeader() 10 | PrintLine(eventPayload) 11 | } 12 | 13 | func newOutput(printAll bool) output { 14 | return newTableOutput(printAll) 15 | } 16 | 17 | type tableOutput struct { 18 | printAll bool 19 | } 20 | 21 | func (t tableOutput) PrintHeader() { 22 | var header string 23 | var args []interface{} 24 | if t.printAll { 25 | header = "%-9s %-9s %-6s %-42s %-16s %-20s %s\n" 26 | args = []interface{}{"TIME", "AF", "PID", "PROCESS", "USER", "DESTINATION", "AS-INFO"} 27 | } else { 28 | header = "%-9s %-9s %-6s %-34s %-16s %-20s\n" 29 | args = []interface{}{"TIME", "AF", "PID", "PROCESS", "USER", "DESTINATION"} 30 | } 31 | 32 | fmt.Printf(header, args...) 33 | } 34 | 35 | func (t tableOutput) PrintLine(e eventPayload) { 36 | time := e.GoTime.Format("15:04:05") 37 | dest := e.DestIP.String() + " " + strconv.Itoa(int(e.DestPort)) 38 | 39 | var header string 40 | var args []interface{} 41 | 42 | if t.printAll { 43 | var asText = "" 44 | if (ASNameInfo{}) != e.ASNameInfo { 45 | asText = "AS" + strconv.Itoa(int(e.ASNameInfo.AsNumber)) + " (" + e.ASNameInfo.Name + ")" 46 | } 47 | header = "%-9s %-9s %-6d %-42s %-16s %-20s %s\n" 48 | args = []interface{}{time, e.AddressFamily, e.Pid, e.ProcessPath + " " + e.ProcessArgs, e.User, dest, asText} 49 | } else { 50 | header = "%-9s %-9s %-6d %-34s %-16s %-20s\n" 51 | args = []interface{}{time, e.AddressFamily, e.Pid, e.ProcessPath, e.User, dest} 52 | } 53 | 54 | fmt.Printf(header, args...) 55 | } 56 | 57 | func newTableOutput(includeAsNumbers bool) output { 58 | return &tableOutput{includeAsNumbers} 59 | } 60 | -------------------------------------------------------------------------------- /samples/socket-connect-bpf-big.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p-/socket-connect-bpf/e04a8d0fbe9b5cc14145a1e1fb1c9f04b81b0afa/samples/socket-connect-bpf-big.gif -------------------------------------------------------------------------------- /samples/socket-connect-bpf-example.txt: -------------------------------------------------------------------------------- 1 | Demonstrations of socket-connect-bpf 2 | 3 | This tool traces the kernel functions performing active socket connections 4 | (eg, via a connect(). 5 | socket-connect-bpf is a BPF/eBPF prototype with a kernel probe attached to 6 | `security_socket_connect`. 7 | 8 | Sample output for `curl github.com` 9 | 10 | % sudo ./socket-connect-bpf 11 | TIME AF PID PROCESS USER DESTINATION 12 | 17:15:58 AF_INET 8549 /usr/bin/curl xdp 127.0.0.53 53 13 | 17:15:58 AF_INET 419 /usr/lib/systemd/systemd-resolved systemd-resolve 192.168.1.1 53 14 | 17:15:58 AF_INET 8549 /usr/bin/curl xdp 140.82.118.3 80 15 | 16 | 17 | Sample output for `curl github.com` with the -a flag (display AS information) in action. 18 | 19 | % sudo ./socket-connect-bpf -a 20 | TIME AF PID PROCESS USER DESTINATION AS-INFO 21 | 17:19:23 AF_INET 8817 /usr/bin/curl github.com xdp 127.0.0.53 53 22 | 17:19:23 AF_INET 419 /usr/lib/systemd/systemd-resolved systemd-resolve 192.168.1.1 53 23 | 17:19:23 AF_INET 419 /usr/lib/systemd/systemd-resolved systemd-resolve 192.168.1.1 53 24 | 17:19:23 AF_INET 8817 /usr/bin/curl github.com xdp 140.82.118.4 80 AS36459 (GITHUB) 25 | -------------------------------------------------------------------------------- /samples/socket-connect-bpf.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p-/socket-connect-bpf/e04a8d0fbe9b5cc14145a1e1fb1c9f04b81b0afa/samples/socket-connect-bpf.gif -------------------------------------------------------------------------------- /securitySocketConnectSrc.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "vmlinux_compact_common.h" 4 | 5 | #if defined(__TARGET_ARCH_arm64) 6 | #include "vmlinux_compact_arm64.h" 7 | #elif defined(__TARGET_ARCH_x86) 8 | #include "vmlinux_compact_amd64.h" 9 | #endif 10 | 11 | #include "bpf_helpers.h" 12 | #include "bpf_tracing.h" 13 | #include "bpf_endian.h" 14 | 15 | #define TASK_COMM_LEN 16 16 | #define AF_UNIX 1 17 | #define AF_UNSPEC 0 18 | #define AF_INET 2 19 | #define AF_INET6 10 20 | 21 | struct ipv4_event_t { 22 | u64 ts_us; 23 | u32 pid; 24 | u32 uid; 25 | u16 af; 26 | char task[TASK_COMM_LEN]; 27 | u32 daddr; 28 | u16 dport; 29 | } __attribute__((packed)); 30 | 31 | struct 32 | { 33 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 34 | } ipv4_events SEC(".maps"); 35 | 36 | 37 | struct ipv6_event_t { 38 | u64 ts_us; 39 | u32 pid; 40 | u32 uid; 41 | u16 af; 42 | char task[TASK_COMM_LEN]; 43 | unsigned __int128 daddr; 44 | u16 dport; 45 | } __attribute__((packed)); 46 | 47 | struct 48 | { 49 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 50 | } ipv6_events SEC(".maps"); 51 | 52 | struct other_socket_event_t { 53 | u64 ts_us; 54 | u32 pid; 55 | u32 uid; 56 | u16 af; 57 | char task[TASK_COMM_LEN]; 58 | } __attribute__((packed)); 59 | 60 | struct 61 | { 62 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 63 | } other_socket_events SEC(".maps"); 64 | 65 | SEC("kprobe/security_socket_connect") 66 | int kprobe_security_socket_connect(struct pt_regs *ctx) { 67 | u64 pid_tgid = bpf_get_current_pid_tgid(); 68 | u32 pid = pid_tgid >> 32; 69 | 70 | u32 uid = bpf_get_current_uid_gid(); 71 | 72 | struct sockaddr *address = (struct sockaddr *)PT_REGS_PARM2(ctx); 73 | 74 | u16 address_family = 0; 75 | bpf_probe_read(&address_family, sizeof(address_family), &address->sa_family); 76 | 77 | if (address_family == AF_INET) { 78 | struct ipv4_event_t data4 = {.pid = pid, .uid = uid, .af = address_family}; 79 | data4.ts_us = bpf_ktime_get_ns() / 1000; 80 | 81 | struct sockaddr_in *daddr = (struct sockaddr_in *)address; 82 | 83 | bpf_probe_read(&data4.daddr, sizeof(data4.daddr), &daddr->sin_addr.s_addr); 84 | 85 | u16 dport = 0; 86 | bpf_probe_read(&dport, sizeof(dport), &daddr->sin_port); 87 | data4.dport = bpf_ntohs(dport); 88 | 89 | bpf_get_current_comm(&data4.task, sizeof(data4.task)); 90 | 91 | if (data4.dport != 0) { 92 | bpf_perf_event_output(ctx, &ipv4_events, BPF_F_CURRENT_CPU, &data4, sizeof(data4)); 93 | } 94 | } 95 | else if (address_family == AF_INET6) { 96 | struct ipv6_event_t data6 = {.pid = pid, .uid = uid, .af = address_family}; 97 | data6.ts_us = bpf_ktime_get_ns() / 1000; 98 | 99 | struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)address; 100 | 101 | bpf_probe_read(&data6.daddr, sizeof(data6.daddr), &daddr6->sin6_addr.in6_u.u6_addr32); 102 | 103 | u16 dport6 = 0; 104 | bpf_probe_read(&dport6, sizeof(dport6), &daddr6->sin6_port); 105 | data6.dport = bpf_ntohs(dport6); 106 | 107 | bpf_get_current_comm(&data6.task, sizeof(data6.task)); 108 | 109 | if (data6.dport != 0) { 110 | bpf_perf_event_output(ctx, &ipv6_events, BPF_F_CURRENT_CPU, &data6, sizeof(data6)); 111 | } 112 | } 113 | else if (address_family != AF_UNIX && address_family != AF_UNSPEC) { // other address families, except UNIX and UNSPEC sockets 114 | struct other_socket_event_t socket_event = {.pid = pid, .uid = uid, .af = address_family}; 115 | socket_event.ts_us = bpf_ktime_get_ns() / 1000; 116 | bpf_get_current_comm(&socket_event.task, sizeof(socket_event.task)); 117 | bpf_perf_event_output(ctx, &other_socket_events, BPF_F_CURRENT_CPU, &socket_event, sizeof(socket_event)); 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | char LICENSE[] SEC("license") = "Dual MIT/GPL"; 124 | -------------------------------------------------------------------------------- /updateASData.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd as/ 4 | 5 | download_and_unzip () { 6 | filename=$1 7 | wget https://iptoasn.com/data/$filename.gz 8 | rm -f $filename 9 | gunzip $filename.gz 10 | } 11 | 12 | download_and_unzip "ip2asn-v4-u32.tsv" 13 | download_and_unzip "ip2asn-v6.tsv" --------------------------------------------------------------------------------