├── .github └── workflows │ └── ci.yml ├── CONTRIBUTING.md ├── COPYRIGHT.txt ├── Documentation └── pinning.md ├── LICENSE-bpf.txt ├── LICENSE.txt ├── README.md ├── bcc ├── module.go ├── perf.go ├── symbol.go └── table.go ├── bpf.go ├── bpf_test.go ├── elf ├── compat.go ├── compat_test.go ├── elf.go ├── elf_test.go ├── elf_unsupported.go ├── errno.go ├── include │ ├── bpf_map.h │ ├── doc.go │ ├── libbpf.h │ ├── nlattr.h │ └── uapi │ │ └── linux │ │ ├── bpf.h │ │ ├── doc.go │ │ ├── if_link.h │ │ ├── netlink.h │ │ └── perf_event.h ├── kernel_version.go ├── kernel_version_test.go ├── module.go ├── module_unsupported.go ├── netlink.c ├── nlattr.c ├── perf.go ├── perf_unsupported.go ├── pinning.go ├── table.go ├── utsname_int8.go └── utsname_uint8.go ├── examples ├── bcc │ ├── bash_readline │ │ └── bash_readline.go │ ├── execsnoop │ │ ├── execsnoop.go │ │ └── output.go │ ├── perf │ │ ├── .gitignore │ │ └── perf.go │ ├── strlen_count │ │ └── strlen_count.go │ └── xdp │ │ └── xdp_drop.go └── tracepipe │ ├── .gitignore │ └── tracepipe.go ├── go.mod ├── pkg ├── bpffs │ └── fs.go ├── cpuonline │ └── cpuonline.go ├── cpupossible │ └── cpupossible.go ├── cpurange │ ├── cpu_range.go │ └── cpu_range_test.go ├── ksym │ ├── ksym.go │ └── ksym_test.go ├── progtestrun │ └── prog_test_run.go └── tracepipe │ ├── trace_pipe.go │ └── trace_pipe_test.go └── tests ├── build ├── dummy-410.o ├── dummy-414.o ├── dummy-46.o ├── dummy-48.o ├── dummy.c └── dummy.o /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | run-tests: 7 | runs-on: ubuntu-20.04 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v2 11 | - name: Setup Go env 12 | uses: actions/setup-go@v2 13 | - name: Build bcc 14 | run: | 15 | set -x 16 | sudo apt-get update 17 | # Use release 9 of llvm etc. - later versions have an unfixed 18 | # bug on Ubuntu: 19 | # https://github.com/iovisor/bcc/issues/2915 20 | sudo apt-get -y install bison build-essential cmake flex git libelf-dev libfl-dev libedit-dev libllvm11 llvm-11-dev libclang-cpp11-dev libclang-common-11-dev libclang1-11 libclang-11-dev python zlib1g-dev 21 | pushd /tmp 22 | git clone --depth 1 --branch v0.25.0 https://github.com/iovisor/bcc.git 23 | mkdir -p bcc/build; cd bcc/build 24 | # Symlink /usr/lib/llvm to avoid "Unable to find clang libraries" 25 | # The directory appears only to be created when installing the 26 | # virtual llvm-dev package. 27 | # https://github.com/iovisor/bcc/issues/492 28 | sudo ln -s /usr/lib/llvm-11 /usr/local/llvm 29 | cmake .. 30 | make 31 | sudo make install 32 | popd 33 | - name: Print system info 34 | run: | 35 | cat /etc/os-release 36 | uname -a 37 | - name: Run integration tests 38 | run: | 39 | sudo go test -tags integration -v ./... 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please consider the following points for your pull requests: 2 | 3 | * We aim to follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 4 | for commit messages, please try to write and format yours accordingly. 5 | * Try to put relevant information for a change into the commit message. 6 | If your commit consists of multiple commits, it's OK to refer to the 7 | individual commits for context (i.e. no need to copy all information 8 | into the PR body). 9 | * Prefix the subject line of your commit with the corresponding 10 | module (`bcc` or `elf`) if sensible. 11 | * Don't mix different changes in a single commit (for example, a bug fix 12 | should not be mixed with a new feature). 13 | * Rebase your branch to keep it up-to-date, don't merge master. 14 | * Rebase your commits to keep the history clean and consistent, we 15 | don't merge "fixups" (for example a commit "Fixes from review"). 16 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 PLUMgrid 2 | Copyright 2016 Kinvolk 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /Documentation/pinning.md: -------------------------------------------------------------------------------- 1 | # Object pinning 2 | 3 | BPF has a persistent view of maps and programs under its own filesystem 4 | `/sys/fs/bpf`. Users are able to make each object visible under the bpffs. 5 | We call it `object pinning`. This is done by calling syscall `bpf(2)` with 6 | a command `BPF_OBJ_PIN`. After doing that, users are able to use the object 7 | with commands such as `BPF_OBJ_GET`, or remove the object with an ordinary 8 | VFS syscall `unlink(2)`. 9 | 10 | Doing that, we can make maps and programs stay alive across process 11 | terminations. This mechanism provides a much more consistent way of sharing 12 | objects with other processes, compared to other solutions such as `tc`, 13 | where objects are shared via Unix domain sockets. 14 | 15 | ## Different pinning options 16 | 17 | `C.bpf_map_def.pinning` (defined in 18 | [bpf.h](https://github.com/iovisor/gobpf/blob/446e57e0e24e/elf/include/bpf.h#L616)) 19 | can be set to one the following pinning options. 20 | 21 | * `PIN_NONE` : object is not pinned 22 | * `PIN_OBJECT_NS` : pinning that is local to an object (to-be-implemented) 23 | * `PIN_GLOBAL_NS` : pinning with a global namespace under e.g. `/sys/fs/bpf/ns1/globals` 24 | * `PIN_CUSTOM_NS` : pinning with a custom path given as section parameter 25 | 26 | ### Pinning with `PIN_CUSTOM_NS` 27 | 28 | When loading a module with `C.bpf_map_def.pinning` set to `PIN_CUSTOM_NS`, 29 | an additional path must be set in the `elf.SectionParams.PinPath` parameter 30 | to `Load()`. For example: 31 | 32 | (C source file for an ELF object) 33 | ``` 34 | struct bpf_map_def SEC("maps/dummy_array_custom") dummy_array_custom = { 35 | .type = BPF_MAP_TYPE_ARRAY, 36 | .key_size = sizeof(int), 37 | .value_size = sizeof(unsigned int), 38 | .max_entries = 1024, 39 | .pinning = PIN_CUSTOM_NS, 40 | }; 41 | ``` 42 | 43 | (Go source file that actually uses the ELF object) 44 | ``` 45 | b := elf.NewModule(customELFFileName) 46 | var secParams = map[string]elf.SectionParams{ 47 | "maps/dummy_array_custom": elf.SectionParams{ 48 | PinPath: "ns1/test1", 49 | }, 50 | } 51 | if err := b.Load(secParams); err != nil { 52 | fmt.Println(err) 53 | } 54 | ``` 55 | 56 | Then you can check if the object is pinned like below: 57 | 58 | ``` 59 | $ ls -l /sys/fs/bpf/ns1/test1 60 | ``` 61 | 62 | ### Unpinning with `PIN_CUSTOM_NS` 63 | 64 | To unpin a custom pinned map, we need an additional path 65 | `elf.CloseOptions.PinPath` as parameter to `CloseExt()`. For example: 66 | 67 | ``` 68 | var closeOptions = map[string]elf.CloseOptions{ 69 | "maps/dummy_array_custom": elf.CloseOptions{ 70 | Unpin: true, 71 | PinPath: "ns1/test1", 72 | }, 73 | } 74 | if err := b.CloseExt(closeOptions); err != nil { 75 | fmt.Println(err) 76 | } 77 | ``` 78 | 79 | Or you can also remove the file just like below: 80 | 81 | ``` 82 | os.Remove("/sys/fs/bpf/ns1/test1") 83 | ``` 84 | -------------------------------------------------------------------------------- /LICENSE-bpf.txt: -------------------------------------------------------------------------------- 1 | The file at /elf/include/bpf.h is a copy of the Linux kernel file 2 | /include/uapi/linux/bpf.h, retrieved from version 4.17, Git commit 3 | 36f9814, available at 4 | https://raw.githubusercontent.com/torvalds/linux/36f9814a494a874d5a0f44843544b4b2539022db/include/uapi/linux/bpf.h. 5 | It is provided here in unmodified source code form. 6 | 7 | As indicated in the file, it is licensed under the GNU General Public License, 8 | version 2.0, with the Linux-syscall-note. Copies of the Linux-syscall-note and 9 | GPL-2.0 license text are included below. gobpf has included this header file 10 | in this repository with the intention of using it solely in the manner 11 | described in the Linux-syscall-note. 12 | 13 | = = = = = 14 | 15 | Linux-syscall-note: 16 | 17 | NOTE! This copyright does *not* cover user programs that use kernel 18 | services by normal system calls - this is merely considered normal use 19 | of the kernel, and does *not* fall under the heading of "derived work". 20 | Also note that the GPL below is copyrighted by the Free Software 21 | Foundation, but the instance of code that it refers to (the Linux 22 | kernel) is copyrighted by me and others who actually wrote it. 23 | 24 | Also note that the only valid version of the GPL as far as the kernel 25 | is concerned is _this_ particular version of the license (ie v2, not 26 | v2.2 or v3.x or whatever), unless explicitly otherwise stated. 27 | 28 | Linus Torvalds 29 | 30 | = = = = = 31 | 32 | GPL-2.0: 33 | 34 | 35 | GNU GENERAL PUBLIC LICENSE 36 | Version 2, June 1991 37 | 38 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 39 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 40 | Everyone is permitted to copy and distribute verbatim copies 41 | of this license document, but changing it is not allowed. 42 | 43 | Preamble 44 | 45 | The licenses for most software are designed to take away your 46 | freedom to share and change it. By contrast, the GNU General Public 47 | License is intended to guarantee your freedom to share and change free 48 | software--to make sure the software is free for all its users. This 49 | General Public License applies to most of the Free Software 50 | Foundation's software and to any other program whose authors commit to 51 | using it. (Some other Free Software Foundation software is covered by 52 | the GNU Library General Public License instead.) You can apply it to 53 | your programs, too. 54 | 55 | When we speak of free software, we are referring to freedom, not 56 | price. Our General Public Licenses are designed to make sure that you 57 | have the freedom to distribute copies of free software (and charge for 58 | this service if you wish), that you receive source code or can get it 59 | if you want it, that you can change the software or use pieces of it 60 | in new free programs; and that you know you can do these things. 61 | 62 | To protect your rights, we need to make restrictions that forbid 63 | anyone to deny you these rights or to ask you to surrender the rights. 64 | These restrictions translate to certain responsibilities for you if you 65 | distribute copies of the software, or if you modify it. 66 | 67 | For example, if you distribute copies of such a program, whether 68 | gratis or for a fee, you must give the recipients all the rights that 69 | you have. You must make sure that they, too, receive or can get the 70 | source code. And you must show them these terms so they know their 71 | rights. 72 | 73 | We protect your rights with two steps: (1) copyright the software, and 74 | (2) offer you this license which gives you legal permission to copy, 75 | distribute and/or modify the software. 76 | 77 | Also, for each author's protection and ours, we want to make certain 78 | that everyone understands that there is no warranty for this free 79 | software. If the software is modified by someone else and passed on, we 80 | want its recipients to know that what they have is not the original, so 81 | that any problems introduced by others will not reflect on the original 82 | authors' reputations. 83 | 84 | Finally, any free program is threatened constantly by software 85 | patents. We wish to avoid the danger that redistributors of a free 86 | program will individually obtain patent licenses, in effect making the 87 | program proprietary. To prevent this, we have made it clear that any 88 | patent must be licensed for everyone's free use or not licensed at all. 89 | 90 | The precise terms and conditions for copying, distribution and 91 | modification follow. 92 | 93 | GNU GENERAL PUBLIC LICENSE 94 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 95 | 96 | 0. This License applies to any program or other work which contains 97 | a notice placed by the copyright holder saying it may be distributed 98 | under the terms of this General Public License. The "Program", below, 99 | refers to any such program or work, and a "work based on the Program" 100 | means either the Program or any derivative work under copyright law: 101 | that is to say, a work containing the Program or a portion of it, 102 | either verbatim or with modifications and/or translated into another 103 | language. (Hereinafter, translation is included without limitation in 104 | the term "modification".) Each licensee is addressed as "you". 105 | 106 | Activities other than copying, distribution and modification are not 107 | covered by this License; they are outside its scope. The act of 108 | running the Program is not restricted, and the output from the Program 109 | is covered only if its contents constitute a work based on the 110 | Program (independent of having been made by running the Program). 111 | Whether that is true depends on what the Program does. 112 | 113 | 1. You may copy and distribute verbatim copies of the Program's 114 | source code as you receive it, in any medium, provided that you 115 | conspicuously and appropriately publish on each copy an appropriate 116 | copyright notice and disclaimer of warranty; keep intact all the 117 | notices that refer to this License and to the absence of any warranty; 118 | and give any other recipients of the Program a copy of this License 119 | along with the Program. 120 | 121 | You may charge a fee for the physical act of transferring a copy, and 122 | you may at your option offer warranty protection in exchange for a fee. 123 | 124 | 2. You may modify your copy or copies of the Program or any portion 125 | of it, thus forming a work based on the Program, and copy and 126 | distribute such modifications or work under the terms of Section 1 127 | above, provided that you also meet all of these conditions: 128 | 129 | a) You must cause the modified files to carry prominent notices 130 | stating that you changed the files and the date of any change. 131 | 132 | b) You must cause any work that you distribute or publish, that in 133 | whole or in part contains or is derived from the Program or any 134 | part thereof, to be licensed as a whole at no charge to all third 135 | parties under the terms of this License. 136 | 137 | c) If the modified program normally reads commands interactively 138 | when run, you must cause it, when started running for such 139 | interactive use in the most ordinary way, to print or display an 140 | announcement including an appropriate copyright notice and a 141 | notice that there is no warranty (or else, saying that you provide 142 | a warranty) and that users may redistribute the program under 143 | these conditions, and telling the user how to view a copy of this 144 | License. (Exception: if the Program itself is interactive but 145 | does not normally print such an announcement, your work based on 146 | the Program is not required to print an announcement.) 147 | 148 | These requirements apply to the modified work as a whole. If 149 | identifiable sections of that work are not derived from the Program, 150 | and can be reasonably considered independent and separate works in 151 | themselves, then this License, and its terms, do not apply to those 152 | sections when you distribute them as separate works. But when you 153 | distribute the same sections as part of a whole which is a work based 154 | on the Program, the distribution of the whole must be on the terms of 155 | this License, whose permissions for other licensees extend to the 156 | entire whole, and thus to each and every part regardless of who wrote it. 157 | 158 | Thus, it is not the intent of this section to claim rights or contest 159 | your rights to work written entirely by you; rather, the intent is to 160 | exercise the right to control the distribution of derivative or 161 | collective works based on the Program. 162 | 163 | In addition, mere aggregation of another work not based on the Program 164 | with the Program (or with a work based on the Program) on a volume of 165 | a storage or distribution medium does not bring the other work under 166 | the scope of this License. 167 | 168 | 3. You may copy and distribute the Program (or a work based on it, 169 | under Section 2) in object code or executable form under the terms of 170 | Sections 1 and 2 above provided that you also do one of the following: 171 | 172 | a) Accompany it with the complete corresponding machine-readable 173 | source code, which must be distributed under the terms of Sections 174 | 1 and 2 above on a medium customarily used for software interchange; or, 175 | 176 | b) Accompany it with a written offer, valid for at least three 177 | years, to give any third party, for a charge no more than your 178 | cost of physically performing source distribution, a complete 179 | machine-readable copy of the corresponding source code, to be 180 | distributed under the terms of Sections 1 and 2 above on a medium 181 | customarily used for software interchange; or, 182 | 183 | c) Accompany it with the information you received as to the offer 184 | to distribute corresponding source code. (This alternative is 185 | allowed only for noncommercial distribution and only if you 186 | received the program in object code or executable form with such 187 | an offer, in accord with Subsection b above.) 188 | 189 | The source code for a work means the preferred form of the work for 190 | making modifications to it. For an executable work, complete source 191 | code means all the source code for all modules it contains, plus any 192 | associated interface definition files, plus the scripts used to 193 | control compilation and installation of the executable. However, as a 194 | special exception, the source code distributed need not include 195 | anything that is normally distributed (in either source or binary 196 | form) with the major components (compiler, kernel, and so on) of the 197 | operating system on which the executable runs, unless that component 198 | itself accompanies the executable. 199 | 200 | If distribution of executable or object code is made by offering 201 | access to copy from a designated place, then offering equivalent 202 | access to copy the source code from the same place counts as 203 | distribution of the source code, even though third parties are not 204 | compelled to copy the source along with the object code. 205 | 206 | 4. You may not copy, modify, sublicense, or distribute the Program 207 | except as expressly provided under this License. Any attempt 208 | otherwise to copy, modify, sublicense or distribute the Program is 209 | void, and will automatically terminate your rights under this License. 210 | However, parties who have received copies, or rights, from you under 211 | this License will not have their licenses terminated so long as such 212 | parties remain in full compliance. 213 | 214 | 5. You are not required to accept this License, since you have not 215 | signed it. However, nothing else grants you permission to modify or 216 | distribute the Program or its derivative works. These actions are 217 | prohibited by law if you do not accept this License. Therefore, by 218 | modifying or distributing the Program (or any work based on the 219 | Program), you indicate your acceptance of this License to do so, and 220 | all its terms and conditions for copying, distributing or modifying 221 | the Program or works based on it. 222 | 223 | 6. Each time you redistribute the Program (or any work based on the 224 | Program), the recipient automatically receives a license from the 225 | original licensor to copy, distribute or modify the Program subject to 226 | these terms and conditions. You may not impose any further 227 | restrictions on the recipients' exercise of the rights granted herein. 228 | You are not responsible for enforcing compliance by third parties to 229 | this License. 230 | 231 | 7. If, as a consequence of a court judgment or allegation of patent 232 | infringement or for any other reason (not limited to patent issues), 233 | conditions are imposed on you (whether by court order, agreement or 234 | otherwise) that contradict the conditions of this License, they do not 235 | excuse you from the conditions of this License. If you cannot 236 | distribute so as to satisfy simultaneously your obligations under this 237 | License and any other pertinent obligations, then as a consequence you 238 | may not distribute the Program at all. For example, if a patent 239 | license would not permit royalty-free redistribution of the Program by 240 | all those who receive copies directly or indirectly through you, then 241 | the only way you could satisfy both it and this License would be to 242 | refrain entirely from distribution of the Program. 243 | 244 | If any portion of this section is held invalid or unenforceable under 245 | any particular circumstance, the balance of the section is intended to 246 | apply and the section as a whole is intended to apply in other 247 | circumstances. 248 | 249 | It is not the purpose of this section to induce you to infringe any 250 | patents or other property right claims or to contest validity of any 251 | such claims; this section has the sole purpose of protecting the 252 | integrity of the free software distribution system, which is 253 | implemented by public license practices. Many people have made 254 | generous contributions to the wide range of software distributed 255 | through that system in reliance on consistent application of that 256 | system; it is up to the author/donor to decide if he or she is willing 257 | to distribute software through any other system and a licensee cannot 258 | impose that choice. 259 | 260 | This section is intended to make thoroughly clear what is believed to 261 | be a consequence of the rest of this License. 262 | 263 | 8. If the distribution and/or use of the Program is restricted in 264 | certain countries either by patents or by copyrighted interfaces, the 265 | original copyright holder who places the Program under this License 266 | may add an explicit geographical distribution limitation excluding 267 | those countries, so that distribution is permitted only in or among 268 | countries not thus excluded. In such case, this License incorporates 269 | the limitation as if written in the body of this License. 270 | 271 | 9. The Free Software Foundation may publish revised and/or new versions 272 | of the General Public License from time to time. Such new versions will 273 | be similar in spirit to the present version, but may differ in detail to 274 | address new problems or concerns. 275 | 276 | Each version is given a distinguishing version number. If the Program 277 | specifies a version number of this License which applies to it and "any 278 | later version", you have the option of following the terms and conditions 279 | either of that version or of any later version published by the Free 280 | Software Foundation. If the Program does not specify a version number of 281 | this License, you may choose any version ever published by the Free Software 282 | Foundation. 283 | 284 | 10. If you wish to incorporate parts of the Program into other free 285 | programs whose distribution conditions are different, write to the author 286 | to ask for permission. For software which is copyrighted by the Free 287 | Software Foundation, write to the Free Software Foundation; we sometimes 288 | make exceptions for this. Our decision will be guided by the two goals 289 | of preserving the free status of all derivatives of our free software and 290 | of promoting the sharing and reuse of software generally. 291 | 292 | NO WARRANTY 293 | 294 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 295 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 296 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 297 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 298 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 299 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 300 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 301 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 302 | REPAIR OR CORRECTION. 303 | 304 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 305 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 306 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 307 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 308 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 309 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 310 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 311 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 312 | POSSIBILITY OF SUCH DAMAGES. 313 | 314 | END OF TERMS AND CONDITIONS 315 | 316 | How to Apply These Terms to Your New Programs 317 | 318 | If you develop a new program, and you want it to be of the greatest 319 | possible use to the public, the best way to achieve this is to make it 320 | free software which everyone can redistribute and change under these terms. 321 | 322 | To do so, attach the following notices to the program. It is safest 323 | to attach them to the start of each source file to most effectively 324 | convey the exclusion of warranty; and each file should have at least 325 | the "copyright" line and a pointer to where the full notice is found. 326 | 327 | 328 | Copyright (C) 329 | 330 | This program is free software; you can redistribute it and/or modify 331 | it under the terms of the GNU General Public License as published by 332 | the Free Software Foundation; either version 2 of the License, or 333 | (at your option) any later version. 334 | 335 | This program is distributed in the hope that it will be useful, 336 | but WITHOUT ANY WARRANTY; without even the implied warranty of 337 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 338 | GNU General Public License for more details. 339 | 340 | You should have received a copy of the GNU General Public License 341 | along with this program; if not, write to the Free Software 342 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 343 | 344 | 345 | Also add information on how to contact you by electronic and paper mail. 346 | 347 | If the program is interactive, make it output a short notice like this 348 | when it starts in an interactive mode: 349 | 350 | Gnomovision version 69, Copyright (C) year name of author 351 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 352 | This is free software, and you are welcome to redistribute it 353 | under certain conditions; type `show c' for details. 354 | 355 | The hypothetical commands `show w' and `show c' should show the appropriate 356 | parts of the General Public License. Of course, the commands you use may 357 | be called something other than `show w' and `show c'; they could even be 358 | mouse-clicks or menu items--whatever suits your program. 359 | 360 | You should also get your employer (if you work as a programmer) or your 361 | school, if any, to sign a "copyright disclaimer" for the program, if 362 | necessary. Here is a sample; alter the names: 363 | 364 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 365 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 366 | 367 | , 1 April 1989 368 | Ty Coon, President of Vice 369 | 370 | This General Public License does not permit incorporating your program into 371 | proprietary programs. If your program is a subroutine library, you may 372 | consider it more useful to permit linking proprietary applications with the 373 | library. If this is what you want to do, use the GNU Library General 374 | Public License instead of this License. 375 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gobpf 2 | 3 | [![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](http://godoc.org/github.com/iovisor/gobpf) [![CI](https://github.com/iovisor/gobpf/actions/workflows/ci.yml/badge.svg)](https://github.com/iovisor/gobpf/actions/workflows/ci.yml) 4 | 5 | This repository provides go bindings for the [bcc framework](https://github.com/iovisor/bcc) 6 | as well as low-level routines to load and use eBPF programs from .elf 7 | files. 8 | 9 | Input and contributions are very welcome. 10 | 11 | We recommend vendoring gobpf and pinning its version as the API is regularly 12 | changing following bcc and Linux updates and releases. 13 | 14 | ## Requirements 15 | 16 | eBPF requires a recent Linux kernel. A good feature list can be found here: 17 | https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md 18 | 19 | ### `github.com/iovisor/gobpf/bcc` 20 | 21 | Install the latest released version of [libbcc](https://github.com/iovisor/bcc/blob/master/INSTALL.md) 22 | (either using a package manager or by building from source). 23 | 24 | ### `github.com/iovisor/gobpf/elf` 25 | 26 | #### Building ELF Object Files 27 | 28 | To build ELF object files for use with the elf package, you must use specific 29 | sections (`SEC("...")`). The following are currently supported: 30 | 31 | * `kprobe/...` 32 | * `cgroup/skb` 33 | * `cgroup/sock` 34 | * `maps/...` 35 | * `socket...` 36 | * `tracepoint...` 37 | * `uprobe/...` 38 | * `uretprobe/...` 39 | * `xdp/...` 40 | 41 | Map definitions must correspond to `bpf_map_def` from [the elf package](https://github.com/iovisor/gobpf/blob/master/elf/include/bpf_map.h). 42 | Otherwise, you will encounter an error like `only one map with size 280 bytes allowed per section (check bpf_map_def)`. 43 | 44 | The [Cilium](https://github.com/cilium/cilium) BPF docs contain helpful info 45 | for using clang/LLVM to compile programs into elf object files: 46 | https://cilium.readthedocs.io/en/latest/bpf/#llvm 47 | 48 | See `tests/dummy.c` for a minimal dummy and https://github.com/weaveworks/tcptracer-bpf 49 | for a real world example. 50 | 51 | ## Examples 52 | 53 | Sample code can be found in the `examples/` directory. Examples can be run as 54 | follows: 55 | 56 | ``` 57 | sudo -E go run examples/bcc/perf/perf.go 58 | ``` 59 | 60 | ## Tests 61 | 62 | ``` 63 | go test -tags integration -v ./... 64 | ``` 65 | -------------------------------------------------------------------------------- /bcc/module.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 PLUMgrid 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package bcc 16 | 17 | import ( 18 | "fmt" 19 | "regexp" 20 | "runtime" 21 | "strings" 22 | "sync" 23 | "syscall" 24 | "unsafe" 25 | 26 | "github.com/iovisor/gobpf/pkg/cpuonline" 27 | ) 28 | 29 | /* 30 | #cgo CFLAGS: -I/usr/include/bcc/compat 31 | #cgo LDFLAGS: -lbcc 32 | #include 33 | #include 34 | #include 35 | 36 | #ifndef LIBBCC_VERSION_GEQ 37 | #define LIBBCC_VERSION_GEQ(a, b, c) 0 38 | #endif 39 | 40 | int bcc_func_load_wrapper(void *program, int prog_type, const char *name, 41 | const struct bpf_insn *insns, int prog_len, 42 | const char *license, unsigned kern_version, 43 | int log_level, char *log_buf, unsigned log_buf_size, 44 | const char *dev_name, int attach_type){ 45 | 46 | #if LIBBCC_VERSION_GEQ(0, 25, 0) 47 | return bcc_func_load(program, prog_type, name, insns, prog_len, license, 48 | kern_version, log_level, log_buf, log_buf_size, 49 | dev_name, attach_type); 50 | #else 51 | return bcc_func_load(program, prog_type, name, insns, prog_len, license, 52 | kern_version, log_level, log_buf, log_buf_size, 53 | dev_name); 54 | #endif 55 | } 56 | 57 | */ 58 | import "C" 59 | 60 | // Module type 61 | type Module struct { 62 | p unsafe.Pointer 63 | funcs map[string]int 64 | kprobes map[string]int 65 | uprobes map[string]int 66 | tracepoints map[string]int 67 | rawTracepoints map[string]int 68 | perfEvents map[string][]int 69 | } 70 | 71 | type compileRequest struct { 72 | code string 73 | cflags []string 74 | rspCh chan *Module 75 | } 76 | 77 | const ( 78 | BPF_PROBE_ENTRY = iota 79 | BPF_PROBE_RETURN 80 | ) 81 | 82 | const ( 83 | XDP_FLAGS_UPDATE_IF_NOEXIST = uint32(1) << iota 84 | XDP_FLAGS_SKB_MODE 85 | XDP_FLAGS_DRV_MODE 86 | XDP_FLAGS_HW_MODE 87 | XDP_FLAGS_MODES = XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE 88 | XDP_FLAGS_MASK = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_MODES 89 | ) 90 | 91 | var ( 92 | defaultCflags []string 93 | compileCh chan compileRequest 94 | bpfInitOnce sync.Once 95 | ) 96 | 97 | func bpfInit() { 98 | defaultCflags = []string{ 99 | fmt.Sprintf("-DNUMCPUS=%d", runtime.NumCPU()), 100 | } 101 | compileCh = make(chan compileRequest) 102 | go compile() 103 | } 104 | 105 | // NewModule constructor 106 | func newModule(code string, cflags []string) *Module { 107 | cflagsC := make([]*C.char, len(defaultCflags)+len(cflags)) 108 | defer func() { 109 | for _, cflag := range cflagsC { 110 | C.free(unsafe.Pointer(cflag)) 111 | } 112 | }() 113 | for i, cflag := range cflags { 114 | cflagsC[i] = C.CString(cflag) 115 | } 116 | for i, cflag := range defaultCflags { 117 | cflagsC[len(cflags)+i] = C.CString(cflag) 118 | } 119 | cs := C.CString(code) 120 | defer C.free(unsafe.Pointer(cs)) 121 | c := C.bpf_module_create_c_from_string(cs, 2, (**C.char)(&cflagsC[0]), C.int(len(cflagsC)), (C.bool)(true), nil) 122 | if c == nil { 123 | return nil 124 | } 125 | return &Module{ 126 | p: c, 127 | funcs: make(map[string]int), 128 | kprobes: make(map[string]int), 129 | uprobes: make(map[string]int), 130 | tracepoints: make(map[string]int), 131 | rawTracepoints: make(map[string]int), 132 | perfEvents: make(map[string][]int), 133 | } 134 | } 135 | 136 | // NewModule asynchronously compiles the code, generates a new BPF 137 | // module and returns it. 138 | func NewModule(code string, cflags []string) *Module { 139 | bpfInitOnce.Do(bpfInit) 140 | ch := make(chan *Module) 141 | compileCh <- compileRequest{code, cflags, ch} 142 | return <-ch 143 | } 144 | 145 | func compile() { 146 | for { 147 | req := <-compileCh 148 | req.rspCh <- newModule(req.code, req.cflags) 149 | } 150 | } 151 | 152 | // Close takes care of closing all kprobes opened by this modules and 153 | // destroys the underlying libbpf module. 154 | func (bpf *Module) Close() { 155 | C.bpf_module_destroy(bpf.p) 156 | for k, v := range bpf.kprobes { 157 | C.bpf_close_perf_event_fd((C.int)(v)) 158 | evNameCS := C.CString(k) 159 | C.bpf_detach_kprobe(evNameCS) 160 | C.free(unsafe.Pointer(evNameCS)) 161 | } 162 | for k, v := range bpf.uprobes { 163 | C.bpf_close_perf_event_fd((C.int)(v)) 164 | evNameCS := C.CString(k) 165 | C.bpf_detach_uprobe(evNameCS) 166 | C.free(unsafe.Pointer(evNameCS)) 167 | } 168 | for k, v := range bpf.tracepoints { 169 | C.bpf_close_perf_event_fd((C.int)(v)) 170 | parts := strings.SplitN(k, ":", 2) 171 | tpCategoryCS := C.CString(parts[0]) 172 | tpNameCS := C.CString(parts[1]) 173 | C.bpf_detach_tracepoint(tpCategoryCS, tpNameCS) 174 | C.free(unsafe.Pointer(tpCategoryCS)) 175 | C.free(unsafe.Pointer(tpNameCS)) 176 | } 177 | for _, vs := range bpf.perfEvents { 178 | for _, v := range vs { 179 | C.bpf_close_perf_event_fd((C.int)(v)) 180 | } 181 | } 182 | for _, fd := range bpf.funcs { 183 | syscall.Close(fd) 184 | } 185 | } 186 | 187 | // GetProgramTag returns a tag for ebpf program under passed fd 188 | func (bpf *Module) GetProgramTag(fd int) (tag uint64, err error) { 189 | _, err = C.bpf_prog_get_tag(C.int(fd), (*C.ulonglong)(unsafe.Pointer(&tag))) 190 | return tag, err 191 | } 192 | 193 | // LoadNet loads a program of type BPF_PROG_TYPE_SCHED_ACT. 194 | func (bpf *Module) LoadNet(name string) (int, error) { 195 | return bpf.Load(name, C.BPF_PROG_TYPE_SCHED_ACT, 0, 0) 196 | } 197 | 198 | // LoadKprobe loads a program of type BPF_PROG_TYPE_KPROBE. 199 | func (bpf *Module) LoadKprobe(name string) (int, error) { 200 | return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0) 201 | } 202 | 203 | // LoadTracepoint loads a program of type BPF_PROG_TYPE_TRACEPOINT 204 | func (bpf *Module) LoadTracepoint(name string) (int, error) { 205 | return bpf.Load(name, C.BPF_PROG_TYPE_TRACEPOINT, 0, 0) 206 | } 207 | 208 | // LoadRawTracepoint loads a program of type BPF_PROG_TYPE_RAW_TRACEPOINT 209 | func (bpf *Module) LoadRawTracepoint(name string) (int, error) { 210 | return bpf.Load(name, C.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, 0) 211 | } 212 | 213 | // LoadPerfEvent loads a program of type BPF_PROG_TYPE_PERF_EVENT 214 | func (bpf *Module) LoadPerfEvent(name string) (int, error) { 215 | return bpf.Load(name, C.BPF_PROG_TYPE_PERF_EVENT, 0, 0) 216 | } 217 | 218 | // LoadUprobe loads a program of type BPF_PROG_TYPE_KPROBE. 219 | func (bpf *Module) LoadUprobe(name string) (int, error) { 220 | return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0) 221 | } 222 | 223 | // Load a program. 224 | func (bpf *Module) Load(name string, progType int, logLevel, logSize uint) (int, error) { 225 | fd, ok := bpf.funcs[name] 226 | if ok { 227 | return fd, nil 228 | } 229 | fd, err := bpf.load(name, progType, logLevel, logSize) 230 | if err != nil { 231 | return -1, err 232 | } 233 | bpf.funcs[name] = fd 234 | return fd, nil 235 | } 236 | 237 | func (bpf *Module) load(name string, progType int, logLevel, logSize uint) (int, error) { 238 | nameCS := C.CString(name) 239 | defer C.free(unsafe.Pointer(nameCS)) 240 | start := (*C.struct_bpf_insn)(C.bpf_function_start(bpf.p, nameCS)) 241 | size := C.int(C.bpf_function_size(bpf.p, nameCS)) 242 | license := C.bpf_module_license(bpf.p) 243 | version := C.bpf_module_kern_version(bpf.p) 244 | if start == nil { 245 | return -1, fmt.Errorf("Module: unable to find %s", name) 246 | } 247 | var logBuf []byte 248 | var logBufP *C.char 249 | if logSize > 0 { 250 | logBuf = make([]byte, logSize) 251 | logBufP = (*C.char)(unsafe.Pointer(&logBuf[0])) 252 | } 253 | fd, err := C.bcc_func_load_wrapper(bpf.p, C.int(uint32(progType)), nameCS, start, size, license, version, C.int(logLevel), logBufP, C.uint(len(logBuf)), nil, C.int(-1)) 254 | if fd < 0 { 255 | return -1, fmt.Errorf("error loading BPF program: %v", err) 256 | } 257 | return int(fd), nil 258 | } 259 | 260 | var kprobeRegexp = regexp.MustCompile("[+.]") 261 | var uprobeRegexp = regexp.MustCompile("[^a-zA-Z0-9_]") 262 | 263 | func (bpf *Module) attachProbe(evName string, attachType uint32, fnName string, fd int, maxActive int) error { 264 | if _, ok := bpf.kprobes[evName]; ok { 265 | return nil 266 | } 267 | 268 | evNameCS := C.CString(evName) 269 | fnNameCS := C.CString(fnName) 270 | res, err := C.bpf_attach_kprobe(C.int(fd), attachType, evNameCS, fnNameCS, (C.uint64_t)(0), C.int(maxActive)) 271 | C.free(unsafe.Pointer(evNameCS)) 272 | C.free(unsafe.Pointer(fnNameCS)) 273 | 274 | if res < 0 { 275 | return fmt.Errorf("failed to attach BPF kprobe: %v", err) 276 | } 277 | bpf.kprobes[evName] = int(res) 278 | return nil 279 | } 280 | 281 | func (bpf *Module) attachUProbe(evName string, attachType uint32, path string, addr uint64, fd, pid int) error { 282 | evNameCS := C.CString(evName) 283 | binaryPathCS := C.CString(path) 284 | res, err := C.bpf_attach_uprobe(C.int(fd), attachType, evNameCS, binaryPathCS, (C.uint64_t)(addr), (C.pid_t)(pid), 0) 285 | C.free(unsafe.Pointer(evNameCS)) 286 | C.free(unsafe.Pointer(binaryPathCS)) 287 | 288 | if res < 0 { 289 | return fmt.Errorf("failed to attach BPF uprobe: %v", err) 290 | } 291 | bpf.uprobes[evName] = int(res) 292 | return nil 293 | } 294 | 295 | // AttachKprobe attaches a kprobe fd to a function. 296 | func (bpf *Module) AttachKprobe(fnName string, fd int, maxActive int) error { 297 | evName := "p_" + kprobeRegexp.ReplaceAllString(fnName, "_") 298 | 299 | return bpf.attachProbe(evName, BPF_PROBE_ENTRY, fnName, fd, maxActive) 300 | } 301 | 302 | // AttachKretprobe attaches a kretprobe fd to a function. 303 | func (bpf *Module) AttachKretprobe(fnName string, fd int, maxActive int) error { 304 | evName := "r_" + kprobeRegexp.ReplaceAllString(fnName, "_") 305 | 306 | return bpf.attachProbe(evName, BPF_PROBE_RETURN, fnName, fd, maxActive) 307 | } 308 | 309 | // AttachTracepoint attaches a tracepoint fd to a function 310 | // The 'name' argument is in the format 'category:name' 311 | func (bpf *Module) AttachTracepoint(name string, fd int) error { 312 | if _, ok := bpf.tracepoints[name]; ok { 313 | return nil 314 | } 315 | 316 | parts := strings.SplitN(name, ":", 2) 317 | if len(parts) < 2 { 318 | return fmt.Errorf("failed to parse tracepoint name, expected %q, got %q", "category:name", name) 319 | } 320 | 321 | tpCategoryCS := C.CString(parts[0]) 322 | tpNameCS := C.CString(parts[1]) 323 | 324 | res, err := C.bpf_attach_tracepoint(C.int(fd), tpCategoryCS, tpNameCS) 325 | 326 | C.free(unsafe.Pointer(tpCategoryCS)) 327 | C.free(unsafe.Pointer(tpNameCS)) 328 | 329 | if res < 0 { 330 | return fmt.Errorf("failed to attach BPF tracepoint: %v", err) 331 | } 332 | bpf.tracepoints[name] = int(res) 333 | return nil 334 | } 335 | 336 | // AttachRawTracepoint attaches a raw tracepoint fd to a function 337 | // The 'name' argument is in the format 'name', there is no category 338 | func (bpf *Module) AttachRawTracepoint(name string, fd int) error { 339 | if _, ok := bpf.rawTracepoints[name]; ok { 340 | return nil 341 | } 342 | 343 | tpNameCS := C.CString(name) 344 | 345 | res, err := C.bpf_attach_raw_tracepoint(C.int(fd), tpNameCS) 346 | 347 | C.free(unsafe.Pointer(tpNameCS)) 348 | 349 | if res < 0 { 350 | return fmt.Errorf("failed to attach BPF tracepoint: %v", err) 351 | } 352 | bpf.rawTracepoints[name] = int(res) 353 | return nil 354 | } 355 | 356 | // AttachPerfEvent attaches a perf event fd to a function 357 | // Argument 'evType' is a member of 'perf_type_id' enum in the kernel 358 | // header 'include/uapi/linux/perf_event.h'. Argument 'evConfig' 359 | // is one of PERF_COUNT_* constants in the same file. 360 | func (bpf *Module) AttachPerfEvent(evType, evConfig int, samplePeriod int, sampleFreq int, pid, cpu, groupFd, fd int) error { 361 | key := fmt.Sprintf("%d:%d", evType, evConfig) 362 | if _, ok := bpf.perfEvents[key]; ok { 363 | return nil 364 | } 365 | 366 | res := []int{} 367 | 368 | if cpu > 0 { 369 | r, err := C.bpf_attach_perf_event(C.int(fd), C.uint32_t(evType), C.uint32_t(evConfig), C.uint64_t(samplePeriod), C.uint64_t(sampleFreq), C.pid_t(pid), C.int(cpu), C.int(groupFd)) 370 | if r < 0 { 371 | return fmt.Errorf("failed to attach BPF perf event: %v", err) 372 | } 373 | 374 | res = append(res, int(r)) 375 | } else { 376 | cpus, err := cpuonline.Get() 377 | if err != nil { 378 | return fmt.Errorf("failed to determine online cpus: %v", err) 379 | } 380 | 381 | for _, i := range cpus { 382 | r, err := C.bpf_attach_perf_event(C.int(fd), C.uint32_t(evType), C.uint32_t(evConfig), C.uint64_t(samplePeriod), C.uint64_t(sampleFreq), C.pid_t(pid), C.int(i), C.int(groupFd)) 383 | if r < 0 { 384 | return fmt.Errorf("failed to attach BPF perf event: %v", err) 385 | } 386 | 387 | res = append(res, int(r)) 388 | } 389 | } 390 | 391 | bpf.perfEvents[key] = res 392 | 393 | return nil 394 | } 395 | 396 | // AttachUprobe attaches a uprobe fd to the symbol in the library or binary 'name' 397 | // The 'name' argument can be given as either a full library path (/usr/lib/..), 398 | // a library without the lib prefix, or as a binary with full path (/bin/bash) 399 | // A pid can be given to attach to, or -1 to attach to all processes 400 | // 401 | // Presently attempts to trace processes running in a different namespace 402 | // to the tracer will fail due to limitations around namespace-switching 403 | // in multi-threaded programs (such as Go programs) 404 | func (bpf *Module) AttachUprobe(name, symbol string, fd, pid int) error { 405 | path, addr, err := resolveSymbolPath(name, symbol, 0x0, pid) 406 | if err != nil { 407 | return err 408 | } 409 | evName := fmt.Sprintf("p_%s_0x%x", uprobeRegexp.ReplaceAllString(path, "_"), addr) 410 | return bpf.attachUProbe(evName, BPF_PROBE_ENTRY, path, addr, fd, pid) 411 | } 412 | 413 | // AttachMatchingUprobes attaches a uprobe fd to all symbols in the library or binary 414 | // 'name' that match a given pattern. 415 | // The 'name' argument can be given as either a full library path (/usr/lib/..), 416 | // a library without the lib prefix, or as a binary with full path (/bin/bash) 417 | // A pid can be given, or -1 to attach to all processes 418 | // 419 | // Presently attempts to trace processes running in a different namespace 420 | // to the tracer will fail due to limitations around namespace-switching 421 | // in multi-threaded programs (such as Go programs) 422 | func (bpf *Module) AttachMatchingUprobes(name, match string, fd, pid int) error { 423 | symbols, err := matchUserSymbols(name, match) 424 | if err != nil { 425 | return fmt.Errorf("unable to match symbols: %s", err) 426 | } 427 | if len(symbols) == 0 { 428 | return fmt.Errorf("no symbols matching %s for %s found", match, name) 429 | } 430 | for _, symbol := range symbols { 431 | if err := bpf.AttachUprobe(name, symbol.name, fd, pid); err != nil { 432 | return err 433 | } 434 | } 435 | return nil 436 | } 437 | 438 | // AttachUretprobe attaches a uretprobe fd to the symbol in the library or binary 'name' 439 | // The 'name' argument can be given as either a full library path (/usr/lib/..), 440 | // a library without the lib prefix, or as a binary with full path (/bin/bash) 441 | // A pid can be given to attach to, or -1 to attach to all processes 442 | // 443 | // Presently attempts to trace processes running in a different namespace 444 | // to the tracer will fail due to limitations around namespace-switching 445 | // in multi-threaded programs (such as Go programs) 446 | func (bpf *Module) AttachUretprobe(name, symbol string, fd, pid int) error { 447 | path, addr, err := resolveSymbolPath(name, symbol, 0x0, pid) 448 | if err != nil { 449 | return err 450 | } 451 | evName := fmt.Sprintf("r_%s_0x%x", uprobeRegexp.ReplaceAllString(path, "_"), addr) 452 | return bpf.attachUProbe(evName, BPF_PROBE_RETURN, path, addr, fd, pid) 453 | } 454 | 455 | // AttachMatchingUretprobes attaches a uretprobe fd to all symbols in the library or binary 456 | // 'name' that match a given pattern. 457 | // The 'name' argument can be given as either a full library path (/usr/lib/..), 458 | // a library without the lib prefix, or as a binary with full path (/bin/bash) 459 | // A pid can be given, or -1 to attach to all processes 460 | // 461 | // Presently attempts to trace processes running in a different namespace 462 | // to the tracer will fail due to limitations around namespace-switching 463 | // in multi-threaded programs (such as Go programs) 464 | func (bpf *Module) AttachMatchingUretprobes(name, match string, fd, pid int) error { 465 | symbols, err := matchUserSymbols(name, match) 466 | if err != nil { 467 | return fmt.Errorf("unable to match symbols: %s", err) 468 | } 469 | if len(symbols) == 0 { 470 | return fmt.Errorf("no symbols matching %s for %s found", match, name) 471 | } 472 | for _, symbol := range symbols { 473 | if err := bpf.AttachUretprobe(name, symbol.name, fd, pid); err != nil { 474 | return err 475 | } 476 | } 477 | return nil 478 | } 479 | 480 | // TableSize returns the number of tables in the module. 481 | func (bpf *Module) TableSize() uint64 { 482 | size := C.bpf_num_tables(bpf.p) 483 | return uint64(size) 484 | } 485 | 486 | // TableId returns the id of a table. 487 | func (bpf *Module) TableId(name string) C.size_t { 488 | cs := C.CString(name) 489 | defer C.free(unsafe.Pointer(cs)) 490 | return C.bpf_table_id(bpf.p, cs) 491 | } 492 | 493 | // TableDesc returns a map with table properties (name, fd, ...). 494 | func (bpf *Module) TableDesc(id uint64) map[string]interface{} { 495 | i := C.size_t(id) 496 | return map[string]interface{}{ 497 | "name": C.GoString(C.bpf_table_name(bpf.p, i)), 498 | "fd": int(C.bpf_table_fd_id(bpf.p, i)), 499 | "key_size": uint64(C.bpf_table_key_size_id(bpf.p, i)), 500 | "leaf_size": uint64(C.bpf_table_leaf_size_id(bpf.p, i)), 501 | "key_desc": C.GoString(C.bpf_table_key_desc_id(bpf.p, i)), 502 | "leaf_desc": C.GoString(C.bpf_table_leaf_desc_id(bpf.p, i)), 503 | } 504 | } 505 | 506 | // TableIter returns a receveier channel to iterate over entries. 507 | func (bpf *Module) TableIter() <-chan map[string]interface{} { 508 | ch := make(chan map[string]interface{}) 509 | go func() { 510 | size := C.bpf_num_tables(bpf.p) 511 | for i := C.size_t(0); i < size; i++ { 512 | ch <- bpf.TableDesc(uint64(i)) 513 | } 514 | close(ch) 515 | }() 516 | return ch 517 | } 518 | 519 | func (bpf *Module) attachXDP(devName string, fd int, flags uint32) error { 520 | devNameCS := C.CString(devName) 521 | res, err := C.bpf_attach_xdp(devNameCS, C.int(fd), C.uint32_t(flags)) 522 | defer C.free(unsafe.Pointer(devNameCS)) 523 | 524 | if res != 0 || err != nil { 525 | return fmt.Errorf("failed to attach BPF xdp to device %v: %v", devName, err) 526 | } 527 | return nil 528 | } 529 | 530 | // AttachXDP attaches a xdp fd to a device. 531 | func (bpf *Module) AttachXDP(devName string, fd int) error { 532 | return bpf.attachXDP(devName, fd, 0) 533 | } 534 | 535 | // AttachXDPWithFlags attaches a xdp fd to a device with flags. 536 | func (bpf *Module) AttachXDPWithFlags(devName string, fd int, flags uint32) error { 537 | return bpf.attachXDP(devName, fd, flags) 538 | } 539 | 540 | // RemoveXDP removes any xdp from this device. 541 | func (bpf *Module) RemoveXDP(devName string) error { 542 | return bpf.attachXDP(devName, -1, 0) 543 | } 544 | 545 | func GetSyscallFnName(name string) string { 546 | return GetSyscallPrefix() + name 547 | } 548 | 549 | var syscallPrefix string 550 | 551 | func GetSyscallPrefix() string { 552 | if syscallPrefix == "" { 553 | _, err := bccResolveName("", "__x64_sys_bpf", -1) 554 | if err == nil { 555 | syscallPrefix = "__x64_sys_" 556 | } else { 557 | syscallPrefix = "sys_" 558 | } 559 | } 560 | return syscallPrefix 561 | } 562 | -------------------------------------------------------------------------------- /bcc/perf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Kinvolk 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package bcc 16 | 17 | import ( 18 | "encoding/binary" 19 | "fmt" 20 | "sync" 21 | "unsafe" 22 | 23 | "github.com/iovisor/gobpf/pkg/cpuonline" 24 | ) 25 | 26 | /* 27 | #cgo CFLAGS: -I/usr/include/bcc/compat 28 | #cgo LDFLAGS: -lbcc 29 | #include 30 | #include 31 | #include 32 | 33 | // perf_reader_raw_cb and perf_reader_lost_cb as defined in bcc libbpf.h 34 | // typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size); 35 | extern void rawCallback(void*, void*, int); 36 | // typedef void (*perf_reader_lost_cb)(void *cb_cookie, uint64_t lost); 37 | extern void lostCallback(void*, uint64_t); 38 | */ 39 | import "C" 40 | 41 | type PerfMap struct { 42 | table *Table 43 | readers []*C.struct_perf_reader 44 | stop chan bool 45 | } 46 | 47 | type callbackData struct { 48 | receiverChan chan []byte 49 | lostChan chan uint64 50 | } 51 | 52 | // BPF_PERF_READER_PAGE_CNT is the default page_cnt used per cpu ring buffer 53 | const BPF_PERF_READER_PAGE_CNT = 8 54 | 55 | var byteOrder binary.ByteOrder 56 | var callbackRegister = make(map[uint64]*callbackData) 57 | var callbackIndex uint64 58 | var mu sync.Mutex 59 | 60 | // In lack of binary.HostEndian ... 61 | func init() { 62 | byteOrder = determineHostByteOrder() 63 | } 64 | 65 | func registerCallback(data *callbackData) uint64 { 66 | mu.Lock() 67 | defer mu.Unlock() 68 | callbackIndex++ 69 | for callbackRegister[callbackIndex] != nil { 70 | callbackIndex++ 71 | } 72 | callbackRegister[callbackIndex] = data 73 | return callbackIndex 74 | } 75 | 76 | func unregisterCallback(i uint64) { 77 | mu.Lock() 78 | defer mu.Unlock() 79 | delete(callbackRegister, i) 80 | } 81 | 82 | func lookupCallback(i uint64) *callbackData { 83 | return callbackRegister[i] 84 | } 85 | 86 | // Gateway function as required with CGO Go >= 1.6 87 | // "If a C-program wants a function pointer, a gateway function has to 88 | // be written. This is because we can't take the address of a Go 89 | // function and give that to C-code since the cgo tool will generate a 90 | // stub in C that should be called." 91 | //export rawCallback 92 | func rawCallback(cbCookie unsafe.Pointer, raw unsafe.Pointer, rawSize C.int) { 93 | callbackData := lookupCallback(uint64(uintptr(cbCookie))) 94 | callbackData.receiverChan <- C.GoBytes(raw, rawSize) 95 | } 96 | 97 | //export lostCallback 98 | func lostCallback(cbCookie unsafe.Pointer, lost C.uint64_t) { 99 | callbackData := lookupCallback(uint64(uintptr(cbCookie))) 100 | if callbackData.lostChan != nil { 101 | callbackData.lostChan <- uint64(lost) 102 | } 103 | } 104 | 105 | // GetHostByteOrder returns the current byte-order. 106 | func GetHostByteOrder() binary.ByteOrder { 107 | return byteOrder 108 | } 109 | 110 | func determineHostByteOrder() binary.ByteOrder { 111 | var i int32 = 0x01020304 112 | u := unsafe.Pointer(&i) 113 | pb := (*byte)(u) 114 | b := *pb 115 | if b == 0x04 { 116 | return binary.LittleEndian 117 | } 118 | 119 | return binary.BigEndian 120 | } 121 | 122 | // InitPerfMap initializes a perf map with a receiver channel, with a default page_cnt. 123 | func InitPerfMap(table *Table, receiverChan chan []byte, lostChan chan uint64) (*PerfMap, error) { 124 | return InitPerfMapWithPageCnt(table, receiverChan, lostChan, BPF_PERF_READER_PAGE_CNT) 125 | } 126 | 127 | // InitPerfMapWithPageCnt initializes a perf map with a receiver channel with a specified page_cnt. 128 | func InitPerfMapWithPageCnt(table *Table, receiverChan chan []byte, lostChan chan uint64, pageCnt int) (*PerfMap, error) { 129 | fd := table.Config()["fd"].(int) 130 | keySize := table.Config()["key_size"].(uint64) 131 | leafSize := table.Config()["leaf_size"].(uint64) 132 | 133 | if keySize != 4 || leafSize != 4 { 134 | return nil, fmt.Errorf("passed table has wrong size") 135 | } 136 | 137 | callbackDataIndex := registerCallback(&callbackData{ 138 | receiverChan, 139 | lostChan, 140 | }) 141 | 142 | key := make([]byte, keySize) 143 | leaf := make([]byte, leafSize) 144 | keyP := unsafe.Pointer(&key[0]) 145 | leafP := unsafe.Pointer(&leaf[0]) 146 | 147 | readers := []*C.struct_perf_reader{} 148 | 149 | cpus, err := cpuonline.Get() 150 | if err != nil { 151 | return nil, fmt.Errorf("failed to determine online cpus: %v", err) 152 | } 153 | 154 | for _, cpu := range cpus { 155 | reader, err := bpfOpenPerfBuffer(cpu, callbackDataIndex, pageCnt) 156 | if err != nil { 157 | return nil, fmt.Errorf("failed to open perf buffer: %v", err) 158 | } 159 | 160 | perfFd := C.perf_reader_fd((*C.struct_perf_reader)(reader)) 161 | 162 | readers = append(readers, (*C.struct_perf_reader)(reader)) 163 | 164 | byteOrder.PutUint32(leaf, uint32(perfFd)) 165 | 166 | r, err := C.bpf_update_elem(C.int(fd), keyP, leafP, 0) 167 | if r != 0 { 168 | return nil, fmt.Errorf("unable to initialize perf map: %v", err) 169 | } 170 | r = C.bpf_get_next_key(C.int(fd), keyP, keyP) 171 | if r != 0 { 172 | break 173 | } 174 | } 175 | return &PerfMap{ 176 | table, 177 | readers, 178 | make(chan bool), 179 | }, nil 180 | } 181 | 182 | // Start to poll the perf map reader and send back event data 183 | // over the connected channel. 184 | func (pm *PerfMap) Start() { 185 | go pm.poll(500) 186 | } 187 | 188 | // Stop to poll the perf map readers after a maximum of 500ms 189 | // (the timeout we use for perf_reader_poll). Ideally we would 190 | // have a way to cancel the poll, but perf_reader_poll doesn't 191 | // support that yet. 192 | func (pm *PerfMap) Stop() { 193 | pm.stop <- true 194 | } 195 | 196 | func (pm *PerfMap) poll(timeout int) { 197 | for { 198 | select { 199 | case <-pm.stop: 200 | return 201 | default: 202 | C.perf_reader_poll(C.int(len(pm.readers)), &pm.readers[0], C.int(timeout)) 203 | } 204 | } 205 | } 206 | 207 | func bpfOpenPerfBuffer(cpu uint, callbackDataIndex uint64, pageCnt int) (unsafe.Pointer, error) { 208 | if (pageCnt & (pageCnt - 1)) != 0 { 209 | return nil, fmt.Errorf("pageCnt must be a power of 2: %d", pageCnt) 210 | } 211 | cpuC := C.int(cpu) 212 | pageCntC := C.int(pageCnt) 213 | reader, err := C.bpf_open_perf_buffer( 214 | (C.perf_reader_raw_cb)(unsafe.Pointer(C.rawCallback)), 215 | (C.perf_reader_lost_cb)(unsafe.Pointer(C.lostCallback)), 216 | unsafe.Pointer(uintptr(callbackDataIndex)), 217 | -1, cpuC, pageCntC) 218 | if reader == nil { 219 | return nil, fmt.Errorf("failed to open perf buffer: %v", err) 220 | } 221 | return reader, nil 222 | } 223 | -------------------------------------------------------------------------------- /bcc/symbol.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Louis McCormack 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package bcc 16 | 17 | import ( 18 | "fmt" 19 | "regexp" 20 | "sync" 21 | "unsafe" 22 | ) 23 | 24 | /* 25 | #cgo CFLAGS: -I/usr/include/bcc/compat 26 | #cgo LDFLAGS: -lbcc 27 | #include 28 | #include 29 | #include 30 | extern void foreach_symbol_callback(char*, uint64_t); 31 | */ 32 | import "C" 33 | 34 | type symbolAddress struct { 35 | name string 36 | addr uint64 37 | } 38 | 39 | //symbolCache will cache module lookups 40 | var symbolCache = struct { 41 | cache map[string][]*symbolAddress 42 | currentModule string 43 | lock *sync.Mutex 44 | }{ 45 | cache: map[string][]*symbolAddress{}, 46 | currentModule: "", 47 | lock: &sync.Mutex{}, 48 | } 49 | 50 | type bccSymbol struct { 51 | name *C.char 52 | demangleName *C.char 53 | module *C.char 54 | offset C.ulonglong 55 | } 56 | 57 | type bccSymbolOption struct { 58 | useDebugFile int 59 | checkDebugFileCrc int 60 | useSymbolType uint32 61 | } 62 | 63 | // resolveSymbolPath returns the file and offset to locate symname in module 64 | func resolveSymbolPath(module string, symname string, addr uint64, pid int) (string, uint64, error) { 65 | if pid == -1 { 66 | pid = 0 67 | } 68 | 69 | modname, offset, err := bccResolveSymname(module, symname, addr, pid) 70 | if err != nil { 71 | return "", 0, fmt.Errorf("unable to locate symbol %s in module %s: %v", symname, module, err) 72 | } 73 | 74 | return modname, offset, nil 75 | } 76 | 77 | func bccResolveSymname(module string, symname string, addr uint64, pid int) (string, uint64, error) { 78 | symbol := &bccSymbol{} 79 | symbolC := (*C.struct_bcc_symbol)(unsafe.Pointer(symbol)) 80 | moduleCS := C.CString(module) 81 | defer C.free(unsafe.Pointer(moduleCS)) 82 | symnameCS := C.CString(symname) 83 | defer C.free(unsafe.Pointer(symnameCS)) 84 | 85 | res, err := C.bcc_resolve_symname(moduleCS, symnameCS, (C.uint64_t)(addr), C.int(pid), nil, symbolC) 86 | if res < 0 { 87 | return "", 0, fmt.Errorf("unable to locate symbol %s in module %s: %v", symname, module, err) 88 | } 89 | 90 | return C.GoString(symbolC.module), (uint64)(symbolC.offset), nil 91 | } 92 | 93 | func bccResolveName(module, symname string, pid int) (uint64, error) { 94 | symbol := &bccSymbolOption{} 95 | symbolC := (*C.struct_bcc_symbol_option)(unsafe.Pointer(symbol)) 96 | 97 | pidC := C.int(pid) 98 | cache := C.bcc_symcache_new(pidC, symbolC) 99 | defer C.bcc_free_symcache(cache, pidC) 100 | 101 | moduleCS := C.CString(module) 102 | defer C.free(unsafe.Pointer(moduleCS)) 103 | 104 | nameCS := C.CString(symname) 105 | defer C.free(unsafe.Pointer(nameCS)) 106 | 107 | var addr uint64 108 | addrC := C.uint64_t(addr) 109 | res := C.bcc_symcache_resolve_name(cache, moduleCS, nameCS, &addrC) 110 | if res < 0 { 111 | return 0, fmt.Errorf("unable to locate symbol %s in module %s", symname, module) 112 | } 113 | 114 | return addr, nil 115 | } 116 | 117 | // getUserSymbolsAndAddresses finds a list of symbols associated with a module, 118 | // along with their addresses. The results are cached in the symbolCache and 119 | // returned 120 | func getUserSymbolsAndAddresses(module string) ([]*symbolAddress, error) { 121 | symbolCache.lock.Lock() 122 | defer symbolCache.lock.Unlock() 123 | // return previously cached list if it exists 124 | if _, ok := symbolCache.cache[module]; ok { 125 | return symbolCache.cache[module], nil 126 | } 127 | 128 | symbolCache.cache[module] = []*symbolAddress{} 129 | symbolCache.currentModule = module 130 | 131 | if err := bccForeachSymbol(module); err != nil { 132 | return nil, err 133 | } 134 | 135 | return symbolCache.cache[module], nil 136 | } 137 | 138 | func matchUserSymbols(module, match string) ([]*symbolAddress, error) { 139 | r, err := regexp.Compile(match) 140 | if err != nil { 141 | return nil, fmt.Errorf("invalid regex %s : %s", match, err) 142 | } 143 | matchedSymbols := []*symbolAddress{} 144 | symbols, err := getUserSymbolsAndAddresses(module) 145 | if err != nil { 146 | return nil, err 147 | } 148 | for _, sym := range symbols { 149 | if r.MatchString(sym.name) { 150 | matchedSymbols = append(matchedSymbols, sym) 151 | } 152 | } 153 | return matchedSymbols, nil 154 | } 155 | 156 | // foreach_symbol_callback is a gateway function that will be exported to C 157 | // so that it can be referenced as a function pointer 158 | //export foreach_symbol_callback 159 | func foreach_symbol_callback(symname *C.char, addr C.uint64_t) { 160 | symbolCache.cache[symbolCache.currentModule] = 161 | append(symbolCache.cache[symbolCache.currentModule], &symbolAddress{C.GoString(symname), (uint64)(addr)}) 162 | } 163 | 164 | func bccForeachSymbol(module string) error { 165 | moduleCS := C.CString(module) 166 | defer C.free(unsafe.Pointer(moduleCS)) 167 | res := C.bcc_foreach_function_symbol(moduleCS, (C.SYM_CB)(unsafe.Pointer(C.foreach_symbol_callback))) 168 | if res < 0 { 169 | return fmt.Errorf("unable to list symbols for %s", module) 170 | } 171 | return nil 172 | } 173 | -------------------------------------------------------------------------------- /bcc/table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 PLUMgrid 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package bcc 16 | 17 | import ( 18 | "bytes" 19 | "errors" 20 | "fmt" 21 | "os" 22 | "unsafe" 23 | 24 | "github.com/iovisor/gobpf/pkg/cpupossible" 25 | ) 26 | 27 | /* 28 | #cgo CFLAGS: -I/usr/include/bcc/compat 29 | #cgo LDFLAGS: -lbcc 30 | #include 31 | #include 32 | #include 33 | */ 34 | import "C" 35 | 36 | var errIterationFailed = errors.New("table.Iter: leaf for next key not found") 37 | 38 | // Table references a BPF table. The zero value cannot be used. 39 | type Table struct { 40 | id C.size_t 41 | module *Module 42 | } 43 | 44 | // New tables returns a refernce to a BPF table. 45 | func NewTable(id C.size_t, module *Module) *Table { 46 | return &Table{ 47 | id: id, 48 | module: module, 49 | } 50 | } 51 | 52 | // ID returns the table id. 53 | func (table *Table) ID() string { 54 | return C.GoString(C.bpf_table_name(table.module.p, table.id)) 55 | } 56 | 57 | // Name returns the table name. 58 | func (table *Table) Name() string { 59 | return C.GoString(C.bpf_table_name(table.module.p, table.id)) 60 | } 61 | 62 | // Config returns the table properties (name, fd, ...). 63 | func (table *Table) Config() map[string]interface{} { 64 | mod := table.module.p 65 | return map[string]interface{}{ 66 | "name": C.GoString(C.bpf_table_name(mod, table.id)), 67 | "fd": int(C.bpf_table_fd_id(mod, table.id)), 68 | "key_size": uint64(C.bpf_table_key_size_id(mod, table.id)), 69 | "leaf_size": uint64(C.bpf_table_leaf_size_id(mod, table.id)), 70 | "key_desc": C.GoString(C.bpf_table_key_desc_id(mod, table.id)), 71 | "leaf_desc": C.GoString(C.bpf_table_leaf_desc_id(mod, table.id)), 72 | } 73 | } 74 | 75 | func (table *Table) LeafStrToBytes(leafStr string) ([]byte, error) { 76 | mod := table.module.p 77 | 78 | leafSize := C.bpf_table_leaf_size_id(mod, table.id) 79 | leaf := make([]byte, leafSize) 80 | leafP := unsafe.Pointer(&leaf[0]) 81 | 82 | leafCS := C.CString(leafStr) 83 | defer C.free(unsafe.Pointer(leafCS)) 84 | 85 | r := C.bpf_table_leaf_sscanf(mod, table.id, leafCS, leafP) 86 | if r != 0 { 87 | return nil, fmt.Errorf("error scanning leaf (%v) from string", leafStr) 88 | } 89 | return leaf, nil 90 | } 91 | 92 | func (table *Table) KeyStrToBytes(keyStr string) ([]byte, error) { 93 | mod := table.module.p 94 | 95 | keySize := C.bpf_table_key_size_id(mod, table.id) 96 | key := make([]byte, keySize) 97 | keyP := unsafe.Pointer(&key[0]) 98 | 99 | keyCS := C.CString(keyStr) 100 | defer C.free(unsafe.Pointer(keyCS)) 101 | 102 | r := C.bpf_table_key_sscanf(mod, table.id, keyCS, keyP) 103 | if r != 0 { 104 | return nil, fmt.Errorf("error scanning key (%v) from string", keyStr) 105 | } 106 | return key, nil 107 | } 108 | 109 | // KeyBytesToStr returns the given key value formatted using the bcc-table's key string printer. 110 | func (table *Table) KeyBytesToStr(key []byte) (string, error) { 111 | keySize := len(key) 112 | keyP := unsafe.Pointer(&key[0]) 113 | 114 | keyStr := make([]byte, keySize*8) 115 | keyStrP := (*C.char)(unsafe.Pointer(&keyStr[0])) 116 | 117 | if res := C.bpf_table_key_snprintf(table.module.p, table.id, keyStrP, C.size_t(len(keyStr)), keyP); res != 0 { 118 | return "", fmt.Errorf("formatting table-key: %d", res) 119 | } 120 | 121 | return string(keyStr[:bytes.IndexByte(keyStr, 0)]), nil 122 | } 123 | 124 | // LeafBytesToStr returns the given leaf value formatted using the bcc-table's leaf string printer. 125 | func (table *Table) LeafBytesToStr(leaf []byte) (string, error) { 126 | leafSize := len(leaf) 127 | leafP := unsafe.Pointer(&leaf[0]) 128 | 129 | leafStr := make([]byte, leafSize*8) 130 | leafStrP := (*C.char)(unsafe.Pointer(&leafStr[0])) 131 | 132 | if res := C.bpf_table_leaf_snprintf(table.module.p, table.id, leafStrP, C.size_t(len(leafStr)), leafP); res != 0 { 133 | return "", fmt.Errorf("formatting table-leaf: %d", res) 134 | } 135 | 136 | return string(leafStr[:bytes.IndexByte(leafStr, 0)]), nil 137 | } 138 | 139 | // Get takes a key and returns the value or nil, and an 'ok' style indicator. 140 | func (table *Table) Get(key []byte) ([]byte, error) { 141 | mod := table.module.p 142 | fd := C.bpf_table_fd_id(mod, table.id) 143 | 144 | keyP := unsafe.Pointer(&key[0]) 145 | 146 | leafSize := C.bpf_table_leaf_size_id(mod, table.id) 147 | mapType := C.bpf_table_type_id(mod, table.id) 148 | switch mapType { 149 | case C.BPF_MAP_TYPE_PERCPU_HASH, C.BPF_MAP_TYPE_PERCPU_ARRAY: 150 | cpus, err := cpupossible.Get() 151 | if err != nil { 152 | return nil, fmt.Errorf("get possible cpus: %w", err) 153 | } 154 | leafSize *= C.size_t(len(cpus)) 155 | } 156 | leaf := make([]byte, leafSize) 157 | leafP := unsafe.Pointer(&leaf[0]) 158 | 159 | r, err := C.bpf_lookup_elem(fd, keyP, leafP) 160 | if r != 0 { 161 | keyStr, errK := table.KeyBytesToStr(key) 162 | if errK != nil { 163 | keyStr = fmt.Sprintf("%v", key) 164 | } 165 | return nil, fmt.Errorf("Table.Get: key %v: %v", keyStr, err) 166 | } 167 | 168 | return leaf, nil 169 | } 170 | 171 | // GetP takes a key and returns the value or nil. 172 | func (table *Table) GetP(key unsafe.Pointer) (unsafe.Pointer, error) { 173 | fd := C.bpf_table_fd_id(table.module.p, table.id) 174 | 175 | leafSize := C.bpf_table_leaf_size_id(table.module.p, table.id) 176 | mapType := C.bpf_table_type_id(table.module.p, table.id) 177 | switch mapType { 178 | case C.BPF_MAP_TYPE_PERCPU_HASH, C.BPF_MAP_TYPE_PERCPU_ARRAY: 179 | cpus, err := cpupossible.Get() 180 | if err != nil { 181 | return nil, fmt.Errorf("get possible cpus: %w", err) 182 | } 183 | leafSize *= C.size_t(len(cpus)) 184 | } 185 | leaf := make([]byte, leafSize) 186 | leafP := unsafe.Pointer(&leaf[0]) 187 | 188 | _, err := C.bpf_lookup_elem(fd, key, leafP) 189 | if err != nil { 190 | return nil, err 191 | } 192 | return leafP, nil 193 | } 194 | 195 | // Set a key to a value. 196 | func (table *Table) Set(key, leaf []byte) error { 197 | fd := C.bpf_table_fd_id(table.module.p, table.id) 198 | 199 | keyP := unsafe.Pointer(&key[0]) 200 | leafP := unsafe.Pointer(&leaf[0]) 201 | 202 | r, err := C.bpf_update_elem(fd, keyP, leafP, 0) 203 | if r != 0 { 204 | keyStr, errK := table.KeyBytesToStr(key) 205 | if errK != nil { 206 | keyStr = fmt.Sprintf("%v", key) 207 | } 208 | leafStr, errL := table.LeafBytesToStr(leaf) 209 | if errL != nil { 210 | leafStr = fmt.Sprintf("%v", leaf) 211 | } 212 | 213 | return fmt.Errorf("Table.Set: update %v to %v: %v", keyStr, leafStr, err) 214 | } 215 | 216 | return nil 217 | } 218 | 219 | // SetP a key to a value as unsafe.Pointer. 220 | func (table *Table) SetP(key, leaf unsafe.Pointer) error { 221 | fd := C.bpf_table_fd_id(table.module.p, table.id) 222 | 223 | _, err := C.bpf_update_elem(fd, key, leaf, 0) 224 | if err != nil { 225 | return err 226 | } 227 | 228 | return nil 229 | } 230 | 231 | // Delete a key. 232 | func (table *Table) Delete(key []byte) error { 233 | fd := C.bpf_table_fd_id(table.module.p, table.id) 234 | keyP := unsafe.Pointer(&key[0]) 235 | r, err := C.bpf_delete_elem(fd, keyP) 236 | if r != 0 { 237 | keyStr, errK := table.KeyBytesToStr(key) 238 | if errK != nil { 239 | keyStr = fmt.Sprintf("%v", key) 240 | } 241 | return fmt.Errorf("Table.Delete: key %v: %v", keyStr, err) 242 | } 243 | return nil 244 | } 245 | 246 | // DeleteP a key. 247 | func (table *Table) DeleteP(key unsafe.Pointer) error { 248 | fd := C.bpf_table_fd_id(table.module.p, table.id) 249 | _, err := C.bpf_delete_elem(fd, key) 250 | if err != nil { 251 | return err 252 | } 253 | return nil 254 | } 255 | 256 | // DeleteAll deletes all entries from the table 257 | func (table *Table) DeleteAll() error { 258 | mod := table.module.p 259 | fd := C.bpf_table_fd_id(mod, table.id) 260 | 261 | keySize := C.bpf_table_key_size_id(mod, table.id) 262 | key := make([]byte, keySize) 263 | keyP := unsafe.Pointer(&key[0]) 264 | for res := C.bpf_get_first_key(fd, keyP, keySize); res == 0; res = C.bpf_get_next_key(fd, keyP, keyP) { 265 | r, err := C.bpf_delete_elem(fd, keyP) 266 | if r != 0 { 267 | return fmt.Errorf("Table.DeleteAll: unable to delete element: %v", err) 268 | } 269 | } 270 | return nil 271 | } 272 | 273 | // TableIterator contains the current position for iteration over a *bcc.Table and provides methods for iteration. 274 | type TableIterator struct { 275 | table *Table 276 | fd C.int 277 | 278 | err error 279 | 280 | key []byte 281 | leaf []byte 282 | } 283 | 284 | // Iter returns an iterator to list all table entries available as raw bytes. 285 | func (table *Table) Iter() *TableIterator { 286 | fd := C.bpf_table_fd_id(table.module.p, table.id) 287 | 288 | return &TableIterator{ 289 | table: table, 290 | fd: fd, 291 | } 292 | } 293 | 294 | // Next looks up the next element and return true if one is available. 295 | func (it *TableIterator) Next() bool { 296 | if it.err != nil { 297 | return false 298 | } 299 | 300 | if it.key == nil { 301 | keySize := C.bpf_table_key_size_id(it.table.module.p, it.table.id) 302 | 303 | key := make([]byte, keySize) 304 | keyP := unsafe.Pointer(&key[0]) 305 | if res, err := C.bpf_get_first_key(it.fd, keyP, keySize); res != 0 { 306 | if !os.IsNotExist(err) { 307 | it.err = err 308 | } 309 | return false 310 | } 311 | 312 | leafSize := C.bpf_table_leaf_size_id(it.table.module.p, it.table.id) 313 | mapType := C.bpf_table_type_id(it.table.module.p, it.table.id) 314 | switch mapType { 315 | case C.BPF_MAP_TYPE_PERCPU_HASH, C.BPF_MAP_TYPE_PERCPU_ARRAY: 316 | cpus, err := cpupossible.Get() 317 | if err != nil { 318 | it.err = fmt.Errorf("get possible cpus: %w", err) 319 | return false 320 | } 321 | leafSize *= C.size_t(len(cpus)) 322 | } 323 | leaf := make([]byte, leafSize) 324 | 325 | it.key = key 326 | it.leaf = leaf 327 | } else { 328 | keyP := unsafe.Pointer(&it.key[0]) 329 | if res, err := C.bpf_get_next_key(it.fd, keyP, keyP); res != 0 { 330 | if !os.IsNotExist(err) { 331 | it.err = err 332 | } 333 | return false 334 | } 335 | } 336 | 337 | keyP := unsafe.Pointer(&it.key[0]) 338 | leafP := unsafe.Pointer(&it.leaf[0]) 339 | if res, err := C.bpf_lookup_elem(it.fd, keyP, leafP); res != 0 { 340 | it.err = errIterationFailed 341 | if !os.IsNotExist(err) { 342 | it.err = err 343 | } 344 | return false 345 | } 346 | 347 | return true 348 | } 349 | 350 | // Key returns the current key value of the iterator, if the most recent call to Next returned true. 351 | // The slice is valid only until the next call to Next. 352 | func (it *TableIterator) Key() []byte { 353 | return it.key 354 | } 355 | 356 | // Leaf returns the current leaf value of the iterator, if the most recent call to Next returned true. 357 | // The slice is valid only until the next call to Next. 358 | func (it *TableIterator) Leaf() []byte { 359 | return it.leaf 360 | } 361 | 362 | // Err returns the last error that ocurred while table.Iter oder iter.Next 363 | func (it *TableIterator) Err() error { 364 | return it.err 365 | } 366 | -------------------------------------------------------------------------------- /bpf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Kinvolk 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package bpf 16 | -------------------------------------------------------------------------------- /bpf_test.go: -------------------------------------------------------------------------------- 1 | // +build integration 2 | 3 | // Copyright 2016 PLUMgrid 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package bpf 18 | 19 | import ( 20 | "bytes" 21 | "encoding/binary" 22 | "fmt" 23 | "os" 24 | "path/filepath" 25 | "strconv" 26 | "syscall" 27 | "testing" 28 | "unsafe" 29 | 30 | "github.com/iovisor/gobpf/bcc" 31 | "github.com/iovisor/gobpf/elf" 32 | "github.com/iovisor/gobpf/pkg/bpffs" 33 | "github.com/iovisor/gobpf/pkg/progtestrun" 34 | ) 35 | 36 | // redefine flags here as cgo in test is not supported 37 | const ( 38 | BPF_ANY = 0 /* create new element or update existing */ 39 | BPF_NOEXIST = 1 /* create new element if it didn't exist */ 40 | BPF_EXIST = 2 41 | ) 42 | 43 | var simple1 string = ` 44 | BPF_TABLE("hash", int, int, table1, 10); 45 | int func1(void *ctx) { 46 | return 0; 47 | } 48 | ` 49 | 50 | var simple2 = ` 51 | struct key { 52 | int key; 53 | }; 54 | struct leaf { 55 | int value; 56 | }; 57 | BPF_HASH(table2, struct key, struct leaf, 10); 58 | int func2(void *ctx) { 59 | return 0; 60 | } 61 | ` 62 | 63 | type key struct { 64 | key uint32 65 | } 66 | 67 | type leaf struct { 68 | value uint32 69 | } 70 | 71 | var kernelVersion uint32 72 | 73 | var ( 74 | kernelVersion46 uint32 75 | kernelVersion47 uint32 76 | kernelVersion48 uint32 77 | kernelVersion410 uint32 78 | kernelVersion412 uint32 79 | kernelVersion414 uint32 80 | ) 81 | 82 | func init() { 83 | kernelVersion46, _ = elf.KernelVersionFromReleaseString("4.6.0") 84 | kernelVersion47, _ = elf.KernelVersionFromReleaseString("4.7.0") 85 | kernelVersion48, _ = elf.KernelVersionFromReleaseString("4.8.0") 86 | kernelVersion410, _ = elf.KernelVersionFromReleaseString("4.10.0") 87 | kernelVersion412, _ = elf.KernelVersionFromReleaseString("4.12.0") 88 | kernelVersion414, _ = elf.KernelVersionFromReleaseString("4.14.0") 89 | } 90 | 91 | func TestModuleLoadBCC(t *testing.T) { 92 | b := bcc.NewModule(simple1, []string{}) 93 | if b == nil { 94 | t.Fatal("prog is nil") 95 | } 96 | defer b.Close() 97 | _, err := b.LoadKprobe("func1") 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | } 102 | 103 | func fillTable1(b *bcc.Module) (*bcc.Table, error) { 104 | table := bcc.NewTable(b.TableId("table1"), b) 105 | key, _ := table.KeyStrToBytes("1") 106 | leaf, _ := table.LeafStrToBytes("11") 107 | if err := table.Set(key, leaf); err != nil { 108 | return nil, fmt.Errorf("table.Set key 1 failed: %v", err) 109 | } 110 | 111 | key, leaf = make([]byte, 4), make([]byte, 4) 112 | bcc.GetHostByteOrder().PutUint32(key, 2) 113 | bcc.GetHostByteOrder().PutUint32(leaf, 22) 114 | if err := table.Set(key, leaf); err != nil { 115 | return nil, fmt.Errorf("table.Set key 2 failed: %v", err) 116 | } 117 | 118 | return table, nil 119 | } 120 | 121 | func TestBCCIterTable(t *testing.T) { 122 | b := bcc.NewModule(simple1, []string{}) 123 | if b == nil { 124 | t.Fatal("prog is nil") 125 | } 126 | defer b.Close() 127 | 128 | table, err := fillTable1(b) 129 | if err != nil { 130 | t.Fatalf("fill table1 failed: %v", err) 131 | } 132 | 133 | hostEndian := bcc.GetHostByteOrder() 134 | resIter := make(map[int32]int32) 135 | iter := table.Iter() 136 | for iter.Next() { 137 | key, leaf := iter.Key(), iter.Leaf() 138 | keyStr, err := table.KeyBytesToStr(key) 139 | if err != nil { 140 | t.Fatalf("table.Iter/KeyBytesToStr failed: cannot print value: %v", err) 141 | } 142 | leafStr, err := table.LeafBytesToStr(leaf) 143 | if err != nil { 144 | t.Fatalf("table.Iter/LeafBytesToStr failed: cannot print value: %v", err) 145 | } 146 | 147 | var k, v int32 148 | if err := binary.Read(bytes.NewBuffer(key), hostEndian, &k); err != nil { 149 | t.Fatalf("table.Iter failed: cannot decode key: %v", err) 150 | } 151 | if err := binary.Read(bytes.NewBuffer(leaf), hostEndian, &v); err != nil { 152 | t.Fatalf("table.Iter failed: cannot decode value: %v", err) 153 | } 154 | 155 | resIter[k] = v 156 | 157 | kS, err := strconv.ParseInt(keyStr[2:], 16, 32) 158 | if err != nil { 159 | t.Fatalf("table.Iter failed: non-number key: %v", err) 160 | } 161 | vS, err := strconv.ParseInt(leafStr[2:], 16, 32) 162 | if err != nil { 163 | t.Fatalf("table.Iter failed: non-number value: %v", err) 164 | } 165 | 166 | if int32(kS) != k || int32(vS) != v { 167 | t.Errorf("table.iter.Values() inconsistent with string values: (%v, %v) vs (%v, %v)", k, v, kS, vS) 168 | } 169 | } 170 | 171 | if iter.Err() != nil { 172 | t.Fatalf("table.Iter failed: iteration finished with unexpected error: %v", iter.Err()) 173 | } 174 | 175 | if count := len(resIter); count != 2 { 176 | t.Fatalf("expected 2 entries in Iter table, not %d", count) 177 | } 178 | 179 | for _, te := range [][]int32{{1, 11}, {2, 22}} { 180 | res := resIter[te[0]] 181 | if res != te[1] { 182 | t.Fatalf("expected entry %d in Iter table to contain %d, but got %d", te[0], te[1], res) 183 | } 184 | } 185 | } 186 | 187 | func TestBCCTableSetPGetPDeleteP(t *testing.T) { 188 | b := bcc.NewModule(simple2, []string{}) 189 | if b == nil { 190 | t.Fatal("prog is nil") 191 | } 192 | defer b.Close() 193 | 194 | table := bcc.NewTable(b.TableId("table2"), b) 195 | 196 | k := &key{0} 197 | l := &leaf{1} 198 | 199 | err := table.SetP(unsafe.Pointer(k), unsafe.Pointer(l)) 200 | if err != nil { 201 | t.Fatal(err) 202 | } 203 | 204 | p, err := table.GetP(unsafe.Pointer(k)) 205 | if err != nil { 206 | t.Fatal(err) 207 | } 208 | v := (*leaf)(p) 209 | if v.value != 1 { 210 | t.Fatalf("expected 1, not %d", v.value) 211 | } 212 | 213 | err = table.DeleteP(unsafe.Pointer(k)) 214 | if err != nil { 215 | t.Fatal(err) 216 | } 217 | 218 | _, err = table.GetP(unsafe.Pointer(k)) 219 | if !os.IsNotExist(err) { 220 | t.Fatal(err) 221 | } 222 | } 223 | 224 | func TestBCCTableDeleteAll(t *testing.T) { 225 | b := bcc.NewModule(simple1, []string{}) 226 | if b == nil { 227 | t.Fatal("prog is nil") 228 | } 229 | defer b.Close() 230 | 231 | table, err := fillTable1(b) 232 | if err != nil { 233 | t.Fatalf("fill table1 failed: %v", err) 234 | } 235 | 236 | count := 0 237 | for it := table.Iter(); it.Next(); { 238 | count++ 239 | } 240 | if count != 2 { 241 | t.Fatalf("expected 2 entries in table, not %d", count) 242 | } 243 | if err := table.DeleteAll(); err != nil { 244 | t.Fatalf("table.DeleteAll failed: %v", err) 245 | } 246 | count = 0 247 | for it := table.Iter(); it.Next(); { 248 | count++ 249 | } 250 | if count != 0 { 251 | t.Fatalf("expected 0 entries in table, not %d", count) 252 | } 253 | } 254 | 255 | func containsMap(maps []*elf.Map, name string) bool { 256 | for _, m := range maps { 257 | if m.Name == name { 258 | return true 259 | } 260 | } 261 | return false 262 | } 263 | 264 | func containsProbe(probes []*elf.Kprobe, name string) bool { 265 | for _, k := range probes { 266 | if k.Name == name { 267 | return true 268 | } 269 | } 270 | return false 271 | } 272 | 273 | func containsUprobe(uprobes []*elf.Uprobe, name string) bool { 274 | for _, u := range uprobes { 275 | if u.Name == name { 276 | return true 277 | } 278 | } 279 | return false 280 | } 281 | 282 | func containsCgroupProg(cgroupProgs []*elf.CgroupProgram, name string) bool { 283 | for _, c := range cgroupProgs { 284 | if c.Name == name { 285 | return true 286 | } 287 | } 288 | return false 289 | } 290 | 291 | func containsTracepointProg(tracepointProgs []*elf.TracepointProgram, name string) bool { 292 | for _, c := range tracepointProgs { 293 | if c.Name == name { 294 | return true 295 | } 296 | } 297 | return false 298 | } 299 | 300 | func containsSocketFilter(socketFilters []*elf.SocketFilter, name string) bool { 301 | for _, c := range socketFilters { 302 | if c.Name == name { 303 | return true 304 | } 305 | } 306 | return false 307 | } 308 | 309 | func checkMaps(t *testing.T, b *elf.Module) { 310 | var expectedMaps = []string{ 311 | "dummy_hash", 312 | "dummy_array", 313 | "dummy_prog_array", 314 | "dummy_perf", 315 | "dummy_array_custom", 316 | } 317 | 318 | if kernelVersion >= kernelVersion46 { 319 | kernel46Maps := []string{ 320 | "dummy_percpu_hash", 321 | "dummy_percpu_array", 322 | "dummy_stack_trace", 323 | } 324 | expectedMaps = append(expectedMaps, kernel46Maps...) 325 | } else { 326 | t.Logf("kernel doesn't support percpu maps and stacktrace maps. Skipping...") 327 | } 328 | 329 | if kernelVersion >= kernelVersion48 { 330 | kernel48Maps := []string{ 331 | "dummy_cgroup_array", 332 | } 333 | expectedMaps = append(expectedMaps, kernel48Maps...) 334 | } else { 335 | t.Logf("kernel doesn't support cgroup array maps. Skipping...") 336 | } 337 | 338 | var maps []*elf.Map 339 | for m := range b.IterMaps() { 340 | maps = append(maps, m) 341 | } 342 | if len(maps) != len(expectedMaps) { 343 | t.Fatalf("unexpected number of maps. Got %d, expected %d", len(maps), len(expectedMaps)) 344 | } 345 | for _, em := range expectedMaps { 346 | if !containsMap(maps, em) { 347 | t.Fatalf("map %q not found", em) 348 | } 349 | } 350 | } 351 | 352 | func checkProbes(t *testing.T, b *elf.Module) { 353 | var expectedProbes = []string{ 354 | "kprobe/dummy", 355 | "kretprobe/dummy", 356 | } 357 | 358 | var probes []*elf.Kprobe 359 | for p := range b.IterKprobes() { 360 | probes = append(probes, p) 361 | } 362 | if len(probes) != len(expectedProbes) { 363 | t.Fatalf("unexpected number of probes. Got %d, expected %d", len(probes), len(expectedProbes)) 364 | } 365 | for _, ek := range expectedProbes { 366 | if !containsProbe(probes, ek) { 367 | t.Fatalf("probe %q not found", ek) 368 | } 369 | } 370 | } 371 | 372 | func checkUprobes(t *testing.T, b *elf.Module) { 373 | var expectedUprobes = []string{ 374 | "uprobe/dummy", 375 | "uretprobe/dummy", 376 | } 377 | 378 | var uprobes []*elf.Uprobe 379 | for p := range b.IterUprobes() { 380 | uprobes = append(uprobes, p) 381 | } 382 | if len(uprobes) != len(expectedUprobes) { 383 | t.Fatalf("unexpected number of uprobes. Got %d, expected %d", len(uprobes), len(expectedUprobes)) 384 | } 385 | for _, ek := range expectedUprobes { 386 | if !containsUprobe(uprobes, ek) { 387 | t.Fatalf("uprobe %q not found", ek) 388 | } 389 | } 390 | } 391 | 392 | func checkCgroupProgs(t *testing.T, b *elf.Module) { 393 | if kernelVersion < kernelVersion410 { 394 | t.Logf("kernel doesn't support cgroup-bpf. Skipping...") 395 | return 396 | } 397 | 398 | var expectedCgroupProgs = []string{ 399 | "cgroup/skb", 400 | "cgroup/sock", 401 | } 402 | 403 | var cgroupProgs []*elf.CgroupProgram 404 | for p := range b.IterCgroupProgram() { 405 | cgroupProgs = append(cgroupProgs, p) 406 | } 407 | if len(cgroupProgs) != len(expectedCgroupProgs) { 408 | t.Fatalf("unexpected number of cgroup programs. Got %d, expected %v", len(cgroupProgs), len(expectedCgroupProgs)) 409 | } 410 | for _, cp := range expectedCgroupProgs { 411 | if !containsCgroupProg(cgroupProgs, cp) { 412 | t.Fatalf("cgroup program %q not found", cp) 413 | } 414 | } 415 | } 416 | 417 | func checkXDPProgs(t *testing.T, b *elf.Module) { 418 | if kernelVersion < kernelVersion48 { 419 | t.Logf("kernel doesn't support XDP. Skipping...") 420 | t.Skip() 421 | } 422 | 423 | var expectedXDPProgs = []string{ 424 | "xdp/prog1", 425 | "xdp/prog2", 426 | } 427 | 428 | var xdpProgs []*elf.XDPProgram 429 | for p := range b.IterXDPProgram() { 430 | xdpProgs = append(xdpProgs, p) 431 | } 432 | if len(xdpProgs) != len(expectedXDPProgs) { 433 | t.Fatalf("unexpected number of XDP programs. Got %d, expected %v", len(xdpProgs), len(expectedXDPProgs)) 434 | } 435 | } 436 | 437 | func checkTracepointProgs(t *testing.T, b *elf.Module) { 438 | if kernelVersion < kernelVersion47 { 439 | t.Logf("kernel doesn't support bpf programs for tracepoints. Skipping...") 440 | return 441 | } 442 | 443 | var expectedTracepointProgs = []string{ 444 | "tracepoint/raw_syscalls/sys_enter", 445 | } 446 | 447 | var tracepointProgs []*elf.TracepointProgram 448 | for p := range b.IterTracepointProgram() { 449 | tracepointProgs = append(tracepointProgs, p) 450 | } 451 | if len(tracepointProgs) != len(expectedTracepointProgs) { 452 | t.Fatalf("unexpected number of tracepoint programs. Got %d, expected %v", len(tracepointProgs), len(expectedTracepointProgs)) 453 | } 454 | for _, p := range expectedTracepointProgs { 455 | if !containsTracepointProg(tracepointProgs, p) { 456 | t.Fatalf("tracepoint program %q not found", p) 457 | } 458 | } 459 | } 460 | 461 | func checkSocketFilters(t *testing.T, b *elf.Module) { 462 | var expectedSocketFilters = []string{ 463 | "socket/dummy", 464 | } 465 | 466 | var socketFilters []*elf.SocketFilter 467 | for sf := range b.IterSocketFilter() { 468 | socketFilters = append(socketFilters, sf) 469 | } 470 | if len(socketFilters) != len(expectedSocketFilters) { 471 | t.Fatalf("unexpected number of socket filters. Got %d, expected %d", len(socketFilters), len(expectedSocketFilters)) 472 | } 473 | for _, sf := range expectedSocketFilters { 474 | if !containsSocketFilter(socketFilters, sf) { 475 | t.Fatalf("socket filter %q not found", sf) 476 | } 477 | } 478 | 479 | fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, syscall.ETH_P_ALL) 480 | if err != nil { 481 | t.Fatalf("unable to open a raw socket: %s", err) 482 | } 483 | defer syscall.Close(fd) 484 | 485 | socketFilter := b.SocketFilter("socket/dummy") 486 | if socketFilter == nil { 487 | t.Fatal("socket filter dummy not found") 488 | } 489 | 490 | if err := elf.AttachSocketFilter(socketFilter, fd); err != nil { 491 | t.Fatalf("failed trying to attach socket filter: %s", err) 492 | } 493 | 494 | if err := elf.DetachSocketFilter(socketFilter, fd); err != nil { 495 | t.Fatalf("failed trying to detach socket filter: %s", err) 496 | } 497 | } 498 | 499 | func checkPinConfig(t *testing.T, expectedPaths []string) { 500 | for _, p := range expectedPaths { 501 | if fi, err := os.Stat(p); os.IsNotExist(err) || !fi.Mode().IsRegular() { 502 | t.Fatalf("pinned object %q not found", p) 503 | } 504 | } 505 | } 506 | 507 | func checkPinConfigCleanup(t *testing.T, expectedPaths []string) { 508 | for _, p := range expectedPaths { 509 | if _, err := os.Stat(p); !os.IsNotExist(err) { 510 | t.Fatalf("pinned object %q is not cleaned up", p) 511 | } 512 | } 513 | } 514 | 515 | func checkUpdateDeleteElement(t *testing.T, b *elf.Module) { 516 | mp := b.Map("dummy_hash") 517 | if mp == nil { 518 | t.Fatal("unable to find dummy_hash map") 519 | } 520 | 521 | key := 1000 522 | value := 1000 523 | if err := b.UpdateElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&value), BPF_ANY); err != nil { 524 | t.Fatal("failed trying to update an element with BPF_ANY") 525 | } 526 | 527 | if err := b.UpdateElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&value), BPF_NOEXIST); err == nil { 528 | t.Fatal("succeeded updating element with BPF_NOEXIST, but an element with the same key was added to the map before") 529 | } 530 | 531 | if err := b.UpdateElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&value), BPF_EXIST); err != nil { 532 | t.Fatal("failed trying to update an element with BPF_EXIST while the key was added to the map before") 533 | } 534 | 535 | if err := b.DeleteElement(mp, unsafe.Pointer(&key)); err != nil { 536 | t.Fatal("failed to delete an element") 537 | } 538 | 539 | if err := b.UpdateElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&value), BPF_EXIST); err == nil { 540 | t.Fatal("succeeded updating element with BPF_EXIST, but the element was deleted from the map before") 541 | } 542 | } 543 | 544 | func checkLookupElement(t *testing.T, b *elf.Module) { 545 | mp := b.Map("dummy_hash") 546 | if mp == nil { 547 | t.Fatal("unable to find dummy_hash map") 548 | } 549 | 550 | key := 2000 551 | value := 2000 552 | if err := b.UpdateElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&value), BPF_ANY); err != nil { 553 | t.Fatal("failed trying to update an element with BPF_ANY") 554 | } 555 | 556 | var lvalue int 557 | if err := b.LookupElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&lvalue)); err != nil { 558 | t.Fatal("failed trying to lookup an element previously added") 559 | } 560 | if value != lvalue { 561 | t.Fatalf("wrong value returned, expected %d, got %d", value, lvalue) 562 | } 563 | 564 | key = 3000 565 | if err := b.LookupElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&lvalue)); err == nil { 566 | t.Fatalf("succeeded to find an element which wasn't added previously") 567 | } 568 | 569 | found := map[int]bool{2000: false} 570 | for i := 4000; i != 4010; i++ { 571 | key = i 572 | value = i 573 | if err := b.UpdateElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&value), BPF_ANY); err != nil { 574 | t.Fatal("failed trying to update an element with BPF_ANY") 575 | } 576 | found[key] = false 577 | } 578 | 579 | key = 0 580 | nextKey := 0 581 | for { 582 | f, err := b.LookupNextElement(mp, unsafe.Pointer(&key), unsafe.Pointer(&nextKey), unsafe.Pointer(&lvalue)) 583 | if err != nil { 584 | t.Fatalf("failed trying to lookup the next element: %s", err) 585 | } 586 | if !f { 587 | break 588 | } 589 | 590 | if nextKey != lvalue { 591 | t.Fatalf("key %d not corresponding to value %d", nextKey, lvalue) 592 | } 593 | 594 | if _, ok := found[nextKey]; !ok { 595 | t.Fatalf("key %d found", nextKey) 596 | } 597 | found[nextKey] = true 598 | 599 | key = nextKey 600 | } 601 | 602 | for key, f := range found { 603 | if !f { 604 | t.Fatalf("expected key %d not found", key) 605 | } 606 | } 607 | } 608 | 609 | func checkProgTestRun(t *testing.T, b *elf.Module) { 610 | if kernelVersion < kernelVersion412 { 611 | t.Logf("kernel doesn't support BPF_PROG_TEST_RUN. Skipping...") 612 | return 613 | } 614 | prog := b.CgroupProgram("cgroup/skb") 615 | if prog == nil { 616 | t.Fatal("unable to find prog") 617 | } 618 | // minimum amount of input data, but unused 619 | data := make([]byte, 14) 620 | returnValue, _, _, err := progtestrun.Run(prog.Fd(), 1, data, nil) 621 | if err != nil { 622 | t.Fatalf("bpf_prog_test_run failed: %v", err) 623 | } 624 | if returnValue != 1 { 625 | t.Fatalf("expected return value 1, got %d", returnValue) 626 | } 627 | } 628 | 629 | func TestModuleLoadELF(t *testing.T) { 630 | var err error 631 | kernelVersion, err = elf.CurrentKernelVersion() 632 | if err != nil { 633 | t.Fatalf("error getting current kernel version: %v", err) 634 | } 635 | 636 | dummyELF := "./tests/dummy.o" 637 | if kernelVersion > kernelVersion414 { 638 | dummyELF = "./tests/dummy-414.o" 639 | } else if kernelVersion > kernelVersion410 { 640 | dummyELF = "./tests/dummy-410.o" 641 | } else if kernelVersion > kernelVersion48 { 642 | dummyELF = "./tests/dummy-48.o" 643 | } else if kernelVersion > kernelVersion46 { 644 | dummyELF = "./tests/dummy-46.o" 645 | } 646 | 647 | var secParams = map[string]elf.SectionParams{ 648 | "maps/dummy_array_custom": elf.SectionParams{ 649 | PinPath: filepath.Join("gobpf-test", "testgroup1"), 650 | }, 651 | } 652 | var closeOptions = map[string]elf.CloseOptions{ 653 | "maps/dummy_array_custom": elf.CloseOptions{ 654 | Unpin: true, 655 | PinPath: filepath.Join("gobpf-test", "testgroup1"), 656 | }, 657 | } 658 | 659 | if err := bpffs.Mount(); err != nil { 660 | t.Fatalf("error mounting bpf fs: %v", err) 661 | } 662 | 663 | b := elf.NewModule(dummyELF) 664 | if b == nil { 665 | t.Fatal("prog is nil") 666 | } 667 | if err := b.Load(secParams); err != nil { 668 | t.Fatal(err) 669 | } 670 | defer func() { 671 | if err := b.CloseExt(closeOptions); err != nil { 672 | t.Fatal(err) 673 | } 674 | checkPinConfigCleanup(t, []string{"/sys/fs/bpf/gobpf-test/testgroup1"}) 675 | }() 676 | 677 | checkMaps(t, b) 678 | checkProbes(t, b) 679 | checkUprobes(t, b) 680 | checkCgroupProgs(t, b) 681 | checkSocketFilters(t, b) 682 | checkTracepointProgs(t, b) 683 | checkXDPProgs(t, b) 684 | checkPinConfig(t, []string{"/sys/fs/bpf/gobpf-test/testgroup1"}) 685 | checkUpdateDeleteElement(t, b) 686 | checkLookupElement(t, b) 687 | checkProgTestRun(t, b) 688 | } 689 | -------------------------------------------------------------------------------- /elf/compat.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // (c) 2018 Suchakra Sharma 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package elf 18 | 19 | import ( 20 | "errors" 21 | "io/ioutil" 22 | "regexp" 23 | "runtime" 24 | ) 25 | 26 | const defaultSymFile = "/proc/kallsyms" 27 | 28 | // Returns the qualified syscall named by going through '/proc/kallsyms' on the 29 | // system on which its executed. It allows BPF programs that may have been compiled 30 | // for older syscall functions to run on newer kernels 31 | func GetSyscallFnName(name string) (string, error) { 32 | // Get kernel symbols 33 | syms, err := ioutil.ReadFile(defaultSymFile) 34 | if err != nil { 35 | return "", err 36 | } 37 | return getSyscallFnNameWithKallsyms(name, string(syms)) 38 | } 39 | 40 | func getSyscallFnNameWithKallsyms(name string, kallsymsContent string) (string, error) { 41 | var arch string 42 | switch runtime.GOARCH { 43 | case "386": 44 | arch = "ia32" 45 | default: 46 | arch = "x64" 47 | } 48 | 49 | // We should search for new syscall function like "__x64__sys_open" 50 | // Note the start of word boundary. Should return exactly one string 51 | regexStr := `(\b__` + arch + `_[Ss]y[sS]_` + name + `\b)` 52 | fnRegex := regexp.MustCompile(regexStr) 53 | 54 | match := fnRegex.FindAllString(kallsymsContent, -1) 55 | 56 | // If nothing found, search for old syscall function to be sure 57 | if len(match) == 0 { 58 | newRegexStr := `(\b[Ss]y[sS]_` + name + `\b)` 59 | fnRegex = regexp.MustCompile(newRegexStr) 60 | newMatch := fnRegex.FindAllString(kallsymsContent, -1) 61 | 62 | // If we get something like 'sys_open' or 'SyS_open', return 63 | // either (they have same addr) else, just return original string 64 | if len(newMatch) >= 1 { 65 | return newMatch[0], nil 66 | } else { 67 | return "", errors.New("could not find a valid syscall name") 68 | } 69 | } 70 | 71 | return match[0], nil 72 | } 73 | -------------------------------------------------------------------------------- /elf/compat_test.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // (c) 2018 ShiftLeft GmbH 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package elf 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | const prefixedKallsymsSymbols = ` 24 | 0000000000000000 W __x32_compat_sys_open_by_handle_at 25 | 0000000000000000 T do_sys_open 26 | 0000000000000000 T __x64_sys_open 27 | 0000000000000000 T __ia32_sys_open 28 | 0000000000000000 T __x64_sys_openat 29 | 0000000000000000 T __ia32_sys_openat 30 | 0000000000000000 T __ia32_compat_sys_open 31 | 0000000000000000 T __ia32_compat_sys_openat 32 | 0000000000000000 T __x64_sys_open_by_handle_at 33 | 0000000000000000 T __ia32_sys_open_by_handle_at 34 | 0000000000000000 T __ia32_compat_sys_open_by_handle_at 35 | 0000000000000000 t proc_sys_open 36 | 0000000000000000 t _eil_addr___ia32_compat_sys_openat 37 | 0000000000000000 t _eil_addr___ia32_compat_sys_open 38 | 0000000000000000 t _eil_addr___ia32_sys_openat 39 | 0000000000000000 t _eil_addr___x64_sys_openat 40 | 0000000000000000 t _eil_addr___ia32_sys_open 41 | 0000000000000000 t _eil_addr___x64_sys_open 42 | 0000000000000000 t _eil_addr___ia32_compat_sys_open_by_handle_at 43 | 0000000000000000 t _eil_addr___ia32_sys_open_by_handle_at 44 | 0000000000000000 t _eil_addr___x64_sys_open_by_handle_at 45 | ` 46 | 47 | const kallsymsSymbols = ` 48 | 0000000000000000 T dentry_open 49 | 0000000000000000 T filp_clone_open 50 | 0000000000000000 T file_open_name 51 | 0000000000000000 T filp_open 52 | 0000000000000000 T do_sys_open 53 | 0000000000000000 T SyS_open 54 | 0000000000000000 T sys_open 55 | 0000000000000000 T SyS_openat 56 | 0000000000000000 T sys_openat 57 | 0000000000000000 T compat_SyS_open 58 | 0000000000000000 T compat_sys_open 59 | 0000000000000000 T compat_SyS_openat 60 | 0000000000000000 T compat_sys_openat 61 | 0000000000000000 T SyS_creat 62 | 0000000000000000 T sys_creat 63 | 0000000000000000 T sys_vhangup 64 | ` 65 | 66 | func TestGetSyscallFnName(t *testing.T) { 67 | fnName, err := getSyscallFnNameWithKallsyms("open", prefixedKallsymsSymbols) 68 | if err != nil && fnName != "__x64_sys_open" { 69 | t.Errorf("expected __x64_sys_open : %s", err) 70 | } 71 | fnName, err = getSyscallFnNameWithKallsyms("open", kallsymsSymbols) 72 | if err != nil { 73 | if fnName != "SyS_open" { 74 | t.Errorf("expected SyS_open :%s", err) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /elf/elf_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Kinvolk 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 | // +build linux 16 | 17 | package elf 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestValidateMapPath(t *testing.T) { 24 | tests := []struct { 25 | input string 26 | expected bool 27 | }{ 28 | { 29 | input: "/sys/fs/bpf/good/path", 30 | expected: true, 31 | }, 32 | { 33 | input: "/sys/fs/bpf/../../bad/path", 34 | expected: false, 35 | }, 36 | { 37 | input: "/sys/fs/bpf/./bad/path", 38 | expected: false, 39 | }, 40 | { 41 | input: "/bad/path", 42 | expected: false, 43 | }, 44 | } 45 | 46 | for i, tt := range tests { 47 | if isValid := validateMapPath(tt.input); isValid != tt.expected { 48 | t.Fatalf("test %d (%s) expected %t but got %t", i, tt.input, tt.expected, isValid) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /elf/elf_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | package elf 4 | 5 | // not supported; dummy struct 6 | type BPFKProbePerf struct{} 7 | type SectionParams struct{} 8 | type Map struct{} 9 | 10 | func (b *Module) Load(parameters map[string]SectionParams) error { 11 | return errNotSupported 12 | } 13 | 14 | func NewBpfPerfEvent(fileName string) *BPFKProbePerf { 15 | // not supported 16 | return nil 17 | } 18 | 19 | func (b *BPFKProbePerf) Load() error { 20 | return errNotSupported 21 | } 22 | 23 | func (b *BPFKProbePerf) PollStart(mapName string, receiverChan chan []byte, lostChan chan uint64) { 24 | // not supported 25 | return 26 | } 27 | 28 | func (b *BPFKProbePerf) PollStop(mapName string) { 29 | // not supported 30 | return 31 | } 32 | 33 | func (m *Map) Fd() int { 34 | // not supported 35 | return -1 36 | } 37 | -------------------------------------------------------------------------------- /elf/errno.go: -------------------------------------------------------------------------------- 1 | package elf 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | errNotSupported = errors.New("not supported") 9 | ) 10 | -------------------------------------------------------------------------------- /elf/include/bpf_map.h: -------------------------------------------------------------------------------- 1 | #define BUF_SIZE_MAP_NS 256 2 | 3 | typedef struct bpf_map_def { 4 | unsigned int type; 5 | unsigned int key_size; 6 | unsigned int value_size; 7 | unsigned int max_entries; 8 | unsigned int map_flags; 9 | unsigned int pinning; 10 | char namespace[BUF_SIZE_MAP_NS]; 11 | } bpf_map_def; 12 | 13 | enum bpf_pin_type { 14 | PIN_NONE = 0, 15 | PIN_OBJECT_NS, 16 | PIN_GLOBAL_NS, 17 | PIN_CUSTOM_NS, 18 | }; 19 | -------------------------------------------------------------------------------- /elf/include/doc.go: -------------------------------------------------------------------------------- 1 | package include 2 | -------------------------------------------------------------------------------- /elf/include/libbpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * Common eBPF ELF object loading operations. 5 | * 6 | * Copyright (C) 2013-2015 Alexei Starovoitov 7 | * Copyright (C) 2015 Wang Nan 8 | * Copyright (C) 2015 Huawei Inc. 9 | */ 10 | 11 | #ifndef __LIBBPF_LIBBPF_H 12 | #define __LIBBPF_LIBBPF_H 13 | 14 | #include 15 | #include 16 | 17 | int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags); 18 | 19 | enum libbpf_errno { 20 | __LIBBPF_ERRNO__START = 4000, 21 | 22 | /* Something wrong in libelf */ 23 | LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START, 24 | LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */ 25 | LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */ 26 | LIBBPF_ERRNO__ENDIAN, /* Endian mismatch */ 27 | LIBBPF_ERRNO__INTERNAL, /* Internal error in libbpf */ 28 | LIBBPF_ERRNO__RELOC, /* Relocation failed */ 29 | LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */ 30 | LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */ 31 | LIBBPF_ERRNO__PROG2BIG, /* Program too big */ 32 | LIBBPF_ERRNO__KVER, /* Incorrect kernel version */ 33 | LIBBPF_ERRNO__PROGTYPE, /* Kernel doesn't support this program type */ 34 | LIBBPF_ERRNO__WRNGPID, /* Wrong pid in netlink message */ 35 | LIBBPF_ERRNO__INVSEQ, /* Invalid netlink sequence */ 36 | LIBBPF_ERRNO__NLPARSE, /* netlink parsing error */ 37 | __LIBBPF_ERRNO__END, 38 | }; 39 | 40 | typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); 41 | 42 | #endif /* __LIBBPF_LIBBPF_H */ 43 | -------------------------------------------------------------------------------- /elf/include/nlattr.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * NETLINK Netlink attributes 5 | * 6 | * Copyright (c) 2003-2013 Thomas Graf 7 | */ 8 | 9 | #ifndef __LIBBPF_NLATTR_H 10 | #define __LIBBPF_NLATTR_H 11 | 12 | #include 13 | #include 14 | /* avoid multiple definition of netlink features */ 15 | #define __LINUX_NETLINK_H 16 | 17 | /** 18 | * Standard attribute types to specify validation policy 19 | */ 20 | enum { 21 | LIBBPF_NLA_UNSPEC, /**< Unspecified type, binary data chunk */ 22 | LIBBPF_NLA_U8, /**< 8 bit integer */ 23 | LIBBPF_NLA_U16, /**< 16 bit integer */ 24 | LIBBPF_NLA_U32, /**< 32 bit integer */ 25 | LIBBPF_NLA_U64, /**< 64 bit integer */ 26 | LIBBPF_NLA_STRING, /**< NUL terminated character string */ 27 | LIBBPF_NLA_FLAG, /**< Flag */ 28 | LIBBPF_NLA_MSECS, /**< Micro seconds (64bit) */ 29 | LIBBPF_NLA_NESTED, /**< Nested attributes */ 30 | __LIBBPF_NLA_TYPE_MAX, 31 | }; 32 | 33 | #define LIBBPF_NLA_TYPE_MAX (__LIBBPF_NLA_TYPE_MAX - 1) 34 | 35 | /** 36 | * @ingroup attr 37 | * Attribute validation policy. 38 | * 39 | * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. 40 | */ 41 | struct libbpf_nla_policy { 42 | /** Type of attribute or LIBBPF_NLA_UNSPEC */ 43 | uint16_t type; 44 | 45 | /** Minimal length of payload required */ 46 | uint16_t minlen; 47 | 48 | /** Maximal length of payload allowed */ 49 | uint16_t maxlen; 50 | }; 51 | 52 | /** 53 | * @ingroup attr 54 | * Iterate over a stream of attributes 55 | * @arg pos loop counter, set to current attribute 56 | * @arg head head of attribute stream 57 | * @arg len length of attribute stream 58 | * @arg rem initialized to len, holds bytes currently remaining in stream 59 | */ 60 | #define libbpf_nla_for_each_attr(pos, head, len, rem) \ 61 | for (pos = head, rem = len; \ 62 | nla_ok(pos, rem); \ 63 | pos = nla_next(pos, &(rem))) 64 | 65 | /** 66 | * libbpf_nla_data - head of payload 67 | * @nla: netlink attribute 68 | */ 69 | static inline void *libbpf_nla_data(const struct nlattr *nla) 70 | { 71 | return (char *) nla + NLA_HDRLEN; 72 | } 73 | 74 | static inline uint8_t libbpf_nla_getattr_u8(const struct nlattr *nla) 75 | { 76 | return *(uint8_t *)libbpf_nla_data(nla); 77 | } 78 | 79 | static inline uint32_t libbpf_nla_getattr_u32(const struct nlattr *nla) 80 | { 81 | return *(uint32_t *)libbpf_nla_data(nla); 82 | } 83 | 84 | static inline const char *libbpf_nla_getattr_str(const struct nlattr *nla) 85 | { 86 | return (const char *)libbpf_nla_data(nla); 87 | } 88 | 89 | /** 90 | * libbpf_nla_len - length of payload 91 | * @nla: netlink attribute 92 | */ 93 | static inline int libbpf_nla_len(const struct nlattr *nla) 94 | { 95 | return nla->nla_len - NLA_HDRLEN; 96 | } 97 | 98 | int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, 99 | int len, struct libbpf_nla_policy *policy); 100 | int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype, 101 | struct nlattr *nla, 102 | struct libbpf_nla_policy *policy); 103 | 104 | int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh); 105 | 106 | #endif /* __LIBBPF_NLATTR_H */ -------------------------------------------------------------------------------- /elf/include/uapi/linux/doc.go: -------------------------------------------------------------------------------- 1 | package linux 2 | -------------------------------------------------------------------------------- /elf/include/uapi/linux/netlink.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | #ifndef _UAPI__LINUX_NETLINK_H 3 | #define _UAPI__LINUX_NETLINK_H 4 | 5 | #include 6 | #include /* for __kernel_sa_family_t */ 7 | #include 8 | 9 | #define NETLINK_ROUTE 0 /* Routing/device hook */ 10 | #define NETLINK_UNUSED 1 /* Unused number */ 11 | #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ 12 | #define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ 13 | #define NETLINK_SOCK_DIAG 4 /* socket monitoring */ 14 | #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ 15 | #define NETLINK_XFRM 6 /* ipsec */ 16 | #define NETLINK_SELINUX 7 /* SELinux event notifications */ 17 | #define NETLINK_ISCSI 8 /* Open-iSCSI */ 18 | #define NETLINK_AUDIT 9 /* auditing */ 19 | #define NETLINK_FIB_LOOKUP 10 20 | #define NETLINK_CONNECTOR 11 21 | #define NETLINK_NETFILTER 12 /* netfilter subsystem */ 22 | #define NETLINK_IP6_FW 13 23 | #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ 24 | #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ 25 | #define NETLINK_GENERIC 16 26 | /* leave room for NETLINK_DM (DM Events) */ 27 | #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ 28 | #define NETLINK_ECRYPTFS 19 29 | #define NETLINK_RDMA 20 30 | #define NETLINK_CRYPTO 21 /* Crypto layer */ 31 | #define NETLINK_SMC 22 /* SMC monitoring */ 32 | 33 | #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG 34 | 35 | #define MAX_LINKS 32 36 | 37 | struct sockaddr_nl { 38 | __kernel_sa_family_t nl_family; /* AF_NETLINK */ 39 | unsigned short nl_pad; /* zero */ 40 | __u32 nl_pid; /* port ID */ 41 | __u32 nl_groups; /* multicast groups mask */ 42 | }; 43 | 44 | struct nlmsghdr { 45 | __u32 nlmsg_len; /* Length of message including header */ 46 | __u16 nlmsg_type; /* Message content */ 47 | __u16 nlmsg_flags; /* Additional flags */ 48 | __u32 nlmsg_seq; /* Sequence number */ 49 | __u32 nlmsg_pid; /* Sending process port ID */ 50 | }; 51 | 52 | /* Flags values */ 53 | 54 | #define NLM_F_REQUEST 0x01 /* It is request message. */ 55 | #define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ 56 | #define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ 57 | #define NLM_F_ECHO 0x08 /* Echo this request */ 58 | #define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ 59 | #define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ 60 | 61 | /* Modifiers to GET request */ 62 | #define NLM_F_ROOT 0x100 /* specify tree root */ 63 | #define NLM_F_MATCH 0x200 /* return all matching */ 64 | #define NLM_F_ATOMIC 0x400 /* atomic GET */ 65 | #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) 66 | 67 | /* Modifiers to NEW request */ 68 | #define NLM_F_REPLACE 0x100 /* Override existing */ 69 | #define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ 70 | #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ 71 | #define NLM_F_APPEND 0x800 /* Add to end of list */ 72 | 73 | /* Modifiers to DELETE request */ 74 | #define NLM_F_NONREC 0x100 /* Do not delete recursively */ 75 | 76 | /* Flags for ACK message */ 77 | #define NLM_F_CAPPED 0x100 /* request was capped */ 78 | #define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ 79 | 80 | /* 81 | 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL 82 | 4.4BSD CHANGE NLM_F_REPLACE 83 | 84 | True CHANGE NLM_F_CREATE|NLM_F_REPLACE 85 | Append NLM_F_CREATE 86 | Check NLM_F_EXCL 87 | */ 88 | 89 | #define NLMSG_ALIGNTO 4U 90 | #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) 91 | #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) 92 | #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) 93 | #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) 94 | #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) 95 | #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ 96 | (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) 97 | #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ 98 | (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ 99 | (nlh)->nlmsg_len <= (len)) 100 | #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) 101 | 102 | #define NLMSG_NOOP 0x1 /* Nothing. */ 103 | #define NLMSG_ERROR 0x2 /* Error */ 104 | #define NLMSG_DONE 0x3 /* End of a dump */ 105 | #define NLMSG_OVERRUN 0x4 /* Data lost */ 106 | 107 | #define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ 108 | 109 | struct nlmsgerr { 110 | int error; 111 | struct nlmsghdr msg; 112 | /* 113 | * followed by the message contents unless NETLINK_CAP_ACK was set 114 | * or the ACK indicates success (error == 0) 115 | * message length is aligned with NLMSG_ALIGN() 116 | */ 117 | /* 118 | * followed by TLVs defined in enum nlmsgerr_attrs 119 | * if NETLINK_EXT_ACK was set 120 | */ 121 | }; 122 | 123 | /** 124 | * enum nlmsgerr_attrs - nlmsgerr attributes 125 | * @NLMSGERR_ATTR_UNUSED: unused 126 | * @NLMSGERR_ATTR_MSG: error message string (string) 127 | * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original 128 | * message, counting from the beginning of the header (u32) 129 | * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to 130 | * be used - in the success case - to identify a created 131 | * object or operation or similar (binary) 132 | * @__NLMSGERR_ATTR_MAX: number of attributes 133 | * @NLMSGERR_ATTR_MAX: highest attribute number 134 | */ 135 | enum nlmsgerr_attrs { 136 | NLMSGERR_ATTR_UNUSED, 137 | NLMSGERR_ATTR_MSG, 138 | NLMSGERR_ATTR_OFFS, 139 | NLMSGERR_ATTR_COOKIE, 140 | 141 | __NLMSGERR_ATTR_MAX, 142 | NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 143 | }; 144 | 145 | #define NETLINK_ADD_MEMBERSHIP 1 146 | #define NETLINK_DROP_MEMBERSHIP 2 147 | #define NETLINK_PKTINFO 3 148 | #define NETLINK_BROADCAST_ERROR 4 149 | #define NETLINK_NO_ENOBUFS 5 150 | #ifndef __KERNEL__ 151 | #define NETLINK_RX_RING 6 152 | #define NETLINK_TX_RING 7 153 | #endif 154 | #define NETLINK_LISTEN_ALL_NSID 8 155 | #define NETLINK_LIST_MEMBERSHIPS 9 156 | #define NETLINK_CAP_ACK 10 157 | #define NETLINK_EXT_ACK 11 158 | #define NETLINK_GET_STRICT_CHK 12 159 | 160 | struct nl_pktinfo { 161 | __u32 group; 162 | }; 163 | 164 | struct nl_mmap_req { 165 | unsigned int nm_block_size; 166 | unsigned int nm_block_nr; 167 | unsigned int nm_frame_size; 168 | unsigned int nm_frame_nr; 169 | }; 170 | 171 | struct nl_mmap_hdr { 172 | unsigned int nm_status; 173 | unsigned int nm_len; 174 | __u32 nm_group; 175 | /* credentials */ 176 | __u32 nm_pid; 177 | __u32 nm_uid; 178 | __u32 nm_gid; 179 | }; 180 | 181 | #ifndef __KERNEL__ 182 | enum nl_mmap_status { 183 | NL_MMAP_STATUS_UNUSED, 184 | NL_MMAP_STATUS_RESERVED, 185 | NL_MMAP_STATUS_VALID, 186 | NL_MMAP_STATUS_COPY, 187 | NL_MMAP_STATUS_SKIP, 188 | }; 189 | 190 | #define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO 191 | #define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) 192 | #define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) 193 | #endif 194 | 195 | #define NET_MAJOR 36 /* Major 36 is reserved for networking */ 196 | 197 | enum { 198 | NETLINK_UNCONNECTED = 0, 199 | NETLINK_CONNECTED, 200 | }; 201 | 202 | /* 203 | * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> 204 | * +---------------------+- - -+- - - - - - - - - -+- - -+ 205 | * | Header | Pad | Payload | Pad | 206 | * | (struct nlattr) | ing | | ing | 207 | * +---------------------+- - -+- - - - - - - - - -+- - -+ 208 | * <-------------- nlattr->nla_len --------------> 209 | */ 210 | 211 | struct nlattr { 212 | __u16 nla_len; 213 | __u16 nla_type; 214 | }; 215 | 216 | /* 217 | * nla_type (16 bits) 218 | * +---+---+-------------------------------+ 219 | * | N | O | Attribute Type | 220 | * +---+---+-------------------------------+ 221 | * N := Carries nested attributes 222 | * O := Payload stored in network byte order 223 | * 224 | * Note: The N and O flag are mutually exclusive. 225 | */ 226 | #define NLA_F_NESTED (1 << 15) 227 | #define NLA_F_NET_BYTEORDER (1 << 14) 228 | #define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) 229 | 230 | #define NLA_ALIGNTO 4 231 | #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) 232 | #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) 233 | 234 | /* Generic 32 bitflags attribute content sent to the kernel. 235 | * 236 | * The value is a bitmap that defines the values being set 237 | * The selector is a bitmask that defines which value is legit 238 | * 239 | * Examples: 240 | * value = 0x0, and selector = 0x1 241 | * implies we are selecting bit 1 and we want to set its value to 0. 242 | * 243 | * value = 0x2, and selector = 0x2 244 | * implies we are selecting bit 2 and we want to set its value to 1. 245 | * 246 | */ 247 | struct nla_bitfield32 { 248 | __u32 value; 249 | __u32 selector; 250 | }; 251 | 252 | #endif /* _UAPI__LINUX_NETLINK_H */ 253 | -------------------------------------------------------------------------------- /elf/kernel_version.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Copyright 2016-2017 Kinvolk 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package elf 18 | 19 | import ( 20 | "fmt" 21 | "io/ioutil" 22 | "regexp" 23 | "strconv" 24 | "strings" 25 | "syscall" 26 | ) 27 | 28 | var versionRegex = regexp.MustCompile(`^(\d+)\.(\d+).(\d+).*$`) 29 | 30 | // KernelVersionFromReleaseString converts a release string with format 31 | // 4.4.2[-1] to a kernel version number in LINUX_VERSION_CODE format. 32 | // That is, for kernel "a.b.c", the version number will be (a<<16 + b<<8 + c) 33 | func KernelVersionFromReleaseString(releaseString string) (uint32, error) { 34 | versionParts := versionRegex.FindStringSubmatch(releaseString) 35 | if len(versionParts) != 4 { 36 | return 0, fmt.Errorf("got invalid release version %q (expected format '4.3.2-1')", releaseString) 37 | } 38 | major, err := strconv.Atoi(versionParts[1]) 39 | if err != nil { 40 | return 0, err 41 | } 42 | 43 | minor, err := strconv.Atoi(versionParts[2]) 44 | if err != nil { 45 | return 0, err 46 | } 47 | 48 | patch, err := strconv.Atoi(versionParts[3]) 49 | if err != nil { 50 | return 0, err 51 | } 52 | out := major*256*256 + minor*256 + patch 53 | return uint32(out), nil 54 | } 55 | 56 | func currentVersionUname() (uint32, error) { 57 | var buf syscall.Utsname 58 | if err := syscall.Uname(&buf); err != nil { 59 | return 0, err 60 | } 61 | releaseString := strings.Trim(utsnameStr(buf.Release[:]), "\x00") 62 | return KernelVersionFromReleaseString(releaseString) 63 | } 64 | 65 | func currentVersionUbuntu() (uint32, error) { 66 | procVersion, err := ioutil.ReadFile("/proc/version_signature") 67 | if err != nil { 68 | return 0, err 69 | } 70 | var u1, u2, releaseString string 71 | _, err = fmt.Sscanf(string(procVersion), "%s %s %s", &u1, &u2, &releaseString) 72 | if err != nil { 73 | return 0, err 74 | } 75 | return KernelVersionFromReleaseString(releaseString) 76 | } 77 | 78 | var debianVersionRegex = regexp.MustCompile(`.* SMP Debian (\d+\.\d+.\d+-\d+)(?:\+[[:alnum:]]*)?.*`) 79 | 80 | func parseDebianVersion(str string) (uint32, error) { 81 | match := debianVersionRegex.FindStringSubmatch(str) 82 | if len(match) != 2 { 83 | return 0, fmt.Errorf("failed to parse kernel version from /proc/version: %s", str) 84 | } 85 | return KernelVersionFromReleaseString(match[1]) 86 | } 87 | 88 | func currentVersionDebian() (uint32, error) { 89 | procVersion, err := ioutil.ReadFile("/proc/version") 90 | if err != nil { 91 | return 0, fmt.Errorf("error reading /proc/version: %s", err) 92 | } 93 | 94 | return parseDebianVersion(string(procVersion)) 95 | } 96 | 97 | // CurrentKernelVersion returns the current kernel version in 98 | // LINUX_VERSION_CODE format (see KernelVersionFromReleaseString()) 99 | func CurrentKernelVersion() (uint32, error) { 100 | // We need extra checks for Debian and Ubuntu as they modify 101 | // the kernel version patch number for compatibilty with 102 | // out-of-tree modules. Linux perf tools do the same for Ubuntu 103 | // systems: https://github.com/torvalds/linux/commit/d18acd15c 104 | // 105 | // See also: 106 | // https://kernel-handbook.alioth.debian.org/ch-versions.html 107 | // https://wiki.ubuntu.com/Kernel/FAQ 108 | version, err := currentVersionUbuntu() 109 | if err == nil { 110 | return version, nil 111 | } 112 | version, err = currentVersionDebian() 113 | if err == nil { 114 | return version, nil 115 | } 116 | return currentVersionUname() 117 | } 118 | -------------------------------------------------------------------------------- /elf/kernel_version_test.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Copyright 2017 Kinvolk 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package elf 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | var testData = []struct { 24 | succeed bool 25 | releaseString string 26 | kernelVersion uint32 27 | }{ 28 | {true, "4.1.2-3", 262402}, 29 | {true, "4.8.14-200.fc24.x86_64", 264206}, 30 | {true, "4.1.2-3foo", 262402}, 31 | {true, "4.1.2foo-1", 262402}, 32 | {true, "4.1.2-rkt-v1", 262402}, 33 | {true, "4.1.2rkt-v1", 262402}, 34 | {true, "4.1.2-3 foo", 262402}, 35 | {false, "foo 4.1.2-3", 0}, 36 | {true, "4.1.2", 262402}, 37 | {false, ".4.1.2", 0}, 38 | {false, "4.1.", 0}, 39 | {false, "4.1", 0}, 40 | } 41 | 42 | func TestKernelVersionFromReleaseString(t *testing.T) { 43 | for _, test := range testData { 44 | version, err := KernelVersionFromReleaseString(test.releaseString) 45 | if err != nil && test.succeed { 46 | t.Errorf("expected %q to succeed: %s", test.releaseString, err) 47 | } else if err == nil && !test.succeed { 48 | t.Errorf("expected %q to fail", test.releaseString) 49 | } 50 | if version != test.kernelVersion { 51 | t.Errorf("expected kernel version %d, got %d", test.kernelVersion, version) 52 | } 53 | } 54 | } 55 | 56 | func TestParseDebianVersion(t *testing.T) { 57 | for _, tc := range []struct { 58 | succeed bool 59 | releaseString string 60 | kernelVersion uint32 61 | }{ 62 | // 4.9.168 63 | {true, "Linux version 4.9.0-9-amd64 (debian-kernel@lists.debian.org) (gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) ) #1 SMP Debian 4.9.168-1+deb9u3 (2019-06-16)", 264616}, 64 | // 4.9.88 65 | {true, "Linux ip-10-0-75-49 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64 GNU/Linux", 264536}, 66 | // 3.0.4 67 | {true, "Linux version 3.16.0-9-amd64 (debian-kernel@lists.debian.org) (gcc version 4.9.2 (Debian 4.9.2-10+deb8u2) ) #1 SMP Debian 3.16.68-1 (2019-05-22)", 200772}, 68 | // Invalid 69 | {false, "Linux version 4.9.125-linuxkit (root@659b6d51c354) (gcc version 6.4.0 (Alpine 6.4.0) ) #1 SMP Fri Sep 7 08:20:28 UTC 2018", 0}, 70 | } { 71 | version, err := parseDebianVersion(tc.releaseString) 72 | if err != nil && tc.succeed { 73 | t.Errorf("expected %q to succeed: %s", tc.releaseString, err) 74 | } else if err == nil && !tc.succeed { 75 | t.Errorf("expected %q to fail", tc.releaseString) 76 | } 77 | if version != tc.kernelVersion { 78 | t.Errorf("expected kernel version %d, got %d", tc.kernelVersion, version) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /elf/module_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | package elf 4 | 5 | import ( 6 | "io" 7 | "unsafe" 8 | ) 9 | 10 | type Module struct{} 11 | type Kprobe struct{} 12 | type CgroupProgram struct{} 13 | type AttachType struct{} 14 | type CloseOptions struct{} 15 | type SocketFilter struct{} 16 | type TracepointProgram struct{} 17 | type SchedProgram struct{} 18 | 19 | func NewModule(fileName string) *Module { 20 | return nil 21 | } 22 | 23 | func NewModuleFromReader(fileReader io.ReaderAt) *Module { 24 | return nil 25 | } 26 | 27 | func (b *Module) EnableKprobe(secName string, maxactive int) error { 28 | return errNotSupported 29 | } 30 | 31 | func (b *Module) IterKprobes() <-chan *Kprobe { 32 | return nil 33 | } 34 | 35 | func (b *Module) EnableKprobes(maxactive int) error { 36 | return errNotSupported 37 | } 38 | 39 | func (b *Module) IterCgroupProgram() <-chan *CgroupProgram { 40 | return nil 41 | } 42 | 43 | func (b *Module) CgroupProgram(name string) *CgroupProgram { 44 | return nil 45 | } 46 | 47 | func (b *Module) Kprobe(name string) *Kprobe { 48 | return nil 49 | } 50 | 51 | func (b *Module) AttachProgram(cgroupProg *CgroupProgram, cgroupPath string, attachType AttachType) error { 52 | return errNotSupported 53 | } 54 | 55 | func (b *Module) Close() error { 56 | return errNotSupported 57 | } 58 | 59 | func (b *Module) CloseExt(options map[string]CloseOptions) error { 60 | return errNotSupported 61 | } 62 | 63 | func (b *Module) DeleteElement(mp *Map, key unsafe.Pointer) error { 64 | return errNotSupported 65 | } 66 | 67 | func (b *Module) EnableTracepoint(secName string) error { 68 | return errNotSupported 69 | } 70 | 71 | func (b *Module) IterMaps() <-chan *Map { 72 | return nil 73 | } 74 | 75 | func (b *Module) IterSocketFilter() <-chan *SocketFilter { 76 | return nil 77 | } 78 | 79 | func (b *Module) IterTracepointProgram() <-chan *TracepointProgram { 80 | return nil 81 | } 82 | 83 | func (b *Module) Log() []byte { 84 | return nil 85 | } 86 | 87 | func (b *Module) LookupElement(mp *Map, key, value unsafe.Pointer) error { 88 | return errNotSupported 89 | } 90 | 91 | func (b *Module) LookupNextElement(mp *Map, key, nextKey, value unsafe.Pointer) (bool, error) { 92 | return false, errNotSupported 93 | } 94 | 95 | func (b *Module) Map(name string) *Map { 96 | return nil 97 | } 98 | 99 | func (b *Module) SchedProgram(name string) *SchedProgram { 100 | return nil 101 | } 102 | 103 | func (b *Module) SocketFilter(name string) *SocketFilter { 104 | return nil 105 | } 106 | 107 | func (b *Module) UpdateElement(mp *Map, key, value unsafe.Pointer, flags uint64) error { 108 | return errNotSupported 109 | } 110 | -------------------------------------------------------------------------------- /elf/netlink.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2018 Facebook */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include "libbpf.h" 16 | #include "nlattr.h" 17 | 18 | #ifndef SOL_NETLINK 19 | #define SOL_NETLINK 270 20 | #endif 21 | 22 | typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 23 | void *cookie); 24 | 25 | static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, 26 | __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, 27 | void *cookie) 28 | { 29 | bool multipart = true; 30 | struct nlmsgerr *err; 31 | struct nlmsghdr *nh; 32 | char buf[4096]; 33 | int len, ret; 34 | 35 | while (multipart) { 36 | multipart = false; 37 | len = recv(sock, buf, sizeof(buf), 0); 38 | if (len < 0) { 39 | ret = -errno; 40 | goto done; 41 | } 42 | 43 | if (len == 0) 44 | break; 45 | 46 | for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); 47 | nh = NLMSG_NEXT(nh, len)) { 48 | if (nh->nlmsg_pid != nl_pid) { 49 | ret = -LIBBPF_ERRNO__WRNGPID; 50 | goto done; 51 | } 52 | if (nh->nlmsg_seq != seq) { 53 | ret = -LIBBPF_ERRNO__INVSEQ; 54 | goto done; 55 | } 56 | if (nh->nlmsg_flags & NLM_F_MULTI) 57 | multipart = true; 58 | switch (nh->nlmsg_type) { 59 | case NLMSG_ERROR: 60 | err = (struct nlmsgerr *)NLMSG_DATA(nh); 61 | if (!err->error) 62 | continue; 63 | ret = err->error; 64 | libbpf_nla_dump_errormsg(nh); 65 | goto done; 66 | case NLMSG_DONE: 67 | return 0; 68 | default: 69 | break; 70 | } 71 | if (_fn) { 72 | ret = _fn(nh, fn, cookie); 73 | if (ret) 74 | return ret; 75 | } 76 | } 77 | } 78 | ret = 0; 79 | done: 80 | return ret; 81 | } 82 | 83 | int libbpf_netlink_open(__u32 *nl_pid) 84 | { 85 | struct sockaddr_nl sa; 86 | socklen_t addrlen; 87 | int one = 1, ret; 88 | int sock; 89 | 90 | memset(&sa, 0, sizeof(sa)); 91 | sa.nl_family = AF_NETLINK; 92 | 93 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 94 | if (sock < 0) 95 | return -errno; 96 | 97 | if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 98 | &one, sizeof(one)) < 0) { 99 | fprintf(stderr, "Netlink error reporting not supported\n"); 100 | } 101 | 102 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 103 | ret = -errno; 104 | goto cleanup; 105 | } 106 | 107 | addrlen = sizeof(sa); 108 | if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 109 | ret = -errno; 110 | goto cleanup; 111 | } 112 | 113 | if (addrlen != sizeof(sa)) { 114 | ret = -LIBBPF_ERRNO__INTERNAL; 115 | goto cleanup; 116 | } 117 | 118 | *nl_pid = sa.nl_pid; 119 | return sock; 120 | 121 | cleanup: 122 | close(sock); 123 | return ret; 124 | } 125 | 126 | int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 127 | { 128 | int sock, seq = 0, ret; 129 | struct nlattr *nla, *nla_xdp; 130 | struct { 131 | struct nlmsghdr nh; 132 | struct ifinfomsg ifinfo; 133 | char attrbuf[64]; 134 | } req; 135 | __u32 nl_pid; 136 | 137 | sock = libbpf_netlink_open(&nl_pid); 138 | if (sock < 0) 139 | return sock; 140 | 141 | memset(&req, 0, sizeof(req)); 142 | req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 143 | req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 144 | req.nh.nlmsg_type = RTM_SETLINK; 145 | req.nh.nlmsg_pid = 0; 146 | req.nh.nlmsg_seq = ++seq; 147 | req.ifinfo.ifi_family = AF_UNSPEC; 148 | req.ifinfo.ifi_index = ifindex; 149 | 150 | nla = (struct nlattr *)(((char *)&req) + NLMSG_ALIGN(req.nh.nlmsg_len)); 151 | nla->nla_type = NLA_F_NESTED | IFLA_XDP; 152 | nla->nla_len = NLA_HDRLEN; 153 | 154 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 155 | nla_xdp->nla_type = IFLA_XDP_FD; 156 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); 157 | memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); 158 | nla->nla_len += nla_xdp->nla_len; 159 | 160 | if (flags) { 161 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 162 | nla_xdp->nla_type = IFLA_XDP_FLAGS; 163 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); 164 | memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); 165 | nla->nla_len += nla_xdp->nla_len; 166 | } 167 | 168 | req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); 169 | 170 | if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { 171 | ret = -errno; 172 | goto cleanup; 173 | } 174 | ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); 175 | 176 | cleanup: 177 | close(sock); 178 | return ret; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /elf/nlattr.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | 3 | /* 4 | * NETLINK Netlink attributes 5 | * 6 | * Copyright (c) 2003-2013 Thomas Graf 7 | */ 8 | 9 | #include 10 | #include "libbpf.h" 11 | #include "nlattr.h" 12 | #include 13 | #include 14 | #include 15 | 16 | typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 17 | void *cookie); 18 | 19 | static uint16_t nla_attr_minlen[LIBBPF_NLA_TYPE_MAX+1] = { 20 | [LIBBPF_NLA_U8] = sizeof(uint8_t), 21 | [LIBBPF_NLA_U16] = sizeof(uint16_t), 22 | [LIBBPF_NLA_U32] = sizeof(uint32_t), 23 | [LIBBPF_NLA_U64] = sizeof(uint64_t), 24 | [LIBBPF_NLA_STRING] = 1, 25 | [LIBBPF_NLA_FLAG] = 0, 26 | }; 27 | 28 | static struct nlattr *nla_next(const struct nlattr *nla, int *remaining) 29 | { 30 | int totlen = NLA_ALIGN(nla->nla_len); 31 | 32 | *remaining -= totlen; 33 | return (struct nlattr *) ((char *) nla + totlen); 34 | } 35 | 36 | static int nla_ok(const struct nlattr *nla, int remaining) 37 | { 38 | return remaining >= sizeof(*nla) && 39 | nla->nla_len >= sizeof(*nla) && 40 | nla->nla_len <= remaining; 41 | } 42 | 43 | static int nla_type(const struct nlattr *nla) 44 | { 45 | return nla->nla_type & NLA_TYPE_MASK; 46 | } 47 | 48 | static int validate_nla(struct nlattr *nla, int maxtype, 49 | struct libbpf_nla_policy *policy) 50 | { 51 | struct libbpf_nla_policy *pt; 52 | unsigned int minlen = 0; 53 | int type = nla_type(nla); 54 | 55 | if (type < 0 || type > maxtype) 56 | return 0; 57 | 58 | pt = &policy[type]; 59 | 60 | if (pt->type > LIBBPF_NLA_TYPE_MAX) 61 | return 0; 62 | 63 | if (pt->minlen) 64 | minlen = pt->minlen; 65 | else if (pt->type != LIBBPF_NLA_UNSPEC) 66 | minlen = nla_attr_minlen[pt->type]; 67 | 68 | if (libbpf_nla_len(nla) < minlen) 69 | return -1; 70 | 71 | if (pt->maxlen && libbpf_nla_len(nla) > pt->maxlen) 72 | return -1; 73 | 74 | if (pt->type == LIBBPF_NLA_STRING) { 75 | char *data = libbpf_nla_data(nla); 76 | 77 | if (data[libbpf_nla_len(nla) - 1] != '\0') 78 | return -1; 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | static inline int nlmsg_len(const struct nlmsghdr *nlh) 85 | { 86 | return nlh->nlmsg_len - NLMSG_HDRLEN; 87 | } 88 | 89 | /** 90 | * Create attribute index based on a stream of attributes. 91 | * @arg tb Index array to be filled (maxtype+1 elements). 92 | * @arg maxtype Maximum attribute type expected and accepted. 93 | * @arg head Head of attribute stream. 94 | * @arg len Length of attribute stream. 95 | * @arg policy Attribute validation policy. 96 | * 97 | * Iterates over the stream of attributes and stores a pointer to each 98 | * attribute in the index array using the attribute type as index to 99 | * the array. Attribute with a type greater than the maximum type 100 | * specified will be silently ignored in order to maintain backwards 101 | * compatibility. If \a policy is not NULL, the attribute will be 102 | * validated using the specified policy. 103 | * 104 | * @see nla_validate 105 | * @return 0 on success or a negative error code. 106 | */ 107 | int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, 108 | int len, struct libbpf_nla_policy *policy) 109 | { 110 | struct nlattr *nla; 111 | int rem, err; 112 | 113 | memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); 114 | 115 | libbpf_nla_for_each_attr(nla, head, len, rem) { 116 | int type = nla_type(nla); 117 | 118 | if (type > maxtype) 119 | continue; 120 | 121 | if (policy) { 122 | err = validate_nla(nla, maxtype, policy); 123 | if (err < 0) 124 | goto errout; 125 | } 126 | 127 | if (tb[type]) 128 | fprintf(stderr, "Attribute of type %#x found multiple times in message, " 129 | "previous attribute is being ignored.\n", type); 130 | 131 | tb[type] = nla; 132 | } 133 | 134 | err = 0; 135 | errout: 136 | return err; 137 | } 138 | 139 | /** 140 | * Create attribute index based on nested attribute 141 | * @arg tb Index array to be filled (maxtype+1 elements). 142 | * @arg maxtype Maximum attribute type expected and accepted. 143 | * @arg nla Nested Attribute. 144 | * @arg policy Attribute validation policy. 145 | * 146 | * Feeds the stream of attributes nested into the specified attribute 147 | * to libbpf_nla_parse(). 148 | * 149 | * @see libbpf_nla_parse 150 | * @return 0 on success or a negative error code. 151 | */ 152 | int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype, 153 | struct nlattr *nla, 154 | struct libbpf_nla_policy *policy) 155 | { 156 | return libbpf_nla_parse(tb, maxtype, libbpf_nla_data(nla), 157 | libbpf_nla_len(nla), policy); 158 | } 159 | 160 | /* dump netlink extended ack error message */ 161 | int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh) 162 | { 163 | struct libbpf_nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = { 164 | [NLMSGERR_ATTR_MSG] = { .type = LIBBPF_NLA_STRING }, 165 | [NLMSGERR_ATTR_OFFS] = { .type = LIBBPF_NLA_U32 }, 166 | }; 167 | struct nlattr *tb[NLMSGERR_ATTR_MAX + 1], *attr; 168 | struct nlmsgerr *err; 169 | char *errmsg = NULL; 170 | int hlen, alen; 171 | 172 | /* no TLVs, nothing to do here */ 173 | if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) 174 | return 0; 175 | 176 | err = (struct nlmsgerr *)NLMSG_DATA(nlh); 177 | hlen = sizeof(*err); 178 | 179 | /* if NLM_F_CAPPED is set then the inner err msg was capped */ 180 | if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) 181 | hlen += nlmsg_len(&err->msg); 182 | 183 | attr = (struct nlattr *) ((void *) err + hlen); 184 | alen = nlh->nlmsg_len - hlen; 185 | 186 | if (libbpf_nla_parse(tb, NLMSGERR_ATTR_MAX, attr, alen, 187 | extack_policy) != 0) { 188 | fprintf(stderr, 189 | "Failed to parse extended error attributes\n"); 190 | return 0; 191 | } 192 | 193 | if (tb[NLMSGERR_ATTR_MSG]) 194 | errmsg = (char *) libbpf_nla_data(tb[NLMSGERR_ATTR_MSG]); 195 | 196 | fprintf(stderr, "Kernel error message: %s\n", errmsg); 197 | 198 | return 0; 199 | } 200 | 201 | -------------------------------------------------------------------------------- /elf/perf.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Copyright 2016 Cilium Project 4 | // Copyright 2016 Sylvain Afchain 5 | // Copyright 2016 Kinvolk 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | package elf 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | "sort" 25 | "syscall" 26 | "unsafe" 27 | 28 | "github.com/iovisor/gobpf/pkg/cpuonline" 29 | ) 30 | 31 | /* 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | // from https://github.com/cilium/cilium/blob/master/pkg/bpf/perf.go 41 | 42 | struct event_sample { 43 | struct perf_event_header header; 44 | uint32_t size; 45 | uint8_t data[]; 46 | }; 47 | 48 | struct read_state { 49 | void *buf; 50 | int buf_len; 51 | // These two fields are for backward reading: as opposed to normal ring buffers, 52 | // backward read buffers don't update the read pointer when reading. 53 | // So we keep the state externally here. 54 | uint64_t data_head_initialized; 55 | uint64_t data_head; 56 | uint64_t wrapped; 57 | }; 58 | 59 | static int perf_event_read(int page_count, int page_size, void *_state, 60 | void *_header, void *_sample_ptr, void *_lost_ptr) 61 | { 62 | volatile struct perf_event_mmap_page *header = _header; 63 | uint64_t data_head = *((volatile uint64_t *) &header->data_head); 64 | uint64_t data_tail = header->data_tail; 65 | uint64_t raw_size = (uint64_t)page_count * page_size; 66 | void *base = ((uint8_t *)header) + page_size; 67 | struct read_state *state = _state; 68 | struct event_sample *e; 69 | void *begin, *end; 70 | void **sample_ptr = (void **) _sample_ptr; 71 | void **lost_ptr = (void **) _lost_ptr; 72 | 73 | // No data to read on this ring 74 | __sync_synchronize(); 75 | if (data_head == data_tail) 76 | return 0; 77 | 78 | begin = base + data_tail % raw_size; 79 | e = begin; 80 | end = base + (data_tail + e->header.size) % raw_size; 81 | 82 | if (state->buf_len < e->header.size || !state->buf) { 83 | state->buf = realloc(state->buf, e->header.size); 84 | state->buf_len = e->header.size; 85 | } 86 | 87 | if (end < begin) { 88 | uint64_t len = base + raw_size - begin; 89 | 90 | memcpy(state->buf, begin, len); 91 | memcpy((char *) state->buf + len, base, e->header.size - len); 92 | 93 | e = state->buf; 94 | } else { 95 | memcpy(state->buf, begin, e->header.size); 96 | } 97 | 98 | switch (e->header.type) { 99 | case PERF_RECORD_SAMPLE: 100 | *sample_ptr = state->buf; 101 | break; 102 | case PERF_RECORD_LOST: 103 | *lost_ptr = state->buf; 104 | break; 105 | } 106 | 107 | __sync_synchronize(); 108 | header->data_tail += e->header.size; 109 | 110 | return e->header.type; 111 | } 112 | 113 | static int perf_event_dump_backward(int page_count, int page_size, void *_state, 114 | void *_header, void *_sample_ptr) 115 | { 116 | volatile struct perf_event_mmap_page *header = _header; 117 | uint64_t data_head = header->data_head; 118 | uint64_t raw_size = (uint64_t)page_count * page_size; 119 | void *base = ((uint8_t *)header) + page_size; 120 | struct read_state *state = _state; 121 | struct perf_event_header *p, *head; 122 | void **sample_ptr = (void **) _sample_ptr; 123 | void *begin, *end; 124 | uint64_t new_head; 125 | 126 | if (state->data_head_initialized == 0) { 127 | state->data_head_initialized = 1; 128 | state->data_head = data_head & (raw_size - 1); 129 | } 130 | 131 | if ((state->wrapped && state->data_head >= data_head) || state->wrapped > 1) { 132 | return 0; 133 | } 134 | 135 | begin = p = base + state->data_head; 136 | 137 | if (p->type != PERF_RECORD_SAMPLE) 138 | return 0; 139 | 140 | new_head = (state->data_head + p->size) & (raw_size - 1); 141 | end = base + new_head; 142 | 143 | if (state->buf_len < p->size || !state->buf) { 144 | state->buf = realloc(state->buf, p->size); 145 | state->buf_len = p->size; 146 | } 147 | 148 | if (end < begin) { 149 | uint64_t len = base + raw_size - begin; 150 | 151 | memcpy(state->buf, begin, len); 152 | memcpy((char *) state->buf + len, base, p->size - len); 153 | } else { 154 | memcpy(state->buf, begin, p->size); 155 | } 156 | 157 | *sample_ptr = state->buf; 158 | 159 | if (new_head <= state->data_head) { 160 | state->wrapped++; 161 | } 162 | 163 | state->data_head = new_head; 164 | 165 | return p->type; 166 | } 167 | */ 168 | import "C" 169 | 170 | type PerfMap struct { 171 | name string 172 | program *Module 173 | pageCount int 174 | receiverChan chan []byte 175 | lostChan chan uint64 176 | pollStop chan struct{} 177 | timestamp func(*[]byte) uint64 178 | } 179 | 180 | // Matching 'struct perf_event_sample in kernel sources 181 | type PerfEventSample struct { 182 | PerfEventHeader 183 | Size uint32 184 | data byte // Size bytes of data 185 | } 186 | 187 | func InitPerfMap(b *Module, mapName string, receiverChan chan []byte, lostChan chan uint64) (*PerfMap, error) { 188 | m, ok := b.maps[mapName] 189 | if !ok { 190 | return nil, fmt.Errorf("no map with name %s", mapName) 191 | } 192 | if receiverChan == nil { 193 | return nil, fmt.Errorf("receiverChan is nil") 194 | } 195 | // Maps are initialized in b.Load(), nothing to do here 196 | return &PerfMap{ 197 | name: mapName, 198 | program: b, 199 | pageCount: m.pageCount, 200 | receiverChan: receiverChan, 201 | lostChan: lostChan, 202 | pollStop: make(chan struct{}), 203 | }, nil 204 | } 205 | 206 | func (pm *PerfMap) SwapAndDumpBackward() (out [][]byte) { 207 | m, ok := pm.program.maps[pm.name] 208 | if !ok { 209 | // should not happen or only when pm.program is 210 | // suddenly changed 211 | panic(fmt.Sprintf("cannot find map %q", pm.name)) 212 | } 213 | 214 | // step 1: create a new perf ring buffer 215 | pmuFds, headers, bases, err := createPerfRingBuffer(true, true, pm.pageCount) 216 | if err != nil { 217 | return 218 | } 219 | 220 | cpus, err := cpuonline.Get() 221 | if err != nil { 222 | return 223 | } 224 | 225 | // step 2: swap file descriptors 226 | // after it the ebpf programs will write to the new map 227 | for index, cpu := range cpus { 228 | // assign perf fd to map 229 | err := pm.program.UpdateElement(m, unsafe.Pointer(&cpu), unsafe.Pointer(&pmuFds[index]), 0) 230 | if err != nil { 231 | return 232 | } 233 | } 234 | 235 | // step 3: dump old buffer 236 | out = pm.DumpBackward() 237 | 238 | // step4: close old buffer 239 | // unmap 240 | for _, base := range m.bases { 241 | err := syscall.Munmap(base) 242 | if err != nil { 243 | return 244 | } 245 | } 246 | 247 | for _, fd := range m.pmuFDs { 248 | // disable 249 | _, _, err2 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), C.PERF_EVENT_IOC_DISABLE, 0) 250 | if err2 != 0 { 251 | return 252 | } 253 | 254 | // close 255 | if err := syscall.Close(int(fd)); err != nil { 256 | return 257 | } 258 | } 259 | 260 | // update file descriptors to new perf ring buffer 261 | m.pmuFDs = pmuFds 262 | m.headers = headers 263 | m.bases = bases 264 | 265 | return 266 | } 267 | 268 | func (pm *PerfMap) DumpBackward() (out [][]byte) { 269 | incoming := OrderedBytesArray{timestamp: pm.timestamp} 270 | 271 | m, ok := pm.program.maps[pm.name] 272 | if !ok { 273 | // should not happen or only when pm.program is 274 | // suddenly changed 275 | panic(fmt.Sprintf("cannot find map %q", pm.name)) 276 | } 277 | 278 | cpuCount := len(m.pmuFDs) 279 | pageSize := os.Getpagesize() 280 | for cpu := 0; cpu < cpuCount; cpu++ { 281 | state := C.struct_read_state{} 282 | ringBufferLoop: 283 | for { 284 | var sample *PerfEventSample 285 | ok := C.perf_event_dump_backward(C.int(pm.pageCount), C.int(pageSize), 286 | unsafe.Pointer(&state), unsafe.Pointer(m.headers[cpu]), 287 | unsafe.Pointer(&sample)) 288 | switch ok { 289 | case 0: 290 | break ringBufferLoop // nothing to read 291 | case C.PERF_RECORD_SAMPLE: 292 | size := sample.Size - 4 293 | b := C.GoBytes(unsafe.Pointer(&sample.data), C.int(size)) 294 | incoming.bytesArray = append(incoming.bytesArray, b) 295 | } 296 | } 297 | } 298 | 299 | if incoming.timestamp != nil { 300 | sort.Sort(incoming) 301 | } 302 | 303 | return incoming.bytesArray 304 | } 305 | 306 | // SetTimestampFunc registers a timestamp callback that will be used to 307 | // reorder the perf events chronologically. 308 | // 309 | // If not set, the order of events sent through receiverChan is not guaranteed. 310 | // 311 | // Typically, the ebpf program will use bpf_ktime_get_ns() to get a timestamp 312 | // and store it in the perf event. The perf event struct is opaque to this 313 | // package, hence the need for a callback. 314 | func (pm *PerfMap) SetTimestampFunc(timestamp func(*[]byte) uint64) { 315 | pm.timestamp = timestamp 316 | } 317 | 318 | func (pm *PerfMap) PollStart() { 319 | incoming := OrderedBytesArray{timestamp: pm.timestamp} 320 | 321 | m, ok := pm.program.maps[pm.name] 322 | if !ok { 323 | // should not happen or only when pm.program is 324 | // suddenly changed 325 | panic(fmt.Sprintf("cannot find map %q", pm.name)) 326 | } 327 | 328 | go func() { 329 | cpuCount := len(m.pmuFDs) 330 | pageSize := os.Getpagesize() 331 | state := C.struct_read_state{} 332 | 333 | defer func() { 334 | close(pm.receiverChan) 335 | if pm.lostChan != nil { 336 | close(pm.lostChan) 337 | } 338 | }() 339 | 340 | for { 341 | select { 342 | case <-pm.pollStop: 343 | break 344 | default: 345 | perfEventPoll(m.pmuFDs) 346 | } 347 | 348 | harvestLoop: 349 | for { 350 | select { 351 | case <-pm.pollStop: 352 | return 353 | default: 354 | } 355 | 356 | var harvestCount C.int 357 | beforeHarvest := NowNanoseconds() 358 | for cpu := 0; cpu < cpuCount; cpu++ { 359 | ringBufferLoop: 360 | for { 361 | var sample *PerfEventSample 362 | var lost *PerfEventLost 363 | 364 | ok := C.perf_event_read(C.int(pm.pageCount), C.int(pageSize), 365 | unsafe.Pointer(&state), unsafe.Pointer(m.headers[cpu]), 366 | unsafe.Pointer(&sample), unsafe.Pointer(&lost)) 367 | 368 | switch ok { 369 | case 0: 370 | break ringBufferLoop // nothing to read 371 | case C.PERF_RECORD_SAMPLE: 372 | size := sample.Size - 4 373 | b := C.GoBytes(unsafe.Pointer(&sample.data), C.int(size)) 374 | incoming.bytesArray = append(incoming.bytesArray, b) 375 | harvestCount++ 376 | if pm.timestamp == nil { 377 | continue ringBufferLoop 378 | } 379 | if incoming.timestamp(&b) > beforeHarvest { 380 | // see comment below 381 | break ringBufferLoop 382 | } 383 | case C.PERF_RECORD_LOST: 384 | if pm.lostChan != nil { 385 | select { 386 | case pm.lostChan <- lost.Lost: 387 | case <-pm.pollStop: 388 | return 389 | } 390 | } 391 | default: 392 | // ignore unknown events 393 | } 394 | } 395 | } 396 | 397 | if incoming.timestamp != nil { 398 | sort.Sort(incoming) 399 | } 400 | for incoming.Len() > 0 { 401 | if incoming.timestamp != nil && incoming.timestamp(&incoming.bytesArray[0]) > beforeHarvest { 402 | // This record has been sent after the beginning of the harvest. Stop 403 | // processing here to keep the order. "incoming" is sorted, so the next 404 | // elements also must not be processed now. 405 | break harvestLoop 406 | } 407 | select { 408 | case pm.receiverChan <- incoming.bytesArray[0]: 409 | case <-pm.pollStop: 410 | return 411 | } 412 | // remove first element 413 | incoming.bytesArray = incoming.bytesArray[1:] 414 | } 415 | if harvestCount == 0 && len(incoming.bytesArray) == 0 { 416 | break harvestLoop 417 | } 418 | } 419 | } 420 | }() 421 | } 422 | 423 | // PollStop stops the goroutine that polls the perf event map. 424 | // Callers must not close receiverChan or lostChan: they will be automatically 425 | // closed on the sender side. 426 | func (pm *PerfMap) PollStop() { 427 | close(pm.pollStop) 428 | } 429 | 430 | func perfEventPoll(fds []C.int) error { 431 | var pfds []C.struct_pollfd 432 | 433 | for i, _ := range fds { 434 | var pfd C.struct_pollfd 435 | 436 | pfd.fd = fds[i] 437 | pfd.events = C.POLLIN 438 | 439 | pfds = append(pfds, pfd) 440 | } 441 | _, err := C.poll(&pfds[0], C.nfds_t(len(fds)), 500) 442 | if err != nil { 443 | return fmt.Errorf("error polling: %v", err.(syscall.Errno)) 444 | } 445 | 446 | return nil 447 | } 448 | 449 | // Assume the timestamp is at the beginning of the user struct 450 | type OrderedBytesArray struct { 451 | bytesArray [][]byte 452 | timestamp func(*[]byte) uint64 453 | } 454 | 455 | func (a OrderedBytesArray) Len() int { 456 | return len(a.bytesArray) 457 | } 458 | 459 | func (a OrderedBytesArray) Swap(i, j int) { 460 | a.bytesArray[i], a.bytesArray[j] = a.bytesArray[j], a.bytesArray[i] 461 | } 462 | 463 | func (a OrderedBytesArray) Less(i, j int) bool { 464 | return a.timestamp(&a.bytesArray[i]) < a.timestamp(&a.bytesArray[j]) 465 | } 466 | 467 | // Matching 'struct perf_event_header in 468 | type PerfEventHeader struct { 469 | Type uint32 470 | Misc uint16 471 | TotalSize uint16 472 | } 473 | 474 | // Matching 'struct perf_event_lost in kernel sources 475 | type PerfEventLost struct { 476 | PerfEventHeader 477 | Id uint64 478 | Lost uint64 479 | } 480 | 481 | // NowNanoseconds returns a time that can be compared to bpf_ktime_get_ns() 482 | func NowNanoseconds() uint64 { 483 | var ts syscall.Timespec 484 | syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 1 /* CLOCK_MONOTONIC */, uintptr(unsafe.Pointer(&ts)), 0) 485 | sec, nsec := ts.Unix() 486 | return 1000*1000*1000*uint64(sec) + uint64(nsec) 487 | } 488 | -------------------------------------------------------------------------------- /elf/perf_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | package elf 4 | 5 | type PerfMap struct{} 6 | 7 | func InitPerfMap(b *Module, mapName string, receiverChan chan []byte, lostChan chan uint64) (*PerfMap, error) { 8 | return nil, errNotSupported 9 | } 10 | 11 | func (pm *PerfMap) SetTimestampFunc(timestamp func(*[]byte) uint64) {} 12 | 13 | func (pm *PerfMap) PollStart() {} 14 | 15 | func (pm *PerfMap) PollStop() {} 16 | 17 | func NowNanoseconds() uint64 { 18 | return 0 19 | } 20 | -------------------------------------------------------------------------------- /elf/pinning.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package elf 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "unsafe" 11 | 12 | "github.com/iovisor/gobpf/pkg/bpffs" 13 | ) 14 | 15 | /* 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | extern __u64 ptr_to_u64(void *); 23 | 24 | int bpf_pin_object(int fd, const char *pathname) 25 | { 26 | union bpf_attr attr; 27 | 28 | memset(&attr, 0, sizeof(attr)); 29 | attr.pathname = ptr_to_u64((void *)pathname); 30 | attr.bpf_fd = fd; 31 | 32 | return syscall(__NR_bpf, BPF_OBJ_PIN, &attr, sizeof(attr)); 33 | } 34 | */ 35 | import "C" 36 | 37 | const ( 38 | BPFDirGlobals = "globals" // as in iproute2's BPF_DIR_GLOBALS 39 | BPFFSPath = "/sys/fs/bpf/" 40 | ) 41 | 42 | func validPinPath(PinPath string) bool { 43 | if !strings.HasPrefix(PinPath, BPFFSPath) { 44 | return false 45 | } 46 | 47 | return filepath.Clean(PinPath) == PinPath 48 | } 49 | 50 | func pinObject(fd int, pinPath string) error { 51 | mounted, err := bpffs.IsMounted() 52 | if err != nil { 53 | return fmt.Errorf("error checking if %q is mounted: %v", BPFFSPath, err) 54 | } 55 | if !mounted { 56 | return fmt.Errorf("bpf fs not mounted at %q", BPFFSPath) 57 | } 58 | err = os.MkdirAll(filepath.Dir(pinPath), 0755) 59 | if err != nil { 60 | return fmt.Errorf("error creating directory %q: %v", filepath.Dir(pinPath), err) 61 | } 62 | _, err = os.Stat(pinPath) 63 | if err == nil { 64 | return fmt.Errorf("aborting, found file at %q", pinPath) 65 | } 66 | if err != nil && !os.IsNotExist(err) { 67 | return fmt.Errorf("failed to stat %q: %v", pinPath, err) 68 | } 69 | pinPathC := C.CString(pinPath) 70 | defer C.free(unsafe.Pointer(pinPathC)) 71 | ret, err := C.bpf_pin_object(C.int(fd), pinPathC) 72 | if ret != 0 { 73 | return fmt.Errorf("error pinning object to %q: %v", pinPath, err) 74 | } 75 | return nil 76 | } 77 | 78 | // PinObjectGlobal pins and object to a name in a namespaces 79 | // e.g. `/sys/fs/bpf/my-namespace/globals/my-name` 80 | func PinObjectGlobal(fd int, namespace, name string) error { 81 | pinPath := filepath.Join(BPFFSPath, namespace, BPFDirGlobals, name) 82 | return pinObject(fd, pinPath) 83 | } 84 | 85 | // PinObject pins an object to a path 86 | func PinObject(fd int, pinPath string) error { 87 | if !validPinPath(pinPath) { 88 | return fmt.Errorf("not a valid pin path: %s", pinPath) 89 | } 90 | return pinObject(fd, pinPath) 91 | } 92 | -------------------------------------------------------------------------------- /elf/table.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Copyright 2016 Cilium Project 4 | // Copyright 2016 Sylvain Afchain 5 | // Copyright 2016 Kinvolk 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | package elf 20 | 21 | import ( 22 | "fmt" 23 | "syscall" 24 | "unsafe" 25 | ) 26 | 27 | /* 28 | #include 29 | #include 30 | 31 | extern __u64 ptr_to_u64(void *); 32 | 33 | // from https://github.com/cilium/cilium/blob/master/pkg/bpf/bpf.go 34 | // Apache License, Version 2.0 35 | 36 | static void create_bpf_update_elem(int fd, void *key, void *value, 37 | unsigned long long flags, void *attr) 38 | { 39 | union bpf_attr* ptr_bpf_attr; 40 | ptr_bpf_attr = (union bpf_attr*)attr; 41 | ptr_bpf_attr->map_fd = fd; 42 | ptr_bpf_attr->key = ptr_to_u64(key); 43 | ptr_bpf_attr->value = ptr_to_u64(value); 44 | ptr_bpf_attr->flags = flags; 45 | } 46 | 47 | static void create_bpf_lookup_elem(int fd, void *key, void *value, void *attr) 48 | { 49 | union bpf_attr* ptr_bpf_attr; 50 | ptr_bpf_attr = (union bpf_attr*)attr; 51 | ptr_bpf_attr->map_fd = fd; 52 | ptr_bpf_attr->key = ptr_to_u64(key); 53 | ptr_bpf_attr->value = ptr_to_u64(value); 54 | } 55 | 56 | static int next_bpf_elem(int fd, void *key, void *next_key, void *attr) 57 | { 58 | union bpf_attr* ptr_bpf_attr; 59 | ptr_bpf_attr = (union bpf_attr*)attr; 60 | ptr_bpf_attr->map_fd = fd; 61 | ptr_bpf_attr->key = ptr_to_u64(key); 62 | ptr_bpf_attr->next_key = ptr_to_u64(next_key); 63 | } 64 | */ 65 | import "C" 66 | 67 | // UpdateElement stores value in key in the map stored in mp. 68 | // The flags can have the following values (if you include "uapi/linux/bpf.h"): 69 | // C.BPF_ANY to create new element or update existing; 70 | // C.BPF_NOEXIST to create new element if it didn't exist; 71 | // C.BPF_EXIST to update existing element. 72 | func (b *Module) UpdateElement(mp *Map, key, value unsafe.Pointer, flags uint64) error { 73 | uba := C.union_bpf_attr{} 74 | C.create_bpf_update_elem( 75 | C.int(mp.m.fd), 76 | key, 77 | value, 78 | C.ulonglong(flags), 79 | unsafe.Pointer(&uba), 80 | ) 81 | ret, _, err := syscall.Syscall( 82 | C.__NR_bpf, 83 | C.BPF_MAP_UPDATE_ELEM, 84 | uintptr(unsafe.Pointer(&uba)), 85 | unsafe.Sizeof(uba), 86 | ) 87 | 88 | if ret != 0 || err != 0 { 89 | return fmt.Errorf("unable to update element: %s", err) 90 | } 91 | 92 | return nil 93 | } 94 | 95 | // LookupElement looks up the given key in the the map stored in mp. 96 | // The value is stored in the value unsafe.Pointer. 97 | func (b *Module) LookupElement(mp *Map, key, value unsafe.Pointer) error { 98 | uba := C.union_bpf_attr{} 99 | C.create_bpf_lookup_elem( 100 | C.int(mp.m.fd), 101 | key, 102 | value, 103 | unsafe.Pointer(&uba), 104 | ) 105 | ret, _, err := syscall.Syscall( 106 | C.__NR_bpf, 107 | C.BPF_MAP_LOOKUP_ELEM, 108 | uintptr(unsafe.Pointer(&uba)), 109 | unsafe.Sizeof(uba), 110 | ) 111 | 112 | if ret != 0 || err != 0 { 113 | return fmt.Errorf("unable to lookup element: %s", err) 114 | } 115 | 116 | return nil 117 | } 118 | 119 | // LookupAndDeleteElement picks up and delete the element in the the map stored in mp. 120 | // The value is stored in the value unsafe.Pointer. 121 | func (b *Module) LookupAndDeleteElement(mp *Map, value unsafe.Pointer) error { 122 | uba := C.union_bpf_attr{} 123 | C.create_bpf_lookup_elem( 124 | C.int(mp.m.fd), 125 | unsafe.Pointer(nil), 126 | value, 127 | unsafe.Pointer(&uba), 128 | ) 129 | ret, _, err := syscall.Syscall( 130 | C.__NR_bpf, 131 | C.BPF_MAP_LOOKUP_AND_DELETE_ELEM, 132 | uintptr(unsafe.Pointer(&uba)), 133 | unsafe.Sizeof(uba), 134 | ) 135 | 136 | if ret != 0 || err != 0 { 137 | return fmt.Errorf("unable to lookup and delete element: %s", err) 138 | } 139 | 140 | return nil 141 | } 142 | 143 | // DeleteElement deletes the given key in the the map stored in mp. 144 | // The key is stored in the key unsafe.Pointer. 145 | func (b *Module) DeleteElement(mp *Map, key unsafe.Pointer) error { 146 | uba := C.union_bpf_attr{} 147 | value := unsafe.Pointer(nil) 148 | C.create_bpf_lookup_elem( 149 | C.int(mp.m.fd), 150 | key, 151 | value, 152 | unsafe.Pointer(&uba), 153 | ) 154 | ret, _, err := syscall.Syscall( 155 | C.__NR_bpf, 156 | C.BPF_MAP_DELETE_ELEM, 157 | uintptr(unsafe.Pointer(&uba)), 158 | unsafe.Sizeof(uba), 159 | ) 160 | 161 | if ret != 0 || err != 0 { 162 | return fmt.Errorf("unable to delete element: %s", err) 163 | } 164 | 165 | return nil 166 | } 167 | 168 | // LookupNextElement looks up the next element in mp using the given key. 169 | // The next key and the value are stored in the nextKey and value parameter. 170 | // Returns false at the end of the mp. 171 | func (b *Module) LookupNextElement(mp *Map, key, nextKey, value unsafe.Pointer) (bool, error) { 172 | uba := C.union_bpf_attr{} 173 | C.next_bpf_elem( 174 | C.int(mp.m.fd), 175 | key, 176 | nextKey, 177 | unsafe.Pointer(&uba), 178 | ) 179 | ret, _, err := syscall.Syscall( 180 | C.__NR_bpf, 181 | C.BPF_MAP_GET_NEXT_KEY, 182 | uintptr(unsafe.Pointer(&uba)), 183 | unsafe.Sizeof(uba), 184 | ) 185 | if err == syscall.ENOENT { 186 | return false, nil 187 | } 188 | if err != 0 { 189 | return false, fmt.Errorf("unable to find next element: %s", err) 190 | } 191 | if ret != 0 { 192 | return false, nil 193 | } 194 | 195 | if err := b.LookupElement(mp, nextKey, value); err != nil { 196 | return false, err 197 | } 198 | return true, nil 199 | } 200 | -------------------------------------------------------------------------------- /elf/utsname_int8.go: -------------------------------------------------------------------------------- 1 | // +build linux,386 linux,amd64 linux,arm64 2 | 3 | package elf 4 | 5 | func utsnameStr(in []int8) string { 6 | out := make([]byte, len(in)) 7 | for i := 0; i < len(in); i++ { 8 | if in[i] == 0 { 9 | break 10 | } 11 | out = append(out, byte(in[i])) 12 | } 13 | return string(out) 14 | } 15 | -------------------------------------------------------------------------------- /elf/utsname_uint8.go: -------------------------------------------------------------------------------- 1 | // +build linux,arm linux,ppc64 linux,ppc64le s390x 2 | 3 | package elf 4 | 5 | func utsnameStr(in []uint8) string { 6 | out := make([]byte, len(in)) 7 | for i := 0; i < len(in); i++ { 8 | if in[i] == 0 { 9 | break 10 | } 11 | out = append(out, byte(in[i])) 12 | } 13 | return string(out) 14 | } 15 | -------------------------------------------------------------------------------- /examples/bcc/bash_readline/bash_readline.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Louis McCormack 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "bytes" 19 | "encoding/binary" 20 | "fmt" 21 | "os" 22 | "os/signal" 23 | 24 | bpf "github.com/iovisor/gobpf/bcc" 25 | ) 26 | 27 | const source string = ` 28 | #include 29 | 30 | struct readline_event_t { 31 | u32 pid; 32 | char str[80]; 33 | } __attribute__((packed)); 34 | 35 | BPF_PERF_OUTPUT(readline_events); 36 | 37 | int get_return_value(struct pt_regs *ctx) { 38 | struct readline_event_t event = {}; 39 | u32 pid; 40 | if (!PT_REGS_RC(ctx)) 41 | return 0; 42 | pid = bpf_get_current_pid_tgid(); 43 | event.pid = pid; 44 | bpf_probe_read(&event.str, sizeof(event.str), (void *)PT_REGS_RC(ctx)); 45 | readline_events.perf_submit(ctx, &event, sizeof(event)); 46 | 47 | return 0; 48 | } 49 | ` 50 | 51 | type readlineEvent struct { 52 | Pid uint32 53 | Str [80]byte 54 | } 55 | 56 | func main() { 57 | m := bpf.NewModule(source, []string{}) 58 | defer m.Close() 59 | 60 | readlineUretprobe, err := m.LoadUprobe("get_return_value") 61 | if err != nil { 62 | fmt.Fprintf(os.Stderr, "Failed to load get_return_value: %s\n", err) 63 | os.Exit(1) 64 | } 65 | 66 | err = m.AttachUretprobe("/bin/bash", "readline", readlineUretprobe, -1) 67 | if err != nil { 68 | fmt.Fprintf(os.Stderr, "Failed to attach return_value: %s\n", err) 69 | os.Exit(1) 70 | } 71 | 72 | table := bpf.NewTable(m.TableId("readline_events"), m) 73 | 74 | channel := make(chan []byte) 75 | 76 | perfMap, err := bpf.InitPerfMap(table, channel, nil) 77 | if err != nil { 78 | fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err) 79 | os.Exit(1) 80 | } 81 | 82 | sig := make(chan os.Signal, 1) 83 | signal.Notify(sig, os.Interrupt, os.Kill) 84 | 85 | fmt.Printf("%10s\t%s\n", "PID", "COMMAND") 86 | go func() { 87 | var event readlineEvent 88 | for { 89 | data := <-channel 90 | err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event) 91 | if err != nil { 92 | fmt.Printf("failed to decode received data: %s\n", err) 93 | continue 94 | } 95 | // Convert C string (null-terminated) to Go string 96 | comm := string(event.Str[:bytes.IndexByte(event.Str[:], 0)]) 97 | fmt.Printf("%10d\t%s\n", event.Pid, comm) 98 | } 99 | }() 100 | 101 | perfMap.Start() 102 | <-sig 103 | perfMap.Stop() 104 | } 105 | -------------------------------------------------------------------------------- /examples/bcc/execsnoop/execsnoop.go: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | package main 14 | 15 | import ( 16 | "bufio" 17 | "bytes" 18 | "encoding/binary" 19 | "flag" 20 | "fmt" 21 | "os" 22 | "os/signal" 23 | "strconv" 24 | "strings" 25 | "unsafe" 26 | 27 | bpf "github.com/iovisor/gobpf/bcc" 28 | ) 29 | 30 | import "C" 31 | 32 | type EventType int32 33 | 34 | const ( 35 | eventArg EventType = iota 36 | eventRet 37 | ) 38 | 39 | const source string = ` 40 | #include 41 | #include 42 | #include 43 | 44 | #define ARGSIZE 128 45 | 46 | enum event_type { 47 | EVENT_ARG, 48 | EVENT_RET, 49 | }; 50 | 51 | struct data_t { 52 | u64 pid; // PID as in the userspace term (i.e. task->tgid in kernel) 53 | u64 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel) 54 | char comm[TASK_COMM_LEN]; 55 | enum event_type type; 56 | char argv[ARGSIZE]; 57 | int retval; 58 | }; 59 | 60 | BPF_PERF_OUTPUT(events); 61 | 62 | static int __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data) 63 | { 64 | bpf_probe_read(data->argv, sizeof(data->argv), ptr); 65 | events.perf_submit(ctx, data, sizeof(struct data_t)); 66 | return 1; 67 | } 68 | 69 | static int submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data) 70 | { 71 | const char *argp = NULL; 72 | bpf_probe_read(&argp, sizeof(argp), ptr); 73 | if (argp) { 74 | return __submit_arg(ctx, (void *)(argp), data); 75 | } 76 | return 0; 77 | } 78 | 79 | int syscall__execve(struct pt_regs *ctx, 80 | const char __user *filename, 81 | const char __user *const __user *__argv, 82 | const char __user *const __user *__envp) 83 | { 84 | // create data here and pass to submit_arg to save stack space (#555) 85 | struct data_t data = {}; 86 | struct task_struct *task; 87 | 88 | data.pid = bpf_get_current_pid_tgid() >> 32; 89 | 90 | task = (struct task_struct *)bpf_get_current_task(); 91 | // Some kernels, like Ubuntu 4.13.0-generic, return 0 92 | // as the real_parent->tgid. 93 | // We use the getPpid function as a fallback in those cases. 94 | // See https://github.com/iovisor/bcc/issues/1883. 95 | data.ppid = task->real_parent->tgid; 96 | 97 | bpf_get_current_comm(&data.comm, sizeof(data.comm)); 98 | data.type = EVENT_ARG; 99 | 100 | __submit_arg(ctx, (void *)filename, &data); 101 | 102 | // skip first arg, as we submitted filename 103 | #pragma unroll 104 | for (int i = 1; i < MAX_ARGS; i++) { 105 | if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) 106 | goto out; 107 | } 108 | 109 | // handle truncated argument list 110 | char ellipsis[] = "..."; 111 | __submit_arg(ctx, (void *)ellipsis, &data); 112 | out: 113 | return 0; 114 | } 115 | 116 | int do_ret_sys_execve(struct pt_regs *ctx) 117 | { 118 | struct data_t data = {}; 119 | struct task_struct *task; 120 | 121 | data.pid = bpf_get_current_pid_tgid() >> 32; 122 | 123 | task = (struct task_struct *)bpf_get_current_task(); 124 | // Some kernels, like Ubuntu 4.13.0-generic, return 0 125 | // as the real_parent->tgid. 126 | // We use the getPpid function as a fallback in those cases. 127 | // See https://github.com/iovisor/bcc/issues/1883. 128 | data.ppid = task->real_parent->tgid; 129 | 130 | bpf_get_current_comm(&data.comm, sizeof(data.comm)); 131 | data.type = EVENT_RET; 132 | data.retval = PT_REGS_RC(ctx); 133 | events.perf_submit(ctx, &data, sizeof(data)); 134 | 135 | return 0; 136 | } 137 | ` 138 | 139 | type execveEvent struct { 140 | Pid uint64 141 | Ppid uint64 142 | Comm [16]byte 143 | Type int32 144 | Argv [128]byte 145 | RetVal int32 146 | } 147 | 148 | type eventPayload struct { 149 | Time string `json:"time,omitempty"` 150 | Comm string `json:"comm"` 151 | Pid uint64 `json:"pid"` 152 | Ppid string `json:"ppid"` 153 | Argv string `json:"argv"` 154 | RetVal int32 `json:"retval"` 155 | } 156 | 157 | // getPpid is a fallback to read the parent PID from /proc. 158 | // Some kernel versions, like 4.13.0 return 0 getting the parent PID 159 | // from the current task, so we need to use this fallback to have 160 | // the parent PID in any kernel. 161 | func getPpid(pid uint64) uint64 { 162 | f, err := os.OpenFile(fmt.Sprintf("/proc/%d/status", pid), os.O_RDONLY, os.ModePerm) 163 | if err != nil { 164 | return 0 165 | } 166 | defer f.Close() 167 | 168 | sc := bufio.NewScanner(f) 169 | for sc.Scan() { 170 | text := sc.Text() 171 | if strings.Contains(text, "PPid:") { 172 | f := strings.Fields(text) 173 | i, _ := strconv.ParseUint(f[len(f)-1], 10, 64) 174 | return i 175 | } 176 | } 177 | return 0 178 | } 179 | 180 | func main() { 181 | run() 182 | } 183 | 184 | func run() { 185 | traceFailed := flag.Bool("x", false, "trace failed exec()s") 186 | timestamps := flag.Bool("t", false, "include timestamps") 187 | quotemarks := flag.Bool("q", false, `add "quotemarks" around arguments`) 188 | filterComm := flag.String("n", "", `only print command lines containing a name, for example "main"`) 189 | filterArg := flag.String("l", "", `only print command where arguments contain an argument, for example "tpkg"`) 190 | format := flag.String("o", "table", "output format, either table or json") 191 | pretty := flag.Bool("p", false, "pretty print json output") 192 | maxArgs := flag.Uint64("m", 20, "maximum number of arguments parsed and displayed, defaults to 20") 193 | 194 | flag.Parse() 195 | 196 | m := bpf.NewModule(strings.Replace(source, "MAX_ARGS", strconv.FormatUint(*maxArgs, 10), -1), []string{}) 197 | defer m.Close() 198 | 199 | fnName := bpf.GetSyscallFnName("execve") 200 | 201 | kprobe, err := m.LoadKprobe("syscall__execve") 202 | if err != nil { 203 | fmt.Fprintf(os.Stderr, "Failed to load syscall__execve: %s\n", err) 204 | os.Exit(1) 205 | } 206 | 207 | // passing -1 for maxActive signifies to use the default 208 | // according to the kernel kprobes documentation 209 | if err := m.AttachKprobe(fnName, kprobe, -1); err != nil { 210 | fmt.Fprintf(os.Stderr, "Failed to attach syscall__execve: %s\n", err) 211 | os.Exit(1) 212 | } 213 | 214 | kretprobe, err := m.LoadKprobe("do_ret_sys_execve") 215 | if err != nil { 216 | fmt.Fprintf(os.Stderr, "Failed to load do_ret_sys_execve: %s\n", err) 217 | os.Exit(1) 218 | } 219 | 220 | // passing -1 for maxActive signifies to use the default 221 | // according to the kernel kretprobes documentation 222 | if err := m.AttachKretprobe(fnName, kretprobe, -1); err != nil { 223 | fmt.Fprintf(os.Stderr, "Failed to attach do_ret_sys_execve: %s\n", err) 224 | os.Exit(1) 225 | } 226 | 227 | table := bpf.NewTable(m.TableId("events"), m) 228 | 229 | channel := make(chan []byte, 1000) 230 | 231 | perfMap, err := bpf.InitPerfMap(table, channel, nil) 232 | if err != nil { 233 | fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err) 234 | os.Exit(1) 235 | } 236 | 237 | sig := make(chan os.Signal, 1) 238 | signal.Notify(sig, os.Interrupt, os.Kill) 239 | 240 | go func() { 241 | out := newOutput(*format, *pretty, *timestamps) 242 | out.PrintHeader() 243 | 244 | args := make(map[uint64][]string) 245 | 246 | for { 247 | data := <-channel 248 | 249 | var event execveEvent 250 | err := binary.Read(bytes.NewBuffer(data), bpf.GetHostByteOrder(), &event) 251 | 252 | if err != nil { 253 | fmt.Printf("failed to decode received data: %s\n", err) 254 | continue 255 | } 256 | 257 | if eventArg == EventType(event.Type) { 258 | e, ok := args[event.Pid] 259 | if !ok { 260 | e = make([]string, 0) 261 | } 262 | argv := (*C.char)(unsafe.Pointer(&event.Argv)) 263 | 264 | e = append(e, C.GoString(argv)) 265 | args[event.Pid] = e 266 | } else { 267 | if event.RetVal != 0 && !*traceFailed { 268 | delete(args, event.Pid) 269 | continue 270 | } 271 | 272 | comm := C.GoString((*C.char)(unsafe.Pointer(&event.Comm))) 273 | if *filterComm != "" && !strings.Contains(comm, *filterComm) { 274 | delete(args, event.Pid) 275 | continue 276 | } 277 | 278 | argv, ok := args[event.Pid] 279 | if !ok { 280 | continue 281 | } 282 | 283 | if *filterArg != "" && !strings.Contains(strings.Join(argv, " "), *filterArg) { 284 | delete(args, event.Pid) 285 | continue 286 | } 287 | 288 | p := eventPayload{ 289 | Pid: event.Pid, 290 | Ppid: "?", 291 | Comm: comm, 292 | RetVal: event.RetVal, 293 | } 294 | 295 | if event.Ppid == 0 { 296 | event.Ppid = getPpid(event.Pid) 297 | } 298 | 299 | if event.Ppid != 0 { 300 | p.Ppid = strconv.FormatUint(event.Ppid, 10) 301 | } 302 | 303 | if *quotemarks { 304 | var b bytes.Buffer 305 | for i, a := range argv { 306 | b.WriteString(strings.Replace(a, `"`, `\"`, -1)) 307 | if i != len(argv)-1 { 308 | b.WriteString(" ") 309 | } 310 | } 311 | p.Argv = b.String() 312 | } else { 313 | p.Argv = strings.Join(argv, " ") 314 | } 315 | p.Argv = strings.TrimSpace(strings.Replace(p.Argv, "\n", "\\n", -1)) 316 | 317 | out.PrintLine(p) 318 | delete(args, event.Pid) 319 | } 320 | } 321 | }() 322 | 323 | perfMap.Start() 324 | <-sig 325 | perfMap.Stop() 326 | } 327 | -------------------------------------------------------------------------------- /examples/bcc/execsnoop/output.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type output interface { 10 | PrintHeader() 11 | PrintLine(eventPayload) 12 | } 13 | 14 | type timing struct { 15 | start time.Time 16 | } 17 | 18 | func newTiming() timing { 19 | return timing{time.Now()} 20 | } 21 | 22 | func (t timing) Now() float64 { 23 | return time.Now().Sub(t.start).Seconds() 24 | } 25 | 26 | func newOutput(name string, pretty, timestamp bool) output { 27 | switch name { 28 | case "json": 29 | return newJSONOutput(pretty, timestamp) 30 | } 31 | return newTableOutput(timestamp) 32 | } 33 | 34 | type tableOutput struct { 35 | timing timing 36 | timestamp bool 37 | } 38 | 39 | func (t tableOutput) PrintHeader() { 40 | header := "%-16s %-6s %-6s %3s %s\n" 41 | args := []interface{}{"PCOMM", "PID", "PPID", "RET", "ARGS"} 42 | if t.timestamp { 43 | header = "%-8s" + header 44 | args = []interface{}{"TIME(s)", "PCOMM", "PID", "PPID", "RET", "ARGS"} 45 | } 46 | fmt.Printf(header, args...) 47 | } 48 | 49 | func (t tableOutput) PrintLine(e eventPayload) { 50 | header := "%-16s %-6d %-6s %3d %s\n" 51 | args := []interface{}{e.Comm, e.Pid, e.Ppid, e.RetVal, e.Argv} 52 | if t.timestamp { 53 | header = "%-8.3f" + header 54 | args = append([]interface{}{t.timing.Now()}, args...) 55 | } 56 | fmt.Printf(header, args...) 57 | } 58 | 59 | func newTableOutput(timestamp bool) output { 60 | return &tableOutput{newTiming(), timestamp} 61 | } 62 | 63 | type jsonOutput struct { 64 | timing timing 65 | pretty bool 66 | timestamp bool 67 | } 68 | 69 | func (jsonOutput) PrintHeader() { 70 | // jsonOutput doesn't have any header 71 | } 72 | 73 | func (j jsonOutput) PrintLine(e eventPayload) { 74 | if j.timestamp { 75 | e.Time = fmt.Sprintf("%.3f", j.timing.Now()) 76 | } 77 | var m []byte 78 | if j.pretty { 79 | m, _ = json.MarshalIndent(e, "", " ") 80 | } else { 81 | m, _ = json.Marshal(e) 82 | } 83 | if len(m) > 0 { 84 | fmt.Println(string(m)) 85 | } 86 | } 87 | 88 | func newJSONOutput(pretty, timestamp bool) output { 89 | return jsonOutput{newTiming(), pretty, timestamp} 90 | } 91 | -------------------------------------------------------------------------------- /examples/bcc/perf/.gitignore: -------------------------------------------------------------------------------- 1 | perf 2 | -------------------------------------------------------------------------------- /examples/bcc/perf/perf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Kinvolk 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "bytes" 19 | "encoding/binary" 20 | "fmt" 21 | "os" 22 | "os/signal" 23 | "unsafe" 24 | 25 | bpf "github.com/iovisor/gobpf/bcc" 26 | ) 27 | 28 | import "C" 29 | 30 | const source string = ` 31 | #include 32 | #include 33 | 34 | typedef struct { 35 | u32 pid; 36 | uid_t uid; 37 | gid_t gid; 38 | int ret; 39 | char filename[256]; 40 | } chown_event_t; 41 | 42 | BPF_PERF_OUTPUT(chown_events); 43 | BPF_HASH(chowncall, u64, chown_event_t); 44 | 45 | int kprobe__sys_fchownat(struct pt_regs *ctx, int dfd, const char *filename, 46 | uid_t uid, gid_t gid, int flag) 47 | { 48 | u64 pid = bpf_get_current_pid_tgid(); 49 | chown_event_t event = { 50 | .pid = pid >> 32, 51 | .uid = uid, 52 | .gid = gid, 53 | }; 54 | bpf_probe_read(&event.filename, sizeof(event.filename), (void *)filename); 55 | chowncall.update(&pid, &event); 56 | return 0; 57 | } 58 | 59 | int kretprobe__sys_fchownat(struct pt_regs *ctx) 60 | { 61 | int ret = PT_REGS_RC(ctx); 62 | u64 pid = bpf_get_current_pid_tgid(); 63 | chown_event_t *eventp = chowncall.lookup(&pid); 64 | if (eventp == 0) { 65 | return 0; 66 | } 67 | chown_event_t event = *eventp; 68 | event.ret = ret; 69 | chown_events.perf_submit(ctx, &event, sizeof(event)); 70 | chowncall.delete(&pid); 71 | return 0; 72 | }; 73 | ` 74 | 75 | type chownEvent struct { 76 | Pid uint32 77 | Uid uint32 78 | Gid uint32 79 | ReturnValue int32 80 | Filename [256]byte 81 | } 82 | 83 | func main() { 84 | m := bpf.NewModule(source, []string{}) 85 | defer m.Close() 86 | 87 | chownKprobe, err := m.LoadKprobe("kprobe__sys_fchownat") 88 | if err != nil { 89 | fmt.Fprintf(os.Stderr, "Failed to load kprobe__sys_fchownat: %s\n", err) 90 | os.Exit(1) 91 | } 92 | 93 | syscallName := bpf.GetSyscallFnName("fchownat") 94 | 95 | // passing -1 for maxActive signifies to use the default 96 | // according to the kernel kprobes documentation 97 | err = m.AttachKprobe(syscallName, chownKprobe, -1) 98 | if err != nil { 99 | fmt.Fprintf(os.Stderr, "Failed to attach kprobe__sys_fchownat: %s\n", err) 100 | os.Exit(1) 101 | } 102 | 103 | chownKretprobe, err := m.LoadKprobe("kretprobe__sys_fchownat") 104 | if err != nil { 105 | fmt.Fprintf(os.Stderr, "Failed to load kretprobe__sys_fchownat: %s\n", err) 106 | os.Exit(1) 107 | } 108 | 109 | // passing -1 for maxActive signifies to use the default 110 | // according to the kernel kretprobes documentation 111 | err = m.AttachKretprobe(syscallName, chownKretprobe, -1) 112 | if err != nil { 113 | fmt.Fprintf(os.Stderr, "Failed to attach kretprobe__sys_fchownat: %s\n", err) 114 | os.Exit(1) 115 | } 116 | 117 | table := bpf.NewTable(m.TableId("chown_events"), m) 118 | 119 | channel := make(chan []byte) 120 | 121 | perfMap, err := bpf.InitPerfMap(table, channel, nil) 122 | if err != nil { 123 | fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err) 124 | os.Exit(1) 125 | } 126 | 127 | sig := make(chan os.Signal, 1) 128 | signal.Notify(sig, os.Interrupt, os.Kill) 129 | 130 | go func() { 131 | var event chownEvent 132 | for { 133 | data := <-channel 134 | err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event) 135 | if err != nil { 136 | fmt.Printf("failed to decode received data: %s\n", err) 137 | continue 138 | } 139 | filename := (*C.char)(unsafe.Pointer(&event.Filename)) 140 | fmt.Printf("uid %d gid %d pid %d called fchownat(2) on %s (return value: %d)\n", 141 | event.Uid, event.Gid, event.Pid, C.GoString(filename), event.ReturnValue) 142 | } 143 | }() 144 | 145 | perfMap.Start() 146 | <-sig 147 | perfMap.Stop() 148 | } 149 | -------------------------------------------------------------------------------- /examples/bcc/strlen_count/strlen_count.go: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | package main 14 | 15 | import ( 16 | "encoding/binary" 17 | "flag" 18 | "fmt" 19 | "os" 20 | "os/signal" 21 | "regexp" 22 | 23 | bpf "github.com/iovisor/gobpf/bcc" 24 | ) 25 | 26 | import "C" 27 | 28 | const source string = ` 29 | #include 30 | typedef char strlenkey_t[80]; 31 | BPF_HASH(counts, strlenkey_t); 32 | 33 | int count(struct pt_regs *ctx) { 34 | if (!PT_REGS_PARM1(ctx)) 35 | return 0; 36 | 37 | strlenkey_t key; 38 | u64 zero = 0, *val; 39 | 40 | bpf_probe_read(&key, sizeof(key), (void *)PT_REGS_PARM1(ctx)); 41 | val = counts.lookup_or_init(&key, &zero); 42 | (*val)++; 43 | return 0; 44 | } 45 | ` 46 | 47 | var ansiEscape = regexp.MustCompile(`[[:cntrl:]]`) 48 | 49 | func main() { 50 | pid := flag.Int("pid", -1, "attach to pid, default is all processes") 51 | flag.Parse() 52 | m := bpf.NewModule(source, []string{}) 53 | defer m.Close() 54 | 55 | strlenUprobe, err := m.LoadUprobe("count") 56 | if err != nil { 57 | fmt.Fprintf(os.Stderr, "Failed to load uprobe count: %s\n", err) 58 | os.Exit(1) 59 | } 60 | 61 | err = m.AttachUprobe("c", "strlen", strlenUprobe, *pid) 62 | if err != nil { 63 | fmt.Fprintf(os.Stderr, "Failed to attach uprobe to strlen: %s\n", err) 64 | os.Exit(1) 65 | } 66 | 67 | table := bpf.NewTable(m.TableId("counts"), m) 68 | 69 | fmt.Println("Tracing strlen()... hit Ctrl-C to end.") 70 | 71 | sig := make(chan os.Signal, 1) 72 | signal.Notify(sig, os.Interrupt) 73 | <-sig 74 | 75 | fmt.Printf("%10s %s\n", "COUNT", "STRING") 76 | for it := table.Iter(); it.Next(); { 77 | k := ansiEscape.ReplaceAll(it.Key(), []byte{}) 78 | v := binary.LittleEndian.Uint64(it.Leaf()) 79 | fmt.Printf("%10d \"%s\"\n", v, k) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/bcc/xdp/xdp_drop.go: -------------------------------------------------------------------------------- 1 | // xdp_drop.go Drop incoming packets on XDP layer and count for which 2 | // protocol type. Based on: 3 | // https://github.com/iovisor/bcc/blob/master/examples/networking/xdp/xdp_drop_count.py 4 | // 5 | // Copyright (c) 2017 GustavoKatel 6 | // Licensed under the Apache License, Version 2.0 (the "License") 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | "os/signal" 14 | 15 | bpf "github.com/iovisor/gobpf/bcc" 16 | ) 17 | 18 | /* 19 | #cgo CFLAGS: -I/usr/include/bcc/compat 20 | #cgo LDFLAGS: -lbcc 21 | #include 22 | #include 23 | void perf_reader_free(void *ptr); 24 | */ 25 | import "C" 26 | 27 | const source string = ` 28 | #define KBUILD_MODNAME "foo" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | BPF_TABLE("array", int, long, dropcnt, 256); 38 | 39 | static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) { 40 | struct iphdr *iph = data + nh_off; 41 | 42 | if ((void*)&iph[1] > data_end) 43 | return 0; 44 | return iph->protocol; 45 | } 46 | 47 | static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) { 48 | struct ipv6hdr *ip6h = data + nh_off; 49 | 50 | if ((void*)&ip6h[1] > data_end) 51 | return 0; 52 | return ip6h->nexthdr; 53 | } 54 | 55 | int xdp_prog1(struct CTXTYPE *ctx) { 56 | 57 | void* data_end = (void*)(long)ctx->data_end; 58 | void* data = (void*)(long)ctx->data; 59 | 60 | struct ethhdr *eth = data; 61 | 62 | // drop packets 63 | int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX 64 | long *value; 65 | uint16_t h_proto; 66 | uint64_t nh_off = 0; 67 | int index; 68 | 69 | nh_off = sizeof(*eth); 70 | 71 | if (data + nh_off > data_end) 72 | return rc; 73 | 74 | h_proto = eth->h_proto; 75 | 76 | // While the following code appears to be duplicated accidentally, 77 | // it's intentional to handle double tags in ethernet frames. 78 | if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { 79 | struct vlan_hdr *vhdr; 80 | 81 | vhdr = data + nh_off; 82 | nh_off += sizeof(struct vlan_hdr); 83 | if (data + nh_off > data_end) 84 | return rc; 85 | h_proto = vhdr->h_vlan_encapsulated_proto; 86 | } 87 | if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { 88 | struct vlan_hdr *vhdr; 89 | 90 | vhdr = data + nh_off; 91 | nh_off += sizeof(struct vlan_hdr); 92 | if (data + nh_off > data_end) 93 | return rc; 94 | h_proto = vhdr->h_vlan_encapsulated_proto; 95 | } 96 | 97 | if (h_proto == htons(ETH_P_IP)) 98 | index = parse_ipv4(data, nh_off, data_end); 99 | else if (h_proto == htons(ETH_P_IPV6)) 100 | index = parse_ipv6(data, nh_off, data_end); 101 | else 102 | index = 0; 103 | 104 | value = dropcnt.lookup(&index); 105 | if (value) lock_xadd(value, 1); 106 | 107 | return rc; 108 | } 109 | ` 110 | 111 | func usage() { 112 | fmt.Printf("Usage: %v \n", os.Args[0]) 113 | fmt.Printf("e.g.: %v eth0\n", os.Args[0]) 114 | os.Exit(1) 115 | } 116 | 117 | func main() { 118 | var device string 119 | 120 | if len(os.Args) != 2 { 121 | usage() 122 | } 123 | 124 | device = os.Args[1] 125 | 126 | ret := "XDP_DROP" 127 | ctxtype := "xdp_md" 128 | 129 | module := bpf.NewModule(source, []string{ 130 | "-w", 131 | "-DRETURNCODE=" + ret, 132 | "-DCTXTYPE=" + ctxtype, 133 | }) 134 | defer module.Close() 135 | 136 | fn, err := module.Load("xdp_prog1", C.BPF_PROG_TYPE_XDP, 1, 65536) 137 | if err != nil { 138 | fmt.Fprintf(os.Stderr, "Failed to load xdp prog: %v\n", err) 139 | os.Exit(1) 140 | } 141 | 142 | err = module.AttachXDP(device, fn) 143 | if err != nil { 144 | fmt.Fprintf(os.Stderr, "Failed to attach xdp prog: %v\n", err) 145 | os.Exit(1) 146 | } 147 | 148 | defer func() { 149 | if err := module.RemoveXDP(device); err != nil { 150 | fmt.Fprintf(os.Stderr, "Failed to remove XDP from %s: %v\n", device, err) 151 | } 152 | }() 153 | 154 | fmt.Println("Dropping packets, hit CTRL+C to stop") 155 | 156 | sig := make(chan os.Signal, 1) 157 | signal.Notify(sig, os.Interrupt, os.Kill) 158 | 159 | dropcnt := bpf.NewTable(module.TableId("dropcnt"), module) 160 | 161 | <-sig 162 | 163 | fmt.Printf("\n{IP protocol-number}: {total dropped pkts}\n") 164 | for it := dropcnt.Iter(); it.Next(); { 165 | key := bpf.GetHostByteOrder().Uint32(it.Key()) 166 | value := bpf.GetHostByteOrder().Uint64(it.Leaf()) 167 | 168 | if value > 0 { 169 | fmt.Printf("%v: %v pkts\n", key, value) 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /examples/tracepipe/.gitignore: -------------------------------------------------------------------------------- 1 | tracepipe 2 | -------------------------------------------------------------------------------- /examples/tracepipe/tracepipe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Kinvolk 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | 21 | "github.com/iovisor/gobpf/pkg/tracepipe" 22 | ) 23 | 24 | func main() { 25 | tp, err := tracepipe.New() 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "%s\n", err) 28 | os.Exit(1) 29 | } 30 | defer tp.Close() 31 | 32 | channel, errorChannel := tp.Channel() 33 | 34 | for { 35 | select { 36 | case event := <-channel: 37 | fmt.Printf("%+v\n", event) 38 | case err := <-errorChannel: 39 | fmt.Printf("%+v\n", err) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/iovisor/gobpf 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /pkg/bpffs/fs.go: -------------------------------------------------------------------------------- 1 | package bpffs 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | ) 8 | 9 | const BPFFSPath = "/sys/fs/bpf" 10 | 11 | var FsMagicBPFFS int32 12 | 13 | func init() { 14 | // https://github.com/coreutils/coreutils/blob/v8.27/src/stat.c#L275 15 | // https://github.com/torvalds/linux/blob/v4.8/include/uapi/linux/magic.h#L80 16 | magic := uint32(0xCAFE4A11) 17 | // 0xCAFE4A11 overflows an int32, which is what's expected by Statfs_t.Type in 32bit platforms. 18 | // To avoid conditional compilation for all 32bit/64bit platforms, we use an unsafe cast 19 | FsMagicBPFFS = *(*int32)(unsafe.Pointer(&magic)) 20 | } 21 | 22 | // IsMountedAt checks if the BPF fs is mounted already in the custom location 23 | func IsMountedAt(mountpoint string) (bool, error) { 24 | var data syscall.Statfs_t 25 | if err := syscall.Statfs(mountpoint, &data); err != nil { 26 | return false, fmt.Errorf("cannot statfs %q: %v", mountpoint, err) 27 | } 28 | return int32(data.Type) == FsMagicBPFFS, nil 29 | } 30 | 31 | // IsMounted checks if the BPF fs is mounted already in the default location 32 | func IsMounted() (bool, error) { 33 | return IsMountedAt(BPFFSPath) 34 | } 35 | 36 | // MountAt mounts the BPF fs in the custom location (if not already mounted) 37 | func MountAt(mountpoint string) error { 38 | mounted, err := IsMountedAt(mountpoint) 39 | if err != nil { 40 | return err 41 | } 42 | if mounted { 43 | return nil 44 | } 45 | if err := syscall.Mount(mountpoint, mountpoint, "bpf", 0, ""); err != nil { 46 | return fmt.Errorf("error mounting %q: %v", mountpoint, err) 47 | } 48 | return nil 49 | } 50 | 51 | // Mount mounts the BPF fs in the default location (if not already mounted) 52 | func Mount() error { 53 | return MountAt(BPFFSPath) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/cpuonline/cpuonline.go: -------------------------------------------------------------------------------- 1 | package cpuonline 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/iovisor/gobpf/pkg/cpurange" 7 | ) 8 | 9 | const cpuOnline = "/sys/devices/system/cpu/online" 10 | 11 | // Get returns a slice with the online CPUs, for example `[0, 2, 3]` 12 | func Get() ([]uint, error) { 13 | buf, err := ioutil.ReadFile(cpuOnline) 14 | if err != nil { 15 | return nil, err 16 | } 17 | return cpurange.ReadCPURange(string(buf)) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/cpupossible/cpupossible.go: -------------------------------------------------------------------------------- 1 | package cpupossible 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/iovisor/gobpf/pkg/cpurange" 7 | ) 8 | 9 | const cpuPossible = "/sys/devices/system/cpu/possible" 10 | 11 | // Get returns a slice with the online CPUs, for example `[0, 2, 3]` 12 | func Get() ([]uint, error) { 13 | buf, err := ioutil.ReadFile(cpuPossible) 14 | if err != nil { 15 | return nil, err 16 | } 17 | return cpurange.ReadCPURange(string(buf)) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/cpurange/cpu_range.go: -------------------------------------------------------------------------------- 1 | package cpurange 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | // loosely based on https://github.com/iovisor/bcc/blob/v0.3.0/src/python/bcc/utils.py#L15 9 | func ReadCPURange(cpuRangeStr string) ([]uint, error) { 10 | var cpus []uint 11 | cpuRangeStr = strings.Trim(cpuRangeStr, "\n ") 12 | for _, cpuRange := range strings.Split(cpuRangeStr, ",") { 13 | rangeOp := strings.SplitN(cpuRange, "-", 2) 14 | first, err := strconv.ParseUint(rangeOp[0], 10, 32) 15 | if err != nil { 16 | return nil, err 17 | } 18 | if len(rangeOp) == 1 { 19 | cpus = append(cpus, uint(first)) 20 | continue 21 | } 22 | last, err := strconv.ParseUint(rangeOp[1], 10, 32) 23 | if err != nil { 24 | return nil, err 25 | } 26 | for n := first; n <= last; n++ { 27 | cpus = append(cpus, uint(n)) 28 | } 29 | } 30 | return cpus, nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/cpurange/cpu_range_test.go: -------------------------------------------------------------------------------- 1 | package cpurange 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetOnlineCPUs(t *testing.T) { 8 | tests := []struct { 9 | data string 10 | expected []uint 11 | valid bool 12 | }{ 13 | { 14 | "", 15 | nil, 16 | false, 17 | }, 18 | { 19 | "0-3\n", 20 | []uint{0, 1, 2, 3}, 21 | true, 22 | }, 23 | { 24 | " 0-2,5", 25 | []uint{0, 1, 2, 5}, 26 | true, 27 | }, 28 | { 29 | "0,2,4-5,7-9", 30 | []uint{0, 2, 4, 5, 7, 8, 9}, 31 | true, 32 | }, 33 | { 34 | "0,2", 35 | []uint{0, 2}, 36 | true, 37 | }, 38 | { 39 | "0", 40 | []uint{0}, 41 | true, 42 | }, 43 | { 44 | "-2,5", 45 | nil, 46 | false, 47 | }, 48 | { 49 | "2-@,5", 50 | nil, 51 | false, 52 | }, 53 | { 54 | "-", 55 | nil, 56 | false, 57 | }, 58 | } 59 | for _, test := range tests { 60 | cpus, err := ReadCPURange(test.data) 61 | if test.valid && err != nil { 62 | t.Errorf("expected input %q to not return an error but got: %v\n", test.data, err) 63 | } 64 | if !test.valid && err == nil { 65 | t.Errorf("expected input %q to return an error\n", test.data) 66 | } 67 | for i := range cpus { 68 | if cpus[i] != test.expected[i] { 69 | t.Errorf("expected %q but got %q\n", test.expected, cpus) 70 | break 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/ksym/ksym.go: -------------------------------------------------------------------------------- 1 | package ksym 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "io" 7 | "os" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | const ( 13 | KALLSYMS = "/proc/kallsyms" 14 | ) 15 | 16 | type ksymCache struct { 17 | sync.RWMutex 18 | ksym map[string]string 19 | } 20 | 21 | var cache ksymCache 22 | 23 | // Ksym translates a kernel memory address into a kernel function name 24 | // using `/proc/kallsyms` 25 | func Ksym(addr string) (string, error) { 26 | if cache.ksym == nil { 27 | cache.ksym = make(map[string]string) 28 | } 29 | 30 | cache.Lock() 31 | defer cache.Unlock() 32 | 33 | if _, ok := cache.ksym[addr]; !ok { 34 | fd, err := os.Open(KALLSYMS) 35 | if err != nil { 36 | return "", err 37 | } 38 | defer fd.Close() 39 | 40 | fn := ksym(addr, fd) 41 | if fn == "" { 42 | return "", errors.New("kernel function not found for " + addr) 43 | } 44 | 45 | cache.ksym[addr] = fn 46 | } 47 | 48 | return cache.ksym[addr], nil 49 | } 50 | 51 | func ksym(addr string, r io.Reader) string { 52 | s := bufio.NewScanner(r) 53 | for s.Scan() { 54 | l := s.Text() 55 | ar := strings.Split(l, " ") 56 | if len(ar) != 3 { 57 | continue 58 | } 59 | 60 | if ar[0] == addr { 61 | return ar[2] 62 | } 63 | } 64 | 65 | return "" 66 | } 67 | -------------------------------------------------------------------------------- /pkg/ksym/ksym_test.go: -------------------------------------------------------------------------------- 1 | package ksym 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestKsym(t *testing.T) { 9 | data := "ffffffff91b2a340 T cgroup_freezing" 10 | 11 | r := strings.NewReader(data) 12 | fn := ksym("ffffffff91b2a340", r) 13 | 14 | if fn != "cgroup_freezing" { 15 | t.Error("unexpected result") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/progtestrun/prog_test_run.go: -------------------------------------------------------------------------------- 1 | package progtestrun 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | ) 7 | 8 | /* 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | static __u64 ptr_to_u64(void *ptr) 17 | { 18 | return (__u64) (unsigned long) ptr; 19 | } 20 | 21 | int bpf_prog_test_run(int fd, int repeat, char *data, int data_size, 22 | char *data_out, int *data_out_size, int *retval, 23 | int *duration) 24 | { 25 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0) 26 | union bpf_attr attr; 27 | int ret; 28 | 29 | memset(&attr, 0, sizeof(attr)); 30 | 31 | attr.test.prog_fd = fd; 32 | attr.test.data_in = ptr_to_u64((void *) data); 33 | attr.test.data_out = ptr_to_u64((void *) data_out); 34 | attr.test.data_size_in = data_size; 35 | attr.test.repeat = repeat; 36 | 37 | ret = syscall(__NR_bpf, BPF_PROG_TEST_RUN, &attr, sizeof(attr)); 38 | if (data_out_size) 39 | *data_out_size = attr.test.data_size_out; 40 | if (retval) 41 | *retval = attr.test.retval; 42 | if (duration) 43 | *duration = attr.test.duration; 44 | return ret; 45 | #else 46 | errno = ENOSYS; 47 | return -1; 48 | #endif 49 | } 50 | */ 51 | import "C" 52 | 53 | // Run exposes BPF_PROG_TEST_RUN to test xdp and skp programs. 54 | // `data` will be passed to your program as `__sk_buff *ptr`. 55 | // `dataOut` (optional) will hold `skb->data` after run, if large enough. 56 | func Run(progFd, repeat int, data []byte, dataOut []byte) (int, int, int, error) { 57 | if data == nil { 58 | // http://elixir.free-electrons.com/linux/v4.12/source/net/bpf/test_run.c#L78 59 | // http://elixir.free-electrons.com/linux/v4.12/source/include/uapi/linux/if_ether.h#L32 60 | return -1, 0, 0, fmt.Errorf("data must be at least 14 bytes (corresponding to ETH_HLEN)") 61 | } 62 | var ( 63 | dataOutPtr *C.char 64 | dataOutLen C.int 65 | returnValue C.int 66 | duration C.int 67 | 68 | dataPtr = (*C.char)(unsafe.Pointer(&data[0])) 69 | dataLen = C.int(len(data)) 70 | ) 71 | if dataOut != nil { 72 | dataOutPtr = (*C.char)(unsafe.Pointer(&dataOut[0])) 73 | } 74 | ret, err := C.bpf_prog_test_run(C.int(progFd), C.int(repeat), dataPtr, dataLen, dataOutPtr, &dataOutLen, &returnValue, &duration) 75 | if ret != 0 { 76 | return -1, 0, 0, fmt.Errorf("bpf_prog_test_run failed: %v (%d)", err, ret) 77 | } 78 | return int(returnValue), int(duration), int(dataOutLen), nil 79 | } 80 | -------------------------------------------------------------------------------- /pkg/tracepipe/trace_pipe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Kinvolk 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tracepipe 16 | 17 | import ( 18 | "bufio" 19 | "fmt" 20 | "io" 21 | "os" 22 | "regexp" 23 | "strings" 24 | ) 25 | 26 | const tracePipeFile = "/sys/kernel/debug/tracing/trace_pipe" 27 | 28 | // TracePipe to read from /sys/kernel/debug/tracing/trace_pipe 29 | // Note that data can be read only once, i.e. if you have more than 30 | // one tracer / channel, only one will receive an event: 31 | // "Once data is read from this file, it is consumed, and will not be 32 | // read again with a sequential read." 33 | // https://www.kernel.org/doc/Documentation/trace/ftrace.txt 34 | type TracePipe struct { 35 | file *os.File 36 | reader *bufio.Reader 37 | stop chan struct{} 38 | } 39 | 40 | // TraceEvent contains the raw event as well as the contents of 41 | // every field as string, as defined under "Output format" in 42 | // https://www.kernel.org/doc/Documentation/trace/ftrace.txt 43 | type TraceEvent struct { 44 | Raw string 45 | Task string 46 | PID string 47 | CPU string 48 | Flags string 49 | Timestamp string 50 | Function string 51 | Message string 52 | } 53 | 54 | func New() (*TracePipe, error) { 55 | f, err := os.Open(tracePipeFile) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return &TracePipe{ 60 | file: f, 61 | reader: bufio.NewReader(f), 62 | stop: make(chan struct{}), 63 | }, nil 64 | } 65 | 66 | // A line from trace_pipe looks like (leading spaces included): 67 | // ` chromium-15581 [000] d... 92783.722567: : Hello, World!` 68 | var traceLineRegexp = regexp.MustCompile(`(.{16})-(\d+) +\[(\d{3})\] (.{4}) +(\d+\.\d+)\: (.*?)\: (.*)`) 69 | 70 | func parseTraceLine(raw string) (*TraceEvent, error) { 71 | fields := traceLineRegexp.FindStringSubmatch(raw) 72 | if len(fields) != 8 { 73 | return nil, fmt.Errorf("received unexpected input %q", raw) 74 | } 75 | return &TraceEvent{ 76 | Raw: raw, 77 | Task: strings.Trim(fields[1], " "), 78 | PID: fields[2], 79 | CPU: fields[3], 80 | Flags: fields[4], 81 | Timestamp: fields[5], 82 | Function: fields[6], 83 | Message: fields[7], 84 | }, nil 85 | } 86 | 87 | func (t *TracePipe) ReadLine() (*TraceEvent, error) { 88 | line, err := t.reader.ReadString('\n') 89 | if err != nil { 90 | return nil, err 91 | } 92 | traceEvent, err := parseTraceLine(line) 93 | if err != nil { 94 | return nil, err 95 | } 96 | return traceEvent, nil 97 | } 98 | 99 | func (t *TracePipe) Channel() (<-chan *TraceEvent, <-chan error) { 100 | channelEvents := make(chan *TraceEvent) 101 | channelErrors := make(chan error) 102 | go func() { 103 | for { 104 | select { 105 | case <-t.stop: 106 | return 107 | default: 108 | } 109 | traceEvent, err := t.ReadLine() 110 | if err != nil { 111 | if err == io.EOF { 112 | continue 113 | } 114 | channelErrors <- err 115 | } else { 116 | channelEvents <- traceEvent 117 | } 118 | } 119 | }() 120 | return channelEvents, channelErrors 121 | } 122 | 123 | func (t *TracePipe) Close() error { 124 | close(t.stop) 125 | return t.file.Close() 126 | } 127 | -------------------------------------------------------------------------------- /pkg/tracepipe/trace_pipe_test.go: -------------------------------------------------------------------------------- 1 | package tracepipe 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestParseTraceLine(t *testing.T) { 8 | testEvents := []struct { 9 | input string 10 | expected TraceEvent 11 | }{ 12 | { 13 | " chromium-15581 [000] d... 92783.722567: : Hello, World!", 14 | TraceEvent{ 15 | Task: "chromium", 16 | Function: "", 17 | Message: "Hello, World!", 18 | }, 19 | }, 20 | { 21 | " curl-18597 [000] dN.. 463.471554: : kretprobe__tcp_v4_connect - pid_tgid 79873506822309\n", 22 | TraceEvent{ 23 | Task: "curl", 24 | Function: "", 25 | Message: "kretprobe__tcp_v4_connect - pid_tgid 79873506822309", 26 | }, 27 | }, 28 | { 29 | " trace_pipe-23553 [000] .... 205825.968557: sys_enter: NR 0 (3, c420098000, 1000, 0, 0, 0)\n", 30 | TraceEvent{ 31 | Task: "trace_pipe", 32 | Function: "sys_enter", 33 | Message: "NR 0 (3, c420098000, 1000, 0, 0, 0)", 34 | }, 35 | }, 36 | { 37 | " trace_pipe-23553 [000] .... 205825.968557: sys_enter: hello: world\n", 38 | TraceEvent{ 39 | Task: "trace_pipe", 40 | Function: "sys_enter", 41 | Message: "hello: world", 42 | }, 43 | }, 44 | } 45 | for _, testEvent := range testEvents { 46 | result, err := parseTraceLine(testEvent.input) 47 | if err != nil { 48 | t.Errorf("%q could not be parsed", testEvent.input) 49 | } 50 | if testEvent.expected.Task != result.Task { 51 | t.Errorf("result task %q doesn't match expected %q", result.Task, testEvent.expected.Task) 52 | } 53 | if testEvent.expected.Function != result.Function { 54 | t.Errorf("result function %q doesn't match expected %q", result.Function, testEvent.expected.Function) 55 | } 56 | if testEvent.expected.Message != result.Message { 57 | t.Errorf("result message %q doesn't match expected %q", result.Message, testEvent.expected.Message) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | DUMMY_SRC=dummy.c 6 | DUMMY_OBJ=dummy.o 7 | 8 | clang -O2 -emit-llvm -c ${DUMMY_SRC} -o - | llc -march=bpf -filetype=obj -o ${DUMMY_OBJ} 9 | for kernel in 46 48 410 414; do 10 | clang -DKERNEL_VERSION="${kernel}" -O2 -emit-llvm -c ${DUMMY_SRC} -o - | llc -march=bpf -filetype=obj -o "dummy-${kernel}.o" 11 | done 12 | -------------------------------------------------------------------------------- /tests/dummy-410.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/gobpf/16120a1bf4d4abc1f9cf37fecfb86009a1631b9f/tests/dummy-410.o -------------------------------------------------------------------------------- /tests/dummy-414.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/gobpf/16120a1bf4d4abc1f9cf37fecfb86009a1631b9f/tests/dummy-414.o -------------------------------------------------------------------------------- /tests/dummy-46.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/gobpf/16120a1bf4d4abc1f9cf37fecfb86009a1631b9f/tests/dummy-46.o -------------------------------------------------------------------------------- /tests/dummy-48.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/gobpf/16120a1bf4d4abc1f9cf37fecfb86009a1631b9f/tests/dummy-48.o -------------------------------------------------------------------------------- /tests/dummy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Compiled with './build' 3 | */ 4 | 5 | #include "../elf/include/uapi/linux/bpf.h" 6 | #include "../elf/include/bpf_map.h" 7 | 8 | #define SEC(NAME) __attribute__((section(NAME), used)) 9 | 10 | #define PERF_MAX_STACK_DEPTH 127 11 | 12 | #define KERNEL_VERSION_GTE(X) (KERNEL_VERSION >= X) 13 | 14 | struct pt_regs{}; 15 | 16 | struct bpf_map_def SEC("maps/dummy_hash") dummy_hash = { 17 | .type = BPF_MAP_TYPE_HASH, 18 | .key_size = sizeof(int), 19 | .value_size = sizeof(unsigned int), 20 | .max_entries = 128, 21 | }; 22 | 23 | struct bpf_map_def SEC("maps/dummy_array") dummy_array = { 24 | .type = BPF_MAP_TYPE_ARRAY, 25 | .key_size = sizeof(int), 26 | .value_size = sizeof(unsigned int), 27 | .max_entries = 128, 28 | }; 29 | 30 | struct bpf_map_def SEC("maps/dummy_prog_array") dummy_prog_array = { 31 | .type = BPF_MAP_TYPE_PROG_ARRAY, 32 | .key_size = sizeof(int), 33 | .value_size = sizeof(unsigned int), 34 | .max_entries = 128, 35 | }; 36 | 37 | struct bpf_map_def SEC("maps/dummy_perf") dummy_perf = { 38 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, 39 | .key_size = sizeof(int), 40 | .value_size = sizeof(unsigned int), 41 | .max_entries = 128, 42 | }; 43 | 44 | #if KERNEL_VERSION_GTE(46) 45 | struct bpf_map_def SEC("maps/dummy_percpu_hash") dummy_percpu_hash = { 46 | .type = BPF_MAP_TYPE_PERCPU_HASH, 47 | .key_size = sizeof(int), 48 | .value_size = sizeof(unsigned int), 49 | .max_entries = 128, 50 | }; 51 | 52 | struct bpf_map_def SEC("maps/dummy_percpu_array") dummy_percpu_array = { 53 | .type = BPF_MAP_TYPE_PERCPU_ARRAY, 54 | .key_size = sizeof(int), 55 | .value_size = sizeof(unsigned int), 56 | .max_entries = 128, 57 | }; 58 | 59 | struct bpf_map_def SEC("maps/dummy_stack_trace") dummy_stack_trace = { 60 | .type = BPF_MAP_TYPE_STACK_TRACE, 61 | .key_size = sizeof(int), 62 | .value_size = PERF_MAX_STACK_DEPTH * sizeof(unsigned long long), 63 | .max_entries = 128, 64 | }; 65 | #endif 66 | 67 | #if KERNEL_VERSION_GTE(48) 68 | struct bpf_map_def SEC("maps/dummy_cgroup_array") dummy_cgroup_array = { 69 | .type = BPF_MAP_TYPE_CGROUP_ARRAY, 70 | .key_size = sizeof(int), 71 | .value_size = sizeof(unsigned int), 72 | .max_entries = 128, 73 | }; 74 | #endif 75 | 76 | struct bpf_map_def SEC("maps/dummy_array_custom") dummy_array_custom = { 77 | .type = BPF_MAP_TYPE_ARRAY, 78 | .key_size = sizeof(int), 79 | .value_size = sizeof(unsigned int), 80 | .max_entries = 128, 81 | .pinning = PIN_CUSTOM_NS, 82 | }; 83 | 84 | SEC("kprobe/dummy") 85 | int kprobe__dummy(struct pt_regs *ctx) 86 | { 87 | return 0; 88 | } 89 | 90 | SEC("kretprobe/dummy") 91 | int kretprobe__dummy(struct pt_regs *ctx) 92 | { 93 | return 0; 94 | } 95 | 96 | SEC("uprobe/dummy") 97 | int uprobe__dummy(struct pt_regs *ctx) 98 | { 99 | return 0; 100 | } 101 | 102 | SEC("uretprobe/dummy") 103 | int uretprobe__dummy(struct pt_regs *ctx) 104 | { 105 | return 0; 106 | } 107 | 108 | #if KERNEL_VERSION_GTE(410) 109 | SEC("cgroup/skb") 110 | int cgroup_skb__dummy(struct __sk_buff *skb) 111 | { 112 | return 1; 113 | } 114 | 115 | SEC("cgroup/sock") 116 | int cgroup_sock__dummy(struct __sk_buff *skb) 117 | { 118 | return 0; 119 | } 120 | #endif 121 | 122 | #if KERNEL_VERSION_GTE(47) 123 | SEC("tracepoint/raw_syscalls/sys_enter") 124 | int tracepoint__raw_sys_enter() 125 | { 126 | return 0; 127 | } 128 | #endif 129 | 130 | SEC("socket/dummy") 131 | int socket__dummy(struct __sk_buff *skb) 132 | { 133 | return 0; 134 | } 135 | 136 | #if KERNEL_VERSION_GTE(48) 137 | SEC("xdp/prog1") 138 | int xdp_drop(struct xdp_md *ctx) { 139 | return XDP_DROP; 140 | } 141 | 142 | SEC("xdp/prog2") 143 | int xdp_pass(struct xdp_md *ctx) { 144 | return XDP_PASS; 145 | } 146 | #endif 147 | 148 | unsigned int _version SEC("version") = 0xFFFFFFFE; 149 | -------------------------------------------------------------------------------- /tests/dummy.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/gobpf/16120a1bf4d4abc1f9cf37fecfb86009a1631b9f/tests/dummy.o --------------------------------------------------------------------------------