├── .gitignore ├── NOTICE-BSD2 ├── NOTICE-GPL2.0 ├── LICENSE-BSD2 ├── README.md ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── .clang-format ├── LICENSE-GPL2.0 ├── examples ├── bcc_sample.py └── bcc_sample.go └── src └── bcc_sensor.c /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NOTICE-BSD2: -------------------------------------------------------------------------------- 1 | cbsensor-linux-bpf 2 | Copyright 2020 VMware, Inc. 3 | 4 | This product is licensed to you under the BSD-2 license (the "License"). You may not use this product except in compliance with the BSD-2 License. 5 | 6 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 7 | 8 | -------------------------------------------------------------------------------- /NOTICE-GPL2.0: -------------------------------------------------------------------------------- 1 | cbsensor-linux-bpf 2 | Copyright 2020 VMware, Inc. 3 | 4 | This product is licensed to you under the GNU GENERAL PUBLIC LICENSE Version 2 license (the "License"). You may not use this product except in compliance with the GPL 2.0 License. 5 | 6 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 7 | 8 | -------------------------------------------------------------------------------- /LICENSE-BSD2: -------------------------------------------------------------------------------- 1 | cbsensor-linux-bpf 2 | Copyright 2020 VMware, Inc. 3 | 4 | The BSD-2 license (the "License") set forth below applies to all files of the cbsensor-linux-bpf project that have an SPDX marker identifying BSD-2 as the applicable license. You may not use this file except in compliance with the License. 5 | 6 | BSD-2 License 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 | 12 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # cbsensor-linux-bpf 3 | 4 | ## Overview 5 | 6 | VMware has ended active development of this project, this repository will no longer be updated. 7 | 8 | The cbsensor-linux-bpf project currently provides a [BCC](https://github.com/iovisor/bcc) compatibile BPF C source code for general process, file and network events. 9 | 10 | ## Try it out [Python] 11 | Run the example [script](examples/bcc_sample.py) on your favorite Linux distro with BCC. Just run with root-like privileges, or whatever privileges you need to load a BPF program. 12 | 13 | ```bash 14 | sudo ./examples/bcc_sample.py ./src/bcc_sensor.c 15 | ``` 16 | 17 | ## Try it out [Golang] 18 | Run the example [code](examples/bcc_sample.go) with golang (version >= 1.13). This code is tested on Ubuntu 20.04 LTS version but should work on any other Linux distros. 19 | * The golang program takes optional argument viz. BPF program name. The default value is src/bcc_sensor.c 20 | * The code can be compiled using "go build" or directly run with "go run" command as should below. 21 | * Root-like privileges are needed to load the BPF program. 22 | 23 | ```bash 24 | go build examples/bcc_sample.go 25 | sudo ./bcc_sample 26 | 27 | OR 28 | 29 | sudo -E go run examples/bcc_sample.go 30 | ``` 31 | 32 | ### Prerequisites 33 | * Works on 4.4 kernels and newer! 34 | * bcc or libbpf for Ubuntu distros 35 | * More bleeding edge kernels might require a newer version of BCC than your distro provides 36 | 37 | ## Documentation 38 | 39 | ### Limitations and Known Issues 40 | 1. Endianness on ports for network events are not all host aligned yet 41 | 2. 4.4 kernels may experience some event data integrity issues 42 | 3. Filepaths have a hard limit on path components returned 43 | 44 | ### Roadmaped Enhancements 45 | 1. Basic packet dropping via `tc` BPF interface 46 | 2. Inode Delete Events 47 | 3. Retrieve files open for exec recursively 48 | 49 | ### Long Term Goals 50 | A potential goal for this project is to eventually create a path to a libbpf + CO-RE BPF based project. 51 | 52 | ## Contributing 53 | The cbsensor-linux-bpf project team welcomes contributions from the community. Before you start working with cbsensor-linux-bpf, please 54 | read our [Developer Certificate of Origin](https://cla.vmware.com/dco). All contributions to this repository must be 55 | signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on 56 | as an open-source patch. For more detailed information, refer to [CONTRIBUTING.md](CONTRIBUTING.md). 57 | [CONTRIBUTING.md](CONTRIBUTING.md) 58 | 59 | ## License 60 | The cbsensor-linux-bpf licenses the BPF kernel space source code under [GNU GPL v2.0](LICENSE-GPL2.0). The example usage source code is licensed under [BSD 2](LICENSE-BSD2). 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to cbsensor-linux-bpf 3 | 4 | The cbsensor-linux-bpf project team welcomes contributions from the community. Before you start working with cbsensor-linux-bpf, please 5 | read our [Developer Certificate of Origin](https://cla.vmware.com/dco). All contributions to this repository must be 6 | signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on 7 | as an open-source patch. 8 | 9 | ## Contribution Flow 10 | 11 | This is a rough outline of what a contributor's workflow looks like: 12 | 13 | - Create a topic branch from where you want to base your work 14 | - Make commits of logical units 15 | - Make sure your commit messages are in the proper format (see below) 16 | - Push your changes to a topic branch in your fork of the repository 17 | - Submit a pull request 18 | 19 | Example: 20 | 21 | ``` shell 22 | git remote add upstream https://github.com/vmware/cbsensor-linux-bpf.git 23 | git checkout -b my-new-feature master 24 | git commit -a 25 | git push origin my-new-feature 26 | ``` 27 | 28 | ### Staying In Sync With Upstream 29 | 30 | When your branch gets out of sync with the vmware/master branch, use the following to update: 31 | 32 | ``` shell 33 | git checkout my-new-feature 34 | git fetch -a 35 | git pull --rebase upstream master 36 | git push --force-with-lease origin my-new-feature 37 | ``` 38 | 39 | ### Updating pull requests 40 | 41 | If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into 42 | existing commits. 43 | 44 | If your pull request contains a single commit or your changes are related to the most recent commit, you can simply 45 | amend the commit. 46 | 47 | ``` shell 48 | git add . 49 | git commit --amend 50 | git push --force-with-lease origin my-new-feature 51 | ``` 52 | 53 | If you need to squash changes into an earlier commit, you can use: 54 | 55 | ``` shell 56 | git add . 57 | git commit --fixup 58 | git rebase -i --autosquash master 59 | git push --force-with-lease origin my-new-feature 60 | ``` 61 | 62 | Be sure to add a comment to the PR indicating your new changes are ready to review, as GitHub does not generate a 63 | notification when you git push. 64 | 65 | ### Code Style 66 | 67 | ### Formatting Commit Messages 68 | 69 | We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). 70 | 71 | Be sure to include any related GitHub issue references in the commit message. See 72 | [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) for referencing issues 73 | and commits. 74 | 75 | ## Reporting Bugs and Creating Issues 76 | 77 | When opening a new issue, try to roughly follow the commit message format conventions above. 78 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | In the interest of fostering an open and welcoming environment, we as 7 | contributors and maintainers pledge to making participation in cbsensor-linux-bpf project and 8 | our community a harassment-free experience for everyone, regardless of age, body 9 | size, disability, ethnicity, sex characteristics, gender identity and expression, 10 | level of experience, education, socio-economic status, nationality, personal 11 | appearance, race, religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | * The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | * Trolling, insulting/derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying the standards of acceptable 38 | behavior and are expected to take appropriate and fair corrective action in 39 | response to any instances of unacceptable behavior. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or 42 | reject comments, commits, code, wiki edits, issues, and other contributions 43 | that are not aligned to this Code of Conduct, or to ban temporarily or 44 | permanently any contributor for other behaviors that they deem inappropriate, 45 | threatening, offensive, or harmful. 46 | 47 | ## Scope 48 | 49 | This Code of Conduct applies both within project spaces and in public spaces 50 | when an individual is representing the project or its community. Examples of 51 | representing a project or community include using an official project e-mail 52 | address, posting via an official social media account, or acting as an appointed 53 | representative at an online or offline event. Representation of a project may be 54 | further defined and clarified by project maintainers. 55 | 56 | ## Enforcement 57 | 58 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 59 | reported by contacting the project team at oss-coc@vmware.com. All 60 | complaints will be reviewed and investigated and will result in a response that 61 | is deemed necessary and appropriate to the circumstances. The project team is 62 | obligated to maintain confidentiality with regard to the reporter of an incident. 63 | Further details of specific enforcement policies may be posted separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | 76 | For answers to common questions about this code of conduct, see 77 | https://www.contributor-covenant.org/faq 78 | 79 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 9. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | AlignEscapedNewlines: Left # Unknown to clang-format-4.0 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false # Unknown to clang-format-5.0 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | SplitEmptyFunction: true # Unknown to clang-format-4.0 45 | SplitEmptyRecord: true # Unknown to clang-format-4.0 46 | SplitEmptyNamespace: true # Unknown to clang-format-4.0 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | CompactNamespaces: false # Unknown to clang-format-4.0 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | FixNamespaceComments: false # Unknown to clang-format-4.0 66 | 67 | IncludeBlocks: Preserve # Unknown to clang-format-5.0 68 | IncludeCategories: 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: None # Unknown to clang-format-5.0 74 | IndentWidth: 8 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: false 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: None 83 | ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 84 | ObjCBlockIndentWidth: 8 85 | ObjCSpaceAfterProperty: true 86 | ObjCSpaceBeforeProtocolList: true 87 | 88 | # Taken from git's rules 89 | PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 90 | PenaltyBreakBeforeFirstCallParameter: 30 91 | PenaltyBreakComment: 10 92 | PenaltyBreakFirstLessLess: 0 93 | PenaltyBreakString: 10 94 | PenaltyExcessCharacter: 100 95 | PenaltyReturnTypeOnItsOwnLine: 60 96 | 97 | PointerAlignment: Right 98 | ReflowComments: false 99 | SortIncludes: false 100 | SortUsingDeclarations: false # Unknown to clang-format-4.0 101 | SpaceAfterCStyleCast: false 102 | SpaceAfterTemplateKeyword: true 103 | SpaceBeforeAssignmentOperators: true 104 | SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 105 | SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 106 | SpaceBeforeParens: ControlStatements 107 | SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 108 | SpaceInEmptyParentheses: false 109 | SpacesBeforeTrailingComments: 1 110 | SpacesInAngles: false 111 | SpacesInContainerLiterals: false 112 | SpacesInCStyleCastParentheses: false 113 | SpacesInParentheses: false 114 | SpacesInSquareBrackets: false 115 | Standard: Cpp03 116 | TabWidth: 8 117 | UseTab: Always 118 | ... 119 | -------------------------------------------------------------------------------- /LICENSE-GPL2.0: -------------------------------------------------------------------------------- 1 | cbsensor-linux-bpf 2 | Copyright 2020 VMware, Inc. 3 | 4 | The GNU GENERAL PUBLIC LICENSE Version 2 license (the "License") set forth below applies to all parts of the cbsensor-linux-bpf project. You may not use this file except in compliance with the License. 5 | 6 | GNU GENERAL PUBLIC LICENSE 7 | Version 2, June 1991 8 | 9 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 10 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | License is intended to guarantee your freedom to share and change free 19 | software--to make sure the software is free for all its users. This 20 | General Public License applies to most of the Free Software 21 | Foundation's software and to any other program whose authors commit to 22 | using it. (Some other Free Software Foundation software is covered by 23 | the GNU Lesser General Public License instead.) You can apply it to 24 | your programs, too. 25 | 26 | When we speak of free software, we are referring to freedom, not 27 | price. Our General Public Licenses are designed to make sure that you 28 | have the freedom to distribute copies of free software (and charge for 29 | this service if you wish), that you receive source code or can get it 30 | if you want it, that you can change the software or use pieces of it 31 | in new free programs; and that you know you can do these things. 32 | 33 | To protect your rights, we need to make restrictions that forbid 34 | anyone to deny you these rights or to ask you to surrender the rights. 35 | These restrictions translate to certain responsibilities for you if you 36 | distribute copies of the software, or if you modify it. 37 | 38 | For example, if you distribute copies of such a program, whether 39 | gratis or for a fee, you must give the recipients all the rights that 40 | you have. You must make sure that they, too, receive or can get the 41 | source code. And you must show them these terms so they know their 42 | rights. 43 | 44 | We protect your rights with two steps: (1) copyright the software, and 45 | (2) offer you this license which gives you legal permission to copy, 46 | distribute and/or modify the software. 47 | 48 | Also, for each author's protection and ours, we want to make certain 49 | that everyone understands that there is no warranty for this free 50 | software. If the software is modified by someone else and passed on, we 51 | want its recipients to know that what they have is not the original, so 52 | that any problems introduced by others will not reflect on the original 53 | authors' reputations. 54 | 55 | Finally, any free program is threatened constantly by software 56 | patents. We wish to avoid the danger that redistributors of a free 57 | program will individually obtain patent licenses, in effect making the 58 | program proprietary. To prevent this, we have made it clear that any 59 | patent must be licensed for everyone's free use or not licensed at all. 60 | 61 | The precise terms and conditions for copying, distribution and 62 | modification follow. 63 | 64 | GNU GENERAL PUBLIC LICENSE 65 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 66 | 67 | 0. This License applies to any program or other work which contains 68 | a notice placed by the copyright holder saying it may be distributed 69 | under the terms of this General Public License. The "Program", below, 70 | refers to any such program or work, and a "work based on the Program" 71 | means either the Program or any derivative work under copyright law: 72 | that is to say, a work containing the Program or a portion of it, 73 | either verbatim or with modifications and/or translated into another 74 | language. (Hereinafter, translation is included without limitation in 75 | the term "modification".) Each licensee is addressed as "you". 76 | 77 | Activities other than copying, distribution and modification are not 78 | covered by this License; they are outside its scope. The act of 79 | running the Program is not restricted, and the output from the Program 80 | is covered only if its contents constitute a work based on the 81 | Program (independent of having been made by running the Program). 82 | Whether that is true depends on what the Program does. 83 | 84 | 1. You may copy and distribute verbatim copies of the Program's 85 | source code as you receive it, in any medium, provided that you 86 | conspicuously and appropriately publish on each copy an appropriate 87 | copyright notice and disclaimer of warranty; keep intact all the 88 | notices that refer to this License and to the absence of any warranty; 89 | and give any other recipients of the Program a copy of this License 90 | along with the Program. 91 | 92 | You may charge a fee for the physical act of transferring a copy, and 93 | you may at your option offer warranty protection in exchange for a fee. 94 | 95 | 2. You may modify your copy or copies of the Program or any portion 96 | of it, thus forming a work based on the Program, and copy and 97 | distribute such modifications or work under the terms of Section 1 98 | above, provided that you also meet all of these conditions: 99 | 100 | a) You must cause the modified files to carry prominent notices 101 | stating that you changed the files and the date of any change. 102 | 103 | b) You must cause any work that you distribute or publish, that in 104 | whole or in part contains or is derived from the Program or any 105 | part thereof, to be licensed as a whole at no charge to all third 106 | parties under the terms of this License. 107 | 108 | c) If the modified program normally reads commands interactively 109 | when run, you must cause it, when started running for such 110 | interactive use in the most ordinary way, to print or display an 111 | announcement including an appropriate copyright notice and a 112 | notice that there is no warranty (or else, saying that you provide 113 | a warranty) and that users may redistribute the program under 114 | these conditions, and telling the user how to view a copy of this 115 | License. (Exception: if the Program itself is interactive but 116 | does not normally print such an announcement, your work based on 117 | the Program is not required to print an announcement.) 118 | 119 | These requirements apply to the modified work as a whole. If 120 | identifiable sections of that work are not derived from the Program, 121 | and can be reasonably considered independent and separate works in 122 | themselves, then this License, and its terms, do not apply to those 123 | sections when you distribute them as separate works. But when you 124 | distribute the same sections as part of a whole which is a work based 125 | on the Program, the distribution of the whole must be on the terms of 126 | this License, whose permissions for other licensees extend to the 127 | entire whole, and thus to each and every part regardless of who wrote it. 128 | 129 | Thus, it is not the intent of this section to claim rights or contest 130 | your rights to work written entirely by you; rather, the intent is to 131 | exercise the right to control the distribution of derivative or 132 | collective works based on the Program. 133 | 134 | In addition, mere aggregation of another work not based on the Program 135 | with the Program (or with a work based on the Program) on a volume of 136 | a storage or distribution medium does not bring the other work under 137 | the scope of this License. 138 | 139 | 3. You may copy and distribute the Program (or a work based on it, 140 | under Section 2) in object code or executable form under the terms of 141 | Sections 1 and 2 above provided that you also do one of the following: 142 | 143 | a) Accompany it with the complete corresponding machine-readable 144 | source code, which must be distributed under the terms of Sections 145 | 1 and 2 above on a medium customarily used for software interchange; or, 146 | 147 | b) Accompany it with a written offer, valid for at least three 148 | years, to give any third party, for a charge no more than your 149 | cost of physically performing source distribution, a complete 150 | machine-readable copy of the corresponding source code, to be 151 | distributed under the terms of Sections 1 and 2 above on a medium 152 | customarily used for software interchange; or, 153 | 154 | c) Accompany it with the information you received as to the offer 155 | to distribute corresponding source code. (This alternative is 156 | allowed only for noncommercial distribution and only if you 157 | received the program in object code or executable form with such 158 | an offer, in accord with Subsection b above.) 159 | 160 | The source code for a work means the preferred form of the work for 161 | making modifications to it. For an executable work, complete source 162 | code means all the source code for all modules it contains, plus any 163 | associated interface definition files, plus the scripts used to 164 | control compilation and installation of the executable. However, as a 165 | special exception, the source code distributed need not include 166 | anything that is normally distributed (in either source or binary 167 | form) with the major components (compiler, kernel, and so on) of the 168 | operating system on which the executable runs, unless that component 169 | itself accompanies the executable. 170 | 171 | If distribution of executable or object code is made by offering 172 | access to copy from a designated place, then offering equivalent 173 | access to copy the source code from the same place counts as 174 | distribution of the source code, even though third parties are not 175 | compelled to copy the source along with the object code. 176 | 177 | 4. You may not copy, modify, sublicense, or distribute the Program 178 | except as expressly provided under this License. Any attempt 179 | otherwise to copy, modify, sublicense or distribute the Program is 180 | void, and will automatically terminate your rights under this License. 181 | However, parties who have received copies, or rights, from you under 182 | this License will not have their licenses terminated so long as such 183 | parties remain in full compliance. 184 | 185 | 5. You are not required to accept this License, since you have not 186 | signed it. However, nothing else grants you permission to modify or 187 | distribute the Program or its derivative works. These actions are 188 | prohibited by law if you do not accept this License. Therefore, by 189 | modifying or distributing the Program (or any work based on the 190 | Program), you indicate your acceptance of this License to do so, and 191 | all its terms and conditions for copying, distributing or modifying 192 | the Program or works based on it. 193 | 194 | 6. Each time you redistribute the Program (or any work based on the 195 | Program), the recipient automatically receives a license from the 196 | original licensor to copy, distribute or modify the Program subject to 197 | these terms and conditions. You may not impose any further 198 | restrictions on the recipients' exercise of the rights granted herein. 199 | You are not responsible for enforcing compliance by third parties to 200 | this License. 201 | 202 | 7. If, as a consequence of a court judgment or allegation of patent 203 | infringement or for any other reason (not limited to patent issues), 204 | conditions are imposed on you (whether by court order, agreement or 205 | otherwise) that contradict the conditions of this License, they do not 206 | excuse you from the conditions of this License. If you cannot 207 | distribute so as to satisfy simultaneously your obligations under this 208 | License and any other pertinent obligations, then as a consequence you 209 | may not distribute the Program at all. For example, if a patent 210 | license would not permit royalty-free redistribution of the Program by 211 | all those who receive copies directly or indirectly through you, then 212 | the only way you could satisfy both it and this License would be to 213 | refrain entirely from distribution of the Program. 214 | 215 | If any portion of this section is held invalid or unenforceable under 216 | any particular circumstance, the balance of the section is intended to 217 | apply and the section as a whole is intended to apply in other 218 | circumstances. 219 | 220 | It is not the purpose of this section to induce you to infringe any 221 | patents or other property right claims or to contest validity of any 222 | such claims; this section has the sole purpose of protecting the 223 | integrity of the free software distribution system, which is 224 | implemented by public license practices. Many people have made 225 | generous contributions to the wide range of software distributed 226 | through that system in reliance on consistent application of that 227 | system; it is up to the author/donor to decide if he or she is willing 228 | to distribute software through any other system and a licensee cannot 229 | impose that choice. 230 | 231 | This section is intended to make thoroughly clear what is believed to 232 | be a consequence of the rest of this License. 233 | 234 | 8. If the distribution and/or use of the Program is restricted in 235 | certain countries either by patents or by copyrighted interfaces, the 236 | original copyright holder who places the Program under this License 237 | may add an explicit geographical distribution limitation excluding 238 | those countries, so that distribution is permitted only in or among 239 | countries not thus excluded. In such case, this License incorporates 240 | the limitation as if written in the body of this License. 241 | 242 | 9. The Free Software Foundation may publish revised and/or new versions 243 | of the General Public License from time to time. Such new versions will 244 | be similar in spirit to the present version, but may differ in detail to 245 | address new problems or concerns. 246 | 247 | Each version is given a distinguishing version number. If the Program 248 | specifies a version number of this License which applies to it and "any 249 | later version", you have the option of following the terms and conditions 250 | either of that version or of any later version published by the Free 251 | Software Foundation. If the Program does not specify a version number of 252 | this License, you may choose any version ever published by the Free Software 253 | Foundation. 254 | 255 | 10. If you wish to incorporate parts of the Program into other free 256 | programs whose distribution conditions are different, write to the author 257 | to ask for permission. For software which is copyrighted by the Free 258 | Software Foundation, write to the Free Software Foundation; we sometimes 259 | make exceptions for this. Our decision will be guided by the two goals 260 | of preserving the free status of all derivatives of our free software and 261 | of promoting the sharing and reuse of software generally. 262 | 263 | NO WARRANTY 264 | 265 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 266 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 267 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 268 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 269 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 270 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 271 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 272 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 273 | REPAIR OR CORRECTION. 274 | 275 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 276 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 277 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 278 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 279 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 280 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 281 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 282 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 283 | POSSIBILITY OF SUCH DAMAGES. 284 | 285 | END OF TERMS AND CONDITIONS 286 | 287 | How to Apply These Terms to Your New Programs 288 | 289 | If you develop a new program, and you want it to be of the greatest 290 | possible use to the public, the best way to achieve this is to make it 291 | free software which everyone can redistribute and change under these terms. 292 | 293 | To do so, attach the following notices to the program. It is safest 294 | to attach them to the start of each source file to most effectively 295 | convey the exclusion of warranty; and each file should have at least 296 | the "copyright" line and a pointer to where the full notice is found. 297 | 298 | 299 | Copyright (C) 300 | 301 | This program is free software; you can redistribute it and/or modify 302 | it under the terms of the GNU General Public License as published by 303 | the Free Software Foundation; either version 2 of the License, or 304 | (at your option) any later version. 305 | 306 | This program is distributed in the hope that it will be useful, 307 | but WITHOUT ANY WARRANTY; without even the implied warranty of 308 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 309 | GNU General Public License for more details. 310 | 311 | You should have received a copy of the GNU General Public License along 312 | with this program; if not, write to the Free Software Foundation, Inc., 313 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 314 | 315 | Also add information on how to contact you by electronic and paper mail. 316 | 317 | If the program is interactive, make it output a short notice like this 318 | when it starts in an interactive mode: 319 | 320 | Gnomovision version 69, Copyright (C) year name of author 321 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 322 | This is free software, and you are welcome to redistribute it 323 | under certain conditions; type `show c' for details. 324 | 325 | The hypothetical commands `show w' and `show c' should show the appropriate 326 | parts of the General Public License. Of course, the commands you use may 327 | be called something other than `show w' and `show c'; they could even be 328 | mouse-clicks or menu items--whatever suits your program. 329 | 330 | You should also get your employer (if you work as a programmer) or your 331 | school, if any, to sign a "copyright disclaimer" for the program, if 332 | necessary. Here is a sample; alter the names: 333 | 334 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 335 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 336 | 337 | , 1 April 1989 338 | Ty Coon, President of Vice 339 | 340 | This General Public License does not permit incorporating your program into 341 | proprietary programs. If your program is a subroutine library, you may 342 | consider it more useful to permit linking proprietary applications with the 343 | library. If this is what you want to do, use the GNU Lesser General 344 | Public License instead of this License. 345 | 346 | -------------------------------------------------------------------------------- /examples/bcc_sample.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright 2020-2021 VMware, Inc. 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | 7 | import ctypes 8 | import socket 9 | import struct 10 | from bcc import BPF 11 | import argparse 12 | 13 | class NetEventData(ctypes.Structure): 14 | _fields_ = [ 15 | ('local_addr', ctypes.c_uint), 16 | ('remote_addr', ctypes.c_uint), 17 | ('remote_port', ctypes.c_ushort), 18 | ('local_port', ctypes.c_ushort), 19 | ('ipver', ctypes.c_ushort), 20 | ('proto', ctypes.c_ushort), 21 | ('dns_flag', ctypes.c_ushort), 22 | ('local_addr6', ctypes.c_uint * 4), 23 | ('remote_addr6', ctypes.c_uint * 4), 24 | ('dns', ctypes.c_char * 40), 25 | ('name_len', ctypes.c_uint), 26 | ] 27 | 28 | class MmapArgs(ctypes.Structure): 29 | _fields_ = [ 30 | ('flags', ctypes.c_ulonglong), 31 | ('prot', ctypes.c_ulonglong), 32 | ] 33 | 34 | class SensorUnion(ctypes.Union): 35 | _fields_ = [ 36 | ('mmap_args', MmapArgs), 37 | ('fname', ctypes.c_char * 255), 38 | ('net', NetEventData), 39 | ] 40 | 41 | class SensorEventMessage(ctypes.Structure): 42 | _fields_ = [ 43 | ('event_time', ctypes.c_ulonglong), 44 | ('tid', ctypes.c_uint), 45 | ('pid', ctypes.c_uint), 46 | ('ev_type', ctypes.c_ubyte), 47 | ('state', ctypes.c_ubyte), 48 | ('uid', ctypes.c_uint), 49 | ('ppid', ctypes.c_uint), 50 | ('inode', ctypes.c_ulonglong), 51 | ('device', ctypes.c_uint), 52 | ('mnt_ns', ctypes.c_uint), 53 | ('union', SensorUnion), 54 | ('retval', ctypes.c_int), 55 | ('start_time', ctypes.c_ulonglong), 56 | ] 57 | 58 | class Probe(object): 59 | def __init__(self, pp, pp_cb_name, is_kretprobe=False): 60 | self.pp = pp 61 | self.pp_cb_name = pp_cb_name 62 | self.is_kretprobe = is_kretprobe 63 | 64 | class EventType(object): 65 | PROCESS_ARG = 0 66 | PROCESS_EXEC = 1 67 | PROCESS_EXIT = 2 68 | PROCESS_CLONE = 3 69 | FILE_READ = 4 70 | FILE_WRITE = 5 71 | FILE_CREATE = 6 72 | FILE_MMAP = 8 73 | FILE_TEST = 9 74 | CONNECT_PRE = 10 75 | CONNECT_ACCEPT = 11 76 | DNS_RESPONSE = 12 77 | WEB_PROXY = 13 78 | FILE_DELETE = 14 79 | FILE_CLOSE = 15 80 | FILE_OPEN = 16 81 | 82 | event_type_map = { 83 | PROCESS_ARG : 'PROCESS_ARG', 84 | PROCESS_EXEC : 'PROCESS_EXEC', 85 | PROCESS_EXIT : 'PROCESS_EXIT', 86 | PROCESS_CLONE : 'PROCESS_CLONE', 87 | FILE_READ : 'FILE_READ', 88 | FILE_WRITE : 'FILE_WRITE', 89 | FILE_CREATE : 'FILE_CREATE', 90 | FILE_MMAP : 'FILE_MMAP', 91 | FILE_TEST : 'FILE_TEST', 92 | CONNECT_PRE : 'NET_CONNECT', 93 | CONNECT_ACCEPT : 'NET_ACCEPT', 94 | DNS_RESPONSE : 'DNS_RESPONSE', 95 | WEB_PROXY : 'WEB_PROXY', 96 | FILE_DELETE : 'FILE_DELETE', 97 | FILE_CLOSE : 'FILE_CLOSE', 98 | FILE_OPEN: 'FILE_OPEN' 99 | } 100 | 101 | enabled_types_map = { 102 | PROCESS_ARG : True, 103 | PROCESS_EXEC : True, 104 | PROCESS_EXIT : True, 105 | PROCESS_CLONE : True, 106 | FILE_READ : True, 107 | FILE_WRITE : True, 108 | FILE_CREATE : True, 109 | FILE_MMAP : True, 110 | FILE_TEST : True, 111 | CONNECT_PRE : True, 112 | CONNECT_ACCEPT : True, 113 | DNS_RESPONSE : True, 114 | WEB_PROXY : True, 115 | FILE_DELETE : True, 116 | FILE_CLOSE : True, 117 | FILE_OPEN: True 118 | } 119 | 120 | PP_NO_EXTRA_DATA = 0 121 | PP_ENTRY_POINT = 1 122 | PP_PATH_COMPONENT = 2 123 | PP_FINALIZED = 3 124 | PP_APPEND = 4 125 | PP_DEBUG = 5 126 | 127 | msg_state = { 128 | PP_NO_EXTRA_DATA : 'PP_NO_EXTRA_DATA', 129 | PP_ENTRY_POINT : 'PP_ENTRY_POINT', 130 | PP_PATH_COMPONENT : 'PP_PATH_COMPONENT', 131 | PP_FINALIZED : 'PP_FINALIZED', 132 | PP_APPEND : 'PP_APPEND', 133 | PP_DEBUG : 'PP_DEBUG', 134 | } 135 | 136 | def SetTypeEnabledState(self, args): 137 | self.enabled_types_map[self.FILE_MMAP] = not args.disable_mmap 138 | self.enabled_types_map[self.FILE_READ] = not args.disable_file 139 | self.enabled_types_map[self.FILE_WRITE] = not args.disable_file 140 | self.enabled_types_map[self.FILE_CREATE] = not args.disable_file 141 | self.enabled_types_map[self.FILE_DELETE] = not args.disable_file 142 | self.enabled_types_map[self.FILE_CLOSE] = not args.disable_file 143 | self.enabled_types_map[self.FILE_OPEN] = not args.disable_file 144 | self.enabled_types_map[self.PROCESS_ARG] = not args.disable_process 145 | self.enabled_types_map[self.PROCESS_EXEC] = not args.disable_process 146 | self.enabled_types_map[self.PROCESS_EXIT] = not args.disable_process 147 | self.enabled_types_map[self.PROCESS_CLONE] = not args.disable_process 148 | self.enabled_types_map[self.CONNECT_PRE] = not args.disable_net 149 | self.enabled_types_map[self.CONNECT_ACCEPT] = not args.disable_net 150 | self.enabled_types_map[self.WEB_PROXY] = not args.disable_net 151 | self.enabled_types_map[self.DNS_RESPONSE] = not args.disable_dns 152 | 153 | def IsTypeEnabled(self, type): 154 | return self.enabled_types_map[type] 155 | 156 | EVENT_TYPE = EventType() 157 | 158 | class FileEvent(object): 159 | filepath = "" 160 | mounts = None 161 | 162 | def __init__(self, event_msg): 163 | self.ev_type = event_msg.ev_type 164 | self.event_time = event_msg.event_time 165 | self.tid = event_msg.tid 166 | self.pid = event_msg.pid 167 | self.ppid = event_msg.ppid 168 | self.uid = event_msg.uid 169 | self.inode = event_msg.inode 170 | self.device = event_msg.device 171 | self.mnt_ns = event_msg.mnt_ns 172 | if event_msg.ev_type in EVENT_TYPE.event_type_map: 173 | self.event_type_str = EVENT_TYPE.event_type_map[event_msg.ev_type] 174 | else: 175 | self.event_type_str = "UNKNOWN" 176 | 177 | # We could poll /proc/self/mounts then 178 | # get the devices and mountpoints from /proc/self/mountinfo 179 | def get_mounts(self): 180 | if not self.mounts: 181 | self.mounts = {} 182 | fh = open("/proc/self/mountinfo", "r") 183 | for line in fh: 184 | x = line.split(" ") 185 | dev = x[2].split(":") 186 | # Device major minor in dev_t may be architecture specific 187 | # Not sure how well this holds up 188 | dev_num = (int(dev[0]) << 8) | (int(dev[1])) 189 | self.mounts[dev_num] = x[4][1:] 190 | #print("%d:%d => %#x" % (int(dev[0]), int(dev[1]), dev_num)) 191 | return self.mounts 192 | 193 | def get_mount_name(self): 194 | _mounts = self.get_mounts() 195 | if _mounts: 196 | if self.device in _mounts: 197 | return _mounts[self.device] 198 | return "" 199 | 200 | def update(self, event_msg): 201 | if event_msg.state == EVENT_TYPE.PP_PATH_COMPONENT: 202 | name = event_msg.union.fname.decode() 203 | if not len(name): 204 | name = self.get_mount_name() 205 | self.filepath = '/%s%s' % (name, self.filepath) 206 | elif (event_msg.state == EVENT_TYPE.PP_FINALIZED): 207 | return self.logstr() 208 | 209 | # Perhaps add mmap args 210 | # uid may not always be set 211 | def logstr(self): 212 | file_event_str = '%lu %s pid:%d ppid:%d uid:%d mnt_ns:%d [%x:%lu]%s' % ( 213 | self.event_time, 214 | self.event_type_str, 215 | self.pid, 216 | self.ppid, 217 | self.uid, 218 | self.mnt_ns, 219 | self.device, 220 | self.inode, 221 | self.filepath, 222 | ) 223 | return file_event_str 224 | 225 | class CloneEvent(object): 226 | filepath = "" 227 | def __init__(self, event_msg): 228 | self.event_time = event_msg.event_time 229 | self.tid = event_msg.tid 230 | self.pid = event_msg.pid 231 | self.uid = event_msg.uid 232 | self.start_time = event_msg.start_time 233 | self.ppid = event_msg.ppid 234 | self.inode = event_msg.inode 235 | self.device = event_msg.device 236 | self.mnt_ns = event_msg.mnt_ns 237 | self.comm = event_msg.union.fname.decode() 238 | 239 | def logstr(self): 240 | pathstr = self.filepath 241 | if not pathstr: 242 | pathstr = self.comm 243 | event_str = '%lu FORK pid:%d ppid:%d uid:%d start_time:%lu mnt_ns:%d [%x:%lu]%s' % ( 244 | self.event_time, 245 | self.pid, 246 | self.ppid, 247 | self.uid, 248 | self.start_time, 249 | self.mnt_ns, 250 | self.device, 251 | self.inode, 252 | pathstr, 253 | ) 254 | return event_str 255 | 256 | # 257 | # Eventually handle script filepath and exe filepath loads 258 | # 259 | class ExecEvent(object): 260 | retval = -1 261 | finalize_filepath = False 262 | set_entrypoint_data = False 263 | script_path = "" 264 | filepath = "" 265 | 266 | def __init__(self, event_msg): 267 | self.event_time = event_msg.event_time 268 | self.tid = event_msg.tid 269 | self.pid = event_msg.pid 270 | self.arg_str = event_msg.union.fname.decode() 271 | self.start_time = 0 272 | self.ppid = 0 273 | self.uid = 0 274 | self.inode = 0 275 | self.device = 0 276 | self.mnt_ns = 0 277 | 278 | def update(self, event_msg): 279 | # print(EVENT_TYPE.event_type_map[event_msg.ev_type], EVENT_TYPE.msg_state[event_msg.state]) 280 | if event_msg.ev_type == EVENT_TYPE.PROCESS_ARG: 281 | if event_msg.state == EVENT_TYPE.PP_FINALIZED: 282 | self.retval = event_msg.retval 283 | return self.logstr() 284 | elif event_msg.state == EVENT_TYPE.PP_ENTRY_POINT: 285 | self.arg_str += ' ' + event_msg.union.fname.decode() 286 | elif event_msg.state == EVENT_TYPE.PP_APPEND: 287 | self.arg_str += event_msg.union.fname.decode() 288 | 289 | if event_msg.ev_type == EVENT_TYPE.PROCESS_EXEC: 290 | if event_msg.state == EVENT_TYPE.PP_ENTRY_POINT: 291 | self.start_time = event_msg.start_time 292 | self.ppid = event_msg.ppid 293 | self.uid = event_msg.uid 294 | self.inode = event_msg.inode 295 | self.device = event_msg.device 296 | self.mnt_ns = event_msg.mnt_ns 297 | elif event_msg.state == EVENT_TYPE.PP_PATH_COMPONENT: 298 | self.filepath = '/%s%s' % (event_msg.union.fname.decode(), self.filepath) 299 | elif (event_msg.state == EVENT_TYPE.PP_FINALIZED): 300 | self.finalize_filepath = True 301 | 302 | def logstr(self): 303 | args = self.arg_str 304 | 305 | exec_event_str = '%lu EXEC pid:%d ppid:%d uid:%d start_time:%lu mnt_ns:%d [%x:%lu]%s ret:%d \'%s\'' % ( 306 | self.event_time, 307 | self.pid, 308 | self.ppid, 309 | self.uid, 310 | self.start_time, 311 | self.mnt_ns, 312 | self.device, 313 | self.inode, 314 | self.filepath, 315 | self.retval, 316 | self.arg_str, 317 | ) 318 | return exec_event_str 319 | 320 | 321 | class NetEvent(object): 322 | def __init__(self, event_msg): 323 | self.event_time = event_msg.event_time 324 | self.tid = event_msg.tid 325 | self.pid = event_msg.pid 326 | 327 | self.ppid = event_msg.ppid 328 | self.start_time = event_msg.start_time 329 | self.mnt_ns = event_msg.mnt_ns 330 | # Not in 4.4 suse kernels 331 | self.uid = event_msg.uid 332 | 333 | self.ev_type_str = EVENT_TYPE.event_type_map[event_msg.ev_type] 334 | 335 | self.flow = None 336 | self.family = None 337 | self.pack_local_addr = None 338 | self.pack_remote_addr = None 339 | self.proto = "TCP" 340 | if event_msg.union.net.proto == 17: 341 | self.proto = "UDP" 342 | 343 | self.local_port = socket.ntohs(int(event_msg.union.net.local_port)) 344 | self.remote_port = socket.ntohs(int(event_msg.union.net.remote_port)) 345 | 346 | if event_msg.ev_type == EVENT_TYPE.CONNECT_ACCEPT: 347 | self.flow = "rx" 348 | elif event_msg.ev_type == EVENT_TYPE.CONNECT_PRE: 349 | self.flow = "tx" 350 | 351 | # AF_INET 352 | if event_msg.union.net.ipver == socket.AF_INET: 353 | self.family = socket.AF_INET 354 | self.pack_local_addr = struct.pack("I", event_msg.union.net.local_addr) 355 | self.pack_remote_addr = struct.pack("I", event_msg.union.net.remote_addr) 356 | # AF_INET6 357 | elif event_msg.union.net.ipver == socket.AF_INET6: 358 | self.family = socket.AF_INET6 359 | self.pack_local_addr = struct.pack("IIII", 360 | event_msg.union.net.local_addr6[0], 361 | event_msg.union.net.local_addr6[1], 362 | event_msg.union.net.local_addr6[2], 363 | event_msg.union.net.local_addr6[3], 364 | ) 365 | self.pack_remote_addr = struct.pack("IIII", 366 | event_msg.union.net.remote_addr6[0], 367 | event_msg.union.net.remote_addr6[1], 368 | event_msg.union.net.remote_addr6[2], 369 | event_msg.union.net.remote_addr6[3], 370 | ) 371 | else: 372 | self.family = socket.AF_INET 373 | self.pack_local_addr = struct.pack("I", 0) 374 | self.pack_remote_addr = struct.pack("I", 0) 375 | 376 | 377 | def logstr(self): 378 | net_event_str = '%lu %s %s pid:%d %s:%d -> %s:%d' % ( 379 | self.event_time, 380 | self.ev_type_str, 381 | self.proto, 382 | self.pid, 383 | socket.inet_ntop(self.family, self.pack_local_addr), 384 | self.local_port, 385 | socket.inet_ntop(self.family, self.pack_remote_addr), 386 | self.remote_port, 387 | ) 388 | return net_event_str 389 | 390 | class DNSEvent(object): 391 | def __init__(self, event_msg): 392 | self.event_time = event_msg.event_time 393 | self.tid = event_msg.tid 394 | self.pid = event_msg.pid 395 | 396 | clone_event_table = {} 397 | exec_event_table = {} 398 | file_event_table = {} 399 | dns_event_table = {} 400 | 401 | 402 | def handle_exit_event(event_msg): 403 | exit_str = '%lu EXIT pid:%d start_time:%lu' % ( 404 | event_msg.event_time, event_msg.pid, event_msg.start_time, 405 | ) 406 | return exit_str 407 | 408 | def handle_clone_event(event_msg): 409 | key = (event_msg.event_time, event_msg.pid) 410 | if (event_msg.state == EVENT_TYPE.PP_NO_EXTRA_DATA): 411 | fork_str = '%lu FORK pid:%d ppid:%d uid:%d start_time:%lu %s' % ( 412 | event_msg.event_time, 413 | event_msg.pid, 414 | event_msg.ppid, 415 | event_msg.uid, 416 | event_msg.start_time, 417 | event_msg.union.fname.decode(), 418 | ) 419 | if key in clone_event_table: 420 | del clone_event_table[key] 421 | return fork_str 422 | if event_msg.state == EVENT_TYPE.PP_ENTRY_POINT: 423 | if key in clone_event_table: 424 | print("Key shouldn't exist") 425 | del clone_event_table[key] 426 | clone_event_table[key] = CloneEvent(event_msg) 427 | return None 428 | 429 | if not key in clone_event_table: 430 | print("Missing clone event entry") 431 | return None 432 | 433 | if event_msg.state == EVENT_TYPE.PP_PATH_COMPONENT: 434 | clone_event_table[key].filepath = '/%s%s' % ( 435 | event_msg.union.fname.decode(), 436 | clone_event_table[key].filepath) 437 | return None 438 | 439 | if event_msg.state == EVENT_TYPE.PP_FINALIZED: 440 | clone_event = clone_event_table[key] 441 | del clone_event_table[key] 442 | return clone_event.logstr() 443 | 444 | def handle_exec_event(event_msg): 445 | key = event_msg.tid 446 | if key in exec_event_table: 447 | ret = exec_event_table[key].update(event_msg) 448 | if ret: 449 | del exec_event_table[key] 450 | return ret 451 | else: 452 | exec_event_table[key] = ExecEvent(event_msg) 453 | 454 | def handle_file_event(event_msg): 455 | key = (event_msg.tid, event_msg.event_time) 456 | if key in file_event_table: 457 | if file_event_table[key].ev_type != event_msg.ev_type: 458 | print("Miss-match of file event types") 459 | return None 460 | 461 | ret = file_event_table[key].update(event_msg) 462 | if ret: 463 | del file_event_table[key] 464 | return ret 465 | else: 466 | if event_msg.state > EVENT_TYPE.PP_ENTRY_POINT: 467 | print("Missing event data") 468 | file_event_table[key] = FileEvent(event_msg) 469 | 470 | def handle_dns_event(event_msg): 471 | pass 472 | def handle_network_event(event_msg): 473 | ret = NetEvent(event_msg) 474 | return ret.logstr() 475 | 476 | def perf_event_cb(cpu, data, size): 477 | event_msg = ctypes.cast(data, ctypes.POINTER(SensorEventMessage)).contents 478 | 479 | if EVENT_TYPE.IsTypeEnabled(event_msg.ev_type): 480 | if event_msg.ev_type == EVENT_TYPE.PROCESS_CLONE: 481 | ret = handle_clone_event(event_msg) 482 | if ret: 483 | print(ret) 484 | elif event_msg.ev_type == EVENT_TYPE.PROCESS_EXIT: 485 | ret = handle_exit_event(event_msg) 486 | if ret: 487 | print(ret) 488 | elif (event_msg.ev_type == EVENT_TYPE.PROCESS_EXEC or 489 | event_msg.ev_type == EVENT_TYPE.PROCESS_ARG): 490 | ret = handle_exec_event(event_msg) 491 | if ret: 492 | print(ret) 493 | elif (event_msg.ev_type == EVENT_TYPE.CONNECT_PRE or 494 | event_msg.ev_type == EVENT_TYPE.CONNECT_ACCEPT): 495 | ret = handle_network_event(event_msg) 496 | if ret: 497 | print(ret) 498 | elif (event_msg.ev_type == EVENT_TYPE.DNS_RESPONSE or 499 | event_msg.ev_type == EVENT_TYPE.WEB_PROXY): 500 | handle_dns_event(event_msg) 501 | elif (event_msg.ev_type == EVENT_TYPE.FILE_WRITE or 502 | event_msg.ev_type == EVENT_TYPE.FILE_MMAP or 503 | event_msg.ev_type == EVENT_TYPE.FILE_CREATE or 504 | event_msg.ev_type == EVENT_TYPE.FILE_DELETE or 505 | event_msg.ev_type == EVENT_TYPE.FILE_CLOSE or 506 | event_msg.ev_type == EVENT_TYPE.FILE_OPEN): 507 | ret = handle_file_event(event_msg) 508 | if ret: 509 | print(ret) 510 | 511 | 512 | def load_script(bcc_kernel_script): 513 | with open(bcc_kernel_script, 'r') as fh: 514 | bpf_text = fh.read() 515 | return BPF(text=bpf_text) 516 | 517 | def load_perf_callback(bcc): 518 | bcc['events'].open_perf_buffer(perf_event_cb, page_cnt=128) 519 | 520 | def check_symbol_exists(symbol): 521 | with open("/proc/kallsyms") as syms: 522 | ops = '' 523 | for line in syms: 524 | (addr, size, name) = line.rstrip().split(" ", 2) 525 | name = name.split("\t")[0] 526 | if name == symbol: 527 | return True 528 | return False 529 | 530 | # List of deprecated functions and number of the replacement functions 531 | # while adding the Probe, its important to add the deprecated function 532 | # and their replacement function(s) in order. 533 | # See example of deprecated function __vfs_write below. 534 | # 2 Replacement functions (viz: vfs_write, __kernel_write) 535 | 536 | deprecated_func_list = ['__vfs_write', 2] 537 | 538 | 539 | # attaching the probes to functions 540 | def attach_probes(bcc): 541 | probes = [ 542 | # PID Clone Events 543 | Probe( 544 | pp='wake_up_new_task', 545 | pp_cb_name='on_wake_up_new_task', 546 | ), 547 | 548 | # cache eviction relate probe 549 | Probe( 550 | pp='security_file_free', 551 | pp_cb_name='on_security_file_free', 552 | ), 553 | 554 | # Process Exit Events 555 | Probe( 556 | pp='security_task_free', 557 | pp_cb_name='on_security_task_free', 558 | ), 559 | 560 | # File Events 561 | Probe( 562 | pp='__vfs_write', 563 | pp_cb_name='trace_write_entry', 564 | ), 565 | # Note, the 2 probe points below. They are replacements of 566 | # __vfs_write for kernel version >= 5.8.0 567 | # We need to attach either __vfs_write OR vfs_write, __kernel_write 568 | # Insert any new probes after __kernel_write 569 | Probe( 570 | pp='vfs_write', 571 | pp_cb_name='trace_write_entry', 572 | ), 573 | Probe( 574 | pp='__kernel_write', 575 | pp_cb_name='trace_write_kentry', 576 | ), 577 | Probe( 578 | pp='security_mmap_file', 579 | pp_cb_name='on_security_mmap_file', 580 | ), 581 | Probe( 582 | pp='security_file_open', 583 | pp_cb_name='on_security_file_open', 584 | ), 585 | Probe( 586 | pp='security_inode_unlink', 587 | pp_cb_name='on_security_inode_unlink', 588 | ), 589 | Probe( 590 | pp='security_inode_rename', 591 | pp_cb_name='on_security_inode_rename', 592 | ), 593 | 594 | # execve and execveat syscalls 595 | Probe( 596 | pp=bcc.get_syscall_fnname("execve"), 597 | pp_cb_name='syscall__on_sys_execve', 598 | ), 599 | Probe( 600 | pp=bcc.get_syscall_fnname("execveat"), 601 | pp_cb_name='syscall__on_sys_execveat', 602 | ), 603 | Probe( 604 | pp=bcc.get_syscall_fnname("execve"), 605 | pp_cb_name='after_sys_execve', 606 | is_kretprobe=True, 607 | ), 608 | Probe( 609 | pp=bcc.get_syscall_fnname("execveat"), 610 | pp_cb_name='after_sys_execve', 611 | is_kretprobe=True, 612 | ), 613 | 614 | # DNS TCP Network Events 615 | Probe( 616 | pp='tcp_sendmsg', 617 | pp_cb_name='trace_tcp_sendmsg', 618 | ), 619 | 620 | #DNS UDP recvmsg Events 621 | Probe( 622 | pp='udp_recvmsg', 623 | pp_cb_name='trace_udp_recvmsg', 624 | ), 625 | Probe( 626 | pp='udpv6_recvmsg', 627 | pp_cb_name='trace_udp_recvmsg', 628 | ), 629 | Probe( 630 | pp='udp_recvmsg', 631 | pp_cb_name='trace_udp_recvmsg_return', 632 | is_kretprobe=True, 633 | ), 634 | Probe( 635 | pp='udpv6_recvmsg', 636 | pp_cb_name='trace_udp_recvmsg_return', 637 | is_kretprobe=True, 638 | ), 639 | 640 | # UDP Tx Events 641 | Probe( 642 | pp='udp_sendmsg', 643 | pp_cb_name='trace_udp_sendmsg', 644 | ), 645 | Probe( 646 | pp='udpv6_sendmsg', 647 | pp_cb_name='trace_udp_sendmsg', 648 | ), 649 | Probe( 650 | pp='udp_sendmsg', 651 | pp_cb_name='trace_udp_sendmsg_return', 652 | is_kretprobe=True, 653 | ), 654 | Probe( 655 | pp='udpv6_sendmsg', 656 | pp_cb_name='trace_udp_sendmsg_return', 657 | is_kretprobe=True, 658 | ), 659 | 660 | # UDP Rx Events 661 | Probe( 662 | pp='__skb_recv_udp', 663 | pp_cb_name='trace_skb_recv_udp', 664 | is_kretprobe=True, 665 | ), 666 | 667 | # TCP Connect Events 668 | Probe( 669 | pp='tcp_v4_connect', 670 | pp_cb_name='trace_connect_v4_entry', 671 | ), 672 | Probe( 673 | pp='tcp_v6_connect', 674 | pp_cb_name='trace_connect_v6_entry', 675 | ), 676 | Probe( 677 | pp='tcp_v4_connect', 678 | pp_cb_name='trace_connect_v4_return', 679 | is_kretprobe=True, 680 | ), 681 | Probe( 682 | pp='tcp_v6_connect', 683 | pp_cb_name='trace_connect_v6_return', 684 | is_kretprobe=True, 685 | ), 686 | 687 | # TCP Accept Events 688 | Probe( 689 | pp='inet_csk_accept', 690 | pp_cb_name='trace_accept_return', 691 | is_kretprobe=True, 692 | ), 693 | ] 694 | 695 | dep_func_skip_flag = 0 696 | for probe in probes: 697 | if dep_func_skip_flag > 0: 698 | dep_func_skip_flag -= 1 699 | continue 700 | 701 | if probe.pp in deprecated_func_list: 702 | if check_symbol_exists(symbol=probe.pp): 703 | count_index = deprecated_func_list.index(probe.pp) 704 | dep_func_skip_flag = deprecated_func_list[count_index + 1] 705 | else: 706 | continue 707 | 708 | if (probe.is_kretprobe): 709 | bcc.attach_kretprobe(event=probe.pp, fn_name=probe.pp_cb_name) 710 | else: 711 | bcc.attach_kprobe(event=probe.pp, fn_name=probe.pp_cb_name) 712 | 713 | 714 | def parseArgs(provided_args=None): 715 | arg_parser = argparse.ArgumentParser(description="BPF Test App") 716 | 717 | # Control what events are printed 718 | arg_parser.add_argument("-m", "--disable-mmap", action="store_true", help="Disable MMAP event printing", 719 | dest="disable_mmap", default=False) 720 | arg_parser.add_argument("-f", "--disable-file", action="store_true", help="Disable FILE event printing", 721 | dest="disable_file", default=False) 722 | arg_parser.add_argument("-p", "--disable-process", action="store_true", help="Disable PROCESS event printing", 723 | dest="disable_process", default=False) 724 | arg_parser.add_argument("-n", "--disable-net", action="store_true", help="Disable NET event printing", 725 | dest="disable_net", default=False) 726 | arg_parser.add_argument("-d", "--disable-dns", action="store_true", help="Disable DNS event printing", 727 | dest="disable_dns", default=False) 728 | 729 | # BPF Source file 730 | arg_parser.add_argument("bpf_source", action="store", help="BPF Probe File") 731 | 732 | return arg_parser.parse_args(provided_args) 733 | 734 | if __name__ == '__main__': 735 | def main(): 736 | import sys 737 | 738 | args = parseArgs() 739 | EVENT_TYPE.SetTypeEnabledState(args) 740 | 741 | bcc = load_script(bcc_kernel_script=args.bpf_source) 742 | attach_probes(bcc) 743 | load_perf_callback(bcc) 744 | 745 | print("Waiting for events...") 746 | while True: 747 | bcc.perf_buffer_poll() 748 | 749 | main() 750 | -------------------------------------------------------------------------------- /examples/bcc_sample.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020-20201 VMware, Inc. 3 | // SPDX-License-Identifier: BSD-2-Clause 4 | // 5 | 6 | package main 7 | 8 | import ( 9 | "bufio" 10 | "bytes" 11 | "encoding/binary" 12 | "fmt" 13 | "io/ioutil" 14 | "os" 15 | "os/signal" 16 | "strconv" 17 | "strings" 18 | 19 | bpf "github.com/iovisor/gobpf/bcc" 20 | ) 21 | 22 | import "C" 23 | 24 | // This holds complete C program 25 | var cProgramCode string 26 | 27 | //# List of deprecated functions and number of the replacement functions 28 | //# while adding the Probe, its important to add the deprecated function 29 | //# and their replacement function(s) in order. 30 | //# See example of deprecated function __vfs_write below. 31 | //# 2 Replacement functions (viz: vfs_write, __kernel_write) 32 | 33 | var deprecatedFuncMap = map[string]int{"__vfs_write": 2} 34 | 35 | type probeMeta struct { 36 | PP string 37 | PPCbName string 38 | IsKretProbe bool 39 | } 40 | 41 | var allProbes = []probeMeta{ 42 | //# PID Clone Events 43 | probeMeta{ 44 | PP: "wake_up_new_task", 45 | PPCbName: "on_wake_up_new_task", 46 | IsKretProbe: false, 47 | }, 48 | //# cache eviction relate probe 49 | probeMeta{ 50 | PP: "security_file_free", 51 | PPCbName: "on_security_file_free", 52 | IsKretProbe: false, 53 | }, 54 | 55 | //# Process Exit Events 56 | probeMeta{ 57 | PP: "security_task_free", 58 | PPCbName: "on_security_task_free", 59 | IsKretProbe: false, 60 | }, 61 | 62 | //# File Events 63 | probeMeta{ 64 | PP: "__vfs_write", 65 | PPCbName: "trace_write_entry", 66 | IsKretProbe: false, 67 | }, 68 | //# Note, the 2 probe points below. They are replacements of 69 | //# __vfs_write for kernel version >= 5.8.0 70 | //# We need to attach either __vfs_write OR vfs_write, __kernel_write 71 | //# Insert any new probes after __kernel_write 72 | probeMeta{ 73 | PP: "vfs_write", 74 | PPCbName: "trace_write_entry", 75 | IsKretProbe: false, 76 | }, 77 | probeMeta{ 78 | PP: "__kernel_write", 79 | PPCbName: "trace_write_kentry", 80 | IsKretProbe: false, 81 | }, 82 | probeMeta{ 83 | PP: "security_mmap_file", 84 | PPCbName: "on_security_mmap_file", 85 | IsKretProbe: false, 86 | }, 87 | probeMeta{ 88 | PP: "security_file_open", 89 | PPCbName: "on_security_file_open", 90 | IsKretProbe: false, 91 | }, 92 | probeMeta{ 93 | PP: "security_inode_unlink", 94 | PPCbName: "on_security_inode_unlink", 95 | IsKretProbe: false, 96 | }, 97 | probeMeta{ 98 | PP: "security_inode_rename", 99 | PPCbName: "on_security_inode_rename", 100 | IsKretProbe: false, 101 | }, 102 | 103 | //# execve and execveat syscalls 104 | probeMeta{ 105 | PP: bpf.GetSyscallFnName("execve"), 106 | PPCbName: "syscall__on_sys_execve", 107 | IsKretProbe: false, 108 | }, 109 | probeMeta{ 110 | PP: bpf.GetSyscallFnName("execveat"), 111 | PPCbName: "syscall__on_sys_execveat", 112 | IsKretProbe: false, 113 | }, 114 | probeMeta{ 115 | PP: bpf.GetSyscallFnName("execve"), 116 | PPCbName: "after_sys_execve", 117 | IsKretProbe: true, 118 | }, 119 | probeMeta{ 120 | PP: bpf.GetSyscallFnName("execveat"), 121 | PPCbName: "after_sys_execve", 122 | IsKretProbe: true, 123 | }, 124 | 125 | //# DNS TCP Network Events 126 | probeMeta{ 127 | PP: "tcp_sendmsg", 128 | PPCbName: "trace_tcp_sendmsg", 129 | IsKretProbe: false, 130 | }, 131 | 132 | //# DNS UDP recvmsg Events 133 | probeMeta{ 134 | PP: "udp_recvmsg", 135 | PPCbName: "trace_udp_recvmsg", 136 | IsKretProbe: false, 137 | }, 138 | probeMeta{ 139 | PP: "udpv6_recvmsg", 140 | PPCbName: "trace_udp_recvmsg", 141 | IsKretProbe: false, 142 | }, 143 | probeMeta{ 144 | PP: "udp_recvmsg", 145 | PPCbName: "trace_udp_recvmsg_return", 146 | IsKretProbe: true, 147 | }, 148 | probeMeta{ 149 | PP: "udpv6_recvmsg", 150 | PPCbName: "trace_udp_recvmsg_return", 151 | IsKretProbe: true, 152 | }, 153 | 154 | //# UDP Tx Events 155 | probeMeta{ 156 | PP: "udp_sendmsg", 157 | PPCbName: "trace_udp_sendmsg", 158 | IsKretProbe: false, 159 | }, 160 | probeMeta{ 161 | PP: "udpv6_sendmsg", 162 | PPCbName: "trace_udp_sendmsg", 163 | IsKretProbe: false, 164 | }, 165 | probeMeta{ 166 | PP: "udp_sendmsg", 167 | PPCbName: "trace_udp_sendmsg_return", 168 | IsKretProbe: true, 169 | }, 170 | probeMeta{ 171 | PP: "udpv6_sendmsg", 172 | PPCbName: "trace_udp_sendmsg_return", 173 | IsKretProbe: true, 174 | }, 175 | 176 | //# UDP Rx Events 177 | probeMeta{ 178 | PP: "__skb_recv_udp", 179 | PPCbName: "trace_skb_recv_udp", 180 | IsKretProbe: true, 181 | }, 182 | 183 | //# TCP Connect Events 184 | probeMeta{ 185 | PP: "tcp_v4_connect", 186 | PPCbName: "trace_connect_v4_entry", 187 | IsKretProbe: false, 188 | }, 189 | probeMeta{ 190 | PP: "tcp_v6_connect", 191 | PPCbName: "trace_connect_v6_entry", 192 | IsKretProbe: false, 193 | }, 194 | probeMeta{ 195 | PP: "tcp_v4_connect", 196 | PPCbName: "trace_connect_v4_return", 197 | IsKretProbe: true, 198 | }, 199 | probeMeta{ 200 | PP: "tcp_v6_connect", 201 | PPCbName: "trace_connect_v6_return", 202 | IsKretProbe: true, 203 | }, 204 | 205 | //# TCP Accept Events 206 | probeMeta{ 207 | PP: "inet_csk_accept", 208 | PPCbName: "trace_accept_return", 209 | IsKretProbe: true, 210 | }, 211 | } 212 | 213 | // LocalAddr is in union with LocalAddr6 214 | // RemoteAddr is in union with RemoteAddr6 215 | type netEventData struct { 216 | LocalAddr uint32 217 | RemoteAddr uint32 218 | RemotePort uint16 219 | LocalPort uint16 220 | IPVer uint16 221 | Proto uint16 222 | DNSFlag uint16 223 | Pad uint16 224 | LocalAddr6 [4]uint32 225 | RemoteAddr6 [4]uint32 226 | DNS [40]byte 227 | NameLen uint32 228 | } 229 | 230 | type mmapArgs struct { 231 | flags uint64 232 | prot uint64 233 | } 234 | 235 | // Global structure read from the kernel event 236 | // use Capital letter to start attributes 237 | // Add Padded bytes to care of alignment (struct data_t) 238 | type sensorEvent struct { 239 | EventTime uint64 240 | Tid uint32 241 | Pid uint32 242 | EvType uint8 243 | State uint8 244 | Pad1 uint16 245 | UID uint32 246 | Ppid uint32 247 | Inode uint64 248 | Device uint32 249 | MntNS uint32 250 | Pad2 [4]byte 251 | Ufname [255]byte 252 | Pad3 byte 253 | RetVal int32 254 | StartTime uint64 255 | } 256 | 257 | ///////////////////// 258 | // main functionality 259 | ///////////////////// 260 | 261 | func main() { 262 | 263 | const perfMapPageCnt = 1024 264 | 265 | fileName := string("src/bcc_sensor.c") 266 | if len(os.Args) > 2 { 267 | fmt.Println("Usage:", os.Args[0], " e.g. src/bcc_sensor.c") 268 | return 269 | } 270 | if len(os.Args) == 2 { 271 | fileName = os.Args[1] 272 | } 273 | if checkPrivileges() != true { 274 | fmt.Println("Insufficient privileges. Can not load BPF program code.") 275 | return 276 | } 277 | 278 | if loadScript(fileName) != true { 279 | return 280 | } 281 | 282 | bpfMod := bpf.NewModule(cProgramCode, []string{}) 283 | defer bpfMod.Close() 284 | 285 | attachProbes(bpfMod) 286 | 287 | // TableId commes from BPF_PERF_OUTPUT 288 | eventTable := bpf.NewTable(bpfMod.TableId("events"), bpfMod) 289 | 290 | cloneEventTable = make(map[string]*cloneEvent) 291 | execEventTable = make(map[uint32]*execEvent) 292 | fileEventTable = make(map[string]*fileEvent) 293 | 294 | eventChannel := make(chan []byte) 295 | 296 | sensorMap, err := bpf.InitPerfMapWithPageCnt(eventTable, eventChannel, nil, perfMapPageCnt) 297 | if err != nil { 298 | fmt.Fprintf(os.Stderr, "Perf map Initialization failed: %s\n", err) 299 | return 300 | } 301 | 302 | fmt.Fprintf(os.Stdout, "Waiting for events... ^C to stop.\n\n") 303 | interruptSignal := make(chan os.Signal, 1) 304 | signal.Notify(interruptSignal, os.Interrupt, os.Kill) 305 | 306 | go func() { 307 | var kevent sensorEvent 308 | for { 309 | eventData := <-eventChannel 310 | //fmt.Fprintf(os.Stdout, "Raw len %d : %+v\n", len(eventData), eventData) 311 | err := binary.Read(bytes.NewBuffer(eventData), binary.LittleEndian, &kevent) 312 | if err != nil { 313 | fmt.Printf("Event decode failed error : %s\n", err) 314 | continue 315 | } 316 | parsePrintEvent(kevent) 317 | } 318 | }() 319 | 320 | sensorMap.Start() 321 | <-interruptSignal 322 | sensorMap.Stop() 323 | } 324 | 325 | func checkPrivileges() bool { 326 | // cap checks TODO 327 | return true 328 | } 329 | 330 | func loadScript(fileName string) bool { 331 | // file sanity checks 332 | statData, err := os.Stat(fileName) 333 | if os.IsNotExist(err) || statData.IsDir() { 334 | fmt.Fprintf(os.Stderr, "File %s does not exist. err: %s\n", fileName, err) 335 | return false 336 | } 337 | fileData, err := ioutil.ReadFile(fileName) 338 | if err != nil { 339 | fmt.Fprintf(os.Stderr, "Reading file Failed : %s\n", err) 340 | return false 341 | } 342 | cProgramCode = string(fileData) 343 | return true 344 | } 345 | 346 | func attachProbes(bpfMod *bpf.Module) int32 { 347 | depFuncSkipFlag := 0 348 | 349 | for i := 0; i < len(allProbes); i++ { 350 | if depFuncSkipFlag > 0 { 351 | depFuncSkipFlag-- 352 | continue 353 | } 354 | if count, found := deprecatedFuncMap[allProbes[i].PP]; found { 355 | if checkSymbolExists(allProbes[i].PP) { 356 | depFuncSkipFlag = count 357 | } else { 358 | continue 359 | } 360 | } 361 | 362 | currKprobe, err := bpfMod.LoadKprobe(allProbes[i].PPCbName) 363 | if err != nil { 364 | fmt.Fprintf(os.Stderr, "Load kprobe Failed : %s\n", err) 365 | return 1 366 | } 367 | 368 | if allProbes[i].IsKretProbe == false { 369 | err = bpfMod.AttachKprobe(allProbes[i].PP, currKprobe, -1) 370 | } else { 371 | err = bpfMod.AttachKretprobe(allProbes[i].PP, currKprobe, -1) 372 | } 373 | if err != nil { 374 | fmt.Fprintf(os.Stderr, "Attach kprobe failed to : %s\n", err) 375 | return 1 376 | } 377 | //fmt.Fprintf(os.Stdout, "Kprobe [%d] %s Ret %t\n", i, 378 | // allProbes[i].PP, allProbes[i].IsKretProbe) 379 | } 380 | fmt.Fprintf(os.Stdout, "\nSuccessfully attached kprobes !!!\n") 381 | return 0 382 | } 383 | 384 | func checkSymbolExists(symbol string) bool { 385 | fileData, err := ioutil.ReadFile("/proc/kallsyms") 386 | if err != nil { 387 | fmt.Fprintf(os.Stderr, "Reading kallsyms Failed : %s\n", err) 388 | return false 389 | } 390 | allsymsString := string(fileData) 391 | return strings.Contains(allsymsString, symbol) 392 | } 393 | 394 | ////////////////////// 395 | // Enum, Constants etc 396 | ////////////////////// 397 | 398 | const ( 399 | evProcessArg = 0 400 | evProcessExec = 1 401 | evProcessExit = 2 402 | evProcessClone = 3 403 | evFileRead = 4 404 | evFileWrite = 5 405 | evFileCreate = 6 406 | evFilePath = 7 407 | evFileMmap = 8 408 | evFileTest = 9 409 | evConnectPre = 10 410 | evConnectAccept = 11 411 | evDNSResponse = 12 412 | evWebProxy = 13 413 | evFileDelete = 14 414 | evFileClose = 15 415 | evFileOpen = 16 416 | ) 417 | 418 | type stateType uint8 419 | 420 | const ( 421 | stPpNoExtraData = 0 422 | stPpEntryPoint = 1 423 | stPpPathComponent = 2 424 | stPpFinalized = 3 425 | stPpAppend = 4 426 | stPpDebug = 5 427 | ) 428 | 429 | func printEventByType(eventType uint8) string { 430 | eventNames := [...]string{ 431 | "PROCESS_ARG", 432 | "PROCESS_EXEC", 433 | "PROCESS_EXIT", 434 | "PROCESS_CLONE", 435 | "FILE_READ", 436 | "FILE_WRITE", 437 | "FILE_CREATE", 438 | "FILE_PATH", 439 | "FILE_MMAP", 440 | "FILE_TEST", 441 | "NET_CONNECT", 442 | "NET_ACCEPT", 443 | "DNS_RESPONSE", 444 | "WEB_PROXY", 445 | "FILE_DELETE", 446 | "FILE_CLOSE", 447 | "FILE_OPEN"} 448 | if len(eventNames) >= int(eventType) { 449 | return eventNames[eventType] 450 | } 451 | return "UNKNOWN" 452 | } 453 | 454 | ////////////////////// 455 | // event parsing logic 456 | ////////////////////// 457 | 458 | func parsePrintEvent(kevent sensorEvent) { 459 | var result string 460 | 461 | switch kevent.EvType { 462 | case evProcessClone: 463 | result = handleCloneEvent(kevent) 464 | 465 | case evProcessExit: 466 | result = handleExitEvent(kevent) 467 | 468 | case evProcessExec, evProcessArg: 469 | result = handleExecEvent(kevent) 470 | 471 | case evConnectPre, evConnectAccept: 472 | result = handleNetworkEvent(kevent) 473 | 474 | case evDNSResponse, evWebProxy: 475 | result = handleDNSEvent(kevent) 476 | 477 | case evFileWrite, evFileMmap, evFileCreate, evFileDelete, evFileClose, evFileOpen: 478 | result = handleFileEvent(kevent) 479 | } 480 | if len(result) > 0 { 481 | fmt.Printf("%s.\n", result) 482 | } 483 | } 484 | 485 | /////////////// 486 | // cloneEvent 487 | /////////////// 488 | 489 | type cloneEvent struct { 490 | filePath string 491 | 492 | eventTime uint64 493 | tid uint32 494 | pid uint32 495 | uid uint32 496 | startTime uint64 497 | ppid uint32 498 | inode uint64 499 | device uint32 500 | mntNS uint32 501 | comm string 502 | } 503 | 504 | // Table for key, value pairs 505 | var cloneEventTable map[string]*cloneEvent 506 | 507 | func (clone *cloneEvent) initFunc(eventMsg sensorEvent) { 508 | clone.filePath = "" 509 | 510 | clone.eventTime = eventMsg.EventTime 511 | clone.tid = eventMsg.Tid 512 | clone.pid = eventMsg.Pid 513 | clone.uid = eventMsg.UID 514 | clone.startTime = eventMsg.StartTime 515 | clone.ppid = eventMsg.Ppid 516 | clone.inode = eventMsg.Inode 517 | clone.device = eventMsg.Device 518 | clone.mntNS = eventMsg.MntNS 519 | clone.comm = eventMsgfNameDecode(eventMsg) 520 | } 521 | 522 | func (clone cloneEvent) logstrFunc() string { 523 | pathStr := clone.filePath 524 | if len(pathStr) == 0 { 525 | pathStr = clone.comm 526 | } 527 | cloneEventStr := fmt.Sprintf("%d FORK pid:%d ppid:%d uid:%d start_time:%d mnt_ns:%d [%x:%d]%s", 528 | clone.eventTime, 529 | clone.pid, 530 | clone.ppid, 531 | clone.uid, 532 | clone.startTime, 533 | clone.mntNS, 534 | clone.device, 535 | clone.inode, 536 | pathStr) 537 | return cloneEventStr 538 | } 539 | 540 | func handleCloneEvent(kevent sensorEvent) string { 541 | key := fmt.Sprintf("%d-%d", kevent.EventTime, kevent.Pid) 542 | if kevent.State == stPpNoExtraData { 543 | forkStr := fmt.Sprintf("%d FORK pid:%d ppid:%d uid:%d start_time:%d %s", 544 | kevent.EventTime, 545 | kevent.Pid, 546 | kevent.Ppid, 547 | kevent.UID, 548 | kevent.StartTime, 549 | eventMsgfNameDecode(kevent)) 550 | if _, found := cloneEventTable[key]; found { 551 | delete(cloneEventTable, key) 552 | } 553 | return forkStr 554 | } else if kevent.State == stPpEntryPoint { 555 | if _, found := cloneEventTable[key]; found { 556 | fmt.Fprintf(os.Stderr, "Key shouldn't exist\n") 557 | delete(cloneEventTable, key) 558 | } 559 | var clone cloneEvent 560 | clone.initFunc(kevent) 561 | cloneEventTable[key] = &clone 562 | return "" 563 | } 564 | 565 | if _, found := cloneEventTable[key]; !found { 566 | fmt.Fprintf(os.Stderr, "Missing clone event entry\n") 567 | return "" 568 | } 569 | 570 | if kevent.State == stPpPathComponent { 571 | clone, _ := cloneEventTable[key] 572 | clone.filePath = fmt.Sprintf("/%s%s", eventMsgfNameDecode(kevent), clone.filePath) 573 | } else if kevent.State == stPpFinalized { 574 | clone, _ := cloneEventTable[key] 575 | delete(cloneEventTable, key) 576 | return clone.logstrFunc() 577 | } 578 | return "" 579 | } 580 | 581 | func handleExitEvent(kevent sensorEvent) string { 582 | exitStr := fmt.Sprintf("%d EXIT pid:%d start_time:%d", 583 | kevent.EventTime, kevent.Pid, kevent.StartTime) 584 | return exitStr 585 | } 586 | 587 | /////////////// 588 | // execEvent 589 | /////////////// 590 | 591 | type execEvent struct { 592 | retVal int32 593 | finalizeFilePath bool 594 | setEntrypointData bool 595 | scriptPath string 596 | filePath string 597 | 598 | eventTime uint64 599 | tid uint32 600 | pid uint32 601 | argStr string 602 | startTime uint64 603 | ppid uint32 604 | uid uint32 605 | inode uint64 606 | device uint32 607 | mntNS uint32 608 | } 609 | 610 | // Table for key, value pairs 611 | var execEventTable map[uint32]*execEvent 612 | 613 | func (exec *execEvent) initFunc(eventMsg sensorEvent) { 614 | exec.retVal = -1 615 | exec.finalizeFilePath = false 616 | exec.setEntrypointData = false 617 | exec.scriptPath = "" 618 | exec.filePath = "" 619 | 620 | exec.eventTime = eventMsg.EventTime 621 | exec.tid = eventMsg.Tid 622 | exec.pid = eventMsg.Pid 623 | exec.argStr = eventMsgfNameDecode(eventMsg) 624 | exec.startTime = 0 625 | exec.ppid = 0 626 | exec.uid = 0 627 | exec.inode = 0 628 | exec.device = 0 629 | exec.mntNS = 0 630 | } 631 | 632 | func (exec *execEvent) updateFunc(eventMsg sensorEvent) string { 633 | if eventMsg.EvType == evProcessArg { 634 | if eventMsg.State == stPpFinalized { 635 | exec.retVal = eventMsg.RetVal 636 | return exec.logstrFunc() 637 | } else if eventMsg.State == stPpEntryPoint { 638 | exec.argStr += " " + eventMsgfNameDecode(eventMsg) 639 | } else if eventMsg.State == stPpAppend { 640 | exec.argStr += eventMsgfNameDecode(eventMsg) 641 | } 642 | } 643 | if eventMsg.EvType == evProcessExec { 644 | if eventMsg.State == stPpEntryPoint { 645 | exec.startTime = eventMsg.StartTime 646 | exec.ppid = eventMsg.Ppid 647 | exec.uid = eventMsg.UID 648 | exec.inode = eventMsg.Inode 649 | exec.device = eventMsg.Device 650 | exec.mntNS = eventMsg.MntNS 651 | } else if eventMsg.State == stPpPathComponent { 652 | exec.filePath = fmt.Sprintf("/%s%s", eventMsgfNameDecode(eventMsg), exec.filePath) 653 | } else if eventMsg.State == stPpFinalized { 654 | exec.finalizeFilePath = true 655 | } 656 | } 657 | return "" 658 | } 659 | 660 | func (exec execEvent) logstrFunc() string { 661 | //args = exec.argStr 662 | execEventStr := fmt.Sprintf("%d EXEC pid:%d ppid:%d uid:%d start_time:%d mnt_ns:%d [%x:%d]%s ret:%d \"%s\"", 663 | exec.eventTime, 664 | exec.pid, 665 | exec.ppid, 666 | exec.uid, 667 | exec.startTime, 668 | exec.mntNS, 669 | exec.device, 670 | exec.inode, 671 | exec.filePath, 672 | exec.retVal, 673 | exec.argStr) 674 | return execEventStr 675 | } 676 | 677 | func handleExecEvent(kevent sensorEvent) string { 678 | key := kevent.Tid 679 | if exec, found := execEventTable[key]; found { 680 | result := exec.updateFunc(kevent) 681 | if len(result) > 0 { 682 | delete(execEventTable, key) 683 | return result 684 | } 685 | } else { 686 | var exec execEvent 687 | exec.initFunc(kevent) 688 | execEventTable[key] = &exec 689 | } 690 | return "" 691 | } 692 | 693 | /////////////// 694 | // netEvent 695 | /////////////// 696 | 697 | type netEvent struct { 698 | eventTime uint64 699 | tid uint32 700 | pid uint32 701 | ppid uint32 702 | startTime uint64 703 | mntNS uint32 704 | uid uint32 705 | eventTypeStr string 706 | flow string 707 | family string 708 | packLocalAddr string 709 | packRemoteAddr string 710 | proto string 711 | localPort uint16 712 | remotePort uint16 713 | } 714 | 715 | func (net *netEvent) initFunc(eventMsg sensorEvent) { 716 | net.eventTime = eventMsg.EventTime 717 | net.tid = eventMsg.Tid 718 | net.pid = eventMsg.Pid 719 | 720 | net.ppid = eventMsg.Ppid 721 | net.startTime = eventMsg.StartTime 722 | net.mntNS = eventMsg.MntNS 723 | //# Not in 4.4 suse kernels 724 | net.uid = eventMsg.UID 725 | 726 | net.eventTypeStr = printEventByType(eventMsg.EvType) 727 | 728 | net.flow = "" 729 | net.family = "" 730 | net.packLocalAddr = "" 731 | net.packRemoteAddr = "" 732 | net.proto = "TCP" 733 | 734 | var netEvent netEventData 735 | err := binary.Read(bytes.NewBuffer(eventMsg.Ufname[:]), binary.LittleEndian, &netEvent) 736 | if err != nil { 737 | fmt.Printf("netEvent decode failed error : %s\n", err) 738 | return 739 | } 740 | 741 | if netEvent.Proto == 17 { 742 | net.proto = "UDP" 743 | } 744 | 745 | net.localPort = ntohs(netEvent.LocalPort) 746 | net.remotePort = ntohs(netEvent.RemotePort) 747 | 748 | if eventMsg.EvType == evConnectAccept { 749 | net.flow = "rx" 750 | } else if eventMsg.EvType == evConnectPre { 751 | net.flow = "tx" 752 | } 753 | 754 | //# AF_INET : IPVer = 2 ... defined in linux/socket.h 755 | bytesLocal := make([]byte, 4) 756 | bytesRemote := make([]byte, 4) 757 | if netEvent.IPVer == 2 { 758 | net.family = "IPv4" 759 | binary.LittleEndian.PutUint32(bytesLocal, netEvent.LocalAddr) 760 | net.packLocalAddr = fmt.Sprintf("%d.%d.%d.%d", 761 | bytesLocal[0], bytesLocal[1], bytesLocal[2], bytesLocal[3]) 762 | binary.LittleEndian.PutUint32(bytesRemote, netEvent.RemoteAddr) 763 | net.packRemoteAddr = fmt.Sprintf("%d.%d.%d.%d", 764 | bytesRemote[0], bytesRemote[1], bytesRemote[2], bytesRemote[3]) 765 | //# AF_INET6 : IPVer = 10 766 | } else if netEvent.IPVer == 10 { 767 | net.family = "IPv6" 768 | for i := 0; i < 4; i++ { 769 | binary.LittleEndian.PutUint32(bytesLocal, netEvent.LocalAddr6[i]) 770 | binary.LittleEndian.PutUint32(bytesRemote, netEvent.RemoteAddr6[i]) 771 | net.packLocalAddr = fmt.Sprintf("%s%d.%d.%d.%d", net.packLocalAddr, 772 | bytesLocal[0], bytesLocal[1], bytesLocal[2], bytesLocal[3]) 773 | net.packRemoteAddr = fmt.Sprintf("%s%d.%d.%d.%d", net.packRemoteAddr, 774 | bytesRemote[0], bytesRemote[1], bytesRemote[2], bytesRemote[3]) 775 | } 776 | } else { 777 | net.family = "IPv4" 778 | net.packLocalAddr = "0" 779 | net.packRemoteAddr = "0" 780 | } 781 | } 782 | 783 | func ntohs(src uint16) uint16 { 784 | bytes := make([]byte, 2) 785 | binary.LittleEndian.PutUint16(bytes, src) 786 | return binary.BigEndian.Uint16(bytes) 787 | } 788 | 789 | func (net netEvent) logstrFunc() string { 790 | netEventStr := fmt.Sprintf("%d %s %s pid:%d %s:%d -> %s:%d", 791 | net.eventTime, 792 | net.eventTypeStr, 793 | net.proto, 794 | net.pid, 795 | net.packLocalAddr, 796 | net.localPort, 797 | net.packRemoteAddr, 798 | net.remotePort) 799 | return netEventStr 800 | } 801 | 802 | func handleNetworkEvent(kevent sensorEvent) string { 803 | var net netEvent 804 | net.initFunc(kevent) 805 | return net.logstrFunc() 806 | } 807 | 808 | func handleDNSEvent(kevent sensorEvent) string { 809 | return "" 810 | } 811 | 812 | /////////////// 813 | // fileEvent 814 | /////////////// 815 | 816 | type fileEvent struct { 817 | filePath string 818 | mounts map[uint32]string 819 | 820 | evType uint8 821 | eventTime uint64 822 | tid uint32 823 | pid uint32 824 | ppid uint32 825 | uid uint32 826 | inode uint64 827 | device uint32 828 | mntNS uint32 829 | eventTypeStr string 830 | } 831 | 832 | // Table for key, value pairs 833 | var fileEventTable map[string]*fileEvent 834 | 835 | func (file *fileEvent) initFunc(eventMsg sensorEvent) { 836 | file.filePath = "" 837 | file.mounts = make(map[uint32]string) 838 | 839 | file.evType = eventMsg.EvType 840 | file.eventTime = eventMsg.EventTime 841 | file.tid = eventMsg.Tid 842 | file.pid = eventMsg.Pid 843 | file.ppid = eventMsg.Ppid 844 | file.uid = eventMsg.UID 845 | file.inode = eventMsg.Inode 846 | file.device = eventMsg.Device 847 | file.mntNS = eventMsg.MntNS 848 | file.eventTypeStr = printEventByType(eventMsg.EvType) 849 | } 850 | 851 | func getMounts(mounts map[uint32]string) { 852 | mountFile, err := os.Open("/proc/self/mountinfo") 853 | if err != nil { 854 | fmt.Fprintf(os.Stderr, "Error opening mountinfo\n") 855 | return 856 | } 857 | defer mountFile.Close() 858 | lineReader := bufio.NewScanner(mountFile) 859 | for lineReader.Scan() { 860 | lineStr := lineReader.Text() 861 | parts := strings.Split(lineStr, " ") 862 | dev := strings.Split(parts[2], ":") 863 | MSB, _ := strconv.Atoi(dev[0]) 864 | LSB, _ := strconv.Atoi(dev[1]) 865 | devNum := uint32((MSB << 8) | LSB) 866 | mounts[devNum] = parts[4][1:] 867 | } 868 | } 869 | 870 | func (file *fileEvent) updateFunc(eventMsg sensorEvent) string { 871 | if eventMsg.State == stPpPathComponent { 872 | name := eventMsgfNameDecode(eventMsg) 873 | if len(name) == 0 { 874 | if len(file.mounts) == 0 { 875 | getMounts(file.mounts) 876 | } 877 | if mount, found := file.mounts[file.device]; found { 878 | name = mount 879 | } 880 | } 881 | file.filePath = fmt.Sprintf("/%s%s", name, file.filePath) 882 | } else if eventMsg.State == stPpFinalized { 883 | return file.logstrFunc() 884 | } 885 | return "" 886 | } 887 | 888 | func (file fileEvent) logstrFunc() string { 889 | fileEventStr := fmt.Sprintf("%d %s pid:%d ppid:%d uid:%d mnt_ns:%d [%x:%d]%s", 890 | file.eventTime, 891 | file.eventTypeStr, 892 | file.pid, 893 | file.ppid, 894 | file.uid, 895 | file.mntNS, 896 | file.device, 897 | file.inode, 898 | file.filePath) 899 | return fileEventStr 900 | } 901 | 902 | func handleFileEvent(kevent sensorEvent) string { 903 | key := fmt.Sprintf("%d-%d", kevent.Tid, kevent.EventTime) 904 | if file, found := fileEventTable[key]; found { 905 | if file.evType != kevent.EvType { 906 | fmt.Fprintf(os.Stderr, "Miss-match of file event types\n") 907 | return "" 908 | } 909 | 910 | result := file.updateFunc(kevent) 911 | if len(result) != 0 { 912 | delete(fileEventTable, key) 913 | return result 914 | } 915 | } else { 916 | if kevent.State > stPpEntryPoint { 917 | fmt.Fprintf(os.Stderr, "Missing event data\n") 918 | } 919 | var file fileEvent 920 | file.initFunc(kevent) 921 | fileEventTable[key] = &file 922 | } 923 | return "" 924 | } 925 | 926 | func eventMsgfNameDecode(eventMsg sensorEvent) string { 927 | var fName [255]byte 928 | i := 0 929 | 930 | for j := 0; j < len(eventMsg.Ufname); j++ { 931 | // max index for i = 254 932 | if eventMsg.Ufname[j] == 0 || i == 254 { 933 | break 934 | } 935 | fName[i] = eventMsg.Ufname[j] 936 | i++ 937 | } 938 | fName[i] = 0 939 | return string(fName[:i]) 940 | } 941 | -------------------------------------------------------------------------------- /src/bcc_sensor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 VMware, Inc. 3 | * SPDX-License-Identifier: GPL-2.0 4 | */ 5 | 6 | // Struct randomization causes issues on 4.13 and some early versions of 4.14 7 | // These are redefined to work around this, per: 8 | // https://lists.iovisor.org/g/iovisor-dev/topic/21386300#1239 9 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) 10 | #ifdef randomized_struct_fields_start 11 | #undef randomized_struct_fields_start 12 | #endif 13 | #define randomized_struct_fields_start struct { 14 | #ifdef randomized_struct_fields_end 15 | #undef randomized_struct_fields_end 16 | #endif 17 | #define randomized_struct_fields_end \ 18 | } \ 19 | ; 20 | #endif 21 | 22 | #ifndef KBUILD_MODNAME 23 | #define KBUILD_MODNAME "vmw_bcc_bpfsensor" 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include 50 | #include 51 | 52 | // Create BPF_LRU if it does not exist. 53 | // Support for lru hashes begins with 4.10, so a regular hash table must be used on earlier 54 | // kernels (https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#tables-aka-maps) 55 | // This follows the form for other BPF_XXXX macros, so should work if it is ever added 56 | #ifndef BPF_LRU 57 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) 58 | #define BPF_LRU1(_name) BPF_TABLE("lru_hash", u64, u64, _name, 10240) 59 | #define BPF_LRU2(_name, _key_type) \ 60 | BPF_TABLE("lru_hash", _key_type, u64, _name, 10240) 61 | #define BPF_LRU3(_name, _key_type, _leaf_type) \ 62 | BPF_TABLE("lru_hash", _key_type, _leaf_type, _name, 10240) 63 | // helper for default-variable macro function 64 | #define BPF_LRUX(_1, _2, _3, NAME, ...) NAME 65 | 66 | // Define a hash function, some arguments optional 67 | // BPF_LRU(name, key_type=u64, leaf_type=u64, size=10240) 68 | #define BPF_LRU(...) \ 69 | BPF_LRUX(__VA_ARGS__, BPF_LRU3, BPF_LRU2, BPF_LRU1)(__VA_ARGS__) 70 | #else 71 | #define BPF_LRU BPF_HASH 72 | #endif 73 | #endif 74 | 75 | #ifndef PT_REGS_RC 76 | #define PT_REGS_RC(x) ((x)->ax) 77 | #endif 78 | 79 | #ifndef bpf_probe_read_str 80 | // Note that these functions are not 100% compatible. The read_str function returns the number of bytes read, 81 | // while the old version returns 0 on success. Some of the logic we use does depend on the non-zero result 82 | // (described later). 83 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) 84 | #define bpf_probe_read_str bpf_probe_read 85 | #endif 86 | #endif 87 | 88 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 89 | #define FALLBACK_FIELD_TYPE(A, B) A 90 | #else 91 | #define FALLBACK_FIELD_TYPE(A, B) B 92 | #endif 93 | 94 | #define CACHE_UDP 95 | 96 | struct mnt_namespace { 97 | atomic_t count; 98 | struct ns_common ns; 99 | }; 100 | 101 | struct mount { 102 | struct hlist_node mnt_hash; 103 | struct mount *mnt_parent; 104 | struct dentry *mnt_mountpoint; 105 | struct vfsmount mnt; 106 | void *cb_args; 107 | } __randomize_layout; 108 | 109 | enum event_type { 110 | EVENT_PROCESS_ARG, 111 | EVENT_PROCESS_EXEC, 112 | EVENT_PROCESS_EXIT, 113 | EVENT_PROCESS_CLONE, 114 | EVENT_FILE_READ, 115 | EVENT_FILE_WRITE, 116 | EVENT_FILE_CREATE, 117 | EVENT_FILE_PATH, 118 | EVENT_FILE_MMAP, 119 | EVENT_FILE_TEST, 120 | EVENT_NET_CONNECT_PRE, 121 | EVENT_NET_CONNECT_ACCEPT, 122 | EVENT_NET_CONNECT_DNS_RESPONSE, 123 | EVENT_NET_CONNECT_WEB_PROXY, 124 | EVENT_FILE_DELETE, 125 | EVENT_FILE_CLOSE, 126 | EVENT_FILE_OPEN 127 | }; 128 | 129 | #define DNS_RESP_PORT_NUM 53 130 | #define DNS_RESP_MAXSIZE 512 131 | #define PROXY_SERVER_MAX_LEN 100 132 | #define DNS_SEGMENT_LEN 40 133 | #define DNS_SEGMENT_FLAGS_START 0x01 134 | #define DNS_SEGMENT_FLAGS_END 0x02 135 | 136 | struct net_t { 137 | u32 __local_addr; // This is deprecated, but left here to be backwards compatible 138 | u32 __remote_addr; // This is deprecated, but left here to be backwards compatible 139 | u16 remote_port; 140 | u16 local_port; 141 | u16 ipver; 142 | u16 protocol; 143 | u16 dns_flag; 144 | union { 145 | u32 local_addr; 146 | u32 local_addr6[4]; 147 | }; 148 | union { 149 | u32 remote_addr; 150 | u32 remote_addr6[4]; 151 | }; 152 | char dns[DNS_SEGMENT_LEN]; 153 | u32 name_len; 154 | }; 155 | 156 | struct mmap_args { 157 | u64 flags; 158 | u64 prot; 159 | }; 160 | 161 | // Tells us the state for a probe point's data message 162 | #define PP_NO_EXTRA_DATA 0 163 | #define PP_ENTRY_POINT 1 164 | #define PP_PATH_COMPONENT 2 165 | #define PP_FINALIZED 3 166 | #define PP_APPEND 4 167 | #define PP_DEBUG 5 168 | 169 | #define MAX_FNAME 255L 170 | 171 | struct data_t { 172 | u64 event_time; // Time the event collection started. (Same across message parts.) 173 | u32 tid; 174 | u32 pid; 175 | u8 type; 176 | u8 state; 177 | u32 uid; 178 | u32 ppid; 179 | u64 inode; 180 | u32 device; 181 | u32 mnt_ns; 182 | union { 183 | struct mmap_args mmap_args; 184 | char fname[MAX_FNAME]; 185 | struct net_t net; 186 | }; 187 | int retval; 188 | u64 start_time; 189 | u64 event_submit_time; // Time we submit the event to bpf. (Unique for each event.) 190 | }; 191 | 192 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 193 | BPF_HASH(last_start_time, u32, u64, 8192); 194 | BPF_HASH(last_parent, u32, u32, 8192); 195 | BPF_HASH(root_fs, u32, void *, 3); // stores last known root fs 196 | #endif 197 | 198 | BPF_PERF_OUTPUT(events); 199 | 200 | static void send_event(struct pt_regs *ctx, struct data_t *data) 201 | { 202 | data->event_submit_time = bpf_ktime_get_ns(); 203 | events.perf_submit(ctx, data, sizeof(struct data_t)); 204 | } 205 | 206 | static inline struct super_block *_sb_from_dentry(struct dentry *dentry) 207 | { 208 | struct super_block *sb = NULL; 209 | // Can't get dentry info return NULL 210 | if (!dentry) { 211 | goto out; 212 | } 213 | // Try dentry inode before dentry's sb 214 | if (dentry->d_inode) { 215 | sb = dentry->d_inode->i_sb; 216 | } 217 | if (sb) { 218 | goto out; 219 | } 220 | // This might not exactly be the sb we are looking for 221 | sb = dentry->d_sb; 222 | 223 | out: 224 | return sb; 225 | } 226 | 227 | static inline struct super_block *_sb_from_file(struct file *file) 228 | { 229 | struct super_block *sb = NULL; 230 | 231 | if (!file) { 232 | goto out; 233 | } 234 | 235 | if (file->f_inode) { 236 | sb = file->f_inode->i_sb; 237 | } 238 | if (sb) { 239 | goto out; 240 | } 241 | sb = _sb_from_dentry(file->f_path.dentry); 242 | 243 | out: 244 | return sb; 245 | } 246 | 247 | static inline bool __is_special_filesystem(struct super_block *sb) 248 | { 249 | if (!sb) { 250 | return false; 251 | } 252 | 253 | switch (sb->s_magic) { 254 | // Special Kernel File Systems 255 | case CGROUP_SUPER_MAGIC: 256 | #ifdef CGROUP2_SUPER_MAGIC 257 | case CGROUP2_SUPER_MAGIC: 258 | #endif /* CGROUP2_SUPER_MAGIC */ 259 | case SELINUX_MAGIC: 260 | #ifdef SMACK_MAGIC 261 | case SMACK_MAGIC: 262 | #endif /* SMACK_MAGIC */ 263 | case SYSFS_MAGIC: 264 | case PROC_SUPER_MAGIC: 265 | case SOCKFS_MAGIC: 266 | case DEVPTS_SUPER_MAGIC: 267 | case FUTEXFS_SUPER_MAGIC: 268 | case ANON_INODE_FS_MAGIC: 269 | case DEBUGFS_MAGIC: 270 | case TRACEFS_MAGIC: 271 | #ifdef BINDERFS_SUPER_MAGIC 272 | case BINDERFS_SUPER_MAGIC: 273 | #endif /* BINDERFS_SUPER_MAGIC */ 274 | #ifdef BPF_FS_MAGIC 275 | case BPF_FS_MAGIC: 276 | #endif /* BPF_FS_MAGIC */ 277 | 278 | return true; 279 | 280 | default: 281 | return false; 282 | } 283 | 284 | return false; 285 | } 286 | 287 | static inline unsigned int __get_mnt_ns_id(struct task_struct *task) 288 | { 289 | struct nsproxy *nsproxy; 290 | 291 | if (task && task->nsproxy) { 292 | return task->nsproxy->mnt_ns->ns.inum; 293 | } 294 | return 0; 295 | } 296 | 297 | static inline void __set_device_from_sb(struct data_t *data, 298 | struct super_block *sb) 299 | { 300 | if (data) { 301 | data->device = 0; 302 | } 303 | 304 | if (!data || !sb) { 305 | return; 306 | } 307 | 308 | data->device = new_encode_dev(sb->s_dev); 309 | } 310 | 311 | static inline void __set_device_from_file(struct data_t *data, 312 | struct file *file) 313 | { 314 | struct super_block *sb = NULL; 315 | 316 | if (data) { 317 | data->device = 0; 318 | } 319 | 320 | if (!data || !file) { 321 | return; 322 | } 323 | 324 | sb = _sb_from_file(file); 325 | if (!sb) { 326 | return; 327 | } 328 | __set_device_from_sb(data, sb); 329 | } 330 | 331 | static inline void __set_inode_from_file(struct data_t *data, struct file *file) 332 | { 333 | struct inode *pinode = NULL; 334 | 335 | if (data) { 336 | data->inode = 0; 337 | } 338 | 339 | if (!data || !file) { 340 | return; 341 | } 342 | 343 | bpf_probe_read(&pinode, sizeof(pinode), &(file->f_inode)); 344 | if (!pinode) { 345 | return; 346 | } 347 | 348 | bpf_probe_read(&data->inode, sizeof(data->inode), &pinode->i_ino); 349 | } 350 | 351 | // Assumed current context is what is valid! 352 | static inline void __set_key_entry_data(struct data_t *data, struct file *file) 353 | { 354 | u64 id; 355 | 356 | data->event_time = bpf_ktime_get_ns(); 357 | 358 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 359 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 360 | data->tid = task->pid; 361 | data->pid = task->tgid; 362 | data->start_time = task->start_time; 363 | data->uid = __kuid_val(task->cred->uid); 364 | data->ppid = task->real_parent->tgid; 365 | data->mnt_ns = __get_mnt_ns_id(task); 366 | #else 367 | id = bpf_get_current_pid_tgid(); 368 | data->tid = id & 0xffffffff; 369 | data->pid = id >> 32; 370 | 371 | u64 *last_start = last_start_time.lookup(&data->pid); 372 | if (last_start) { 373 | data->start_time = *last_start; 374 | } 375 | u32 *ppid = last_parent.lookup(&data->pid); 376 | if (ppid) { 377 | data->ppid = *ppid; 378 | } 379 | #endif 380 | 381 | __set_inode_from_file(data, file); 382 | } 383 | 384 | static u8 __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data) 385 | { 386 | // Note: On some kernels bpf_probe_read_str does not exist. In this case it is 387 | // substituted by bpf_probe_read. The return value for these two cases mean something 388 | // different, but that is OK for our logic. 389 | // Note: On older kernel this may read past the actual arg list into the env. 390 | u8 result = bpf_probe_read_str(data->fname, MAX_FNAME, ptr); 391 | send_event(ctx, data); 392 | return result; 393 | } 394 | 395 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 396 | #define MAXARG 30 397 | #else 398 | #define MAXARG 20 399 | #endif 400 | 401 | // PSCLNX-6764 - Improve EXEC event performance 402 | // This logic should be refactored to write the multiple args into a single 403 | // event buffer instead of one event per arg. 404 | static void submit_all_args(struct pt_regs *ctx, 405 | const char __user *const __user *_argv, 406 | struct data_t *data) 407 | { 408 | void *argp = NULL; 409 | void *next_argp = NULL; 410 | int index = 0; 411 | 412 | #pragma unroll 413 | for (int i = 0; i < MAXARG; i++) { 414 | if (next_argp) { 415 | // If there is more data to read in this arg, we tell the collector 416 | // to continue with the previous arg (and not add a ' '). 417 | data->state = PP_APPEND; 418 | argp = next_argp; 419 | next_argp = NULL; 420 | } else { 421 | // This is a new arg 422 | data->state = PP_ENTRY_POINT; 423 | bpf_probe_read(&argp, sizeof(argp), &_argv[index++]); 424 | } 425 | if (!argp) { 426 | // We have reached the last arg so bail out 427 | goto out; 428 | } 429 | 430 | // Read the arg data and send an event. We expect the result to be the bytes sent 431 | // in the event. On older kernels, this may be 0 which is OK. It just means that 432 | // we will always truncate the arg. 433 | u8 bytes_written = __submit_arg(ctx, argp, data); 434 | next_argp = NULL; 435 | 436 | if (bytes_written == MAX_FNAME) { 437 | // If we have filled the buffer exactly, it means that there is additional 438 | // data for this arg. 439 | // Advance the read pointer by the bytes written (minus the null terminator) 440 | next_argp = argp + bytes_written - 1; 441 | } 442 | } 443 | 444 | // handle truncated argument list 445 | char ellipsis[] = "..."; 446 | __submit_arg(ctx, (void *)ellipsis, data); 447 | 448 | out: 449 | return; 450 | } 451 | 452 | #ifndef MAX_PATH_ITER 453 | #define MAX_PATH_ITER 24 454 | #endif 455 | static inline int __do_file_path(struct pt_regs *ctx, struct dentry *dentry, 456 | struct vfsmount *mnt, struct data_t *data) 457 | { 458 | struct mount *real_mount = NULL; 459 | struct mount *mnt_parent = NULL; 460 | struct dentry *mnt_root = NULL; 461 | struct dentry *new_mnt_root = NULL; 462 | struct dentry *parent_dentry = NULL; 463 | struct qstr sp = {}; 464 | 465 | struct dentry *root_fs_dentry = NULL; 466 | struct vfsmount *root_fs_vfsmnt = NULL; 467 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 468 | // We can ifdef this block to make this act more like either 469 | // d_absolute_path or __d_path 470 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 471 | if (task->fs) { 472 | // We can get root fs path from mnt_ns or task 473 | root_fs_vfsmnt = task->fs->root.mnt; 474 | root_fs_dentry = task->fs->root.dentry; 475 | } 476 | #else 477 | u32 index = 0; 478 | struct dentry **t_dentry = (struct dentry **)root_fs.lookup(&index); 479 | if (t_dentry) { 480 | root_fs_dentry = *t_dentry; 481 | } 482 | index = 1; 483 | struct vfsmount **t_vfsmount = 484 | (struct vfsmount **)root_fs.lookup(&index); 485 | if (t_vfsmount) { 486 | root_fs_vfsmnt = *t_vfsmount; 487 | } 488 | #endif 489 | 490 | mnt_root = mnt->mnt_root; 491 | 492 | // poorman's container_of 493 | real_mount = ((void *)mnt) - offsetof(struct mount, mnt); 494 | 495 | // compiler doesn't seem to mind accessing stuff without bpf_probe_read 496 | mnt_parent = real_mount->mnt_parent; 497 | 498 | /* 499 | * File Path Walking. This may not be completely accurate but 500 | * should hold for most cases. Paths for private mount namespaces might work. 501 | */ 502 | data->state = PP_PATH_COMPONENT; 503 | #pragma clang loop unroll(full) 504 | for (int i = 1; i < MAX_PATH_ITER; ++i) { 505 | if (dentry == root_fs_dentry) { 506 | goto out; 507 | } 508 | 509 | bpf_probe_read(&parent_dentry, sizeof(parent_dentry), 510 | &(dentry->d_parent)); 511 | if (dentry == parent_dentry || dentry == mnt_root) { 512 | bpf_probe_read(&dentry, sizeof(struct dentry *), 513 | &(real_mount->mnt_mountpoint)); 514 | real_mount = mnt_parent; 515 | bpf_probe_read(&mnt, sizeof(struct vfsmnt *), 516 | &(real_mount->mnt)); 517 | mnt_root = mnt->mnt_root; 518 | if (mnt == root_fs_vfsmnt) { 519 | goto out; 520 | } 521 | 522 | // prefetch next real mount parent. 523 | mnt_parent = real_mount->mnt_parent; 524 | if (mnt_parent == real_mount) { 525 | goto out; 526 | } 527 | } else { 528 | bpf_probe_read(&sp, sizeof(sp), 529 | (void *)&(dentry->d_name)); 530 | bpf_probe_read(&data->fname, sizeof(data->fname), 531 | sp.name); 532 | dentry = parent_dentry; 533 | send_event(ctx, data); 534 | } 535 | } 536 | 537 | out: 538 | data->state = PP_FINALIZED; 539 | return 0; 540 | } 541 | 542 | static inline int __do_dentry_path(struct pt_regs *ctx, struct dentry *dentry, 543 | struct data_t *data) 544 | { 545 | struct dentry *current_dentry = NULL; 546 | struct dentry *parent_dentry = NULL; 547 | struct qstr sp = {}; 548 | 549 | struct dentry *root_fs_dentry = NULL; 550 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 551 | 552 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 553 | if (task->fs) { 554 | root_fs_dentry = task->fs->root.dentry; 555 | } 556 | #else 557 | u32 index = 0; 558 | struct dentry **t_dentry = (struct dentry **)root_fs.lookup(&index); 559 | if (t_dentry) { 560 | root_fs_dentry = *t_dentry; 561 | } 562 | #endif 563 | bpf_probe_read(&sp, sizeof(struct qstr), (void *)&(dentry->d_name)); 564 | if (sp.name == NULL) { 565 | goto out; 566 | } 567 | bpf_probe_read(&data->fname, sizeof(data->fname), (void *)sp.name); 568 | 569 | bpf_probe_read(&parent_dentry, sizeof(parent_dentry), 570 | &(dentry->d_parent)); 571 | bpf_probe_read(¤t_dentry, sizeof(current_dentry), &(dentry)); 572 | data->state = PP_PATH_COMPONENT; 573 | 574 | #pragma unroll 575 | for (int i = 0; i < MAX_PATH_ITER; i++) { 576 | if (dentry == root_fs_dentry) { 577 | goto out; 578 | } 579 | 580 | if (parent_dentry == current_dentry || parent_dentry == NULL) { 581 | break; 582 | } 583 | bpf_probe_read(&sp, sizeof(struct qstr), 584 | (void *)&(current_dentry->d_name)); 585 | if ((void *)sp.name != NULL) { 586 | bpf_probe_read(data->fname, sizeof(data->fname), 587 | (void *)sp.name); 588 | send_event(ctx, data); 589 | } 590 | 591 | bpf_probe_read(¤t_dentry, sizeof(current_dentry), 592 | &(parent_dentry)); 593 | bpf_probe_read(&parent_dentry, sizeof(parent_dentry), 594 | &(parent_dentry->d_parent)); 595 | } 596 | 597 | data->fname[0] = '\0'; 598 | send_event(ctx, data); 599 | 600 | out: 601 | data->state = PP_FINALIZED; 602 | return 0; 603 | } 604 | 605 | int syscall__on_sys_execveat(struct pt_regs *ctx, int fd, 606 | const char __user *filename, 607 | const char __user *const __user *argv, 608 | const char __user *const __user *envp, int flags) 609 | { 610 | struct data_t data = {}; 611 | 612 | __set_key_entry_data(&data, NULL); 613 | data.type = EVENT_PROCESS_ARG; 614 | data.state = PP_ENTRY_POINT; 615 | 616 | submit_all_args(ctx, argv, &data); 617 | 618 | return 0; 619 | } 620 | int syscall__on_sys_execve(struct pt_regs *ctx, const char __user *filename, 621 | const char __user *const __user *argv, 622 | const char __user *const __user *envp) 623 | { 624 | struct data_t data = {}; 625 | 626 | __set_key_entry_data(&data, NULL); 627 | data.type = EVENT_PROCESS_ARG; 628 | data.state = PP_ENTRY_POINT; 629 | 630 | submit_all_args(ctx, argv, &data); 631 | 632 | return 0; 633 | } 634 | 635 | //Note that this can be called more than one from the same pid 636 | int after_sys_execve(struct pt_regs *ctx) 637 | { 638 | struct data_t data = {}; 639 | 640 | __set_key_entry_data(&data, NULL); 641 | data.event_time = bpf_ktime_get_ns(); 642 | data.state = PP_FINALIZED; 643 | data.type = EVENT_PROCESS_ARG; 644 | data.retval = PT_REGS_RC(ctx); 645 | 646 | send_event(ctx, &data); 647 | 648 | return 0; 649 | } 650 | 651 | struct file_data { 652 | u64 pid; 653 | u64 device; 654 | u64 inode; 655 | }; 656 | 657 | // This hash tracks the "observed" file-create events. This will not be 100% accurate because we will report a 658 | // file create for any file the first time it is opened with WRITE|TRUNCATE (even if it already exists). It 659 | // will however serve to de-dup some events. (Ie.. If a program does frequent open/write/close.) 660 | BPF_LRU(file_map, struct file_data, u32); 661 | 662 | // Older kernels do not support the struct fields so allow for fallback 663 | BPF_LRU(file_write_cache, u64, FALLBACK_FIELD_TYPE(struct file_data, u32)); 664 | BPF_LRU(file_creat_cache, u64, u32); 665 | 666 | // Only need this hook for kernels without lru_hash 667 | int on_security_file_free(struct pt_regs *ctx, struct file *file) 668 | { 669 | if (!file) { 670 | return 0; 671 | } 672 | u64 file_cache_key = (u64)file; 673 | 674 | void *cachep = file_write_cache.lookup(&file_cache_key); 675 | if (cachep) { 676 | struct data_t data = {}; 677 | __set_key_entry_data(&data, NULL); 678 | 679 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 680 | data.device = ((struct file_data *)cachep)->device; 681 | data.inode = ((struct file_data *)cachep)->inode; 682 | #else 683 | data.device = 0; 684 | data.inode = 0; 685 | __set_device_from_file(&data, file); 686 | __set_inode_from_file(&data, file); 687 | #endif 688 | 689 | data.state = PP_ENTRY_POINT; 690 | data.type = EVENT_FILE_CLOSE; 691 | send_event(ctx, &data); 692 | 693 | __do_file_path(ctx, file->f_path.dentry, file->f_path.mnt, 694 | &data); 695 | send_event(ctx, &data); 696 | } 697 | 698 | file_write_cache.delete(&file_cache_key); 699 | file_creat_cache.delete(&file_cache_key); 700 | return 0; 701 | } 702 | 703 | int on_security_mmap_file(struct pt_regs *ctx, struct file *file, 704 | unsigned long prot, unsigned long flags) 705 | { 706 | unsigned long exec_flags; 707 | struct data_t data = {}; 708 | 709 | if (!file) { 710 | goto out; 711 | } 712 | if (!(prot & PROT_EXEC)) { 713 | goto out; 714 | } 715 | 716 | exec_flags = flags & (MAP_DENYWRITE | MAP_EXECUTABLE); 717 | if (exec_flags == (MAP_DENYWRITE | MAP_EXECUTABLE)) { 718 | data.type = EVENT_PROCESS_EXEC; 719 | } else { 720 | data.type = EVENT_FILE_MMAP; 721 | } 722 | 723 | // event specific data 724 | data.state = PP_ENTRY_POINT; 725 | __set_key_entry_data(&data, file); 726 | __set_device_from_file(&data, file); 727 | data.mmap_args.flags = flags; 728 | data.mmap_args.prot = prot; 729 | // submit initial event data 730 | send_event(ctx, &data); 731 | 732 | // submit file path event data 733 | __do_file_path(ctx, file->f_path.dentry, file->f_path.mnt, &data); 734 | send_event(ctx, &data); 735 | out: 736 | return 0; 737 | } 738 | 739 | static inline int __trace_write_entry(struct pt_regs *ctx, struct file *file, 740 | char __user *buf, size_t count) 741 | { 742 | struct data_t data = {}; 743 | struct super_block *sb = NULL; 744 | struct inode *inode = NULL; 745 | int mode; 746 | 747 | if (!file) { 748 | goto out; 749 | } 750 | 751 | sb = _sb_from_file(file); 752 | if (!sb) { 753 | goto out; 754 | } 755 | 756 | if (__is_special_filesystem(sb)) { 757 | goto out; 758 | } 759 | 760 | bpf_probe_read(&inode, sizeof(inode), &(file->f_inode)); 761 | if (!inode) { 762 | goto out; 763 | } 764 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 765 | bpf_probe_read(&mode, sizeof(mode), &(inode->i_mode)); 766 | if (!S_ISREG(mode)) { 767 | goto out; 768 | } 769 | #endif 770 | __set_key_entry_data(&data, file); 771 | __set_device_from_sb(&data, sb); 772 | 773 | u64 file_cache_key = (u64)file; 774 | 775 | void *cachep = file_write_cache.lookup(&file_cache_key); 776 | if (cachep) { 777 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 778 | struct file_data cache_data = *((struct file_data *)cachep); 779 | pid_t pid = cache_data.pid; 780 | cache_data.pid = data.pid; 781 | #else 782 | u32 cache_data = *(u32 *)cachep; 783 | pid_t pid = cache_data; 784 | cache_data = data.pid; 785 | #endif 786 | 787 | // if we really care about that multiple tasks 788 | // these are likely threads or less likely inherited from a fork 789 | if (pid == data.pid) { 790 | goto out; 791 | } 792 | 793 | file_write_cache.update(&file_cache_key, &cache_data); 794 | goto out; 795 | } else { 796 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 797 | struct file_data cache_data = { .pid = data.pid, 798 | .device = data.device, 799 | .inode = data.inode }; 800 | #else 801 | u32 cache_data = data.pid; 802 | #endif 803 | file_write_cache.insert(&file_cache_key, &cache_data); 804 | } 805 | 806 | data.state = PP_ENTRY_POINT; 807 | data.type = EVENT_FILE_WRITE; 808 | send_event(ctx, &data); 809 | 810 | __do_file_path(ctx, file->f_path.dentry, file->f_path.mnt, &data); 811 | send_event(ctx, &data); 812 | out: 813 | return 0; 814 | } 815 | 816 | int trace_write_entry(struct pt_regs *ctx, struct file *file, char __user *buf, 817 | size_t count) 818 | { 819 | return (__trace_write_entry(ctx, file, buf, count)); 820 | } 821 | 822 | // This is mainly for kernel > 5.8.0 823 | int trace_write_kentry(struct pt_regs *ctx, struct file *file, const void *buf, 824 | size_t count) 825 | { 826 | return (__trace_write_entry(ctx, file, (char *)buf, count)); 827 | } 828 | 829 | // This hook may not be very accurate but at least tells us the intent 830 | // to create the file if needed. So this will likely be written to next. 831 | int on_security_file_open(struct pt_regs *ctx, struct file *file) 832 | { 833 | struct data_t data = {}; 834 | struct super_block *sb = NULL; 835 | struct inode *inode = NULL; 836 | int mode; 837 | 838 | if (!file) { 839 | goto out; 840 | } 841 | 842 | if ((file->f_flags & (O_CREAT | O_TRUNC))) { 843 | data.type = EVENT_FILE_CREATE; 844 | } else if (!(file->f_flags & (O_RDWR | O_WRONLY))) { 845 | data.type = EVENT_FILE_OPEN; 846 | } else { 847 | goto out; 848 | } 849 | 850 | sb = _sb_from_file(file); 851 | if (!sb) { 852 | goto out; 853 | } 854 | 855 | if (__is_special_filesystem(sb)) { 856 | goto out; 857 | } 858 | 859 | bpf_probe_read(&inode, sizeof(inode), &(file->f_inode)); 860 | if (!inode) { 861 | goto out; 862 | } 863 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 864 | bpf_probe_read(&mode, sizeof(mode), &(inode->i_mode)); 865 | if (!S_ISREG(mode)) { 866 | goto out; 867 | } 868 | #endif 869 | __set_key_entry_data(&data, file); 870 | __set_device_from_sb(&data, sb); 871 | 872 | u32 *cachep; 873 | u64 file_cache_key = (u64)file; 874 | 875 | struct file_data key = { .device = data.device, .inode = data.inode }; 876 | 877 | // If this is a create event and is already tracked, skip the event. 878 | // Otherwise add it to the tracking table. 879 | // Skip this behavior if this is an open event. 880 | u32 *file_exists = file_map.lookup(&key); 881 | if (data.type == EVENT_FILE_CREATE) { 882 | if (file_exists) { 883 | goto out; 884 | } 885 | 886 | file_map.update(&key, &data.pid); 887 | cachep = file_creat_cache.lookup(&file_cache_key); 888 | if (cachep) { 889 | if (*cachep == data.pid) { 890 | goto out; 891 | } 892 | file_creat_cache.update(&file_cache_key, &data.pid); 893 | goto out; 894 | } else { 895 | file_creat_cache.insert(&file_cache_key, &data.pid); 896 | } 897 | } 898 | 899 | data.state = PP_ENTRY_POINT; 900 | send_event(ctx, &data); 901 | 902 | __do_file_path(ctx, file->f_path.dentry, file->f_path.mnt, &data); 903 | 904 | send_event(ctx, &data); 905 | 906 | out: 907 | return 0; 908 | } 909 | 910 | int on_security_inode_unlink(struct pt_regs *ctx, struct inode *dir, 911 | struct dentry *dentry) 912 | { 913 | struct data_t data = {}; 914 | struct super_block *sb = NULL; 915 | struct inode *inode = NULL; 916 | int mode; 917 | 918 | if (!dentry) { 919 | goto out; 920 | } 921 | 922 | sb = _sb_from_dentry(dentry); 923 | if (!sb) { 924 | goto out; 925 | } 926 | 927 | if (__is_special_filesystem(sb)) { 928 | goto out; 929 | } 930 | 931 | __set_key_entry_data(&data, NULL); 932 | 933 | bpf_probe_read(&inode, sizeof(inode), &(dentry->d_inode)); 934 | if (inode) { 935 | bpf_probe_read(&data.inode, sizeof(data.inode), &inode->i_ino); 936 | } 937 | 938 | __set_device_from_sb(&data, sb); 939 | 940 | // Delete the file from the tracking so that it will be reported the next time it is created. 941 | struct file_data key = { .device = data.device, .inode = data.inode }; 942 | 943 | file_map.delete(&key); 944 | 945 | data.state = PP_ENTRY_POINT; 946 | data.type = EVENT_FILE_DELETE; 947 | send_event(ctx, &data); 948 | 949 | __do_dentry_path(ctx, dentry, &data); 950 | 951 | send_event(ctx, &data); 952 | 953 | out: 954 | return 0; 955 | } 956 | 957 | int on_security_inode_rename(struct pt_regs *ctx, struct inode *old_dir, 958 | struct dentry *old_dentry, struct inode *new_dir, 959 | struct dentry *new_dentry, unsigned int flags) 960 | { 961 | struct data_t data = {}; 962 | struct super_block *old_sb = NULL; 963 | struct super_block *new_sb = NULL; 964 | struct inode *inode = NULL; 965 | 966 | old_sb = _sb_from_dentry(old_dentry); 967 | if (!old_sb || __is_special_filesystem(old_sb)) { 968 | goto out; 969 | } 970 | 971 | // Send the delete event for the path where the file is being moved from 972 | __set_key_entry_data(&data, NULL); 973 | 974 | data.state = PP_ENTRY_POINT; 975 | data.type = EVENT_FILE_DELETE; 976 | bpf_probe_read(&inode, sizeof(inode), &(old_dentry->d_inode)); 977 | if (inode) { 978 | bpf_probe_read(&data.inode, sizeof(data.inode), &inode->i_ino); 979 | } 980 | 981 | struct file_data old_key = { .device = data.device, 982 | .inode = data.inode }; 983 | file_map.delete(&old_key); 984 | 985 | __set_device_from_sb(&data, old_sb); 986 | send_event(ctx, &data); 987 | __do_dentry_path(ctx, old_dentry, &data); 988 | send_event(ctx, &data); 989 | 990 | // If the target destination already exists, 991 | // send a delete event for the file that will be overwritten 992 | if (new_dentry && new_dentry->d_inode != NULL) { 993 | new_sb = _sb_from_dentry(new_dentry); 994 | if (new_sb && !__is_special_filesystem(new_sb)) { 995 | __set_key_entry_data(&data, NULL); 996 | 997 | data.state = PP_ENTRY_POINT; 998 | data.type = EVENT_FILE_DELETE; 999 | bpf_probe_read(&inode, sizeof(inode), 1000 | &(new_dentry->d_inode)); 1001 | if (inode) { 1002 | bpf_probe_read(&data.inode, sizeof(data.inode), 1003 | &inode->i_ino); 1004 | } 1005 | 1006 | struct file_data new_key = { .device = data.device, 1007 | .inode = data.inode }; 1008 | file_map.delete(&new_key); 1009 | 1010 | __set_device_from_sb(&data, new_sb); 1011 | send_event(ctx, &data); 1012 | __do_dentry_path(ctx, new_dentry, &data); 1013 | send_event(ctx, &data); 1014 | } 1015 | } 1016 | 1017 | // Send the create event for the path where the file is being moved to 1018 | // (the path will be the one reported in the new dentry, but the inode 1019 | // will persist and be the one from the old dentry) 1020 | inode = NULL; 1021 | data.state = PP_ENTRY_POINT; 1022 | data.type = EVENT_FILE_CREATE; 1023 | bpf_probe_read(&inode, sizeof(inode), &(old_dentry->d_inode)); 1024 | if (inode) { 1025 | bpf_probe_read(&data.inode, sizeof(data.inode), &inode->i_ino); 1026 | } 1027 | __set_device_from_sb(&data, new_sb ? new_sb : old_sb); 1028 | send_event(ctx, &data); 1029 | __do_dentry_path(ctx, new_dentry, &data); 1030 | send_event(ctx, &data); 1031 | 1032 | out: 1033 | return 0; 1034 | } 1035 | 1036 | int on_wake_up_new_task(struct pt_regs *ctx, struct task_struct *task) 1037 | { 1038 | struct inode *pinode = NULL; 1039 | struct data_t data = {}; 1040 | struct file *exe_file = NULL; 1041 | if (!task) { 1042 | goto out; 1043 | } 1044 | 1045 | if (task->tgid != task->pid) { 1046 | goto out; 1047 | } 1048 | 1049 | data.event_time = bpf_ktime_get_ns(); 1050 | data.type = EVENT_PROCESS_CLONE; 1051 | data.tid = task->pid; 1052 | data.pid = task->tgid; 1053 | data.start_time = task->start_time; 1054 | data.ppid = task->real_parent->tgid; 1055 | data.state = PP_NO_EXTRA_DATA; 1056 | data.uid = __kuid_val(task->real_parent->cred->uid); 1057 | data.mnt_ns = __get_mnt_ns_id(task); 1058 | 1059 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 1060 | // Store last reported start_time where ever we can grab the task struct 1061 | // For older kernels we could probe when tasks are scheduled to wake 1062 | // to cache more tasks. 1063 | u64 parent_start_time = task->real_parent->start_time; 1064 | last_start_time.update(&data.ppid, &parent_start_time); 1065 | last_parent.update(&data.pid, &data.ppid); 1066 | last_start_time.update(&data.pid, &data.start_time); 1067 | last_parent.update(&data.pid, &data.ppid); 1068 | 1069 | // Poorman's method for storing root fs path data. 1070 | // This is to prevent us from iterating past '/' 1071 | u32 index; 1072 | struct dentry *root_fs_dentry = task->fs->root.dentry; 1073 | struct vfsmount *root_fs_vfsmount = task->fs->root.mnt; 1074 | index = 0; 1075 | root_fs.update(&index, (void *)&root_fs_dentry); 1076 | index += 1; 1077 | root_fs.update(&index, (void *)&root_fs_vfsmount); 1078 | #endif 1079 | 1080 | // Get this in case it's a non-standard process 1081 | bpf_get_current_comm(&data.fname, TASK_COMM_LEN); 1082 | if ((task->flags & PF_KTHREAD) || !task->mm) { 1083 | send_event(ctx, &data); 1084 | goto out; 1085 | } 1086 | 1087 | exe_file = task->mm->exe_file; 1088 | if (!exe_file) { 1089 | send_event(ctx, &data); 1090 | goto out; 1091 | } 1092 | bpf_probe_read(&pinode, sizeof(pinode), &(exe_file->f_inode)); 1093 | if (!pinode) { 1094 | send_event(ctx, &data); 1095 | goto out; 1096 | } 1097 | bpf_probe_read(&data.inode, sizeof(data.inode), &pinode->i_ino); 1098 | __set_device_from_file(&data, exe_file); 1099 | 1100 | data.state = PP_ENTRY_POINT; 1101 | 1102 | send_event(ctx, &data); 1103 | __do_file_path(ctx, exe_file->f_path.dentry, exe_file->f_path.mnt, 1104 | &data); 1105 | send_event(ctx, &data); 1106 | 1107 | out: 1108 | return 0; 1109 | } 1110 | 1111 | #ifdef CACHE_UDP 1112 | struct ip_key { 1113 | uint32_t pid; 1114 | uint64_t start_time; 1115 | uint16_t remote_port; 1116 | uint16_t local_port; 1117 | uint32_t remote_addr; 1118 | uint32_t local_addr; 1119 | }; 1120 | struct ip6_key { 1121 | uint32_t pid; 1122 | uint64_t start_time; 1123 | uint16_t remote_port; 1124 | uint16_t local_port; 1125 | uint32_t remote_addr6[4]; 1126 | uint32_t local_addr6[4]; 1127 | }; 1128 | #define FLOW_TX 0x01 1129 | #define FLOW_RX 0x02 1130 | struct ip_entry { 1131 | u8 flow; 1132 | }; 1133 | 1134 | BPF_LRU(ip_cache, FALLBACK_FIELD_TYPE(struct ip_key, u32), 1135 | FALLBACK_FIELD_TYPE(struct ip_entry, struct ip_key)); 1136 | BPF_LRU(ip6_cache, FALLBACK_FIELD_TYPE(struct ip6_key, u32), 1137 | FALLBACK_FIELD_TYPE(struct ip_entry, struct ip6_key)); 1138 | 1139 | static inline bool has_ip_cache(struct ip_key *ip_key, u8 flow) 1140 | { 1141 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 1142 | struct ip_key *ip_entry = ip_cache.lookup(&ip_key->pid); 1143 | if (ip_entry) { 1144 | if (ip_entry->remote_port == ip_key->remote_port && 1145 | ip_entry->local_port == ip_key->local_port && 1146 | ip_entry->remote_addr == ip_key->remote_addr && 1147 | ip_entry->local_addr == ip_key->local_addr) { 1148 | return true; 1149 | } else { 1150 | // Update entry 1151 | ip_cache.update(&ip_key->pid, ip_key); 1152 | } 1153 | } else { 1154 | ip_cache.insert(&ip_key->pid, ip_key); 1155 | } 1156 | #else 1157 | struct ip_entry *ip_entry = ip_cache.lookup(ip_key); 1158 | if (ip_entry) { 1159 | if ((ip_entry->flow & flow)) { 1160 | return true; 1161 | } 1162 | // Updates map entry 1163 | ip_entry->flow |= flow; 1164 | } else { 1165 | struct ip_entry new_entry = {}; 1166 | new_entry.flow = flow; 1167 | ip_cache.insert(ip_key, &new_entry); 1168 | } 1169 | #endif 1170 | return false; 1171 | } 1172 | 1173 | static inline bool has_ip6_cache(struct ip6_key *ip6_key, u8 flow) 1174 | { 1175 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 1176 | struct ip6_key *ip_entry = ip6_cache.lookup(&ip6_key->pid); 1177 | if (ip_entry) { 1178 | if (ip_entry->remote_port == ip6_key->remote_port && 1179 | ip_entry->local_port == ip6_key->local_port && 1180 | ip_entry->remote_addr6[0] == ip6_key->remote_addr6[0] && 1181 | ip_entry->remote_addr6[1] == ip6_key->remote_addr6[1] && 1182 | ip_entry->remote_addr6[2] == ip6_key->remote_addr6[2] && 1183 | ip_entry->remote_addr6[3] == ip6_key->remote_addr6[3] && 1184 | ip_entry->local_addr6[0] == ip6_key->local_addr6[0] && 1185 | ip_entry->local_addr6[1] == ip6_key->local_addr6[1] && 1186 | ip_entry->local_addr6[2] == ip6_key->local_addr6[2] && 1187 | ip_entry->local_addr6[3] == ip6_key->local_addr6[3]) { 1188 | return true; 1189 | } else { 1190 | // Update entry 1191 | ip6_cache.update(&ip6_key->pid, ip6_key); 1192 | } 1193 | } else { 1194 | ip6_cache.insert(&ip6_key->pid, ip6_key); 1195 | } 1196 | #else 1197 | struct ip_entry *ip_entry = ip6_cache.lookup(ip6_key); 1198 | if (ip_entry) { 1199 | if ((ip_entry->flow & flow)) { 1200 | return true; 1201 | } 1202 | // Updates map entry 1203 | ip_entry->flow |= flow; 1204 | } else { 1205 | struct ip_entry new_entry = {}; 1206 | new_entry.flow = flow; 1207 | ip6_cache.insert(ip6_key, &new_entry); 1208 | } 1209 | #endif 1210 | return false; 1211 | } 1212 | #endif /* CACHE_UDP */ 1213 | 1214 | int on_security_task_free(struct pt_regs *ctx, struct task_struct *task) 1215 | { 1216 | struct data_t data = {}; 1217 | if (!task) { 1218 | goto out; 1219 | } 1220 | if (task->tgid != task->pid) { 1221 | goto out; 1222 | } 1223 | 1224 | data.event_time = bpf_ktime_get_ns(); 1225 | data.type = EVENT_PROCESS_EXIT; 1226 | data.tid = task->pid; 1227 | data.pid = task->tgid; 1228 | data.start_time = task->start_time; 1229 | if (task->real_parent) { 1230 | data.ppid = task->real_parent->tgid; 1231 | } 1232 | send_event(ctx, &data); 1233 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 1234 | last_start_time.delete(&data.pid); 1235 | last_parent.delete(&data.pid); 1236 | #ifdef CACHE_UDP 1237 | // Remove burst cache entries 1238 | // We only need to do this for older kernels that do not have an LRU 1239 | ip_cache.delete(&data.pid); 1240 | ip6_cache.delete(&data.pid); 1241 | #endif /* CACHE_UDP */ 1242 | #endif 1243 | out: 1244 | return 0; 1245 | } 1246 | 1247 | BPF_LRU(currsock, u64, struct sock *); 1248 | BPF_LRU(currsock2, u64, struct msghdr *); 1249 | BPF_LRU(currsock3, u64, struct sock *); 1250 | 1251 | int trace_connect_v4_entry(struct pt_regs *ctx, struct sock *sk) 1252 | { 1253 | u64 id = bpf_get_current_pid_tgid(); 1254 | currsock.update(&id, &sk); 1255 | return 0; 1256 | } 1257 | 1258 | int trace_connect_v6_entry(struct pt_regs *ctx, struct sock *sk) 1259 | { 1260 | u64 id = bpf_get_current_pid_tgid(); 1261 | currsock.update(&id, &sk); 1262 | return 0; 1263 | } 1264 | 1265 | static inline bool check_family(struct sock *sk, u16 expected_family) 1266 | { 1267 | u16 family = sk->__sk_common.skc_family; 1268 | return family == expected_family; 1269 | } 1270 | 1271 | static int trace_connect_return(struct pt_regs *ctx) 1272 | { 1273 | u64 id = bpf_get_current_pid_tgid(); 1274 | u32 pid = id >> 32; 1275 | int ret = PT_REGS_RC(ctx); 1276 | if (ret != 0) { 1277 | currsock.delete(&id); 1278 | return 0; 1279 | } 1280 | 1281 | struct sock **skpp; 1282 | skpp = currsock.lookup(&id); 1283 | if (skpp == 0) { 1284 | return 0; 1285 | } 1286 | 1287 | struct data_t data = {}; 1288 | struct sock *skp = *skpp; 1289 | u16 dport = skp->__sk_common.skc_dport; 1290 | 1291 | __set_key_entry_data(&data, NULL); 1292 | data.type = EVENT_NET_CONNECT_PRE; 1293 | data.net.protocol = IPPROTO_TCP; 1294 | data.net.remote_port = dport; 1295 | 1296 | struct inet_sock *sockp = (struct inet_sock *)skp; 1297 | data.net.local_port = sockp->inet_sport; 1298 | 1299 | if (check_family(skp, AF_INET)) { 1300 | data.net.ipver = AF_INET; 1301 | data.net.__local_addr = data.net.local_addr = 1302 | skp->__sk_common.skc_rcv_saddr; 1303 | data.net.__remote_addr = data.net.remote_addr = 1304 | skp->__sk_common.skc_daddr; 1305 | 1306 | send_event(ctx, &data); 1307 | } else if (check_family(skp, AF_INET6)) { 1308 | data.net.ipver = AF_INET6; 1309 | bpf_probe_read( 1310 | &data.net.local_addr6, sizeof(data.net.local_addr6), 1311 | skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); 1312 | bpf_probe_read(&data.net.remote_addr6, 1313 | sizeof(data.net.remote_addr6), 1314 | skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32); 1315 | 1316 | send_event(ctx, &data); 1317 | } 1318 | 1319 | currsock.delete(&id); 1320 | return 0; 1321 | } 1322 | 1323 | int trace_connect_v4_return(struct pt_regs *ctx) 1324 | { 1325 | return trace_connect_return(ctx); 1326 | } 1327 | 1328 | int trace_connect_v6_return(struct pt_regs *ctx) 1329 | { 1330 | return trace_connect_return(ctx); 1331 | } 1332 | 1333 | int trace_skb_recv_udp(struct pt_regs *ctx) 1334 | { 1335 | u64 id = bpf_get_current_pid_tgid(); 1336 | u32 pid = id >> 32; 1337 | 1338 | struct sk_buff *skb = (struct sk_buff *)PT_REGS_RC(ctx); 1339 | if (skb == NULL) { 1340 | return 0; 1341 | } 1342 | 1343 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 1344 | // Older kernels we probe __skb_recv_datagram which can be used by 1345 | // other protocols. We filter by sk_family or skb->protocol 1346 | if (!skb->sk) { 1347 | return 0; 1348 | } 1349 | if (!(skb->sk->sk_family == AF_INET || 1350 | skb->sk->sk_family == AF_INET6)) { 1351 | return 0; 1352 | } 1353 | #endif 1354 | struct udphdr *udphdr = NULL; 1355 | 1356 | // Get a pointer to the network header and the header length. 1357 | // We use the header length to decide if this is IPv4 or IPv6 1358 | void *hdr = (struct iphdr *)(skb->head + skb->network_header); 1359 | u32 hdr_len = skb->transport_header - skb->network_header; 1360 | 1361 | struct data_t data = {}; 1362 | 1363 | data.type = EVENT_NET_CONNECT_ACCEPT; 1364 | __set_key_entry_data(&data, NULL); 1365 | 1366 | data.net.protocol = IPPROTO_UDP; 1367 | 1368 | udphdr = (struct udphdr *)(skb->head + skb->transport_header); 1369 | data.net.remote_port = udphdr->source; 1370 | data.net.local_port = udphdr->dest; 1371 | 1372 | if (hdr_len == sizeof(struct iphdr)) { 1373 | struct iphdr *iphdr = (struct iphdr *)hdr; 1374 | 1375 | data.net.ipver = AF_INET; 1376 | data.net.__local_addr = data.net.local_addr = iphdr->daddr; 1377 | data.net.__remote_addr = data.net.remote_addr = iphdr->saddr; 1378 | 1379 | #ifdef CACHE_UDP 1380 | struct ip_key ip_key = {}; 1381 | ip_key.pid = data.pid; 1382 | ip_key.start_time = data.start_time; 1383 | ip_key.remote_port = 1384 | 0; // Ignore the remote port for incoming connections 1385 | bpf_probe_read(&ip_key.local_port, sizeof(data.net.local_port), 1386 | &data.net.local_port); 1387 | bpf_probe_read(&ip_key.remote_addr, 1388 | sizeof(data.net.remote_addr), 1389 | &data.net.remote_addr); 1390 | bpf_probe_read(&ip_key.local_addr, sizeof(data.net.local_addr), 1391 | &data.net.local_addr); 1392 | if (has_ip_cache(&ip_key, FLOW_RX)) { 1393 | return 0; 1394 | } 1395 | #endif /* CACHE_UDP */ 1396 | } else if (hdr_len == sizeof(struct ipv6hdr)) { 1397 | // Why IPv6 address/port is read in a differen way than IPv4: 1398 | // - BPF C compiled to BPF instructions don't always do what we expect 1399 | // - especially when accessing members of a struct containing bitfields 1400 | struct ipv6hdr *ipv6hdr = (struct ipv6hdr *)hdr; 1401 | 1402 | data.net.ipver = AF_INET6; 1403 | bpf_probe_read(data.net.local_addr6, sizeof(uint32_t) * 4, 1404 | &ipv6hdr->daddr.s6_addr32); 1405 | bpf_probe_read(data.net.remote_addr6, sizeof(uint32_t) * 4, 1406 | &ipv6hdr->saddr.s6_addr32); 1407 | 1408 | #ifdef CACHE_UDP 1409 | struct ip6_key ip_key = {}; 1410 | ip_key.pid = data.pid; 1411 | ip_key.start_time = data.start_time; 1412 | ip_key.remote_port = 1413 | 0; // Ignore the remote port for incoming connections 1414 | bpf_probe_read(&ip_key.local_port, sizeof(data.net.local_port), 1415 | &data.net.local_port); 1416 | bpf_probe_read(ip_key.remote_addr6, 1417 | sizeof(data.net.remote_addr6), 1418 | &ipv6hdr->daddr.s6_addr32); 1419 | bpf_probe_read(ip_key.local_addr6, sizeof(data.net.local_addr6), 1420 | &ipv6hdr->saddr.s6_addr32); 1421 | if (has_ip6_cache(&ip_key, FLOW_RX)) { 1422 | return 0; 1423 | } 1424 | #endif /* CACHE_UDP */ 1425 | } else { 1426 | return 0; 1427 | } 1428 | 1429 | send_event(ctx, &data); 1430 | 1431 | return 0; 1432 | } 1433 | 1434 | int trace_accept_return(struct pt_regs *ctx) 1435 | { 1436 | u64 id = bpf_get_current_pid_tgid(); 1437 | u32 pid = id >> 32; 1438 | 1439 | struct sock *newsk = (struct sock *)PT_REGS_RC(ctx); 1440 | if (newsk == NULL) { 1441 | return 0; 1442 | } 1443 | 1444 | struct data_t data = {}; 1445 | 1446 | __set_key_entry_data(&data, NULL); 1447 | data.type = EVENT_NET_CONNECT_ACCEPT; 1448 | data.net.protocol = IPPROTO_TCP; 1449 | 1450 | data.net.ipver = newsk->__sk_common.skc_family; 1451 | bpf_probe_read(&data.net.local_port, sizeof(newsk->__sk_common.skc_num), 1452 | &newsk->__sk_common.skc_num); 1453 | data.net.local_port = htons(data.net.local_port); 1454 | data.net.remote_port = 1455 | newsk->__sk_common.skc_dport; // network order dport 1456 | 1457 | if (check_family(newsk, AF_INET)) { 1458 | data.net.__local_addr = data.net.local_addr = 1459 | newsk->__sk_common.skc_rcv_saddr; 1460 | data.net.__remote_addr = data.net.remote_addr = 1461 | newsk->__sk_common.skc_daddr; 1462 | 1463 | if (data.net.local_addr != 0 && data.net.remote_addr != 0 && 1464 | data.net.local_port != 0 && data.net.remote_port != 0) { 1465 | send_event(ctx, &data); 1466 | } 1467 | } else if (check_family(newsk, AF_INET6)) { 1468 | bpf_probe_read( 1469 | &data.net.local_addr6, sizeof(data.net.local_addr6), 1470 | newsk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); 1471 | bpf_probe_read(&data.net.remote_addr6, 1472 | sizeof(data.net.remote_addr6), 1473 | newsk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); 1474 | 1475 | send_event(ctx, &data); 1476 | } 1477 | return 0; 1478 | } 1479 | 1480 | int trace_udp_recvmsg(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg, 1481 | size_t length, int noblock, int flags) 1482 | { 1483 | u64 pid; 1484 | 1485 | pid = bpf_get_current_pid_tgid(); 1486 | if (flags != MSG_PEEK) { 1487 | currsock2.update(&pid, &msg); 1488 | currsock3.update(&pid, &sk); 1489 | } 1490 | 1491 | return 0; 1492 | } 1493 | 1494 | int trace_udp_recvmsg_return(struct pt_regs *ctx, struct sock *sk, 1495 | struct msghdr *msg) 1496 | { 1497 | int ret = PT_REGS_RC(ctx); 1498 | u64 id = bpf_get_current_pid_tgid(); 1499 | u32 pid = id >> 32; 1500 | 1501 | struct msghdr **msgpp; // for DNS receive probe 1502 | 1503 | msgpp = currsock2.lookup(&id); 1504 | if (msgpp == 0) { 1505 | return 0; // missed entry 1506 | } 1507 | 1508 | struct sock **skpp; 1509 | skpp = currsock3.lookup(&id); 1510 | if (skpp == 0) { 1511 | return 0; 1512 | } 1513 | 1514 | if (ret <= 0) { 1515 | currsock2.delete(&id); 1516 | return 0; 1517 | } 1518 | 1519 | struct data_t data = {}; 1520 | __set_key_entry_data(&data, NULL); 1521 | data.type = EVENT_NET_CONNECT_DNS_RESPONSE; 1522 | data.net.protocol = IPPROTO_UDP; 1523 | 1524 | struct sock *skp = *skpp; 1525 | data.net.ipver = skp->__sk_common.skc_family; 1526 | 1527 | // Send DNS info if port is DNS 1528 | struct msghdr *msgp = *msgpp; 1529 | 1530 | const char __user *dns; 1531 | dns = (msgp->msg_iter).iov->iov_base; 1532 | 1533 | u16 dport = (((struct sockaddr_in *)(msgp->msg_name))->sin_port); 1534 | u16 len = ret; 1535 | data.net.name_len = ret; 1536 | 1537 | if (DNS_RESP_PORT_NUM == ntohs(dport)) { 1538 | #pragma unroll 1539 | for (int i = 1; i <= (DNS_RESP_MAXSIZE / DNS_SEGMENT_LEN) + 1; 1540 | ++i) { 1541 | if (len > 0 && len < DNS_RESP_MAXSIZE) { 1542 | data.net.dns_flag = 0; 1543 | bpf_probe_read(&data.net.dns, DNS_SEGMENT_LEN, 1544 | dns); 1545 | if (i == 1) 1546 | data.net.dns_flag = 1547 | DNS_SEGMENT_FLAGS_START; 1548 | if (len <= 40) 1549 | data.net.dns_flag |= 1550 | DNS_SEGMENT_FLAGS_END; 1551 | 1552 | send_event(ctx, &data); 1553 | len = len - DNS_SEGMENT_LEN; 1554 | dns = dns + DNS_SEGMENT_LEN; 1555 | } else { 1556 | break; 1557 | } 1558 | } 1559 | } 1560 | 1561 | currsock2.delete(&id); 1562 | currsock3.delete(&id); 1563 | return 0; 1564 | } 1565 | 1566 | int trace_udp_sendmsg(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg) 1567 | { 1568 | u64 id; 1569 | 1570 | id = bpf_get_current_pid_tgid(); 1571 | currsock3.update(&id, &sk); 1572 | return 0; 1573 | } 1574 | 1575 | int trace_udp_sendmsg_return(struct pt_regs *ctx, struct sock *sk, 1576 | struct msghdr *msg) 1577 | { 1578 | int ret = PT_REGS_RC(ctx); 1579 | u64 id = bpf_get_current_pid_tgid(); 1580 | 1581 | struct sock **skpp; 1582 | skpp = currsock3.lookup(&id); 1583 | if (skpp == 0) { 1584 | return 0; 1585 | } 1586 | 1587 | if (ret <= 0) { 1588 | currsock3.delete(&id); 1589 | return 0; 1590 | } 1591 | 1592 | struct data_t data = {}; 1593 | __set_key_entry_data(&data, NULL); 1594 | data.type = EVENT_NET_CONNECT_PRE; 1595 | data.net.protocol = IPPROTO_UDP; 1596 | 1597 | // get ip version 1598 | struct sock *skp = *skpp; 1599 | 1600 | data.net.ipver = skp->__sk_common.skc_family; 1601 | bpf_probe_read(&data.net.local_port, sizeof(skp->__sk_common.skc_num), 1602 | &skp->__sk_common.skc_num); 1603 | data.net.local_port = htons(data.net.local_port); 1604 | data.net.remote_port = 1605 | skp->__sk_common.skc_dport; // already network order 1606 | 1607 | if (check_family(skp, AF_INET)) { 1608 | data.net.__remote_addr = data.net.remote_addr = 1609 | skp->__sk_common.skc_daddr; 1610 | data.net.__local_addr = data.net.local_addr = 1611 | skp->__sk_common.skc_rcv_saddr; 1612 | 1613 | #ifdef CACHE_UDP 1614 | struct ip_key ip_key = {}; 1615 | ip_key.pid = data.pid; 1616 | ip_key.start_time = data.start_time; 1617 | bpf_probe_read(&ip_key.remote_port, 1618 | sizeof(data.net.remote_port), 1619 | &data.net.remote_port); 1620 | ip_key.local_port = 1621 | 0; // Ignore the local port for outgoing connections 1622 | bpf_probe_read(&ip_key.remote_addr, 1623 | sizeof(data.net.remote_addr), 1624 | &data.net.remote_addr); 1625 | bpf_probe_read(&ip_key.local_addr, sizeof(data.net.local_addr), 1626 | &data.net.local_addr); 1627 | 1628 | if (has_ip_cache(&ip_key, FLOW_TX)) { 1629 | goto out; 1630 | } 1631 | #endif /* CACHE_UDP */ 1632 | } else if (check_family(skp, AF_INET6)) { 1633 | bpf_probe_read( 1634 | &data.net.remote_addr6, sizeof(data.net.remote_addr6), 1635 | &(skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32)); 1636 | bpf_probe_read( 1637 | &data.net.local_addr6, sizeof(data.net.local_addr6), 1638 | &(skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32)); 1639 | 1640 | #ifdef CACHE_UDP 1641 | struct ip6_key ip_key = {}; 1642 | ip_key.pid = data.pid; 1643 | ip_key.start_time = data.start_time; 1644 | bpf_probe_read(&ip_key.remote_port, 1645 | sizeof(data.net.remote_port), 1646 | &data.net.remote_port); 1647 | ip_key.local_port = 1648 | 0; // Ignore the local port for outgoing connections 1649 | bpf_probe_read( 1650 | ip_key.remote_addr6, sizeof(data.net.remote_addr6), 1651 | &(skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32)); 1652 | bpf_probe_read( 1653 | ip_key.local_addr6, sizeof(data.net.local_addr6), 1654 | &(skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32)); 1655 | if (has_ip6_cache(&ip_key, FLOW_TX)) { 1656 | goto out; 1657 | } 1658 | #endif /* CACHE_UDP */ 1659 | } 1660 | send_event(ctx, &data); 1661 | 1662 | out: 1663 | currsock3.delete(&id); 1664 | return 0; 1665 | } 1666 | 1667 | int trace_tcp_sendmsg(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg) 1668 | { 1669 | struct data_t data = {}; 1670 | int cmd = 0; 1671 | int offset = 0; 1672 | 1673 | // filter proxy traffic 1674 | const char __user *p = (msg->msg_iter).iov->iov_base; 1675 | __kernel_size_t cmd_len = (msg->msg_iter).iov->iov_len; 1676 | 1677 | if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T') && (p[4] != '/')) { 1678 | cmd = 0; 1679 | offset = 3; 1680 | goto CATCH; 1681 | } 1682 | if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T') && (p[4] != '/')) { 1683 | cmd = 1; 1684 | offset = 3; 1685 | goto CATCH; 1686 | } 1687 | if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T') && 1688 | (p[5] != '/')) { 1689 | cmd = 2; 1690 | offset = 4; 1691 | goto CATCH; 1692 | } 1693 | if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && 1694 | (p[4] == 'T') && (p[5] == 'E') && (p[7] != '/')) { 1695 | cmd = 3; 1696 | offset = 6; 1697 | goto CATCH; 1698 | } 1699 | if ((p[0] == 'C') && (p[1] == 'O') && (p[2] == 'N') && (p[3] == 'N') && 1700 | (p[4] == 'E') && (p[5] == 'C') && (p[6] == 'T') && (p[8] != '/')) { 1701 | cmd = 4; 1702 | offset = 7; 1703 | goto CATCH; 1704 | } 1705 | return 0; 1706 | 1707 | CATCH: 1708 | data.type = EVENT_NET_CONNECT_WEB_PROXY; 1709 | __set_key_entry_data(&data, NULL); 1710 | 1711 | data.net.name_len = cmd_len; 1712 | 1713 | // TODO: calculate real url length 1714 | int len = PROXY_SERVER_MAX_LEN; 1715 | 1716 | data.net.ipver = sk->__sk_common.skc_family; 1717 | bpf_probe_read(&data.net.local_port, sizeof(sk->__sk_common.skc_num), 1718 | &sk->__sk_common.skc_num); 1719 | data.net.local_port = htons(data.net.local_port); 1720 | data.net.remote_port = sk->__sk_common.skc_dport; 1721 | 1722 | if (check_family(sk, AF_INET)) { 1723 | data.net.__local_addr = data.net.local_addr = 1724 | sk->__sk_common.skc_rcv_saddr; 1725 | data.net.__remote_addr = data.net.remote_addr = 1726 | sk->__sk_common.skc_daddr; 1727 | } else if (check_family(sk, AF_INET6)) { 1728 | bpf_probe_read( 1729 | &data.net.local_addr6, sizeof(data.net.local_addr6), 1730 | sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); 1731 | bpf_probe_read(&data.net.remote_addr6, 1732 | sizeof(data.net.remote_addr6), 1733 | sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); 1734 | } 1735 | 1736 | p = p + offset + 1; 1737 | #pragma unroll 1738 | for (int i = 1; i <= (PROXY_SERVER_MAX_LEN / DNS_SEGMENT_LEN) + 1; 1739 | ++i) { 1740 | if (len > 0 && len < DNS_RESP_MAXSIZE) { 1741 | data.net.dns_flag = 0; 1742 | bpf_probe_read(&data.net.dns, DNS_SEGMENT_LEN, p); 1743 | if (i == 1) 1744 | data.net.dns_flag = DNS_SEGMENT_FLAGS_START; 1745 | if (len <= 40) 1746 | data.net.dns_flag |= DNS_SEGMENT_FLAGS_END; 1747 | 1748 | send_event(ctx, &data); 1749 | len = len - DNS_SEGMENT_LEN; 1750 | p = p + DNS_SEGMENT_LEN; 1751 | } else { 1752 | break; 1753 | } 1754 | } 1755 | 1756 | return 0; 1757 | } 1758 | 1759 | struct sched_process_exit_args { 1760 | __u64 pad; 1761 | char comm[16]; 1762 | pid_t pid; 1763 | int prio; 1764 | }; 1765 | 1766 | int on_sched_process_exit(struct sched_process_exit_args *arg) 1767 | { 1768 | struct data_t data = {}; 1769 | struct task_struct *task = NULL; 1770 | unsigned int flags = 0; 1771 | pid_t tgid = 0; 1772 | pid_t pid = 0; 1773 | 1774 | if (!arg) { 1775 | goto out; 1776 | } 1777 | 1778 | // only used in older versions 1779 | pid = arg->pid; 1780 | tgid = arg->pid; 1781 | 1782 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) 1783 | // only works on newer kernels 1784 | task = (struct task_struct *)bpf_get_current_task(); 1785 | if (!task) { 1786 | goto out; 1787 | } 1788 | 1789 | if (arg->pid == task->pid) { 1790 | pid = task->pid; 1791 | tgid = task->tgid; 1792 | } 1793 | 1794 | bpf_probe_read(&flags, sizeof(flags), &task->flags); 1795 | if (flags & PF_KTHREAD) 1796 | goto out; 1797 | 1798 | #endif 1799 | 1800 | if (tgid != pid) { 1801 | goto out; 1802 | } 1803 | 1804 | data.event_time = bpf_ktime_get_ns(); 1805 | data.type = EVENT_PROCESS_EXIT; 1806 | data.tid = pid; 1807 | data.pid = tgid; 1808 | data.start_time = task->start_time; 1809 | if (task && task->real_parent) { 1810 | data.ppid = task->real_parent->tgid; 1811 | } 1812 | send_event((struct pt_regs *)arg, &data); 1813 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) 1814 | last_start_time.delete(&data.pid); 1815 | last_parent.delete(&data.pid); 1816 | #ifdef CACHE_UDP 1817 | // Remove burst cache entries 1818 | // We only need to do this for older kernels that do not have an LRU 1819 | ip_cache.delete(&data.pid); 1820 | ip6_cache.delete(&data.pid); 1821 | #endif /* CACHE_UDP */ 1822 | #endif 1823 | out: 1824 | return 0; 1825 | } 1826 | --------------------------------------------------------------------------------