├── CONTRIBUTING.md ├── LICENSE.txt ├── Makefile ├── README.md ├── cmd ├── dump_comm.go ├── dump_mbuf.go ├── dump_raw.go ├── dump_skb.go ├── root.go └── version.go ├── doc ├── example │ ├── ext_action │ │ ├── ip_output_action.c │ │ ├── mbuf_action.c │ │ ├── sk_stat_action.c │ │ └── skb_user_action.c │ └── ext_filter │ │ ├── skb_filter_tcp_fin.c │ │ └── skb_user_filter.c └── jpg │ ├── ext-action.jpg │ └── x_gather.jpeg ├── go.mod ├── go.sum ├── main.go └── pkg ├── cbpf_filter ├── cbpf.go └── cbpf_test.go ├── dump ├── code │ ├── autofix │ │ └── autofix_skb.go │ ├── c │ │ ├── comm.h │ │ ├── mbuf.c │ │ ├── mbuf_uprobe.c │ │ ├── mbuf_uprobe_vector.c │ │ ├── mbuf_usdt.c │ │ ├── mbuf_usdt_vector.c │ │ ├── raw_uprobe.c │ │ ├── raw_usdt.c │ │ ├── skb.c │ │ ├── skb.h │ │ ├── skb_comm.c │ │ ├── skb_fake.c │ │ ├── skb_kprobe.c │ │ ├── skb_tracepoint.c │ │ ├── user.h │ │ └── user_comm.h │ ├── code.go │ ├── code_embed.go │ ├── code_mbuf.go │ ├── code_raw.go │ ├── code_skb.go │ └── tracepoint.go ├── driver │ ├── driver.go │ ├── driver_mbuf.go │ ├── driver_skb.go │ ├── gather │ │ ├── contianer.go │ │ ├── gather.go │ │ ├── info.go │ │ └── utils.go │ ├── output │ │ ├── color.go │ │ ├── output.go │ │ ├── pcap_converter.go │ │ ├── pcap_output_base.go │ │ ├── pcap_output_file.go │ │ ├── pcap_output_file_rotate.go │ │ ├── pcap_output_gather.go │ │ ├── pcap_output_tcpdump.go │ │ └── tcpdump_proxy.go │ └── stack │ │ └── stack.go ├── dump.go ├── dump_proto.go ├── mbuf.go ├── raw.go ├── skb.go ├── tparser │ └── tparser.go └── xproto │ └── xproto.go ├── extend ├── extend.go └── filed.go ├── gmt └── gmt.go ├── ksym └── ksym.go ├── pcap ├── proto.go └── writer.go └── util ├── cmd.go ├── endian.go ├── kernel_version.go └── log.go /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | # How to Contribute 19 | 20 | We'd love to accept your patches and contributions to this project. There are 21 | just a few small guidelines you need to follow. 22 | 23 | ## Contributor License Agreement 24 | 25 | Contributions to this project must be accompanied by a Contributor License 26 | Agreement. You (or your employer) retain the copyright to your contribution; 27 | this simply gives us permission to use and redistribute your contributions as 28 | part of the project. 29 | 30 | You generally only need to submit a CLA once, so if you've already submitted one 31 | (even if it was for a different project), you probably don't need to do it 32 | again. 33 | 34 | ## Changes Accepted 35 | 36 | Please file issues before doing substantial work; this will ensure that others 37 | don't duplicate the work and that there's a chance to discuss any design issues. 38 | 39 | ## AUTHORS file 40 | 41 | If you would like to receive additional recognition for your contribution, you 42 | may add yourself (or your organization) to the AUTHORS file. This keeps track of 43 | those who have made significant contributions to the project. Please add the 44 | entity who owns the copyright for your contribution. The source control history 45 | remains the most accurate source for individual contributions. 46 | 47 | ## Pull Requests 48 | We actively welcome your pull requests. 49 | 50 | 1. Fork the repo and create your branch from `main`. 51 | 2. If you've added code that should be tested, add tests. 52 | 3. If you've changed APIs, update the documentation. 53 | 4. Ensure the test suite passes. 54 | 5. Make sure your code lints. 55 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 56 | 57 | ## Issues 58 | 59 | We use GitHub issues to track public bugs. Please ensure your description is 60 | clear and has sufficient instructions to be able to reproduce the issue. 61 | 62 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | all: netcap 4 | 5 | netcap: 6 | @go build 7 | 8 | clean: 9 | @rm netcap -f 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netcap 2 | 3 | ## 简介 4 | 5 | netcap(内部称为xcap)是一个基于bcc,可对含skb作为参数的系统函数,或者基于DPDK的mbuf抓包分析的工具。简单试用如下: 6 | 7 | ```shell 8 | netcap skb -f icmp_rcv@1 -e "host 10.227.0.72" -i eth0 9 | ``` 10 | 11 | ## 安装依赖 12 | 13 | netcap工具依赖安装`bcc`包。 14 | 15 | 16 | ## 命令行参数说明 17 | 18 | 可以通过netcap的help获取用法资料以及example如下: 19 | 20 | ```shell 21 | # 查看 skb 模式支持的参数 22 | netcap help skb 23 | # 查看 mbuf 模式支持的参数 24 | netcap help mbuf 25 | ``` 26 | 27 | 本质上,命令行参数分为以下3类: 28 | 29 | * skb模式与mbuf模式通用的 30 | * 仅skb模式的 31 | * 仅mbuf/raw模式的 32 | 33 | ### 通用命令行参数 34 | 35 | * -f function_name<@param1><@param2> 36 | 37 | 设置需要trace的函数格式, function_name表示要trace的函数名称,并可支持多点抓包,用逗号分隔,例如 `ip_local_deliver@1,icmp_rcv@1`。 38 | 39 | 对于skb模式,支持: 40 | 41 | * -f tracepoint:net:netif_receive_skb 是tracepoint的方式,此时无需param1和param2。 42 | * -f icmp_rcv@1 是kprbe的方式,需要param1,表示skb是被trace函数的第几个参数(从1开始),无需param2。 43 | * -f tcp_rcv_established@2@1 这里@2表示第2个参数是skb,后面的@1是ext扩展参数,关于此参数详见 --ext-action 描述。 44 | 45 | 对于mbuf模式, 由于DPDK会使用mbuf数组,故使用param2的时候表示是mbuf数组,仅仅使用param1的时候是mbuf: 46 | 47 | * -f usdt:rx_one@1 是usdt的方式的,并且rx_one的usdt第一个参数param1是mbuf指针。 48 | * -f usdt:rx_array@1@2 是usdt的方式的,并且rx_array的usdt第一个参数param1是mbuf指针的数组,第二个参数param2是前面说的数组的大小。 49 | * -f func@1 是uprobe的trace,此处表示mbuf。 50 | * -f func_array@1@2 是uprobe的trace,此处表示mbuf数组。 51 | 52 | Note: -f 参数较为复杂,可以结合后面的Example理解。 53 | 54 | * -e expression 55 | 56 | 设置需要过滤数据包的表达式,此表达式基于tcpdump的语法。 57 | 58 | * -t tcpdump-flags 59 | 60 | 设置tcpdump的flags, 例如想以 -nnve 的tcpdump方式查看数据包,即可输入 -t "-nnve"。 61 | 62 | * -w file 63 | 64 | --write-file-rotate rotate-num 65 | 66 | 抓包写文件,同tcpdump,通过 --write-file-rotate 指定的 rotate-num 表示循环抓包,即抓最后的rotate-num个包,写入pcap文件。 67 | 68 | * -c capture-count 69 | 70 | 抓多少个包后退出,设置为0或不设置表示不限制次数,同tcpdump。 71 | 72 | * --capture-max-size max 73 | 74 | 这个size是指抓的包,保存到buff的大小,如果包长超过这个值,则包会被截断。 75 | 76 | * --gather 77 | 78 | 汇总输出用法,此参数为开关参数,打开此开关忽略 -w 参数。 79 | 80 | gather功能仅在多点trace下使用,并以第一个被trace的点为基准值,跟踪数据包到达后续trace点的延迟信息。 81 | 82 | gather功能它持有的子参数如下: 83 | 84 | * --gather-buffer-size size : 设置buff的大小。 85 | * --gather-timeout timeout : 超时时间。 86 | * --gather-distinguish-by-pointer : 以指针(skb或mbuf)来标识同一个数据包,否则以包内容。 87 | * --gather-output-color : 设置打印gather的颜色: red|green|yellow|blue|purple|cyan。 88 | 89 | 此功能较为复杂,可参照Example理解。 90 | 91 | * --ext-filter filename.c 92 | 93 | 用户自定义filter,[只看TCP的FIN包的自定义filter例子](doc/example/ext_filter/skb_filter_tcp_fin.c) 94 | ```shell 95 | netcap skb -f tcp_drop@2 -e 'port 8888' -t "-nnv" --ext-filter skb_filter_tcp_fin.c 96 | ``` 97 | 98 | * --ext-action filename.c 99 | 100 | 用户自定义的匹配后的action,在action中netcap会把user填充的自定义的结构体的信息与pkt一起输出到控制台。[输出sock信息的例子](doc/example/ext_action/sk_stat_action.c) 101 | ```shell 102 | # 其中 tcp_rcv_established @2表示第2个参数为skb, @1表示第1个参数sk作为ext传入到 --ext-action指定的函数 103 | netcap skb -f tcp_rcv_established@2@1 -e "port 8888" --ext-action sk_stat_action.c 104 | ``` 105 | 输出如下图所示: 106 | ![](doc/jpg/ext-action.jpg) 107 | * --ext-output-color : 设置打印user Ouput的颜色: red|green|yellow|blue|purple|cyan。 108 | 109 | * --dry-run 110 | 111 | 并不真正执行,只是打印出生成的ebpf代码。 112 | 113 | ### skb模式的参数 114 | 115 | * -i interface 116 | 117 | 例如 -i eth0, 同tcpdump 118 | 119 | * --fake-hdr 120 | 121 | 在某些TX方向函数(例如 __ip_finish_output),内核还没有给它填充eth,ip等头信息,使用此选项可以由netcap根据skb的sock信息伪造出header来适配tcpdump过滤语法。 122 | 123 | * --skb-data 124 | 125 | 不设置此参数表示使用 netcap 的默认策略来寻找packet开始地址,若设置此标志,则使用 skb->data + offset 作为数据包的开始。其中的offset由 --skb-data-offset 设置。需要设置此参数的场景:某些特殊的函数行为,导致默认策略找到的地址错误。 126 | 127 | * -S 128 | 129 | 开关参数,设置后,伴随pcap打印此包对应的kstack,并忽略 --gather 和 -w 参数。 130 | 131 | * --stack-dump-color : 设置打印kstack的颜色: red|green|yellow|blue|purple|cyan。 132 | 133 | ### mbuf/raw模式的参数 134 | 注:mbuf用于DPDK开发,raw用于AFXDP开发。 135 | 注:usdt受限于上游库修改,用兴趣的可以自行魔改。 136 | 137 | * -p pid 138 | 139 | 设置要trace的DPDK程序的pid。 140 | 141 | ## Examle 142 | 143 | ### skb的常规用法 144 | 145 | ```shell 146 | # 在icmp_rcv抓包,并指定eth0为收包网卡,并按tcpdump语法过滤 147 | netcap skb -f icmp_rcv@1 -e "host 10.227.0.72" -i eth0 -t "-nnv" 148 | 149 | # 把抓到的包写入文件icmp.pcap 150 | netcap skb -f icmp_rcv@1 -e "host 10.227.0.72" -i eth0 -w icmp.pcap 151 | 152 | # 打印kstack 153 | netcap skb -f icmp_rcv@1 -e "host 10.227.0.72" -i eth0 -S 154 | 155 | # 在tracepoint抓包,注意:tracepoint不需要使用@传递skb是第几个参数 156 | netcap skb -f tracepoint:net:netif_receive_skb@1 -i eth0 -e "host 10.227.0.72" 157 | ``` 158 | 159 | ### mbuf的常规用法 160 | 161 | ```shell 162 | # 在pid为1111的DPDK进程中,对于you_func函数(uprobe)抓包,其中mbuf指针作为此函数的第一个参数 163 | netcap mbuf -f you_func@1 -e "tcp and port 80" -t "-nnve" --pid 1111 164 | 165 | # 在pid为1111的DPDK进程中,对于对于you_func函数函数(uprobe)抓包,其中vec_func传递的是mbuf指针数组,故需2个参数 166 | netcap mbuf -f vec_func@1@2 -e "tcp and port 80" -t "-nnve" --pid 1111 167 | 168 | # 在多个函数同时抓包 169 | netcap mbuf -f func_rcv@1,func_send@1 -e "tcp" --pid 111 170 | ``` 171 | 172 | ### gather用法 173 | 174 | gather只能针对trace多个函数(即 -f 后面要用多个函数),并把第一个作为基准。举例如下: 175 | 176 | ```shell 177 | # 使用gather统计以下函数,并用cyan的颜色输出信息 178 | netcap skb -f tracepoint:net:netif_receive_skb,ip_local_deliver@1,ip_local_deliver_finish@3,icmp_rcv@1 -e "host 10.227.0.72 and icmp" -i eth0 --gather --gather-output-color cyan 179 | ``` 180 | 181 | ![](doc/jpg/x_gather.jpeg) 182 | -------------------------------------------------------------------------------- /cmd/dump_comm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "context" 20 | "log" 21 | 22 | "github.com/bytedance/netcap/pkg/dump" 23 | "github.com/spf13/cobra" 24 | ) 25 | 26 | var ( 27 | dumpTraceFunction string = "" 28 | dumpFilterExpression string = "" 29 | dumpWriteFilePath string = "" 30 | dumpWriteFileRotate uint32 = 0 31 | dumpTcpdumpFlags string = "-nn" 32 | dumpCount uint32 = 0 33 | 34 | extFilterFile string = "" 35 | extActionFile string = "" 36 | extOutputColor string = "" 37 | 38 | isDryRun bool = false 39 | isGatherStatistic bool = false 40 | gatherTimeoutSec uint32 = 0 41 | gatherBufferSize uint32 = 0 42 | gatherOutputColor string = "" 43 | gatherDistinguishByPointer bool = false 44 | captureMaxSize uint32 = 256 45 | ) 46 | 47 | func dumpCmdLine(c *cobra.Command) { 48 | c.PersistentFlags().StringVarP(&dumpWriteFilePath, "write-file", "w", dumpWriteFilePath, 49 | "dump pcap that write to file") 50 | c.PersistentFlags().Uint32VarP(&dumpWriteFileRotate, "write-file-rotate", "", dumpWriteFileRotate, 51 | "wirte pcap-file on rotate of count of pkts,it's only work under -w.\nset 0 means disable it. (default 0)") 52 | 53 | c.PersistentFlags().StringVarP(&dumpFilterExpression, 54 | "expression", "e", dumpFilterExpression, "tcpdump expression") 55 | c.PersistentFlags().StringVarP(&dumpTraceFunction, 56 | "function", "f", dumpTraceFunction, "trace function with @param that begin from 1") 57 | c.PersistentFlags().StringVarP(&dumpTcpdumpFlags, 58 | "tcpdump-flags", "t", dumpTcpdumpFlags, `tcpdump flags such as "-nnve"`) 59 | c.PersistentFlags().Uint32VarP(&dumpCount, "count", "c", dumpCount, 60 | "the count of capture packets") 61 | c.PersistentFlags().Uint32VarP(&captureMaxSize, "capture-max-size", "", captureMaxSize, 62 | "the buff size of capture packe, if the pkt_len exceeds this value, \nthen it will be truncated, and it's in range:[128,1514]") 63 | 64 | c.PersistentFlags().StringVarP(&extFilterFile, "ext-filter", "", extFilterFile, 65 | "user filter ebpf file path") 66 | c.PersistentFlags().StringVarP(&extActionFile, "ext-action", "", extActionFile, 67 | "user action ebpf file path") 68 | c.PersistentFlags().StringVarP(&extOutputColor, "ext-output-color", "", extOutputColor, 69 | "user output color: red|green|yellow|blue|purple|cyan") 70 | 71 | c.PersistentFlags().BoolVarP(&isDryRun, "dry-run", "", isDryRun, "NOT true run, only dump ebpf C code") 72 | 73 | c.PersistentFlags().BoolVarP(&isGatherStatistic, "gather", "G", isGatherStatistic, 74 | "gather statistic, only work at multi trace, with this flag then ignore -w") 75 | c.PersistentFlags().BoolVarP(&gatherDistinguishByPointer, "gather-distinguish-by-pointer", "", 76 | gatherDistinguishByPointer, "distinguish two skb(mbuf) by pointer, otherwise by contents") 77 | c.PersistentFlags().Uint32VarP(&gatherTimeoutSec, "gather-timeout", "", gatherTimeoutSec, 78 | "gather timeout on second") 79 | c.PersistentFlags().Uint32VarP(&gatherBufferSize, "gather-buffer-size", "", gatherBufferSize, 80 | "gather buffer size which is between [16, 2048]") 81 | c.PersistentFlags().StringVarP(&gatherOutputColor, "gather-output-color", "", gatherOutputColor, 82 | "gather output color: red|green|yellow|blue|purple|cyan") 83 | } 84 | 85 | func runDump(dumpOp dump.Operator) { 86 | 87 | err := dumpOp.Run(context.TODO()) 88 | if err != nil { 89 | log.Fatalf("Dump operator run err: %v", err) 90 | } 91 | } 92 | 93 | func commOption() *dump.Option { 94 | opt := &dump.Option{ 95 | ExtFilterFilePath: extFilterFile, 96 | ExtActionFilePath: extActionFile, 97 | ExtOutputColor: extOutputColor, 98 | TcpdumpFlags: dumpTcpdumpFlags, 99 | TcpdumpExpression: dumpFilterExpression, 100 | TraceFunction: dumpTraceFunction, 101 | DumpWriteFilePath: dumpWriteFilePath, 102 | DumpWriteFileRotate: dumpWriteFileRotate, 103 | DumpCount: dumpCount, 104 | CaptureMaxSize: captureMaxSize, 105 | IsDryRun: isDryRun, 106 | IsGatherStatistic: isGatherStatistic, 107 | GatherTimeoutSec: gatherTimeoutSec, 108 | GatherBufferSize: gatherBufferSize, 109 | GatherOutputColor: gatherOutputColor, 110 | GatherDistinguishByPointer: gatherDistinguishByPointer, 111 | } 112 | 113 | return opt 114 | } 115 | 116 | func commCheck() error { 117 | 118 | if captureMaxSize < 128 { 119 | captureMaxSize = 128 120 | } else if captureMaxSize > 1514 { 121 | captureMaxSize = 1514 122 | } 123 | 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /cmd/dump_mbuf.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "log" 20 | 21 | "github.com/bytedance/netcap/pkg/dump" 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var ( 26 | mUserPID int = 0 27 | ) 28 | 29 | func mbufCmdExampleString() string { 30 | str := "## mbuf mode is for the DPDK app\n\n" 31 | str += "# capture dpdk function with tcpdump filter expression:\n" 32 | str += "netcap mbuf -f you_func@1 -e \"tcp and port 80\" -t \"-nnve\" --pid 1111\n\n" 33 | 34 | str += "# capture dpdk vector mbufs function, such as: vec_func(struct rte_mbuf **mbufs, uint16_t mbuf_size):\n" 35 | str += "netcap mbuf -f vec_func\033[1;32m@1@2\033[0m -e \"tcp and port 80\" -t \"-nnve\" --pid 1111\n\n" 36 | 37 | str += "# capture func_a and func_vector_b(vector fucntion):\n" 38 | str += "netcap mbuf -f func_a@1\033[1;32m,\033[0mfunc_vector_b\033[1;32m@1@2\033[0m -e \"tcp\" --pid 111\n\n" 39 | 40 | str += "# capture vector function vfunc, functon func_a, usdt ufunc:\n" 41 | str += "netcap mbuf -f vfunc@1@2,func_a@1,usdt:ufunc@1 --pid 111\n\n" 42 | 43 | str += "# --gather and --user-action are similar to skb-mode\n" 44 | 45 | return str 46 | } 47 | 48 | // rootCmd represents the base command when called without any subcommands 49 | var mbufCmd = &cobra.Command{ 50 | Use: "mbuf", 51 | Short: "Dump mbuf with tcpdump expression", 52 | Example: mbufCmdExampleString(), 53 | Run: func(cmd *cobra.Command, args []string) { 54 | doMbufDump() 55 | }, 56 | } 57 | 58 | func init() { 59 | rootCmd.AddCommand(mbufCmd) 60 | 61 | dumpCmdLine(mbufCmd) 62 | 63 | mbufCmd.PersistentFlags().IntVarP(&mUserPID, "pid", "p", mUserPID, "PID of the target process") 64 | } 65 | 66 | func mbufCheck() error { 67 | return commCheck() 68 | } 69 | 70 | func doMbufDump() { 71 | 72 | err := mbufCheck() 73 | if err != nil { 74 | log.Fatalf("mbuf mode param err: %v", err) 75 | return 76 | } 77 | 78 | opt := &dump.MbufOption{ 79 | Option: *commOption(), 80 | Pid: mUserPID, 81 | } 82 | 83 | mbufDump, err := dump.NewMbufDump(opt) 84 | if err != nil { 85 | log.Fatalf("Dump err: %v", err) 86 | return 87 | } 88 | 89 | runDump(mbufDump) 90 | } 91 | -------------------------------------------------------------------------------- /cmd/dump_raw.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "log" 20 | 21 | "github.com/bytedance/netcap/pkg/dump" 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | func rawCmdExampleString() string { 26 | str := "## raw mode is for the function such as : func(char *pkt_data, uint16_t pkt_size) in usr-mode app\n\n" 27 | str += "# capture raw function with tcpdump filter expression:\n" 28 | str += "netcap raw -f you_func\033[1;31m@1@2\033[0m -e \"tcp and port 80\" -t \"-nnve\" --pid 1111\n\n" 29 | str += "# Other are similar to mbuf-mode\n" 30 | return str 31 | } 32 | 33 | // rootCmd represents the base command when called without any subcommands 34 | var rawCmd = &cobra.Command{ 35 | Use: "raw", 36 | Short: "Dump raw(packet) with tcpdump expression", 37 | Example: rawCmdExampleString(), 38 | Run: func(cmd *cobra.Command, args []string) { 39 | doRawDump() 40 | }, 41 | } 42 | 43 | func init() { 44 | rootCmd.AddCommand(rawCmd) 45 | 46 | dumpCmdLine(rawCmd) 47 | 48 | rawCmd.PersistentFlags().IntVarP(&mUserPID, "pid", "p", mUserPID, "PID of the target process") 49 | } 50 | 51 | func rawCheck() error { 52 | return commCheck() 53 | } 54 | 55 | func doRawDump() { 56 | 57 | err := rawCheck() 58 | if err != nil { 59 | log.Fatalf("raw mode param err: %v", err) 60 | return 61 | } 62 | 63 | opt := &dump.RawOption{ 64 | Option: *commOption(), 65 | Pid: mUserPID, 66 | } 67 | 68 | mbufDump, err := dump.NewRawDump(opt) 69 | if err != nil { 70 | log.Fatalf("Dump err: %v", err) 71 | return 72 | } 73 | 74 | runDump(mbufDump) 75 | } 76 | -------------------------------------------------------------------------------- /cmd/dump_skb.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "log" 20 | 21 | "github.com/bytedance/netcap/pkg/dump" 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var ( 26 | skbInterface string = "" 27 | isDumpStack bool = false 28 | dumpStackColor string = "" 29 | isSkbFakeHdr bool = false 30 | isUseSkbData bool = false 31 | useSkbDataOffset int32 = 0 32 | ) 33 | 34 | func skbCmdExampleString() string { 35 | str := "## skb mode is for the kernel\n\n" 36 | str += "# simple used to caputre skb in icmp_rcv with tcpdump filter expression:\n" 37 | str += "netcap skb -f icmp_rcv@1 -e \"host 10.227.0.72\" \n\n" 38 | 39 | str += "# capture at tracepoint, and \033[1;31mDON'T\033[0m need @param at tracepoint\n" 40 | str += "netcap skb -f tracepoint:net:netif_receive_skb -i eth0 -e \"host 10.227.0.72\"\n\n" 41 | 42 | str += "# capture pcap with dump kstack:\n" 43 | str += "netcap skb -f tracepoint:skb:kfree_skb -e \"host 10.227.0.72\" \033[1;32m-S\033[0m \n\n" 44 | 45 | str += "# capture kernel function dev_queue_xmit at eth0 with tcpdump flags -nnve:\n" 46 | str += "netcap skb -f dev_queue_xmit@1 -e \"tcp and port 80\" -i eth0 \033[1;32m-t\033[0m \"-nnve\"\n\n" 47 | 48 | str += "# capture with user-filter or user-action:\n" 49 | str += "netcap skb -f icmp_rcv@1 -e \"host 10.227.0.72\" -i eth0 \033[1;32m--user-filter\033[0m filter.c\n" 50 | str += "netcap skb -f icmp_rcv@1 -e \"host 10.227.0.72\" -i eth0 \033[1;32m--user-action\033[0m action.c\n\n" 51 | 52 | str += "# capture in gather mode:\n" 53 | str += "netcap skb \033[1;32m-G\033[0m -f tracepoint:net:netif_receive_skb,ip_local_deliver@1,ip_local_deliver_finish@3,icmp_rcv@1 -e \"host 10.227.0.72 and icmp\" -i eth0\n\n" 54 | 55 | str += "# how to use --skb-data: \n" 56 | str += "Instead of use : netcap skb -f icmp_rcv@1 -e \"host 10.227.0.72\" \n" 57 | str += "You can use : netcap skb -f icmp_rcv@1 -e \"host 10.227.0.72\" --skb-data --skb-data-offset -34\n" 58 | str += "This -34 mean skb->data with an offset of -34 as packet begin. 34 = sizeof(iphdr)+sizeof(ethhdr).\n\n" 59 | 60 | str += "# captrue packet in TX which does't has some header, but can use --fake-hdr simulate these headers by socks:\n" 61 | str += "netcap skb -f __ip_finish_output@3 -e \"udp and host 10.227.0.72\" \033[1;32m--fake-hdr\033[0m\n" 62 | 63 | return str 64 | } 65 | 66 | // rootCmd represents the base command when called without any subcommands 67 | var skbCmd = &cobra.Command{ 68 | Use: "skb", 69 | Short: "Dump skb with tcpdump expression", 70 | Example: skbCmdExampleString(), 71 | Run: func(cmd *cobra.Command, args []string) { 72 | doSkbDump() 73 | }, 74 | } 75 | 76 | func init() { 77 | rootCmd.AddCommand(skbCmd) 78 | 79 | dumpCmdLine(skbCmd) 80 | 81 | skbCmd.PersistentFlags().StringVarP(&skbInterface, 82 | "interface", "i", skbInterface, "net interface such as eth0") 83 | 84 | skbCmd.PersistentFlags().BoolVarP(&isDumpStack, "stack-dump", "S", isDumpStack, 85 | "dump stack with pcap, if has this flag then ignore --gather(-G) and -w") 86 | skbCmd.PersistentFlags().StringVarP(&dumpStackColor, "stack-dump-color", "", dumpStackColor, 87 | "stack dump output color: red|green|yellow|blue|purple|cyan") 88 | 89 | skbCmd.PersistentFlags().BoolVarP(&isSkbFakeHdr, "fake-hdr", "", isSkbFakeHdr, 90 | "fake skb's eth ip tcp or udp header by sock,\nIf has this flag then ignore --skb-data") 91 | 92 | skbCmd.PersistentFlags().BoolVarP(&isUseSkbData, "skb-data", "", isUseSkbData, 93 | "use skb->data and --skb-data-offset as offset to set packet begin, without this then auto set by netcap.") 94 | skbCmd.PersistentFlags().Int32VarP(&useSkbDataOffset, "skb-data-offset", "", useSkbDataOffset, 95 | "it's only work under --skb-data. Set the offset of skb->data for the begin data of packet.") 96 | 97 | } 98 | 99 | func skbCheck() error { 100 | return commCheck() 101 | } 102 | 103 | func doSkbDump() { 104 | 105 | err := skbCheck() 106 | if err != nil { 107 | log.Fatalf("skb mode param err: %v", err) 108 | return 109 | } 110 | 111 | opt := &dump.SkbOption{ 112 | Option: *commOption(), 113 | Interface: skbInterface, 114 | IsDumpStack: isDumpStack, 115 | DumpStackColor: dumpStackColor, 116 | IsFakeHdr: isSkbFakeHdr, 117 | IsUseSkbData: isUseSkbData, 118 | SkbDataOffset: useSkbDataOffset, 119 | } 120 | 121 | skbDump, err := dump.NewSkbDump(opt) 122 | if err != nil { 123 | log.Fatalf("Dump err: %v", err) 124 | return 125 | } 126 | 127 | runDump(skbDump) 128 | 129 | } 130 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "os" 20 | "os/signal" 21 | "syscall" 22 | 23 | "github.com/spf13/cobra" 24 | ) 25 | 26 | // rootCmd represents the base command when called without any subcommands 27 | var rootCmd = &cobra.Command{ 28 | Use: "netcap", 29 | Short: "Capture skb/mbuf with tcpdump expression", 30 | Example: ` 31 | $ netcap help skb 32 | $ netcap help mbuf 33 | $ netcap help raw 34 | `, 35 | } 36 | 37 | // Execute adds all child commands to the root command and sets flags appropriately. 38 | // This is called by main.main(). It only needs to happen once to the rootCmd. 39 | func Execute() { 40 | cobra.CheckErr(rootCmd.Execute()) 41 | } 42 | 43 | // Process the SIGINT signal, the program will print the stderr if the program receive the SIGINT. 44 | // So, it should be exit with no err messages. 45 | func SignalProcess() { 46 | stopChan := make(chan struct{}, 1) 47 | signalChan := make(chan os.Signal, 1) 48 | go func() { 49 | <-signalChan 50 | stopChan <- struct{}{} 51 | os.Exit(0) 52 | }() 53 | signal.Notify(signalChan, syscall.SIGINT) 54 | } 55 | 56 | func init() { 57 | SignalProcess() 58 | } 59 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cmd 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | var ( 25 | GitCommit = "" 26 | GitBranch = "" 27 | GitState = "" 28 | GitSummary = "" 29 | BuildDate = "" 30 | Version = "1.0.1" 31 | ) 32 | 33 | func dumpVersion() { 34 | fmt.Printf("GitCommit : %s\n", GitCommit) 35 | fmt.Printf("GitBranch : %s\n", GitBranch) 36 | fmt.Printf("GitSummary : %s\n", GitSummary) 37 | fmt.Printf("BuildDate : %s\n", BuildDate) 38 | fmt.Printf("Version : %s\n", Version) 39 | } 40 | 41 | // versionCmd represents the version command 42 | var versionCmd = &cobra.Command{ 43 | Use: "version", 44 | Short: "Version of netcap", 45 | Run: func(cmd *cobra.Command, args []string) { 46 | dumpVersion() 47 | }, 48 | } 49 | 50 | func init() { 51 | rootCmd.AddCommand(versionCmd) 52 | } 53 | -------------------------------------------------------------------------------- /doc/example/ext_action/ip_output_action.c: -------------------------------------------------------------------------------- 1 | 2 | // xcap skb -f __ip_finish_output@3 -e "udp and host 10.227.0.72" -t "-nnv" 3 | 4 | struct xcap_user_extend { 5 | int skb_len; 6 | int skb_datalen; 7 | 8 | uint32_t gso_size; 9 | uint32_t gso_segs; 10 | uint32_t gso_type; // format: 0x%x 11 | }; 12 | 13 | static inline int xcap_skb_action(void *ext, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index) 14 | { 15 | struct sk_buff *skb; 16 | struct skb_shared_info *info; 17 | void *head; 18 | uint32_t end; 19 | 20 | skb = (struct sk_buff *)pkt; 21 | 22 | head = _(skb->head); 23 | end = _(skb->end); 24 | info = (struct skb_shared_info *)(head + end); 25 | 26 | user->skb_len = skb->len; 27 | user->skb_datalen = skb->data_len; 28 | 29 | user->gso_size = _(info->gso_size); 30 | user->gso_segs = _(info->gso_segs); 31 | user->gso_type = _(info->gso_type); 32 | 33 | return 1; 34 | } 35 | -------------------------------------------------------------------------------- /doc/example/ext_action/mbuf_action.c: -------------------------------------------------------------------------------- 1 | 2 | struct xcap_user_extend { 3 | uint64_t mbuf; // format: 0x%x 4 | uint16_t id; // 5 | uint16_t trace_index; 6 | }; 7 | 8 | // Return 0 means not need to ouput 9 | static inline int xcap_ext_action(void *ext, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index) 10 | { 11 | user->mbuf = (uint64_t)pkt; 12 | 13 | user->trace_index = trace_index; 14 | 15 | user->id = 0; 16 | // bpf_trace_printk("user skb action in size %d, idx: %d\n", sizeof(struct xcap_user_extend), trace_index); 17 | 18 | return 1; 19 | } 20 | -------------------------------------------------------------------------------- /doc/example/ext_action/sk_stat_action.c: -------------------------------------------------------------------------------- 1 | // xcap skb -f tcp_rcv_established@2@1 -e "port 8888" --ext-action sk_stat_action.c 2 | 3 | #include 4 | 5 | struct xcap_user_extend { 6 | int state; // format: 0x%x 7 | int segs_in; 8 | }; 9 | 10 | // Return 0 means not need to ouput 11 | static inline int xcap_ext_action(void *ext, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index) 12 | { 13 | struct sock *sk = (struct sock*)ext; 14 | struct tcp_sock *tp = (struct tcp_sock *)sk; 15 | 16 | if (sk == NULL) { 17 | bpf_trace_printk("cat not get sk\n"); 18 | return 0; 19 | } 20 | 21 | user->state = sk->__sk_common.skc_state; 22 | user->segs_in = tp->segs_in; 23 | return 1; 24 | } 25 | -------------------------------------------------------------------------------- /doc/example/ext_action/skb_user_action.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | struct xcap_user_extend { 4 | int a; // format: 0x%x 5 | uint32_t b; // 6 | 7 | int64_t c; 8 | uint8_t x1; // format: %c 9 | uint8_t x2; // format: 0x%x 10 | uint16_t x3; // format: 0x%x 11 | }; 12 | 13 | // Return 0 means not need to ouput 14 | static inline int xcap_ext_action(void *ext, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index) 15 | { 16 | user->a = 0x12345678; 17 | user->b = 1000; 18 | user->c = 2002; 19 | user->x1 = 'M'; 20 | user->x2 = 0x11; 21 | user->x3 = 0xabcd; 22 | bpf_trace_printk("user skb action in size %d, idx: %d\n", sizeof(struct xcap_user_extend), trace_index); 23 | 24 | return 1; 25 | } 26 | -------------------------------------------------------------------------------- /doc/example/ext_filter/skb_filter_tcp_fin.c: -------------------------------------------------------------------------------- 1 | struct _tcphdr { 2 | __be16 source; 3 | __be16 dest; 4 | __be32 seq; 5 | __be32 ack_seq; 6 | __u16 flags; 7 | __be16 window; 8 | __sum16 check; 9 | __be16 urg_ptr; 10 | }; 11 | static inline int xcap_ext_filter(void *ext, void *pkt, u16 trace_index) 12 | { 13 | struct _tcphdr *tp; 14 | struct sk_buff *skb = (struct sk_buff *)pkt; 15 | 16 | tp = (struct _tcphdr*)(skb->head + skb->transport_header); 17 | 18 | // FIN 19 | if (tp->flags & 0x0100 ) { 20 | return 1; 21 | } 22 | 23 | return 0; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /doc/example/ext_filter/skb_user_filter.c: -------------------------------------------------------------------------------- 1 | 2 | // Return 0 means it's not need, pls filter out it. 3 | static inline int xcap_ext_filter(void *ext, void *pkt, u16 trace_index) 4 | { 5 | bpf_trace_printk("user skb filter in index: %d\n", trace_index); 6 | return 1; 7 | } 8 | -------------------------------------------------------------------------------- /doc/jpg/ext-action.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytedance/netcap/027dd2a763ebc893474f398b1eb28aab156e51c7/doc/jpg/ext-action.jpg -------------------------------------------------------------------------------- /doc/jpg/x_gather.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytedance/netcap/027dd2a763ebc893474f398b1eb28aab156e51c7/doc/jpg/x_gather.jpeg -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bytedance/netcap 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.12.3 7 | github.com/cloudflare/cbpfc v0.0.0-20231012060448-992ed7573b5c 8 | github.com/google/gopacket v1.1.19 9 | github.com/iovisor/gobpf v0.2.0 10 | github.com/pkg/errors v0.9.1 11 | github.com/sirupsen/logrus v1.9.3 12 | github.com/spf13/cobra v1.7.0 13 | golang.org/x/net v0.18.0 14 | ) 15 | 16 | require ( 17 | github.com/google/go-cmp v0.6.0 // indirect 18 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 19 | github.com/rogpeppe/go-internal v1.10.0 // indirect 20 | github.com/spf13/pflag v1.0.5 // indirect 21 | github.com/stretchr/testify v1.8.4 // indirect 22 | golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect 23 | golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= 2 | github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= 3 | github.com/cloudflare/cbpfc v0.0.0-20231012060448-992ed7573b5c h1:eL28/0MQZch0JXD//dc/AaF3eM6lhIpx9dsweKjAHu8= 4 | github.com/cloudflare/cbpfc v0.0.0-20231012060448-992ed7573b5c/go.mod h1:X/9cHz8JVzKlvoZyKBgMgrogKZlLf+pWjmm5gSUm5dI= 5 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= 10 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 11 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 12 | github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 13 | github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 14 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 15 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 16 | github.com/iovisor/gobpf v0.2.0 h1:34xkQxft+35GagXBk3n23eqhm0v7q0ejeVirb8sqEOQ= 17 | github.com/iovisor/gobpf v0.2.0/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4= 18 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 19 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 20 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 21 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 22 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 23 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 24 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 25 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 26 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 27 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 28 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 29 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 30 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 31 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 32 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 33 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 34 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 35 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 36 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 37 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 38 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 39 | golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= 40 | golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= 41 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 42 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 43 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 44 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 45 | golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= 46 | golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= 47 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 48 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 49 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 50 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 51 | golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c h1:3kC/TjQ+xzIblQv39bCOyRk8fbEeJcDHwbyxPUU2BpA= 52 | golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 53 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 54 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 55 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 56 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 57 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 58 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 59 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 60 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import "github.com/bytedance/netcap/cmd" 19 | 20 | func main() { 21 | cmd.Execute() 22 | } 23 | -------------------------------------------------------------------------------- /pkg/cbpf_filter/cbpf.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cbpf_filter 17 | 18 | import ( 19 | "fmt" 20 | "strings" 21 | 22 | "github.com/cilium/ebpf/asm" 23 | "github.com/cloudflare/cbpfc" 24 | "github.com/google/gopacket/layers" 25 | "github.com/google/gopacket/pcap" 26 | "github.com/pkg/errors" 27 | "golang.org/x/net/bpf" 28 | ) 29 | 30 | type CbpfFilter struct { 31 | linkType layers.LinkType 32 | cBpfInst []bpf.Instruction 33 | } 34 | 35 | func New(expr string, linkType layers.LinkType) (*CbpfFilter, error) { 36 | p := &CbpfFilter{ 37 | linkType: linkType, 38 | } 39 | 40 | expr = strings.TrimSpace(expr) 41 | inst, err := TcpdumpExprToBPF(expr, linkType) 42 | if err != nil { 43 | return nil, fmt.Errorf("tcpdump expr %v to bpf err : %v", expr, err) 44 | } 45 | p.cBpfInst = inst 46 | 47 | return p, nil 48 | } 49 | 50 | func (p *CbpfFilter) ToEbpf() (asm.Instructions, error) { 51 | return p.ToEbpfWithOption(cbpfc.EBPFOpts{ 52 | PacketStart: asm.R0, 53 | PacketEnd: asm.R1, 54 | 55 | Result: asm.R2, 56 | ResultLabel: "result", 57 | 58 | Working: [4]asm.Register{asm.R2, asm.R3, asm.R4, asm.R5}, 59 | 60 | StackOffset: 0, 61 | LabelPrefix: "filter", 62 | }) 63 | } 64 | 65 | func (p *CbpfFilter) ToEbpfWithOption(opts cbpfc.EBPFOpts) (asm.Instructions, error) { 66 | return cbpfc.ToEBPF(p.cBpfInst, opts) 67 | } 68 | 69 | func (p *CbpfFilter) ToC() (string, error) { 70 | return p.ToCWithOption(cbpfc.COpts{ 71 | FunctionName: "do_filter", 72 | }) 73 | } 74 | 75 | func (p *CbpfFilter) ToCWithOption(opts cbpfc.COpts) (string, error) { 76 | return cbpfc.ToC(p.cBpfInst, opts) 77 | } 78 | 79 | // tcpdumpExprToBPF converts a tcpdump / libpcap filter expression to cBPF using libpcap 80 | func TcpdumpExprToBPF(filterExpr string, linkType layers.LinkType) ([]bpf.Instruction, error) { 81 | // We treat any != 0 filter return code as a match 82 | insns, err := pcap.CompileBPFFilter(linkType, 1, filterExpr) 83 | if err != nil { 84 | return nil, errors.Wrap(err, "compiling expression to BPF") 85 | } 86 | 87 | return pcapInsnToX(insns), nil 88 | } 89 | 90 | func pcapInsnToX(insns []pcap.BPFInstruction) []bpf.Instruction { 91 | xInsns := make([]bpf.Instruction, len(insns)) 92 | 93 | for i, insn := range insns { 94 | xInsns[i] = bpf.RawInstruction{ 95 | Op: insn.Code, 96 | Jt: insn.Jt, 97 | Jf: insn.Jf, 98 | K: insn.K, 99 | }.Disassemble() 100 | } 101 | 102 | return xInsns 103 | } 104 | -------------------------------------------------------------------------------- /pkg/cbpf_filter/cbpf_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package cbpf_filter 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/google/gopacket/layers" 22 | ) 23 | 24 | func TestCbpfFilterNew(t *testing.T) { 25 | linkType := layers.LinkTypeEthernet 26 | p, err := New("host 192.168.1.1 and port 80", linkType) 27 | if err != nil { 28 | t.Fatalf("new with normal tcpdump expr err : %v", err) 29 | } 30 | t.Logf("bpf inst %v", p.cBpfInst) 31 | 32 | b, err := p.ToEbpf() 33 | if err != nil { 34 | t.Fatalf("to ebpf err : %v", err) 35 | } 36 | 37 | t.Logf("ebpf inst %v", b) 38 | 39 | cfunc, err := p.ToC() 40 | if err != nil { 41 | t.Fatalf("to c err : %v", err) 42 | } 43 | t.Logf("c func %v", cfunc) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/dump/code/autofix/autofix_skb.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package autofix 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/bytedance/netcap/pkg/dump/tparser" 22 | "github.com/bytedance/netcap/pkg/util" 23 | ) 24 | 25 | type Option struct { 26 | FunctionDesc []tparser.FunctionDescribe 27 | } 28 | 29 | type Operator interface { 30 | GenerateFixCode() string 31 | } 32 | 33 | type autoFixSkbImpl struct { 34 | opt *Option 35 | 36 | currentKernel *util.KernelVersion 37 | } 38 | 39 | func New(opt *Option) Operator { 40 | s := &autoFixSkbImpl{ 41 | opt: opt, 42 | currentKernel: util.GetKernelVersion(), 43 | } 44 | 45 | return s 46 | } 47 | 48 | func (s *autoFixSkbImpl) GenerateFixCode() string { 49 | 50 | str := "" 51 | 52 | if s.currentKernel == nil { 53 | return str 54 | } 55 | 56 | for i := 0; i < len(s.opt.FunctionDesc); i++ { 57 | desc := &s.opt.FunctionDesc[i] 58 | 59 | if desc.Prefix == "" { 60 | str += s.fixKprobe(desc, i) 61 | } 62 | } 63 | 64 | return str 65 | } 66 | 67 | func (s *autoFixSkbImpl) generateUseSkbData(traceIndex int, offset int32) string { 68 | 69 | fstr := 70 | `if (trace_index == %d) { 71 | data = skb_data + %d; 72 | }` 73 | 74 | return fmt.Sprintf(fstr, traceIndex, offset) 75 | } 76 | 77 | func (s *autoFixSkbImpl) fixKprobe(desc *tparser.FunctionDescribe, traceIndex int) string { 78 | 79 | str := "" 80 | 81 | if desc.FunctionName == "__dev_direct_xmit" || desc.FunctionName == "xsk_destruct_skb" { 82 | if s.currentKernel.Major >= 5 && s.currentKernel.Minor >= 10 { 83 | str += s.generateUseSkbData(traceIndex, 0) 84 | } 85 | } 86 | 87 | return str 88 | } 89 | -------------------------------------------------------------------------------- /pkg/dump/code/c/comm.h: -------------------------------------------------------------------------------- 1 | // Comm function in skb and mbuf 2 | struct pcap_hdr { 3 | void *ptr; 4 | u64 time_stamp; 5 | u32 len; 6 | u16 extend_offset; 7 | u16 buf_offset; 8 | u16 caplen; 9 | u16 trace_position_index; 10 | int extend_action_ret; 11 | u32 stack_id; 12 | u32 reserved; 13 | }; 14 | 15 | #ifdef ENABLE_EXT_ACTION 16 | 17 | #else 18 | struct xcap_user_extend { 19 | 20 | }; 21 | #endif 22 | 23 | struct pcap { 24 | struct pcap_hdr hdr; 25 | 26 | struct xcap_user_extend user; 27 | 28 | u8 buf[CAPTURE_LEN]; 29 | }; 30 | 31 | static inline void __pcap_fill_header(struct pcap* pcap, void *ptr, u32 len, u32 caplen, u16 trace_index) 32 | { 33 | pcap->hdr.time_stamp = bpf_ktime_get_ns(); 34 | pcap->hdr.ptr = ptr; 35 | pcap->hdr.len = len; 36 | 37 | pcap->hdr.extend_offset = (void*)(&pcap->user) - (void*)pcap; 38 | pcap->hdr.buf_offset = (void*)pcap->buf - (void *)pcap; 39 | 40 | pcap->hdr.caplen = (u16)caplen; 41 | pcap->hdr.trace_position_index = trace_index; 42 | pcap->hdr.extend_action_ret = 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /pkg/dump/code/c/mbuf.c: -------------------------------------------------------------------------------- 1 | 2 | static inline void __process_mbuf(struct pt_regs *ctx, struct pcap *pcap, void *ptr_mbuf, u16 trace_index) 3 | { 4 | u64 mbuf; 5 | u16 offset; 6 | u16 data_len; 7 | u32 pkt_len; 8 | 9 | bpf_probe_read_user(&mbuf, sizeof(mbuf), ptr_mbuf); 10 | bpf_probe_read_user(&offset, sizeof(offset), (void *)ptr_mbuf + MBUF_DATA_OFFSET); 11 | bpf_probe_read_user(&data_len, sizeof(data_len), (void *)ptr_mbuf + MBUF_LEN_OFFSET); 12 | bpf_probe_read_user(&pkt_len, sizeof(pkt_len), (void *)ptr_mbuf + MBUF_PKTLEN_OFFSET); 13 | 14 | return __process_user(ctx, pcap, (void *)mbuf, (void*)(mbuf+offset), data_len, pkt_len, trace_index); 15 | } 16 | 17 | static inline void __process_mbuf_vector(struct pt_regs*ctx, struct pcap* pcap, void **mbufs, u16 mbufs_size, u16 trace_index) 18 | { 19 | void *addr = 0; 20 | 21 | #pragma unroll 22 | for (int i = 0; i < MAX_MBUF_ARRAY_SIZE; i++) { 23 | if (i >= mbufs_size) { 24 | return; 25 | } 26 | bpf_probe_read_user(&addr, sizeof(addr), &mbufs[i]); 27 | __process_mbuf(ctx, pcap, addr, trace_index); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/dump/code/c/mbuf_uprobe.c: -------------------------------------------------------------------------------- 1 | 2 | int UPROBE(struct pt_regs *ctx) 3 | { 4 | struct pcap *pcap; 5 | u32 key = 0; 6 | void *mbuf_ptr; 7 | 8 | if (!(pcap = xcap_mbuf_scratch.lookup(&key))) { 9 | return 0; 10 | } 11 | 12 | mbuf_ptr = (void *)PT_REGS_PARM_MBUF(ctx); 13 | __process_mbuf(ctx, pcap, mbuf_ptr, TRACE_INDEX); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /pkg/dump/code/c/mbuf_uprobe_vector.c: -------------------------------------------------------------------------------- 1 | 2 | int UPROBE_VECTOR(struct pt_regs *ctx) 3 | { 4 | struct pcap *pcap; 5 | u32 key = 0; 6 | void **mbufs; 7 | u16 mbufs_size; 8 | 9 | if (!(pcap = xcap_mbuf_scratch.lookup(&key))) { 10 | return 0; 11 | } 12 | 13 | mbufs = (void **)pt_regs_parm_vector_mbufs(ctx); 14 | mbufs_size = (u16)pt_regs_parm_vector_size(ctx); 15 | 16 | __process_mbuf_vector(ctx, pcap, mbufs, mbufs_size, TRACE_INDEX); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /pkg/dump/code/c/mbuf_usdt.c: -------------------------------------------------------------------------------- 1 | 2 | int USDT(struct pt_regs *ctx) 3 | { 4 | struct pcap *pcap; 5 | u32 key = 0; 6 | void *mbuf_ptr; 7 | 8 | if (!(pcap = xcap_mbuf_scratch.lookup(&key))) { 9 | return 0; 10 | } 11 | 12 | bpf_usdt_readarg(PARAM_INDEX_1, ctx, &mbuf_ptr); 13 | __process_mbuf(ctx, pcap, mbuf_ptr, TRACE_INDEX); 14 | 15 | return 0; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /pkg/dump/code/c/mbuf_usdt_vector.c: -------------------------------------------------------------------------------- 1 | 2 | int USDT_VECTOR(struct pt_regs *ctx) 3 | { 4 | struct pcap *pcap; 5 | u32 key = 0; 6 | void **mbufs; 7 | u16 mbufs_size; 8 | 9 | if (!(pcap = xcap_mbuf_scratch.lookup(&key))) { 10 | return 0; 11 | } 12 | 13 | bpf_usdt_readarg(PARAM_INDEX_1, ctx, &mbufs); 14 | bpf_usdt_readarg(PARAM_INDEX_2, ctx, &mbufs_size); 15 | 16 | __process_mbuf_vector(ctx, pcap, mbufs, mbufs_size, TRACE_INDEX); 17 | 18 | return 0; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /pkg/dump/code/c/raw_uprobe.c: -------------------------------------------------------------------------------- 1 | int UPROBE(struct pt_regs *ctx) 2 | { 3 | struct pcap *pcap; 4 | u32 key = 0; 5 | void *data; 6 | u16 len; 7 | 8 | if (!(pcap = xcap_mbuf_scratch.lookup(&key))) { 9 | return 0; 10 | } 11 | 12 | data = (void *)PT_REGS_PARM_DATA(ctx); 13 | len = (u16)PT_REGS_PARM_LEN(ctx); 14 | 15 | __process_user(ctx, pcap, data, data, len, len, TRACE_INDEX); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /pkg/dump/code/c/raw_usdt.c: -------------------------------------------------------------------------------- 1 | int USDT(struct pt_regs *ctx) 2 | { 3 | struct pcap *pcap; 4 | u32 key = 0; 5 | void* data = 0; 6 | u16 len; 7 | 8 | if (!(pcap = xcap_mbuf_scratch.lookup(&key))) { 9 | return 0; 10 | } 11 | 12 | bpf_usdt_readarg(PARAM_INDEX_1, ctx, &data); 13 | bpf_usdt_readarg(PARAM_INDEX_2, ctx, &len); 14 | 15 | __process_user(ctx, pcap, data, data, len, len, TRACE_INDEX); 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /pkg/dump/code/c/skb.c: -------------------------------------------------------------------------------- 1 | 2 | static inline int __xcap_probe_skb(void *ctx, struct sk_buff *skb, void *ext, u16 trace_index) 3 | { 4 | u64 pkt_len, liner_len, pullen = 0; 5 | void *data; 6 | struct pcap *pcap; 7 | u32 key = 0; 8 | void *skb_head, *skb_data; 9 | u64 skb_len, skb_datalen; 10 | u64 copy_liner_len; 11 | u64 caplen; 12 | 13 | if (!(pcap = xcap_skb_scratch.lookup(&key))) 14 | goto end; 15 | 16 | #ifdef CONFIG_IFINDEX 17 | if (!filter_by_ifindex(skb)) { 18 | goto end; 19 | } 20 | #endif 21 | 22 | skb_head = (void*)(_(skb->head)); 23 | skb_data = (void*)(_(skb->data)); 24 | skb_len = _(skb->len); 25 | skb_datalen = _(skb->data_len); 26 | 27 | // mac header was set 28 | if (_(skb->mac_header) != (u16)~0U) { 29 | data = (void *)(skb_head + _(skb->mac_header)); 30 | pullen = skb_data - data; 31 | } else { 32 | data = skb_head; 33 | pullen = 0; 34 | } 35 | 36 | XCAP_FIX_DATA 37 | 38 | liner_len = skb_len - skb_datalen + pullen; 39 | pkt_len = skb_len + pullen; 40 | 41 | copy_liner_len = liner_len > sizeof(pcap->buf) ? sizeof(pcap->buf) : liner_len; 42 | 43 | if (copy_liner_len > sizeof(pcap->buf)) 44 | goto end; 45 | 46 | if (bpf_probe_read_kernel(pcap->buf, copy_liner_len, data)) 47 | goto end; 48 | 49 | caplen = copy_liner_len; 50 | 51 | #ifdef ENABLE_FILTER 52 | if (!do_filter(pcap->buf, pcap->buf + caplen)) { 53 | goto end; 54 | } 55 | #endif 56 | 57 | #ifdef ENABLE_EXT_FILTER 58 | if (!xcap_ext_filter(ext, skb, trace_index)) { 59 | goto end; 60 | } 61 | #endif 62 | 63 | __pcap_fill_header(pcap, skb, pkt_len, caplen, trace_index); 64 | skb_capture_event_notify(ctx, skb, pkt_len, pcap, ext); 65 | 66 | end: 67 | return 0; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /pkg/dump/code/c/skb.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define _(P) ({typeof(P) val; bpf_probe_read_kernel(&val, sizeof(val), &P); val;}) 8 | -------------------------------------------------------------------------------- /pkg/dump/code/c/skb_comm.c: -------------------------------------------------------------------------------- 1 | 2 | BPF_PERCPU_ARRAY(xcap_skb_scratch, struct pcap, 1); 3 | 4 | #ifdef STACK_DUMP 5 | BPF_STACK_TRACE(xcap_stack, 2048); 6 | #endif 7 | 8 | BPF_PERF_OUTPUT(xcap_pcap_event); 9 | 10 | static inline void skb_capture_event_notify(void *ctx, struct sk_buff *skb, u32 len, struct pcap *pcap, void *ext) 11 | { 12 | #ifdef STACK_DUMP 13 | pcap->hdr.stack_id = xcap_stack.get_stackid(ctx, 0); 14 | #endif 15 | 16 | #ifdef ENABLE_EXT_ACTION 17 | pcap->hdr.extend_action_ret = xcap_ext_action(ext, skb, len, &pcap->user, pcap->hdr.trace_position_index); 18 | #endif 19 | xcap_pcap_event.perf_submit(ctx, pcap, sizeof(*pcap)); 20 | } 21 | 22 | #ifdef CONFIG_IFINDEX 23 | static inline int filter_by_ifindex(struct sk_buff *skb) { 24 | struct net_device *dev = NULL; 25 | int ifindex = 0; 26 | 27 | if (CONFIG_IFINDEX == 0) { 28 | return 1; 29 | } 30 | 31 | dev = _(skb->dev); 32 | ifindex = _(dev->ifindex); 33 | 34 | if (ifindex == CONFIG_IFINDEX) { 35 | return 1; 36 | } 37 | 38 | return 0; 39 | } 40 | #endif /* CONFIG_IFINDEX */ 41 | -------------------------------------------------------------------------------- /pkg/dump/code/c/skb_fake.c: -------------------------------------------------------------------------------- 1 | 2 | #ifdef SK_PROTO_16BIT 3 | 4 | static inline u8 sk_get_proto(struct sock *sk) 5 | { 6 | return _(sk->sk_protocol); 7 | } 8 | 9 | #else //!SK_PROTO_16BIT 10 | 11 | static inline u8 sk_get_proto(struct sock *sk) 12 | { 13 | return (_(sk->__sk_flags_offset[0]) & SK_FL_PROTO_MASK) >> SK_FL_PROTO_SHIFT; 14 | } 15 | 16 | #endif //SK_PROTO_16BIT 17 | 18 | static inline int skb_init_fake_ethhdr(struct sk_buff *skb, struct ethhdr *eth) 19 | { 20 | if (_(skb->protocol)) { 21 | eth->h_proto = _(skb->protocol); 22 | } else if (_(skb->sk)) { 23 | struct sock *sk = _(skb->sk); 24 | 25 | switch (_(sk->sk_family)) { 26 | case AF_INET: 27 | eth->h_proto = htons(ETH_P_IP); 28 | break; 29 | case AF_INET6: 30 | eth->h_proto = htons(ETH_P_IPV6); 31 | break; 32 | default: 33 | goto err; 34 | } 35 | } 36 | return 0; 37 | err: 38 | return -1; 39 | } 40 | 41 | static inline int __skb_init_fake_iphdr(struct sk_buff *skb, struct iphdr *iph, int tot_len) 42 | { 43 | u8 tos = 0; 44 | struct sock *sk = _(skb->sk); 45 | 46 | *((u16 *)iph) = htons((4 << 12) | (5 << 8) | (tos & 0xff)); 47 | iph->tot_len = htons(tot_len); 48 | iph->protocol = sk_get_proto(sk); 49 | iph->saddr = _(sk->sk_rcv_saddr); 50 | iph->daddr = _(sk->sk_daddr); 51 | 52 | return 0; 53 | err: 54 | return -1; 55 | } 56 | 57 | static inline int skb_init_fake_iphdr(struct sk_buff *skb, struct iphdr *iph, int tot_len) 58 | { 59 | if (!_(skb->sk)) 60 | goto err; 61 | 62 | return __skb_init_fake_iphdr(skb, iph, tot_len); 63 | err: 64 | return -1; 65 | } 66 | 67 | static inline int __skb_init_fake_ipv6hdr(struct sk_buff *skb, struct ipv6hdr *ip6h, int payload_len) 68 | { 69 | u32 flowlabel = 0; 70 | unsigned int tclass = 0; 71 | struct sock *sk = _(skb->sk); 72 | 73 | *(u32 *)ip6h = htonl(0x60000000 | (tclass << 20)) | flowlabel; 74 | ip6h->payload_len = htons(payload_len); 75 | ip6h->nexthdr = sk_get_proto(sk); 76 | ip6h->saddr = _(sk->sk_v6_rcv_saddr); 77 | ip6h->daddr = _(sk->sk_v6_daddr); 78 | 79 | return 0; 80 | err: 81 | return -1; 82 | } 83 | 84 | static inline int skb_init_fake_ipv6hdr(struct sk_buff *skb, struct ipv6hdr *ip6h, int payload_len) 85 | { 86 | if (!_(skb->sk)) 87 | goto err; 88 | 89 | return __skb_init_fake_ipv6hdr(skb, ip6h, payload_len); 90 | err: 91 | return -1; 92 | } 93 | 94 | static inline int __xcap_probe_skb(void *ctx, struct sk_buff *skb, void *ext, u16 trace_index) 95 | { 96 | int headlen, copylen, pullen, pkt_len = 0; 97 | void *data; 98 | struct pcap *pcap; 99 | u32 key = 0, offset = 0; 100 | u16 caplen; 101 | 102 | if (!(pcap = xcap_skb_scratch.lookup(&key))) 103 | goto end; 104 | 105 | #ifdef CONFIG_IFINDEX 106 | if (!filter_by_ifindex(skb)) { 107 | goto end; 108 | } 109 | #endif 110 | 111 | // mac header was set 112 | if (_(skb->mac_header) != (u16)~0U) { 113 | data = (void *)(_(skb->head) + _(skb->mac_header)); 114 | pullen = (unsigned long)_(skb->data) - (unsigned long)data; 115 | 116 | 117 | } else if (_(skb->network_header)) { 118 | if (skb_init_fake_ethhdr(skb, (struct ethhdr *)pcap->buf)) 119 | goto end; 120 | 121 | data = (void *)(_(skb->head) + _(skb->network_header)); 122 | offset = sizeof(struct ethhdr); 123 | } else if (_(skb->transport_header) != (u16)~0U) { 124 | struct ethhdr *eth = (struct ethhdr *)pcap->buf; 125 | 126 | if (skb_init_fake_ethhdr(skb, eth)) 127 | goto end; 128 | 129 | switch (eth->h_proto) { 130 | case ntohs(ETH_P_IP): 131 | if (skb_init_fake_iphdr(skb, 132 | (struct iphdr *)(pcap->buf + sizeof(struct ethhdr)), 133 | _(skb->len) + sizeof(struct iphdr))) 134 | goto end; 135 | 136 | offset = sizeof(struct ethhdr) + sizeof(struct iphdr); 137 | break; 138 | case ntohs(ETH_P_IPV6): 139 | if (skb_init_fake_ipv6hdr(skb, 140 | (struct ipv6hdr *)(pcap->buf + sizeof(struct ethhdr)), 141 | _(skb->len))) 142 | goto end; 143 | 144 | offset = sizeof(struct ethhdr) + sizeof(struct ipv6hdr); 145 | break; 146 | default: 147 | goto end; 148 | } 149 | 150 | data = (void *)(_(skb->head) + _(skb->transport_header)); 151 | } else { 152 | goto end; 153 | } 154 | 155 | caplen = sizeof(pcap->buf); 156 | headlen = _(skb->len) - _(skb->data_len) + offset + pullen; 157 | pkt_len = _(skb->len) + offset + pullen; 158 | 159 | if (headlen < caplen) 160 | caplen = headlen; 161 | 162 | copylen = caplen - offset; 163 | if (copylen > sizeof(pcap->buf) - offset) 164 | goto end; 165 | 166 | if (bpf_probe_read_kernel(pcap->buf + offset, copylen, data)) 167 | goto end; 168 | 169 | #ifdef ENABLE_FILTER 170 | if (!do_filter(pcap->buf, pcap->buf + caplen)) { 171 | goto end; 172 | } 173 | #endif 174 | 175 | #ifdef ENABLE_EXT_FILTER 176 | if (!xcap_ext_filter(ext, skb, trace_index)) { 177 | goto end; 178 | } 179 | #endif 180 | 181 | __pcap_fill_header(pcap, skb, pkt_len, caplen, trace_index); 182 | skb_capture_event_notify(ctx, skb, pkt_len, pcap, ext); 183 | 184 | end: 185 | return 0; 186 | } 187 | 188 | 189 | -------------------------------------------------------------------------------- /pkg/dump/code/c/skb_kprobe.c: -------------------------------------------------------------------------------- 1 | 2 | int SKB_KPROBE(struct pt_regs *ctx) 3 | { 4 | struct sk_buff *skb; 5 | void *ext = NULL; 6 | 7 | SKB_SET_EXT_PARAM 8 | 9 | if (!(skb = (struct sk_buff *)SKB_REGS_PARAM_X(ctx))) { 10 | return 0; 11 | } 12 | 13 | return __xcap_probe_skb(ctx, skb, ext, TRACE_INDEX); 14 | } 15 | -------------------------------------------------------------------------------- /pkg/dump/code/c/skb_tracepoint.c: -------------------------------------------------------------------------------- 1 | 2 | struct TP_STRUCT_args { 3 | uint8_t others[TP_SKB_OFFSET]; 4 | void *skbaddr; 5 | }; 6 | 7 | int SKB_TRACEPOINT(struct TP_STRUCT_args *args) 8 | { 9 | struct sk_buff *skb = (struct sk_buff*)(args->skbaddr); 10 | 11 | return __xcap_probe_skb(args, skb, NULL, TRACE_INDEX); 12 | } 13 | -------------------------------------------------------------------------------- /pkg/dump/code/c/user.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /pkg/dump/code/c/user_comm.h: -------------------------------------------------------------------------------- 1 | #ifndef MBUF_DATA_OFFSET 2 | #define MBUF_DATA_OFFSET 16 3 | #endif //MBUF_DATA_OFFSET 4 | 5 | #ifndef MBUF_LEN_OFFSET 6 | #define MBUF_LEN_OFFSET 40 7 | #endif 8 | 9 | #ifndef MBUF_PKTLEN_OFFSET 10 | #define MBUF_PKTLEN_OFFSET 36 11 | #endif 12 | 13 | #ifndef MAX_MBUF_ARRAY_SIZE 14 | #define MAX_MBUF_ARRAY_SIZE 512 15 | #endif //MAX_MBUF_ARRAY_SIZE 16 | 17 | BPF_PERCPU_ARRAY(xcap_mbuf_scratch, struct pcap, 1); 18 | 19 | BPF_PERF_OUTPUT(xcap_pcap_event); 20 | 21 | static inline void mbuf_capture_event_notify(struct pt_regs *ctx, void *ptr_mbuf, struct pcap *pcap, u16 trace_index) 22 | { 23 | xcap_pcap_event.perf_submit(ctx, pcap, sizeof(*pcap)); 24 | } 25 | 26 | static inline void __process_user(struct pt_regs *ctx, struct pcap *pcap, void *ptr_pkt, 27 | void *udata, u16 len, u32 pkt_len, u16 trace_index) 28 | { 29 | u16 cap_len; 30 | 31 | bpf_probe_read_user(pcap->buf, sizeof(pcap->buf), udata); 32 | 33 | cap_len = len; 34 | if (cap_len > sizeof(pcap->buf)) { 35 | cap_len = sizeof(pcap->buf); 36 | } 37 | 38 | #ifdef ENABLE_FILTER 39 | if (!do_filter(pcap->buf, pcap->buf + cap_len)) 40 | return; 41 | #endif 42 | 43 | #ifdef ENABLE_EXT_FILTER 44 | if (!xcap_ext_filter(NULL, ptr_pkt, trace_index)) 45 | return; 46 | #endif 47 | 48 | __pcap_fill_header(pcap, ptr_pkt, pkt_len, cap_len, trace_index); 49 | 50 | #ifdef ENABLE_EXT_ACTION 51 | pcap->hdr.extend_action_ret = xcap_ext_action(NULL, ptr_pkt, pkt_len, &pcap->user, trace_index); 52 | #endif 53 | 54 | mbuf_capture_event_notify(ctx, ptr_pkt, pcap, trace_index); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /pkg/dump/code/code.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package code 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/bytedance/netcap/pkg/cbpf_filter" 23 | "github.com/bytedance/netcap/pkg/dump/tparser" 24 | "github.com/google/gopacket/layers" 25 | ) 26 | 27 | type TraceInfo struct { 28 | EbpfFunctionName string 29 | FunctionDesc tparser.FunctionDescribe 30 | } 31 | 32 | type Operator interface { 33 | GetEbpfCode() string 34 | GetComplieFlags() []string 35 | GetUserActionCode() string 36 | 37 | GetTraceInfo() []TraceInfo 38 | } 39 | 40 | type Option struct { 41 | ExtFilterFilePath string 42 | ExtActionFilePath string 43 | TcpdumpExpression string 44 | CaptureMaxSize uint32 45 | 46 | IsDumpStack bool 47 | 48 | FunctionDesc []tparser.FunctionDescribe 49 | } 50 | 51 | type baseCodeImpl struct { 52 | ebpfCode string 53 | tcpdumpFilterCode string 54 | userFilterCode string 55 | userActionCode string 56 | commMarcoCode string 57 | 58 | compileFlags []string 59 | traceInfo []TraceInfo 60 | } 61 | 62 | func (s *baseCodeImpl) GetEbpfCode() string { 63 | return s.ebpfCode 64 | } 65 | 66 | func (s *baseCodeImpl) GetUserActionCode() string { 67 | return s.userActionCode 68 | } 69 | 70 | func (s *baseCodeImpl) GetComplieFlags() []string { 71 | return s.compileFlags 72 | } 73 | 74 | func (s *baseCodeImpl) GetTraceInfo() []TraceInfo { 75 | return s.traceInfo 76 | } 77 | 78 | func (s *baseCodeImpl) _generateCbpfFilter(tcpdumpExpression string) (string, error) { 79 | f, err := cbpf_filter.New(tcpdumpExpression, layers.LinkTypeEthernet) 80 | if err != nil { 81 | return "", err 82 | } 83 | return f.ToC() 84 | } 85 | 86 | func _readFile(path string) (string, error) { 87 | content, err := os.ReadFile(path) 88 | 89 | if err != nil { 90 | return "", err 91 | } 92 | 93 | return string(content), nil 94 | } 95 | 96 | func (s *baseCodeImpl) baseGenerate(opt *Option) error { 97 | 98 | var err error 99 | 100 | if opt.TcpdumpExpression != "" { 101 | s.tcpdumpFilterCode, err = s._generateCbpfFilter(opt.TcpdumpExpression) 102 | if err != nil { 103 | return err 104 | } 105 | s.tcpdumpFilterCode += "\n" 106 | s.compileFlags = append(s.compileFlags, "-DENABLE_FILTER") 107 | } else { 108 | s.tcpdumpFilterCode = "" 109 | } 110 | 111 | if opt.ExtFilterFilePath != "" { 112 | s.userFilterCode, err = _readFile(opt.ExtFilterFilePath) 113 | if err != nil { 114 | return err 115 | } 116 | s.compileFlags = append(s.compileFlags, "-DENABLE_EXT_FILTER") 117 | } else { 118 | s.userFilterCode = "" 119 | } 120 | 121 | if opt.ExtActionFilePath != "" { 122 | s.userActionCode, err = _readFile(opt.ExtActionFilePath) 123 | if err != nil { 124 | return err 125 | } 126 | s.compileFlags = append(s.compileFlags, "-DENABLE_EXT_ACTION") 127 | } else { 128 | s.userActionCode = "" 129 | } 130 | 131 | if opt.IsDumpStack { 132 | s.compileFlags = append(s.compileFlags, "-DSTACK_DUMP") 133 | } 134 | 135 | s.commMarcoCode = fmt.Sprintf("#define CAPTURE_LEN %d\n\n", opt.CaptureMaxSize) 136 | 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /pkg/dump/code/code_embed.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package code 17 | 18 | import ( 19 | _ "embed" 20 | ) 21 | 22 | // 23 | 24 | //go:embed c/skb.h 25 | var codeSkbH string 26 | 27 | //go:embed c/skb_comm.c 28 | var codeSkbCommC string 29 | 30 | //go:embed c/skb.c 31 | var codeSkbC string 32 | 33 | //go:embed c/skb_fake.c 34 | var codeSkbFakeC string 35 | 36 | //go:embed c/skb_kprobe.c 37 | var codeSkbKprobeC string 38 | 39 | //go:embed c/skb_tracepoint.c 40 | var codeSkbTracepointC string 41 | 42 | //go:embed c/comm.h 43 | var codeCommH string 44 | 45 | //go:embed c/user.h 46 | var codeUserH string 47 | 48 | //go:embed c/user_comm.h 49 | var codeUserCommH string 50 | 51 | //go:embed c/mbuf.c 52 | var codeMbufC string 53 | 54 | //go:embed c/mbuf_uprobe.c 55 | var codeMbufUProbeC string 56 | 57 | //go:embed c/mbuf_uprobe_vector.c 58 | var codeMbufUProbeVectorC string 59 | 60 | //go:embed c/mbuf_usdt.c 61 | var codeMbufUSDTC string 62 | 63 | //go:embed c/mbuf_usdt_vector.c 64 | var codeMbufUSDTVectorC string 65 | 66 | //go:embed c/raw_usdt.c 67 | var codeRawUSDTC string 68 | 69 | //go:embed c/raw_uprobe.c 70 | var codeRawUprobeC string 71 | -------------------------------------------------------------------------------- /pkg/dump/code/code_mbuf.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package code 17 | 18 | import ( 19 | "fmt" 20 | "strconv" 21 | "strings" 22 | 23 | "github.com/bytedance/netcap/pkg/dump/tparser" 24 | ) 25 | 26 | type MbufOption struct { 27 | Option 28 | } 29 | 30 | type mbufCodeImpl struct { 31 | baseCodeImpl 32 | 33 | opt *MbufOption 34 | } 35 | 36 | func NewMbufCode(opt *MbufOption) (Operator, error) { 37 | s := &mbufCodeImpl{ 38 | opt: opt, 39 | } 40 | 41 | err := s.generate() 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return s, nil 47 | } 48 | 49 | func (s *mbufCodeImpl) generate() error { 50 | err := s.baseGenerate(&s.opt.Option) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | s.ebpfCode = codeUserH 56 | 57 | marcoCode, err := s.generateMbufMarco() 58 | if err != nil { 59 | return err 60 | } 61 | 62 | s.ebpfCode += marcoCode 63 | 64 | s.ebpfCode += s.tcpdumpFilterCode 65 | s.ebpfCode += s.userFilterCode 66 | s.ebpfCode += s.userActionCode 67 | 68 | s.ebpfCode += codeCommH 69 | s.ebpfCode += codeUserCommH 70 | s.ebpfCode += codeMbufC 71 | 72 | for i := 0; i < len(s.opt.FunctionDesc); i++ { 73 | code, info, err := s.generateMbufFunctionCode(&s.opt.FunctionDesc[i], i) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | s.ebpfCode += code 79 | s.traceInfo = append(s.traceInfo, *info) 80 | } 81 | 82 | return nil 83 | } 84 | 85 | func (s *mbufCodeImpl) generateMbufMarco() (string, error) { 86 | 87 | str := s.commMarcoCode 88 | 89 | return str, nil 90 | } 91 | 92 | func (s *mbufCodeImpl) generateMbufFunctionCode(desc *tparser.FunctionDescribe, 93 | traceIndex int) (string, *TraceInfo, error) { 94 | 95 | if desc.ParamIndex1 == 0 { 96 | return "", nil, fmt.Errorf("mbuf trace-function need param %v, such as func@1", desc) 97 | } 98 | 99 | if desc.Prefix == "" { 100 | return s.generateMbufUProbeFunction(desc, traceIndex) 101 | 102 | } else if desc.Prefix == "usdt" { 103 | return s.generateMbufUSDTFunction(desc, traceIndex) 104 | 105 | } else { 106 | return "", nil, fmt.Errorf("mbuf trace-function has wrong prefix: %v", desc) 107 | } 108 | 109 | } 110 | 111 | func (s *mbufCodeImpl) generateMbufUSDTFunction(desc *tparser.FunctionDescribe, 112 | traceIndex int) (string, *TraceInfo, error) { 113 | 114 | var code string 115 | 116 | info := &TraceInfo{ 117 | FunctionDesc: *desc, 118 | } 119 | 120 | if desc.ParamIndex2 == 0 { 121 | info.EbpfFunctionName = "xcap_usdt_" + desc.FunctionName 122 | code = codeMbufUSDTC 123 | code = strings.ReplaceAll(code, "USDT", info.EbpfFunctionName) 124 | code = strings.ReplaceAll(code, "PARAM_INDEX_1", strconv.FormatInt(int64(desc.ParamIndex1), 10)) 125 | 126 | } else { 127 | info.EbpfFunctionName = "xcap_usdt_vec_" + desc.FunctionName 128 | code = codeMbufUSDTVectorC 129 | code = strings.ReplaceAll(code, "USDT_VECTOR", info.EbpfFunctionName) 130 | code = strings.ReplaceAll(code, "PARAM_INDEX_1", strconv.FormatInt(int64(desc.ParamIndex1), 10)) 131 | code = strings.ReplaceAll(code, "PARAM_INDEX_2", strconv.FormatInt(int64(desc.ParamIndex2), 10)) 132 | } 133 | code = strings.ReplaceAll(code, "TRACE_INDEX", strconv.FormatInt(int64(traceIndex), 10)) 134 | 135 | return code, info, nil 136 | } 137 | 138 | func (s *mbufCodeImpl) generateMbufUProbeFunction(desc *tparser.FunctionDescribe, 139 | traceIndex int) (string, *TraceInfo, error) { 140 | 141 | var code string 142 | 143 | info := &TraceInfo{ 144 | FunctionDesc: *desc, 145 | } 146 | 147 | if desc.ParamIndex2 == 0 { 148 | info.EbpfFunctionName = "xcap_uprobe_" + desc.FunctionName 149 | code = codeMbufUProbeC 150 | code = strings.ReplaceAll(code, "UPROBE", info.EbpfFunctionName) 151 | code = strings.ReplaceAll(code, "PT_REGS_PARM_MBUF", "PT_REGS_PARM"+strconv.FormatInt(int64(desc.ParamIndex1), 10)) 152 | 153 | } else { 154 | info.EbpfFunctionName = "xcap_uprobe_vec_" + desc.FunctionName 155 | code = codeMbufUProbeVectorC 156 | code = strings.ReplaceAll(code, "UPROBE_VECTOR", info.EbpfFunctionName) 157 | code = strings.ReplaceAll(code, "PT_REGS_PARM_VECTOR_MBUFS", "PT_REGS_PARM"+strconv.FormatInt(int64(desc.ParamIndex1), 10)) 158 | code = strings.ReplaceAll(code, "PT_REGS_PARM_VECTOR_SIZE", "PT_REGS_PARM"+strconv.FormatInt(int64(desc.ParamIndex2), 10)) 159 | } 160 | code = strings.ReplaceAll(code, "TRACE_INDEX", strconv.FormatInt(int64(traceIndex), 10)) 161 | 162 | return code, info, nil 163 | } 164 | -------------------------------------------------------------------------------- /pkg/dump/code/code_raw.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package code 17 | 18 | import ( 19 | "fmt" 20 | "strconv" 21 | "strings" 22 | 23 | "github.com/bytedance/netcap/pkg/dump/tparser" 24 | ) 25 | 26 | type RawOption struct { 27 | Option 28 | } 29 | 30 | type rawCodeImpl struct { 31 | baseCodeImpl 32 | 33 | opt *RawOption 34 | } 35 | 36 | func NewRawCode(opt *RawOption) (Operator, error) { 37 | s := &rawCodeImpl{ 38 | opt: opt, 39 | } 40 | 41 | err := s.generate() 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return s, nil 47 | } 48 | 49 | func (s *rawCodeImpl) generate() error { 50 | err := s.baseGenerate(&s.opt.Option) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | s.ebpfCode = codeUserH 56 | 57 | marcoCode, err := s.generateRawMarco() 58 | if err != nil { 59 | return err 60 | } 61 | 62 | s.ebpfCode += marcoCode 63 | 64 | s.ebpfCode += s.tcpdumpFilterCode 65 | s.ebpfCode += s.userFilterCode 66 | s.ebpfCode += s.userActionCode 67 | 68 | s.ebpfCode += codeCommH 69 | s.ebpfCode += codeUserCommH 70 | 71 | for i := 0; i < len(s.opt.FunctionDesc); i++ { 72 | code, info, err := s.generateRawFunctionCode(&s.opt.FunctionDesc[i], i) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | s.ebpfCode += code 78 | s.traceInfo = append(s.traceInfo, *info) 79 | } 80 | return nil 81 | } 82 | 83 | func (s *rawCodeImpl) generateRawMarco() (string, error) { 84 | str := s.commMarcoCode 85 | return str, nil 86 | } 87 | 88 | func (s *rawCodeImpl) generateRawFunctionCode(desc *tparser.FunctionDescribe, 89 | traceIndex int) (string, *TraceInfo, error) { 90 | if desc.Prefix == "" { 91 | return s.generateRawUprobeFunction(desc, traceIndex) 92 | 93 | } else if desc.Prefix == "usdt" { 94 | return s.generateRawUSDTFunction(desc, traceIndex) 95 | 96 | } else { 97 | return "", nil, fmt.Errorf("raw trace-function has wrong prefix: %v", desc) 98 | } 99 | } 100 | 101 | func (s *rawCodeImpl) getPrameIndex(desc *tparser.FunctionDescribe) (int, int) { 102 | param1 := desc.ParamIndex1 103 | param2 := desc.ParamIndex2 104 | 105 | if param1 == 0 { 106 | param1 = 1 107 | } 108 | 109 | if param2 == 0 { 110 | param2 = param1 + 1 111 | } 112 | 113 | return param1, param2 114 | } 115 | 116 | func (s *rawCodeImpl) generateRawUSDTFunction(desc *tparser.FunctionDescribe, 117 | traceIndex int) (string, *TraceInfo, error) { 118 | 119 | var code string 120 | param1, param2 := s.getPrameIndex(desc) 121 | 122 | info := &TraceInfo{ 123 | FunctionDesc: *desc, 124 | } 125 | 126 | info.EbpfFunctionName = "xcap_usdt_" + desc.FunctionName 127 | code = codeRawUSDTC 128 | 129 | code = strings.ReplaceAll(code, "USDT", info.EbpfFunctionName) 130 | code = strings.ReplaceAll(code, "PARAM_INDEX_1", strconv.FormatInt(int64(param1), 10)) 131 | code = strings.ReplaceAll(code, "PARAM_INDEX_2", strconv.FormatInt(int64(param2), 10)) 132 | 133 | code = strings.ReplaceAll(code, "TRACE_INDEX", strconv.FormatInt(int64(traceIndex), 10)) 134 | 135 | return code, info, nil 136 | } 137 | 138 | func (s *rawCodeImpl) generateRawUprobeFunction(desc *tparser.FunctionDescribe, 139 | traceIndex int) (string, *TraceInfo, error) { 140 | 141 | var code string 142 | param1, param2 := s.getPrameIndex(desc) 143 | 144 | info := &TraceInfo{ 145 | FunctionDesc: *desc, 146 | } 147 | 148 | info.EbpfFunctionName = "xcap_uprobe_" + desc.FunctionName 149 | code = codeRawUprobeC 150 | code = strings.ReplaceAll(code, "UPROBE", info.EbpfFunctionName) 151 | code = strings.ReplaceAll(code, "PT_REGS_PARM_DATA", "PT_REGS_PARM"+strconv.FormatInt(int64(param1), 10)) 152 | code = strings.ReplaceAll(code, "PT_REGS_PARM_LEN", "PT_REGS_PARM"+strconv.FormatInt(int64(param2), 10)) 153 | 154 | code = strings.ReplaceAll(code, "TRACE_INDEX", strconv.FormatInt(int64(traceIndex), 10)) 155 | return code, info, nil 156 | } 157 | -------------------------------------------------------------------------------- /pkg/dump/code/code_skb.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package code 17 | 18 | import ( 19 | "fmt" 20 | "net" 21 | "strconv" 22 | "strings" 23 | 24 | "github.com/bytedance/netcap/pkg/dump/code/autofix" 25 | "github.com/bytedance/netcap/pkg/dump/tparser" 26 | ) 27 | 28 | type SkbOption struct { 29 | Option 30 | 31 | Interface string 32 | IsFakeHdr bool 33 | IsUseSkbData bool 34 | SkbDataOffset int32 35 | } 36 | 37 | type skbCodeImpl struct { 38 | baseCodeImpl 39 | 40 | opt *SkbOption 41 | 42 | fixSkb autofix.Operator 43 | } 44 | 45 | func NewSkbCode(opt *SkbOption) (Operator, error) { 46 | 47 | if opt.IsFakeHdr { 48 | opt.IsUseSkbData = false 49 | } 50 | 51 | s := &skbCodeImpl{ 52 | opt: opt, 53 | fixSkb: autofix.New(&autofix.Option{ 54 | FunctionDesc: opt.FunctionDesc, 55 | }), 56 | } 57 | 58 | err := s.generate() 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | return s, nil 64 | } 65 | 66 | func (s *skbCodeImpl) generate() error { 67 | 68 | err := s.baseGenerate(&s.opt.Option) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | s.ebpfCode = codeSkbH 74 | 75 | marcoCode, err := s.generateSkbMarco(s.opt) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | s.ebpfCode += marcoCode 81 | 82 | s.ebpfCode += s.tcpdumpFilterCode 83 | s.ebpfCode += s.userFilterCode 84 | s.ebpfCode += s.userActionCode 85 | 86 | s.ebpfCode += codeCommH 87 | s.ebpfCode += codeSkbCommC 88 | 89 | if s.opt.IsFakeHdr { 90 | s.ebpfCode += codeSkbFakeC 91 | } else { 92 | s.ebpfCode += codeSkbC 93 | } 94 | 95 | for i := 0; i < len(s.opt.FunctionDesc); i++ { 96 | code, info, err := s.generateSkbFunctionCode(&s.opt.FunctionDesc[i], i) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | s.ebpfCode += code 102 | s.traceInfo = append(s.traceInfo, *info) 103 | } 104 | 105 | return s.fixSkbCode() 106 | } 107 | 108 | func _getNetIfindex(devName string) (int, error) { 109 | iface, err := net.InterfaceByName(devName) 110 | if err != nil { 111 | return 0, err 112 | } 113 | 114 | ifindex := iface.Index 115 | return ifindex, nil 116 | } 117 | 118 | func (s *skbCodeImpl) generateSkbMarco(opt *SkbOption) (string, error) { 119 | 120 | str := s.commMarcoCode 121 | 122 | if opt.Interface != "" && opt.Interface != "any" { 123 | 124 | ifIndex, err := _getNetIfindex(opt.Interface) 125 | if err != nil { 126 | fmt.Printf("-i Input error: %s\n", opt.Interface) 127 | return "", err 128 | } 129 | 130 | str += fmt.Sprintf("#define CONFIG_IFINDEX %d\n", ifIndex) 131 | } 132 | 133 | if opt.IsFakeHdr { 134 | str += "#define CONFIG_ENABLE_FAKEHDR \n" 135 | } 136 | 137 | return str, nil 138 | } 139 | 140 | func (s *skbCodeImpl) generateSkbFunctionCode(desc *tparser.FunctionDescribe, traceIndex int) (string, *TraceInfo, error) { 141 | 142 | if desc.Prefix == "" { 143 | return s.generateSkbKrpbe(desc, traceIndex) 144 | } else if desc.Prefix == "tp" || desc.Prefix == "tracepoint" { 145 | return s.generateSkbTracepoint(desc, traceIndex) 146 | } else { 147 | return "", nil, fmt.Errorf("skb trace-function not support prex: %v", desc) 148 | } 149 | } 150 | 151 | func (s *skbCodeImpl) generateSkbKrpbe(desc *tparser.FunctionDescribe, traceIndex int) (string, *TraceInfo, error) { 152 | 153 | if desc.ParamIndex1 == 0 { 154 | return "", nil, fmt.Errorf("skb trace-function kprobe need param %v, such as func@1", desc) 155 | } 156 | 157 | if desc.ParamIndex2 == desc.ParamIndex1 { 158 | return "", nil, fmt.Errorf("skb trace-function kprobe ext-param-index error equal to skb-param-index: %v", desc) 159 | } 160 | 161 | info := &TraceInfo{ 162 | EbpfFunctionName: "xcap_kprobe_" + strings.ReplaceAll(desc.FunctionName, ".", "_"), 163 | FunctionDesc: *desc, 164 | } 165 | 166 | code := codeSkbKprobeC 167 | code = strings.ReplaceAll(code, "SKB_KPROBE", info.EbpfFunctionName) 168 | code = strings.ReplaceAll(code, "SKB_REGS_PARAM_X", "PT_REGS_PARM"+strconv.FormatInt(int64(desc.ParamIndex1), 10)) 169 | code = strings.ReplaceAll(code, "TRACE_INDEX", strconv.FormatInt(int64(traceIndex), 10)) 170 | 171 | if desc.ParamIndex2 != 0 { 172 | code = strings.ReplaceAll(code, "SKB_SET_EXT_PARAM", 173 | "ext = (void*)PT_REGS_PARM"+strconv.FormatInt(int64(desc.ParamIndex2), 10)+"(ctx);") 174 | } else { 175 | code = strings.ReplaceAll(code, "SKB_SET_EXT_PARAM", "") 176 | } 177 | 178 | return code, info, nil 179 | } 180 | 181 | func (s *skbCodeImpl) generateSkbTracepoint(desc *tparser.FunctionDescribe, traceIndex int) (string, *TraceInfo, error) { 182 | if desc.ParamIndex1 != 0 { 183 | return "", nil, fmt.Errorf("skb trace-function tracepoint does'nt need param by @: %v", desc) 184 | } 185 | 186 | arr := strings.SplitN(desc.FunctionName, ":", 2) 187 | if len(arr) != 2 { 188 | return "", nil, fmt.Errorf("skb trace-function tracepoint format error: %v", desc) 189 | } 190 | 191 | offset, err := getSkbOffsetOnTracepoint(arr[0], arr[1]) 192 | if err != nil { 193 | return "", nil, err 194 | } 195 | 196 | info := &TraceInfo{ 197 | EbpfFunctionName: "xcap_tp_" + arr[1], 198 | FunctionDesc: *desc, 199 | } 200 | 201 | code := codeSkbTracepointC 202 | code = strings.ReplaceAll(code, "SKB_TRACEPOINT", info.EbpfFunctionName) 203 | code = strings.ReplaceAll(code, "TP_STRUCT", arr[1]) 204 | code = strings.ReplaceAll(code, "TP_SKB_OFFSET", strconv.FormatInt(int64(offset), 10)) 205 | code = strings.ReplaceAll(code, "TRACE_INDEX", strconv.FormatInt(int64(traceIndex), 10)) 206 | 207 | return code, info, nil 208 | } 209 | 210 | func (s *skbCodeImpl) fixSkbCode() error { 211 | 212 | str := "" 213 | 214 | if s.opt.IsUseSkbData { 215 | str = fmt.Sprintf("data = skb_data + %d;", s.opt.SkbDataOffset) 216 | 217 | } else { 218 | str = s.fixSkb.GenerateFixCode() 219 | } 220 | 221 | s.ebpfCode = strings.ReplaceAll(s.ebpfCode, "XCAP_FIX_DATA", str) 222 | 223 | return nil 224 | } 225 | -------------------------------------------------------------------------------- /pkg/dump/code/tracepoint.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package code 17 | 18 | import ( 19 | "fmt" 20 | "regexp" 21 | "strconv" 22 | "strings" 23 | ) 24 | 25 | func getSkbOffsetOnTracepoint(title string, name string) (int, error) { 26 | 27 | path := fmt.Sprintf("/sys/kernel/debug/tracing/events/%s/%s/format", title, name) 28 | 29 | str, err := _readFile(path) 30 | if err != nil { 31 | return 0, err 32 | } 33 | 34 | arr := strings.Split(str, "\n") 35 | 36 | mode := `\s*field:(const\s+)*void\s+\*\s+skbaddr;\s+offset:(\d+);` 37 | 38 | reg := regexp.MustCompile(mode) 39 | 40 | for i := 0; i < len(arr); i++ { 41 | match := reg.FindStringSubmatch(arr[i]) 42 | 43 | if len(match) <= 1 { 44 | continue 45 | } 46 | return strconv.Atoi(match[len(match)-1]) 47 | } 48 | 49 | return 0, fmt.Errorf("tracepoint does not has skb param: %s", path) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/dump/driver/driver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package driver 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | "log" 22 | "os" 23 | "os/signal" 24 | "syscall" 25 | "time" 26 | 27 | "github.com/bytedance/netcap/pkg/dump/code" 28 | "github.com/bytedance/netcap/pkg/dump/driver/gather" 29 | "github.com/bytedance/netcap/pkg/dump/driver/output" 30 | "github.com/bytedance/netcap/pkg/dump/driver/stack" 31 | "github.com/bytedance/netcap/pkg/dump/xproto" 32 | "github.com/bytedance/netcap/pkg/extend" 33 | "github.com/iovisor/gobpf/bcc" 34 | ) 35 | 36 | const ( 37 | perfChannelSize = 1024 38 | ) 39 | 40 | type Option struct { 41 | ExtOuputColor string 42 | DumpWriteFilePath string 43 | DumpWriteFileRotate uint32 44 | TcpdumpFlags string 45 | DumpCount uint32 46 | Code code.Operator 47 | IsGatherStatistic bool 48 | GatherTimeoutSec uint32 49 | GatherBufferSize uint32 50 | GatherOutputColor string 51 | GatherDistinguishByPointer bool 52 | 53 | IsDumpStack bool 54 | DumpStackColor string 55 | } 56 | 57 | type Operator interface { 58 | Init() error 59 | Run(ctx context.Context) error 60 | } 61 | 62 | type driverBaseImpl struct { 63 | m *bcc.Module 64 | gather gather.Operator 65 | outputDriver output.Operator 66 | extendOp extend.Operator 67 | perfMap *bcc.PerfMap 68 | perfChannel chan []byte 69 | tickChannel chan int 70 | maxDumpCount uint32 71 | 72 | dumpCount uint32 73 | } 74 | 75 | func (s *driverBaseImpl) Run(_ context.Context) error { 76 | sig := make(chan os.Signal, 1) 77 | signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) 78 | 79 | go s.loop() 80 | go s.tick() 81 | 82 | s.perfMap.Start() 83 | <-sig 84 | fmt.Printf("\n") 85 | s.perfMap.Stop() 86 | 87 | if s.gather != nil { 88 | s.gather.Close() 89 | } 90 | s.outputDriver.Close() 91 | if s.extendOp != nil { 92 | s.extendOp.Close() 93 | } 94 | return nil 95 | } 96 | 97 | func (s *driverBaseImpl) tick() { 98 | 99 | for { 100 | time.Sleep(time.Duration(10) * time.Millisecond) 101 | s.tickChannel <- 1 102 | } 103 | } 104 | 105 | func (s *driverBaseImpl) loop() { 106 | 107 | for { 108 | select { 109 | 110 | case data, ok := <-s.perfChannel: 111 | if !ok { 112 | os.Exit(0) 113 | } 114 | s.dumpCount, _ = s.outputDriver.Output(data, s.dumpCount) 115 | 116 | case <-s.tickChannel: 117 | s.dumpCount, _ = s.outputDriver.OnTick(s.dumpCount) 118 | } 119 | 120 | if s.maxDumpCount != 0 { 121 | if s.dumpCount >= s.maxDumpCount { 122 | os.Exit(0) 123 | } 124 | } 125 | } 126 | } 127 | 128 | func (s *driverBaseImpl) _baseNewStackOperator(opt *Option, isDumpStack bool) (stack.Operator, error) { 129 | 130 | if !isDumpStack { 131 | return nil, nil 132 | } 133 | 134 | stackTable := bcc.NewTable(s.m.TableId(xproto.StackTableName), s.m) 135 | if stackTable == nil { 136 | return nil, fmt.Errorf("create stack-table error") 137 | } 138 | 139 | stackOpt := &stack.Option{ 140 | StackTable: stackTable, 141 | } 142 | 143 | return stack.New(stackOpt) 144 | } 145 | 146 | func (s *driverBaseImpl) _baseNewOutput(opt *Option) (output.Operator, error) { 147 | 148 | stackOp, err := s._baseNewStackOperator(opt, opt.IsDumpStack) 149 | if err != nil { 150 | return nil, err 151 | } 152 | 153 | outputOpt := &output.Option{ 154 | ExtendOp: s.extendOp, 155 | ExtOutputColor: opt.ExtOuputColor, 156 | TcpdumpFlags: opt.TcpdumpFlags, 157 | WritePcapFilePath: opt.DumpWriteFilePath, 158 | WritePcapFileRotate: opt.DumpWriteFileRotate, 159 | GatherOp: s.gather, 160 | GatherOutputColor: opt.GatherOutputColor, 161 | StackOp: stackOp, 162 | DumpStackColor: opt.DumpStackColor, 163 | } 164 | 165 | return output.New(outputOpt) 166 | 167 | } 168 | 169 | func (s *driverBaseImpl) baseInit(opt *Option) error { 170 | 171 | s.dumpCount = 0 172 | s.maxDumpCount = opt.DumpCount 173 | s.perfChannel = make(chan []byte, perfChannelSize) 174 | s.tickChannel = make(chan int) 175 | s.extendOp = extend.New(&extend.Option{UserActionCode: opt.Code.GetUserActionCode()}) 176 | 177 | var err error 178 | 179 | if opt.IsGatherStatistic { 180 | s.gather, err = gather.New(&gather.Option{ 181 | Code: opt.Code, 182 | GatherTimeoutSec: opt.GatherTimeoutSec, 183 | GatherBufferSize: opt.GatherBufferSize, 184 | GatherDistinguishByPointer: opt.GatherDistinguishByPointer, 185 | }) 186 | if err != nil { 187 | return err 188 | } 189 | } 190 | 191 | s.outputDriver, err = s._baseNewOutput(opt) 192 | if err != nil { 193 | return err 194 | } 195 | 196 | err = s._baseEbpfPerfTable() 197 | if err != nil { 198 | return err 199 | } 200 | 201 | return nil 202 | } 203 | 204 | func (s *driverBaseImpl) _baseEbpfPerfTable() error { 205 | 206 | table := bcc.NewTable(s.m.TableId(xproto.PerfTableName), s.m) 207 | 208 | perfMap, err := bcc.InitPerfMap(table, s.perfChannel, nil) 209 | if err != nil { 210 | log.Fatalf("init perf map err: %v", err) 211 | return err 212 | } 213 | s.perfMap = perfMap 214 | 215 | return nil 216 | } 217 | -------------------------------------------------------------------------------- /pkg/dump/driver/driver_mbuf.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package driver 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/bytedance/netcap/pkg/dump/code" 22 | "github.com/iovisor/gobpf/bcc" 23 | ) 24 | 25 | type MbufOption struct { 26 | Option 27 | 28 | Pid int 29 | } 30 | 31 | type mbufDriverImpl struct { 32 | driverBaseImpl 33 | 34 | opt *MbufOption 35 | 36 | ebpfCode string 37 | } 38 | 39 | func NewMbufDriver(opt *MbufOption) Operator { 40 | s := &mbufDriverImpl{ 41 | opt: opt, 42 | } 43 | 44 | return s 45 | } 46 | 47 | func (s *mbufDriverImpl) Init() error { 48 | 49 | err := s.buildMbuf() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | err = s.baseInit(&s.opt.Option) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return nil 60 | } 61 | 62 | func (s *mbufDriverImpl) buildMbuf() error { 63 | 64 | s.ebpfCode = s.opt.Code.GetEbpfCode() 65 | infos := s.opt.Code.GetTraceInfo() 66 | 67 | s.m = bcc.NewModule(s.ebpfCode, s.opt.Code.GetComplieFlags()) 68 | if s.m == nil { 69 | return fmt.Errorf("bcc.NewModule error") 70 | } 71 | 72 | return s.attachTrace(infos) 73 | } 74 | 75 | func (s *mbufDriverImpl) attachUProbe(info *code.TraceInfo) error { 76 | 77 | prog, err := s.m.LoadUprobe(info.EbpfFunctionName) 78 | if err != nil { 79 | return err 80 | } 81 | err = s.m.AttachUprobe(fmt.Sprintf("/proc/%v/exe", s.opt.Pid), info.FunctionDesc.FunctionName, prog, s.opt.Pid) 82 | if err != nil { 83 | return err 84 | } 85 | return nil 86 | } 87 | 88 | func (s *mbufDriverImpl) attachTrace(infos []code.TraceInfo) error { 89 | var err error 90 | for i := 0; i < len(infos); i++ { 91 | if infos[i].FunctionDesc.Prefix == "usdt" { 92 | continue 93 | } 94 | err = s.attachUProbe(&infos[i]) 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /pkg/dump/driver/driver_skb.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package driver 17 | 18 | import ( 19 | "fmt" 20 | "log" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/code" 23 | "github.com/iovisor/gobpf/bcc" 24 | ) 25 | 26 | type SkbOption struct { 27 | Option 28 | } 29 | 30 | type skbDriverImpl struct { 31 | driverBaseImpl 32 | 33 | opt *SkbOption 34 | } 35 | 36 | func NewSkbDriver(opt *SkbOption) Operator { 37 | s := &skbDriverImpl{ 38 | opt: opt, 39 | } 40 | 41 | return s 42 | } 43 | 44 | func (s *skbDriverImpl) attachSkbKprobe(info *code.TraceInfo) error { 45 | prog, err := s.m.LoadKprobe(info.EbpfFunctionName) 46 | if err != nil { 47 | return err 48 | } 49 | err = s.m.AttachKprobe(info.FunctionDesc.FunctionName, prog, -1) 50 | if err != nil { 51 | log.Fatalf("attach kprobe %s err: %v", info.FunctionDesc.FunctionName, err) 52 | return err 53 | } 54 | return nil 55 | } 56 | 57 | func (s *skbDriverImpl) attachSkbTracepoint(info *code.TraceInfo) error { 58 | prog, err := s.m.LoadTracepoint(info.EbpfFunctionName) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | err = s.m.AttachTracepoint(info.FunctionDesc.FunctionName, prog) 64 | if err != nil { 65 | log.Fatalf("attach tracepoint %s prog %d err: %v", info.FunctionDesc.FunctionName, prog, err) 66 | return err 67 | } 68 | return nil 69 | } 70 | 71 | func (s *skbDriverImpl) attachSkbFunctions() error { 72 | 73 | infos := s.opt.Code.GetTraceInfo() 74 | 75 | for i := 0; i < len(infos); i++ { 76 | prefix := infos[i].FunctionDesc.Prefix 77 | var err error 78 | 79 | if prefix == "" { 80 | err = s.attachSkbKprobe(&infos[i]) 81 | } else if prefix == "tp" || prefix == "tracepoint" { 82 | err = s.attachSkbTracepoint(&infos[i]) 83 | } else { 84 | err = fmt.Errorf("skb not support prefix: %s", prefix) 85 | } 86 | 87 | if err != nil { 88 | return err 89 | } 90 | } 91 | 92 | return nil 93 | } 94 | 95 | func (s *skbDriverImpl) Init() error { 96 | 97 | s.m = bcc.NewModule(s.opt.Code.GetEbpfCode(), s.opt.Code.GetComplieFlags()) 98 | 99 | if s.m == nil { 100 | return fmt.Errorf("bcc.NewModule error") 101 | } 102 | 103 | err := s.attachSkbFunctions() 104 | if err != nil { 105 | return err 106 | } 107 | 108 | err = s.baseInit(&s.opt.Option) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /pkg/dump/driver/gather/contianer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package gather 17 | 18 | import ( 19 | "container/list" 20 | "time" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/xproto" 23 | ) 24 | 25 | const ( 26 | minCache = 16 27 | maxCache = 2048 28 | defaultCache = 64 29 | defaultTimeoutSec = 2 30 | ) 31 | 32 | type container interface { 33 | LookUp(pkt *xproto.PcapCaputre) *GatherInfo 34 | 35 | Add(g *GatherInfo) error 36 | Del(g *GatherInfo) error 37 | 38 | GetTimeout() *GatherInfo 39 | 40 | Close() 41 | } 42 | 43 | type containerImpl struct { 44 | pktList *list.List 45 | timeOutSec uint32 46 | cacheSize int 47 | distinguishByPointer bool 48 | } 49 | 50 | func newContainer(opt *Option) container { 51 | s := &containerImpl{ 52 | pktList: list.New(), 53 | timeOutSec: opt.GatherTimeoutSec, 54 | cacheSize: int(opt.GatherBufferSize), 55 | distinguishByPointer: opt.GatherDistinguishByPointer, 56 | } 57 | if s.cacheSize < minCache || s.cacheSize > maxCache { 58 | s.cacheSize = defaultCache 59 | } 60 | if s.timeOutSec <= 0 { 61 | s.timeOutSec = defaultTimeoutSec 62 | } 63 | 64 | return s 65 | } 66 | 67 | func (s *containerImpl) isSamePacket(p1 *xproto.PcapCaputre, p2 *xproto.PcapCaputre) bool { 68 | 69 | if s.distinguishByPointer { 70 | return p1.Meta.Ptr == p2.Meta.Ptr 71 | } 72 | 73 | return isSamePacketByContent(p1, p2) 74 | } 75 | 76 | func (s *containerImpl) LookUp(pkt *xproto.PcapCaputre) *GatherInfo { 77 | for e := s.pktList.Front(); e != nil; e = e.Next() { 78 | 79 | g, ok := e.Value.(*GatherInfo) 80 | if !ok { 81 | continue 82 | } 83 | 84 | if s.isSamePacket(pkt, g.Packet) { 85 | return g 86 | } 87 | } 88 | 89 | return nil 90 | } 91 | 92 | func (s *containerImpl) Add(g *GatherInfo) error { 93 | 94 | e := s.pktList.PushBack(g) 95 | g.elemet = e 96 | 97 | return nil 98 | } 99 | 100 | func (s *containerImpl) Del(g *GatherInfo) error { 101 | s.pktList.Remove(g.elemet) 102 | return nil 103 | } 104 | 105 | func (s *containerImpl) GetTimeout() *GatherInfo { 106 | e := s.pktList.Front() 107 | 108 | if e == nil { 109 | return nil 110 | } 111 | g, ok := e.Value.(*GatherInfo) 112 | if !ok { 113 | return nil 114 | } 115 | 116 | if s.isTimeout(g) { 117 | _ = s.Del(g) 118 | return g 119 | } 120 | return nil 121 | } 122 | 123 | func (s *containerImpl) Close() { 124 | 125 | } 126 | 127 | func (s *containerImpl) isTimeout(g *GatherInfo) bool { 128 | 129 | if s.pktList.Len() >= s.cacheSize { 130 | return true 131 | } 132 | 133 | now := time.Now() 134 | 135 | duration := now.Sub(g.time) 136 | 137 | return duration >= time.Duration(s.timeOutSec)*time.Second 138 | } 139 | -------------------------------------------------------------------------------- /pkg/dump/driver/gather/gather.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package gather 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/bytedance/netcap/pkg/dump/code" 22 | "github.com/bytedance/netcap/pkg/dump/xproto" 23 | ) 24 | 25 | type Option struct { 26 | Code code.Operator 27 | GatherTimeoutSec uint32 28 | GatherBufferSize uint32 29 | GatherDistinguishByPointer bool 30 | } 31 | 32 | type Operator interface { 33 | Feed(pkt *xproto.PcapCaputre) *GatherInfo 34 | Get() *GatherInfo 35 | ToString(g *GatherInfo, n uint32) string 36 | Close() 37 | } 38 | 39 | type gatherImpl struct { 40 | traceInfos []code.TraceInfo 41 | c container 42 | } 43 | 44 | func New(opt *Option) (Operator, error) { 45 | s := &gatherImpl{ 46 | traceInfos: opt.Code.GetTraceInfo(), 47 | c: newContainer(opt), 48 | } 49 | 50 | return s, nil 51 | } 52 | 53 | func (s *gatherImpl) feed(pkt *xproto.PcapCaputre) *GatherInfo { 54 | g := s.c.LookUp(pkt) 55 | 56 | if pkt.Meta.TracePosIndex == 0 { 57 | // head packet 58 | if g != nil { 59 | _ = s.c.Del(g) 60 | } 61 | ng := s.newGather(pkt) 62 | _ = s.c.Add(ng) 63 | return g 64 | } 65 | 66 | // follow packet 67 | if g != nil { 68 | g.attachFollow(pkt) 69 | if g.isFull() { 70 | _ = s.c.Del(g) 71 | return g 72 | } 73 | } 74 | return nil 75 | } 76 | 77 | func (s *gatherImpl) Feed(pkt *xproto.PcapCaputre) *GatherInfo { 78 | 79 | if int(pkt.Meta.TracePosIndex) >= len(s.traceInfos) { 80 | return nil 81 | } 82 | 83 | g := s.feed(pkt) 84 | 85 | if g == nil { 86 | return s.c.GetTimeout() 87 | } 88 | 89 | return g 90 | } 91 | 92 | func (s *gatherImpl) Get() *GatherInfo { 93 | return s.c.GetTimeout() 94 | } 95 | 96 | func (s *gatherImpl) Close() { 97 | s.c.Close() 98 | } 99 | 100 | func (s *gatherImpl) newGather(pkt *xproto.PcapCaputre) *GatherInfo { 101 | return newGatherInfo(pkt, len(s.traceInfos)) 102 | } 103 | 104 | func (s *gatherImpl) ToString(g *GatherInfo, n uint32) string { 105 | 106 | titleFuncName := s.getFunctionName(int(g.Packet.Meta.TracePosIndex)) 107 | str := s.getTitleStr(titleFuncName, n) 108 | 109 | for i := 1; i < len(g.Items); i++ { 110 | str += s.getFollowStr(i, &g.Items[i], titleFuncName) 111 | } 112 | 113 | return str 114 | } 115 | 116 | func (s *gatherImpl) getFunctionName(tracePosIndex int) string { 117 | t := s.traceInfos[tracePosIndex] 118 | var funcName string 119 | 120 | if t.FunctionDesc.Prefix != "" { 121 | funcName = t.FunctionDesc.Prefix + ":" + t.FunctionDesc.FunctionName 122 | } else { 123 | funcName = t.FunctionDesc.FunctionName 124 | } 125 | return funcName 126 | } 127 | 128 | func (s *gatherImpl) getTitleStr(titleFunc string, n uint32) string { 129 | return fmt.Sprintf("+-- No'%d packet received in %s\n", n, titleFunc) 130 | } 131 | 132 | func (s *gatherImpl) getFollowStr(index int, item *Item, titleFunc string) string { 133 | 134 | name := s.getFunctionName(index) 135 | 136 | if item.Received { 137 | return fmt.Sprintf("| * %d (ns) later received in %s \n", item.LatencyNs, name) 138 | } else { 139 | return fmt.Sprintf("| * NOT received at %s\n", name) 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /pkg/dump/driver/gather/info.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package gather 17 | 18 | import ( 19 | "container/list" 20 | "time" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/xproto" 23 | ) 24 | 25 | type Item struct { 26 | Received bool 27 | LatencyNs uint64 28 | } 29 | 30 | type GatherInfo struct { 31 | // base packet: 32 | Packet *xproto.PcapCaputre 33 | Items []Item 34 | 35 | elemet *list.Element 36 | time time.Time 37 | } 38 | 39 | func newGatherInfo(pkt *xproto.PcapCaputre, size int) *GatherInfo { 40 | ng := &GatherInfo{ 41 | Packet: pkt, 42 | Items: make([]Item, size), 43 | } 44 | 45 | ng.time = time.Now() 46 | ng.Items[0].Received = true 47 | ng.Items[0].LatencyNs = 0 48 | 49 | return ng 50 | } 51 | 52 | func (s *GatherInfo) isFull() bool { 53 | for i := 1; i < len(s.Items); i++ { 54 | if !s.Items[i].Received { 55 | return false 56 | } 57 | } 58 | return true 59 | } 60 | 61 | func (s *GatherInfo) attachFollow(pkt *xproto.PcapCaputre) { 62 | 63 | idx := pkt.Meta.TracePosIndex 64 | 65 | if s.Items[idx].Received { 66 | return 67 | } 68 | 69 | s.Items[idx].LatencyNs = pkt.Meta.TimeStampNs - s.Packet.Meta.TimeStampNs 70 | s.Items[idx].Received = true 71 | } 72 | -------------------------------------------------------------------------------- /pkg/dump/driver/gather/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package gather 17 | 18 | import "github.com/bytedance/netcap/pkg/dump/xproto" 19 | 20 | func isSamePacketByContent(p1 *xproto.PcapCaputre, p2 *xproto.PcapCaputre) bool { 21 | if p1.Meta.CaptureLength != p2.Meta.CaptureLength { 22 | return false 23 | } 24 | if p1.Meta.PacketLength != p2.Meta.PacketLength { 25 | return false 26 | } 27 | 28 | for i := 0; i < int(p1.Meta.CaptureLength); i++ { 29 | if p1.PacketData[i] != p2.PacketData[i] { 30 | return false 31 | } 32 | } 33 | 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/color.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | func getOutputColor(color string) (prefix string, suffix string) { 19 | if color == "" { 20 | color = "green" 21 | } 22 | 23 | if color == "red" { 24 | prefix = "\033[0;31m" 25 | suffix = "\033[0m" 26 | } else if color == "green" { 27 | prefix = "\033[0;32m" 28 | suffix = "\033[0m" 29 | } else if color == "yellow" { 30 | prefix = "\033[0;33m" 31 | suffix = "\033[0m" 32 | } else if color == "blue" { 33 | prefix = "\033[0;34m" 34 | suffix = "\033[0m" 35 | } else if color == "purple" { 36 | prefix = "\033[0;35m" 37 | suffix = "\033[0m" 38 | } else if color == "cyan" { 39 | prefix = "\033[0;36m" 40 | suffix = "\033[0m" 41 | } else { 42 | prefix = "" 43 | suffix = "" 44 | } 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/output.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/bytedance/netcap/pkg/dump/driver/gather" 22 | "github.com/bytedance/netcap/pkg/dump/driver/stack" 23 | "github.com/bytedance/netcap/pkg/extend" 24 | ) 25 | 26 | type Option struct { 27 | ExtOutputColor string 28 | ExtendOp extend.Operator 29 | TcpdumpFlags string 30 | WritePcapFilePath string 31 | WritePcapFileRotate uint32 32 | GatherOp gather.Operator 33 | GatherOutputColor string 34 | StackOp stack.Operator 35 | DumpStackColor string 36 | } 37 | 38 | type Operator interface { 39 | Output(raw []byte, n uint32) (uint32, error) 40 | OnTick(n uint32) (uint32, error) 41 | Close() 42 | } 43 | 44 | func New(opt *Option) (Operator, error) { 45 | 46 | if opt.StackOp != nil { 47 | return newOutputTcpdump(opt) 48 | } 49 | 50 | if opt.GatherOp != nil { 51 | return newOutputGather(opt) 52 | } 53 | 54 | if opt.WritePcapFilePath != "" { 55 | if opt.WritePcapFileRotate == 0 { 56 | return newOutputFile(opt) 57 | } else { 58 | return newOutputFileRotate(opt) 59 | } 60 | } 61 | 62 | return newOutputTcpdump(opt) 63 | } 64 | 65 | type baseImpl struct { 66 | extendOper extend.Operator 67 | 68 | userOutputPrefix string 69 | userOutputSuffix string 70 | } 71 | 72 | func (s *baseImpl) _baseInit(opt *Option) { 73 | s.extendOper = opt.ExtendOp 74 | 75 | s.userOutputPrefix, s.userOutputSuffix = getOutputColor(opt.ExtOutputColor) 76 | } 77 | 78 | func (s *baseImpl) _baseClose() { 79 | 80 | } 81 | 82 | func (s *baseImpl) _baseUserOutput(userRet int32, userData []byte, n uint32) { 83 | 84 | if userRet == 0 || s.extendOper == nil { 85 | return 86 | } 87 | 88 | str := s.extendOper.Convert(userData) 89 | 90 | fmt.Printf("%s--> No %d's pkt User Action >> %s%s\n", s.userOutputPrefix, n, str, s.userOutputSuffix) 91 | } 92 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/pcap_converter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "fmt" 20 | "unsafe" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/xproto" 23 | "github.com/bytedance/netcap/pkg/gmt" 24 | ) 25 | 26 | type pcapConverter interface { 27 | Convert(raw []byte) (*xproto.PcapCaputre, error) 28 | } 29 | 30 | type captureOperatorImpl struct { 31 | gmtOp gmt.Operator 32 | } 33 | 34 | func newPcapConverter() pcapConverter { 35 | s := &captureOperatorImpl{ 36 | gmtOp: gmt.NewGMT(), 37 | } 38 | 39 | return s 40 | } 41 | 42 | func (s *captureOperatorImpl) Convert(raw []byte) (*xproto.PcapCaputre, error) { 43 | 44 | var pkt *xproto.PacketMeta = *(**xproto.PacketMeta)(unsafe.Pointer(&raw)) 45 | 46 | totalSize := int(pkt.BufferOffset) + int(pkt.CaptureLength) 47 | 48 | if totalSize > len(raw) { 49 | return nil, fmt.Errorf("size error need %d but only %d", totalSize, len(raw)) 50 | } 51 | 52 | xc := &xproto.PcapCaputre{ 53 | Meta: *pkt, 54 | UserExtendData: raw[pkt.ExtendOffset:pkt.BufferOffset], 55 | PacketData: raw[pkt.BufferOffset:totalSize], 56 | } 57 | 58 | xc.Meta.TimeStampNs = s.gmtOp.MonotonicToGMT(xc.Meta.TimeStampNs) 59 | 60 | return xc, nil 61 | } 62 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/pcap_output_base.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "os" 20 | 21 | "github.com/bytedance/netcap/pkg/dump/xproto" 22 | "github.com/bytedance/netcap/pkg/pcap" 23 | ) 24 | 25 | type pcapBase struct { 26 | baseImpl 27 | pcapWriter pcap.PcapWriter 28 | converter pcapConverter 29 | } 30 | 31 | func (s *pcapBase) pcapBuild(opt *Option, outFile *os.File) error { 32 | 33 | s._baseInit(opt) 34 | 35 | p, err := pcap.NewPcapWriter(outFile) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | s.pcapWriter = p 41 | s.converter = newPcapConverter() 42 | 43 | return nil 44 | } 45 | 46 | func (s *pcapBase) pcapClose() { 47 | s.pcapWriter.Close() 48 | s._baseClose() 49 | } 50 | 51 | func (s *pcapBase) pcapOutput(info *xproto.PcapCaputre, n uint32) { 52 | pktInfo := &pcap.PacketInfo{ 53 | TimeUs: int64(info.Meta.TimeStampNs / 1000), 54 | CapLen: uint32(info.Meta.CaptureLength), 55 | Len: info.Meta.PacketLength, 56 | } 57 | 58 | _ = s.pcapWriter.WritePacket(info.PacketData, pktInfo) 59 | s.pcapWriter.Flush() 60 | 61 | s._baseUserOutput(info.Meta.ExtendUserRet, info.UserExtendData, n) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/pcap_output_file.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | ) 22 | 23 | type outputFile struct { 24 | pcapBase 25 | } 26 | 27 | func newOutputFile(opt *Option) (Operator, error) { 28 | 29 | path := opt.WritePcapFilePath 30 | 31 | s := &outputFile{} 32 | 33 | _ = os.Remove(path) 34 | 35 | file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | err = s.pcapBuild(opt, file) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return s, nil 46 | } 47 | 48 | func (s *outputFile) OnTick(n uint32) (uint32, error) { 49 | return n, nil 50 | } 51 | 52 | func (s *outputFile) Output(raw []byte, n uint32) (uint32, error) { 53 | 54 | info, err := s.converter.Convert(raw) 55 | if err != nil { 56 | return n + 1, err 57 | } 58 | 59 | s.pcapOutput(info, n) 60 | 61 | if s.extendOper == nil { 62 | fmt.Printf("\rcapture packet: %5d", n+1) 63 | } 64 | 65 | return n + 1, nil 66 | } 67 | 68 | func (s *outputFile) Close() { 69 | 70 | s.pcapClose() 71 | 72 | fmt.Printf("\n") 73 | } 74 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/pcap_output_file_rotate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "container/list" 20 | "fmt" 21 | "os" 22 | 23 | "github.com/bytedance/netcap/pkg/dump/xproto" 24 | "github.com/bytedance/netcap/pkg/pcap" 25 | ) 26 | 27 | type outputFileRotate struct { 28 | pcapBase 29 | 30 | infoList *list.List 31 | max uint32 32 | } 33 | 34 | func newOutputFileRotate(opt *Option) (Operator, error) { 35 | path := opt.WritePcapFilePath 36 | 37 | s := &outputFileRotate{ 38 | infoList: list.New(), 39 | max: opt.WritePcapFileRotate, 40 | } 41 | 42 | _ = os.Remove(path) 43 | 44 | file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | err = s.pcapBuild(opt, file) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return s, nil 55 | } 56 | 57 | func (s *outputFileRotate) OnTick(n uint32) (uint32, error) { 58 | return n, nil 59 | } 60 | 61 | func (s *outputFileRotate) Output(raw []byte, n uint32) (uint32, error) { 62 | 63 | info, err := s.converter.Convert(raw) 64 | if err != nil { 65 | return n + 1, err 66 | } 67 | 68 | if s.infoList.Len() >= int(s.max) { 69 | s.infoList.Remove(s.infoList.Front()) 70 | } 71 | 72 | s.infoList.PushBack(info) 73 | 74 | if s.extendOper == nil { 75 | fmt.Printf("\rcapture packet: %5d", n+1) 76 | } 77 | 78 | return n + 1, nil 79 | } 80 | 81 | func (s *outputFileRotate) Close() { 82 | 83 | fmt.Printf("\nNow, Write %d packts to file...\n", s.infoList.Len()) 84 | 85 | s.writeAllPacpToFile() 86 | 87 | s.pcapClose() 88 | 89 | fmt.Printf("Write Finished.\n\n") 90 | } 91 | 92 | func (s *outputFileRotate) writeAllPacpToFile() { 93 | 94 | for e := s.infoList.Front(); e != nil; e = e.Next() { 95 | info, ok := e.Value.(*xproto.PcapCaputre) 96 | if !ok { 97 | continue 98 | } 99 | pktInfo := &pcap.PacketInfo{ 100 | TimeUs: int64(info.Meta.TimeStampNs / 1000), 101 | CapLen: uint32(info.Meta.CaptureLength), 102 | Len: info.Meta.PacketLength, 103 | } 104 | 105 | _ = s.pcapWriter.WritePacket(info.PacketData, pktInfo) 106 | } 107 | 108 | s.pcapWriter.Flush() 109 | } 110 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/pcap_output_gather.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/driver/gather" 23 | ) 24 | 25 | type outputGather struct { 26 | pcapBase 27 | 28 | gatherOp gather.Operator 29 | proxy tcpdumpProxy 30 | prefix string 31 | suffix string 32 | } 33 | 34 | func newOutputGather(opt *Option) (Operator, error) { 35 | s := &outputGather{ 36 | gatherOp: opt.GatherOp, 37 | proxy: newTcpdumpProxy(os.Stdout, opt.TcpdumpFlags), 38 | } 39 | 40 | s.prefix, s.suffix = getOutputColor(opt.GatherOutputColor) 41 | 42 | err := s.pcapBuild(opt, s.proxy.GetInput()) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return s, nil 48 | } 49 | 50 | func (s *outputGather) Output(raw []byte, n uint32) (uint32, error) { 51 | 52 | info, err := s.converter.Convert(raw) 53 | if err != nil { 54 | return n, err 55 | } 56 | 57 | g := s.gatherOp.Feed(info) 58 | 59 | if g == nil { 60 | return n, nil 61 | } 62 | 63 | return s.outputGatherInfo(g, n) 64 | } 65 | 66 | func (s *outputGather) OnTick(n uint32) (uint32, error) { 67 | 68 | g := s.gatherOp.Get() 69 | 70 | if g == nil { 71 | return n, nil 72 | } 73 | 74 | return s.outputGatherInfo(g, n) 75 | } 76 | 77 | func (s *outputGather) outputGatherInfo(info *gather.GatherInfo, n uint32) (uint32, error) { 78 | s.pcapOutput(info.Packet, n) 79 | 80 | str := s.gatherOp.ToString(info, n) 81 | 82 | fmt.Printf("%s%s%s", s.prefix, str, s.suffix) 83 | 84 | return n + 1, nil 85 | } 86 | 87 | func (s *outputGather) Close() { 88 | s.pcapClose() 89 | } 90 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/pcap_output_tcpdump.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/driver/stack" 23 | ) 24 | 25 | type outputTcpdump struct { 26 | pcapBase 27 | 28 | proxy tcpdumpProxy 29 | stackOp stack.Operator 30 | stackOutputPrefix string 31 | stackOutputSuffix string 32 | } 33 | 34 | func newOutputTcpdump(opt *Option) (Operator, error) { 35 | 36 | s := &outputTcpdump{ 37 | proxy: newTcpdumpProxy(os.Stdout, opt.TcpdumpFlags), 38 | stackOp: opt.StackOp, 39 | } 40 | 41 | err := s.pcapBuild(opt, s.proxy.GetInput()) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | s.stackOutputPrefix, s.stackOutputSuffix = getOutputColor(opt.DumpStackColor) 47 | 48 | return s, nil 49 | } 50 | 51 | func (s *outputTcpdump) Output(raw []byte, n uint32) (uint32, error) { 52 | 53 | info, err := s.converter.Convert(raw) 54 | if err != nil { 55 | return n + 1, err 56 | } 57 | 58 | s.pcapOutput(info, n) 59 | 60 | s.stackOutput(n, info.Meta.StackID) 61 | 62 | return n + 1, nil 63 | } 64 | 65 | func (s *outputTcpdump) OnTick(n uint32) (uint32, error) { 66 | return n, nil 67 | } 68 | 69 | func (s *outputTcpdump) Close() { 70 | s.pcapClose() 71 | } 72 | 73 | func (s *outputTcpdump) stackOutput(n uint32, stackID uint32) { 74 | 75 | if s.stackOp == nil { 76 | return 77 | } 78 | 79 | fmt.Printf("%s--> No %d's packet's kstack: \n%s%s\n", s.stackOutputPrefix, n, 80 | s.stackOp.GetStack(stackID), s.stackOutputSuffix) 81 | } 82 | -------------------------------------------------------------------------------- /pkg/dump/driver/output/tcpdump_proxy.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package output 17 | 18 | import ( 19 | "bytes" 20 | "fmt" 21 | "log" 22 | "os" 23 | "os/exec" 24 | ) 25 | 26 | type tcpdumpProxy interface { 27 | GetInput() *os.File 28 | } 29 | 30 | type tcpdumpProxyImpl struct { 31 | cmd *exec.Cmd 32 | inputFile *os.File 33 | } 34 | 35 | func newTcpdumpProxy(out *os.File, flags string) tcpdumpProxy { 36 | s := &tcpdumpProxyImpl{} 37 | 38 | r1, w1, _ := os.Pipe() 39 | 40 | cmdStr := fmt.Sprintf("tcpdump -r - %v ", flags) 41 | 42 | s.cmd = exec.Command("bash", "-c", cmdStr) 43 | 44 | s.cmd.Stdin = r1 45 | s.cmd.Stdout = out 46 | s.inputFile = w1 47 | 48 | go s.run() 49 | 50 | return s 51 | } 52 | 53 | func (s *tcpdumpProxyImpl) GetInput() *os.File { 54 | return s.inputFile 55 | } 56 | 57 | func (s *tcpdumpProxyImpl) run() { 58 | var ( 59 | stdErr bytes.Buffer 60 | ) 61 | s.cmd.Stderr = &stdErr 62 | err := s.cmd.Run() 63 | if err != nil { 64 | log.Fatalf("cmd run failed:%s,%s\n", stdErr.String(), err.Error()) 65 | os.Exit(0) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /pkg/dump/driver/stack/stack.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package stack 17 | 18 | import ( 19 | "fmt" 20 | "unsafe" 21 | 22 | "github.com/bytedance/netcap/pkg/ksym" 23 | "github.com/iovisor/gobpf/bcc" 24 | ) 25 | 26 | type Option struct { 27 | StackTable *bcc.Table 28 | } 29 | 30 | type Operator interface { 31 | GetStack(id uint32) string 32 | } 33 | 34 | type stackImpl struct { 35 | table *bcc.Table 36 | kSymbol ksym.Operator 37 | } 38 | 39 | const ( 40 | maxStackDepth = 64 41 | ) 42 | 43 | type StackData struct { 44 | IPs [maxStackDepth]uint64 45 | } 46 | 47 | func New(opt *Option) (Operator, error) { 48 | k, err := ksym.New() 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | s := &stackImpl{ 54 | table: opt.StackTable, 55 | kSymbol: k, 56 | } 57 | 58 | return s, nil 59 | } 60 | 61 | func (s *stackImpl) GetStack(id uint32) string { 62 | val, err := s.table.GetP(unsafe.Pointer(&id)) 63 | if err != nil { 64 | return "" 65 | } 66 | str := "" 67 | var stack *StackData = (*StackData)(unsafe.Pointer(val)) 68 | for _, ip := range stack.IPs { 69 | if ip > 0 { 70 | 71 | item := s.kSymbol.LookUpByAddr(ip) 72 | if item == nil { 73 | continue 74 | } 75 | 76 | str += symbolToString(item, ip) 77 | } 78 | } 79 | 80 | return str 81 | } 82 | 83 | func symbolToString(item *ksym.Symbol, ip uint64) string { 84 | 85 | str := fmt.Sprintf(" %s+0x%x %s\n", item.Name, ip-item.Addr, item.Module) 86 | 87 | return str 88 | } 89 | -------------------------------------------------------------------------------- /pkg/dump/dump.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package dump 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/code" 23 | "github.com/bytedance/netcap/pkg/dump/driver" 24 | "github.com/bytedance/netcap/pkg/dump/tparser" 25 | ) 26 | 27 | type Operator interface { 28 | Run(ctx context.Context) error 29 | } 30 | 31 | type Option struct { 32 | ExtFilterFilePath string 33 | ExtActionFilePath string 34 | ExtOutputColor string 35 | TcpdumpFlags string 36 | TcpdumpExpression string 37 | TraceFunction string 38 | DumpWriteFilePath string 39 | DumpWriteFileRotate uint32 40 | 41 | DumpCount uint32 42 | CaptureMaxSize uint32 43 | 44 | IsDryRun bool 45 | IsGatherStatistic bool 46 | GatherTimeoutSec uint32 47 | GatherBufferSize uint32 48 | GatherOutputColor string 49 | GatherDistinguishByPointer bool 50 | } 51 | 52 | type dumpBaseImpl struct { 53 | driver driver.Operator 54 | parser tparser.Operator 55 | } 56 | 57 | func (s *dumpBaseImpl) Run(ctx context.Context) error { 58 | return s.driver.Run(ctx) 59 | } 60 | 61 | func (s *dumpBaseImpl) baseInit(opt *Option) error { 62 | s.parser = tparser.New() 63 | 64 | err := s.parser.Parse(opt.TraceFunction) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | if opt.IsGatherStatistic { 70 | if len(s.parser.Get()) <= 1 { 71 | return fmt.Errorf("--statistic only work in multi trace") 72 | } 73 | } 74 | 75 | return nil 76 | } 77 | 78 | func driverOption(opt *Option, c code.Operator, isStackDump bool, stackDumpColor string) *driver.Option { 79 | drverOp := &driver.Option{ 80 | ExtOuputColor: opt.ExtOutputColor, 81 | DumpWriteFilePath: opt.DumpWriteFilePath, 82 | DumpWriteFileRotate: opt.DumpWriteFileRotate, 83 | TcpdumpFlags: opt.TcpdumpFlags, 84 | DumpCount: opt.DumpCount, 85 | Code: c, 86 | IsGatherStatistic: opt.IsGatherStatistic, 87 | GatherTimeoutSec: opt.GatherTimeoutSec, 88 | GatherBufferSize: opt.GatherBufferSize, 89 | GatherOutputColor: opt.GatherOutputColor, 90 | GatherDistinguishByPointer: opt.GatherDistinguishByPointer, 91 | IsDumpStack: isStackDump, 92 | DumpStackColor: stackDumpColor, 93 | } 94 | 95 | return drverOp 96 | } 97 | -------------------------------------------------------------------------------- /pkg/dump/dump_proto.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package dump 17 | 18 | const ( 19 | ModeSkb = 0 20 | ModeMbuf = 1 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/dump/mbuf.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package dump 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/code" 23 | "github.com/bytedance/netcap/pkg/dump/driver" 24 | ) 25 | 26 | type MbufOption struct { 27 | Option 28 | 29 | Pid int 30 | } 31 | 32 | type mbufDumpImpl struct { 33 | dumpBaseImpl 34 | } 35 | 36 | func NewMbufDump(opt *MbufOption) (Operator, error) { 37 | 38 | s := &mbufDumpImpl{} 39 | 40 | err := s.baseInit(&opt.Option) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | mbufOpt := &code.MbufOption{ 46 | Option: code.Option{ 47 | TcpdumpExpression: opt.TcpdumpExpression, 48 | ExtFilterFilePath: opt.ExtFilterFilePath, 49 | ExtActionFilePath: opt.ExtActionFilePath, 50 | CaptureMaxSize: opt.CaptureMaxSize, 51 | FunctionDesc: s.parser.Get(), 52 | IsDumpStack: false, 53 | }, 54 | } 55 | 56 | mbufCode, err := code.NewMbufCode(mbufOpt) 57 | if err != nil { 58 | return nil, err 59 | } 60 | if opt.IsDryRun { 61 | fmt.Printf("\n%s\n", mbufCode.GetEbpfCode()) 62 | os.Exit(0) 63 | } 64 | 65 | driverOpt := &driver.MbufOption{ 66 | Option: *driverOption(&opt.Option, mbufCode, false, ""), 67 | Pid: opt.Pid, 68 | } 69 | s.driver = driver.NewMbufDriver(driverOpt) 70 | 71 | err = s.driver.Init() 72 | if err != nil { 73 | return nil, err 74 | } 75 | return s, nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/dump/raw.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package dump 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/code" 23 | "github.com/bytedance/netcap/pkg/dump/driver" 24 | ) 25 | 26 | type RawOption struct { 27 | Option 28 | 29 | Pid int 30 | } 31 | 32 | type rawDumpImpl struct { 33 | dumpBaseImpl 34 | } 35 | 36 | func NewRawDump(opt *RawOption) (Operator, error) { 37 | 38 | s := &rawDumpImpl{} 39 | 40 | err := s.baseInit(&opt.Option) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | rawOpt := &code.RawOption{ 46 | Option: code.Option{ 47 | TcpdumpExpression: opt.TcpdumpExpression, 48 | ExtFilterFilePath: opt.ExtFilterFilePath, 49 | ExtActionFilePath: opt.ExtActionFilePath, 50 | CaptureMaxSize: opt.CaptureMaxSize, 51 | FunctionDesc: s.parser.Get(), 52 | IsDumpStack: false, 53 | }, 54 | } 55 | 56 | rawCode, err := code.NewRawCode(rawOpt) 57 | if err != nil { 58 | return nil, err 59 | } 60 | if opt.IsDryRun { 61 | fmt.Printf("\n%s\n", rawCode.GetEbpfCode()) 62 | os.Exit(0) 63 | } 64 | 65 | driverOpt := &driver.MbufOption{ 66 | Option: *driverOption(&opt.Option, rawCode, false, ""), 67 | Pid: opt.Pid, 68 | } 69 | s.driver = driver.NewMbufDriver(driverOpt) 70 | 71 | err = s.driver.Init() 72 | if err != nil { 73 | return nil, err 74 | } 75 | return s, nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/dump/skb.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package dump 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/bytedance/netcap/pkg/dump/code" 23 | "github.com/bytedance/netcap/pkg/dump/driver" 24 | ) 25 | 26 | type SkbOption struct { 27 | Option 28 | 29 | Interface string 30 | IsDumpStack bool 31 | DumpStackColor string 32 | IsFakeHdr bool 33 | IsUseSkbData bool 34 | SkbDataOffset int32 35 | } 36 | 37 | type skbDumpImpl struct { 38 | dumpBaseImpl 39 | } 40 | 41 | func NewSkbDump(opt *SkbOption) (Operator, error) { 42 | 43 | s := &skbDumpImpl{} 44 | 45 | err := s.baseInit(&opt.Option) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | skbOpt := &code.SkbOption{ 51 | Option: code.Option{ 52 | TcpdumpExpression: opt.TcpdumpExpression, 53 | ExtFilterFilePath: opt.ExtFilterFilePath, 54 | ExtActionFilePath: opt.ExtActionFilePath, 55 | CaptureMaxSize: opt.CaptureMaxSize, 56 | FunctionDesc: s.parser.Get(), 57 | IsDumpStack: opt.IsDumpStack, 58 | }, 59 | Interface: opt.Interface, 60 | IsFakeHdr: opt.IsFakeHdr, 61 | IsUseSkbData: opt.IsUseSkbData, 62 | SkbDataOffset: opt.SkbDataOffset, 63 | } 64 | 65 | skbCode, err := code.NewSkbCode(skbOpt) 66 | if err != nil { 67 | return nil, err 68 | } 69 | if opt.IsDryRun { 70 | fmt.Printf("\n%s\n", skbCode.GetEbpfCode()) 71 | os.Exit(0) 72 | } 73 | 74 | driverOpt := &driver.SkbOption{ 75 | Option: *driverOption(&opt.Option, skbCode, opt.IsDumpStack, opt.DumpStackColor), 76 | } 77 | s.driver = driver.NewSkbDriver(driverOpt) 78 | 79 | err = s.driver.Init() 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | return s, nil 85 | } 86 | -------------------------------------------------------------------------------- /pkg/dump/tparser/tparser.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package tparser 17 | 18 | import ( 19 | "fmt" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | type FunctionDescribe struct { 25 | Prefix string 26 | FunctionName string 27 | ParamIndex1 int 28 | ParamIndex2 int 29 | } 30 | 31 | type Operator interface { 32 | Parse(str string) error 33 | Get() []FunctionDescribe 34 | } 35 | 36 | type tparserImpl struct { 37 | funcDesc []FunctionDescribe 38 | } 39 | 40 | func New() Operator { 41 | s := &tparserImpl{} 42 | 43 | return s 44 | } 45 | 46 | func (s *tparserImpl) Parse(str string) error { 47 | s.funcDesc = nil 48 | 49 | arr := strings.Split(str, ",") 50 | 51 | for i := 0; i < len(arr); i++ { 52 | err := s.parseFunctionWithPrefix(arr[i]) 53 | if err != nil { 54 | return err 55 | } 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func (s *tparserImpl) Get() []FunctionDescribe { 62 | return s.funcDesc 63 | } 64 | 65 | func (s *tparserImpl) parseFunction(funcStr string, desc *FunctionDescribe) error { 66 | 67 | var err error 68 | 69 | arr := strings.Split(funcStr, "@") 70 | 71 | if len(arr) > 3 { 72 | return fmt.Errorf("cant more than 3@ per function: %s", funcStr) 73 | } 74 | 75 | desc.FunctionName = arr[0] 76 | 77 | if desc.FunctionName == "" { 78 | return fmt.Errorf("no trace function name input") 79 | } 80 | 81 | if len(arr) <= 1 { 82 | return nil 83 | } 84 | 85 | desc.ParamIndex1, err = strconv.Atoi(arr[1]) 86 | if err != nil || desc.ParamIndex1 <= 0 { 87 | return fmt.Errorf("param index1 must be >=1 ") 88 | } 89 | 90 | if len(arr) == 3 { 91 | if arr[2] == "" { 92 | desc.ParamIndex2 = desc.ParamIndex1 + 1 93 | } else { 94 | desc.ParamIndex2, err = strconv.Atoi(arr[2]) 95 | } 96 | if err != nil || desc.ParamIndex2 <= 0 { 97 | return fmt.Errorf("param index2 must be >=1 ") 98 | } 99 | } 100 | 101 | return nil 102 | } 103 | 104 | func (s *tparserImpl) parseFunctionWithPrefix(traceFunction string) error { 105 | 106 | arr := strings.SplitN(traceFunction, ":", 2) 107 | 108 | desc := FunctionDescribe{} 109 | 110 | var funcStr string 111 | 112 | if len(arr) == 2 { 113 | desc.Prefix = arr[0] 114 | funcStr = arr[1] 115 | } else { 116 | desc.Prefix = "" 117 | funcStr = arr[0] 118 | } 119 | 120 | err := s.parseFunction(funcStr, &desc) 121 | if err != nil { 122 | return err 123 | } 124 | 125 | s.funcDesc = append(s.funcDesc, desc) 126 | 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /pkg/dump/xproto/xproto.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package xproto 17 | 18 | // PacketMeta is the proto-struct between ebpf and golang 19 | type PacketMeta struct { 20 | Ptr uint64 21 | TimeStampNs uint64 22 | PacketLength uint32 23 | ExtendOffset uint16 24 | BufferOffset uint16 25 | CaptureLength uint16 26 | TracePosIndex uint16 27 | ExtendUserRet int32 28 | StackID uint32 29 | Reserved uint32 30 | } 31 | 32 | type PcapCaputre struct { 33 | Meta PacketMeta 34 | PacketData []byte 35 | UserExtendData []byte 36 | } 37 | 38 | const ( 39 | PerfTableName = "xcap_pcap_event" 40 | StackTableName = "xcap_stack" 41 | ) 42 | -------------------------------------------------------------------------------- /pkg/extend/extend.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package extend 17 | 18 | import ( 19 | "regexp" 20 | "strings" 21 | ) 22 | 23 | type Option struct { 24 | UserActionCode string 25 | } 26 | 27 | type Operator interface { 28 | Convert(data []byte) string 29 | 30 | Close() 31 | } 32 | 33 | type operatorImpl struct { 34 | fields []filedOperator 35 | 36 | prefix string 37 | suffix string 38 | } 39 | 40 | func New(opt *Option) Operator { 41 | 42 | code := opt.UserActionCode 43 | 44 | if code == "" { 45 | return nil 46 | } 47 | 48 | e := &operatorImpl{ 49 | prefix: "{", 50 | suffix: "}", 51 | } 52 | 53 | e.parse(code) 54 | 55 | return e 56 | } 57 | 58 | func (s *operatorImpl) Convert(data []byte) string { 59 | ret := s.prefix 60 | 61 | for _, field := range s.fields { 62 | var str string 63 | data, str = field.Convert(data) 64 | 65 | ret += str + "; " 66 | } 67 | ret += s.suffix 68 | 69 | return ret 70 | } 71 | 72 | func (s *operatorImpl) Close() { 73 | 74 | } 75 | 76 | func (s *operatorImpl) parse(str string) { 77 | mode := `(?s)struct\s+xcap_user_extend\s+{(.+?)}` 78 | 79 | reg := regexp.MustCompile(mode) 80 | 81 | match := reg.FindStringSubmatch(str) 82 | 83 | if len(match) <= 1 { 84 | return 85 | } 86 | 87 | arr := strings.Split(match[1], "\n") 88 | 89 | regType := regexp.MustCompile(`\s+(\S+)\s+(\S+)\s*`) 90 | regFormat := regexp.MustCompile(`\s*//\s*format:\s(\S+)`) 91 | 92 | for i := 0; i < len(arr); i++ { 93 | s.parseLine(arr[i], regType, regFormat) 94 | } 95 | } 96 | 97 | func (s *operatorImpl) parseLine(line string, regType *regexp.Regexp, regFormat *regexp.Regexp) { 98 | 99 | arr := strings.SplitN(line, ";", 2) 100 | 101 | if len(arr) != 2 { 102 | return 103 | } 104 | 105 | match := regType.FindStringSubmatch(arr[0]) 106 | match2 := regFormat.FindStringSubmatch(arr[1]) 107 | 108 | if len(match) != 3 { 109 | return 110 | } 111 | 112 | t := match[1] 113 | n := match[2] 114 | f := "" 115 | 116 | if len(match2) == 2 { 117 | f = match2[1] 118 | } 119 | 120 | s.fields = append(s.fields, newField(n, t, f)) 121 | } 122 | -------------------------------------------------------------------------------- /pkg/extend/filed.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package extend 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/bytedance/netcap/pkg/util" 22 | ) 23 | 24 | type filedOperator interface { 25 | Convert(data []byte) ([]byte, string) 26 | } 27 | 28 | type structField struct { 29 | fieldName string 30 | fieldType string 31 | outputFormat string 32 | } 33 | 34 | func (s *structField) getFormatString(d string) string { 35 | 36 | f := s.fieldName + ": " 37 | 38 | if s.outputFormat != "" { 39 | f += s.outputFormat 40 | } else { 41 | f += d 42 | } 43 | return f 44 | } 45 | 46 | func (s *structField) Convert(data []byte) ([]byte, string) { 47 | 48 | if s.fieldType == "int8" || s.fieldType == "int8_t" || s.fieldType == "char" { 49 | v := int8(data[0]) 50 | 51 | output := fmt.Sprintf(s.getFormatString("%d"), v) 52 | return data[1:], output 53 | 54 | } else if s.fieldType == "uint8" || s.fieldType == "uint8_t" || s.fieldType == "uchar" { 55 | v := uint8(data[0]) 56 | 57 | output := fmt.Sprintf(s.getFormatString("%d"), v) 58 | return data[1:], output 59 | 60 | } else if s.fieldType == "int16" || s.fieldType == "int16_t" { 61 | 62 | v := int16(util.NativeEndian.Uint16(data[:])) 63 | 64 | output := fmt.Sprintf(s.getFormatString("%d"), v) 65 | return data[2:], output 66 | 67 | } else if s.fieldType == "uint16" || s.fieldType == "uint16_t" { 68 | 69 | v := util.NativeEndian.Uint16(data[:]) 70 | 71 | output := fmt.Sprintf(s.getFormatString("%d"), v) 72 | return data[2:], output 73 | 74 | } else if s.fieldType == "int" || s.fieldType == "int32_t" { 75 | tmp := util.NativeEndian.Uint32(data[:]) 76 | v := int(tmp) 77 | 78 | form := s.getFormatString("%d") 79 | output := fmt.Sprintf(form, v) 80 | return data[4:], output 81 | 82 | } else if s.fieldType == "uint32" || s.fieldType == "uint32_t" { 83 | 84 | v := util.NativeEndian.Uint32(data[:]) 85 | 86 | output := fmt.Sprintf(s.getFormatString("%d"), v) 87 | return data[4:], output 88 | 89 | } else if s.fieldType == "int64" || s.fieldType == "int64_t" { 90 | 91 | v := int64(util.NativeEndian.Uint64(data[:])) 92 | 93 | output := fmt.Sprintf(s.getFormatString("%d"), v) 94 | return data[8:], output 95 | } else if s.fieldType == "uint64" || s.fieldType == "uint64_t" { 96 | 97 | v := util.NativeEndian.Uint64(data[:]) 98 | 99 | output := fmt.Sprintf(s.getFormatString("%d"), v) 100 | return data[8:], output 101 | } 102 | 103 | return data, "" 104 | } 105 | 106 | func newField(n string, t string, f string) filedOperator { 107 | 108 | s := &structField{ 109 | fieldName: n, 110 | fieldType: t, 111 | outputFormat: f, 112 | } 113 | 114 | return s 115 | } 116 | -------------------------------------------------------------------------------- /pkg/gmt/gmt.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package gmt 17 | 18 | import ( 19 | "syscall" 20 | "time" 21 | "unsafe" 22 | ) 23 | 24 | // Greenwich Mean Time 25 | 26 | type Operator interface { 27 | // convert monotonic time to GMT 28 | MonotonicToGMT(ns uint64) uint64 29 | } 30 | 31 | type operatorImpl struct { 32 | deltaNS uint64 33 | } 34 | 35 | func NewGMT() Operator { 36 | s := &operatorImpl{} 37 | 38 | monic := monotonicNow() 39 | now := (uint64)(time.Now().UnixNano()) 40 | s.deltaNS = now - monic 41 | 42 | return s 43 | } 44 | 45 | type timeSpec struct { 46 | Sec uint64 47 | Nsec uint64 48 | } 49 | 50 | const ( 51 | CLOCK_MONOTONIC = 1 52 | ) 53 | 54 | func monotonicNow() uint64 { 55 | var ts timeSpec 56 | 57 | _, _, err := syscall.Syscall(syscall.SYS_CLOCK_GETTIME, uintptr(CLOCK_MONOTONIC), uintptr(unsafe.Pointer(&ts)), 0) 58 | if err != 0 { 59 | return 0 60 | } 61 | return ts.Sec*1000000000 + ts.Nsec 62 | } 63 | 64 | func (s *operatorImpl) MonotonicToGMT(ns uint64) uint64 { 65 | 66 | return ns + s.deltaNS 67 | } 68 | -------------------------------------------------------------------------------- /pkg/ksym/ksym.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package ksym 17 | 18 | import ( 19 | "bufio" 20 | "os" 21 | "sort" 22 | "strconv" 23 | "strings" 24 | ) 25 | 26 | type Symbol struct { 27 | Addr uint64 28 | Name string 29 | Module string 30 | } 31 | 32 | type Operator interface { 33 | LookUpByAddr(addr uint64) *Symbol 34 | } 35 | 36 | type operImpl struct { 37 | symbols []Symbol 38 | } 39 | 40 | type bySAddr []Symbol 41 | 42 | func (a bySAddr) Len() int { return len(a) } 43 | func (a bySAddr) Less(i, j int) bool { return a[i].Addr < a[j].Addr } 44 | func (a bySAddr) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 45 | 46 | func New() (Operator, error) { 47 | 48 | s := &operImpl{} 49 | 50 | err := s.build() 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | return s, nil 56 | } 57 | 58 | func (s *operImpl) LookUpByAddr(addr uint64) *Symbol { 59 | total := len(s.symbols) 60 | i, j := 0, total 61 | for i < j { 62 | h := int(uint(i+j) >> 1) 63 | if s.symbols[h].Addr <= addr { 64 | if h+1 < total && s.symbols[h+1].Addr > addr { 65 | return &s.symbols[h] 66 | } 67 | i = h + 1 68 | } else { 69 | j = h 70 | } 71 | } 72 | return nil 73 | } 74 | 75 | func (s *operImpl) build() error { 76 | file, err := os.Open("/proc/kallsyms") 77 | if err != nil { 78 | return err 79 | } 80 | defer file.Close() 81 | 82 | scanner := bufio.NewScanner(file) 83 | for scanner.Scan() { 84 | line := strings.Split(scanner.Text(), " ") 85 | name := line[2] 86 | module := "" 87 | addr, err := strconv.ParseUint(line[0], 16, 64) 88 | if err != nil { 89 | return err 90 | } 91 | arr := strings.Split(name, "\t") 92 | if len(arr) >= 2 { 93 | name = arr[0] 94 | module = arr[1] 95 | } 96 | sym := Symbol{ 97 | Addr: addr, 98 | Name: name, 99 | Module: module, 100 | } 101 | s.symbols = append(s.symbols, sym) 102 | } 103 | 104 | if err := scanner.Err(); err != nil { 105 | return err 106 | } 107 | sort.Sort(bySAddr(s.symbols)) 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /pkg/pcap/proto.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package pcap 17 | 18 | const ( 19 | pcapFileHeaderSize = 24 20 | pcapPacketHeaderSize = 16 21 | ) 22 | 23 | type pcapFileHeader struct { 24 | magic uint32 25 | versionMajor uint16 26 | versionMinor uint16 27 | thisZone int 28 | sigFigs uint32 29 | snapLen uint32 30 | linkType uint32 31 | } 32 | 33 | type pcapPacketHeader struct { 34 | sec uint32 35 | us uint32 36 | capLen uint32 37 | len uint32 38 | } 39 | -------------------------------------------------------------------------------- /pkg/pcap/writer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package pcap 17 | 18 | import ( 19 | "encoding/binary" 20 | "os" 21 | ) 22 | 23 | type PcapWriter interface { 24 | WritePacket(data []byte, info *PacketInfo) error 25 | Flush() 26 | Close() 27 | } 28 | 29 | type pcapWriterImpl struct { 30 | file *os.File 31 | 32 | packetMeta [pcapPacketHeaderSize]byte 33 | } 34 | 35 | type PacketInfo struct { 36 | TimeUs int64 37 | CapLen uint32 38 | Len uint32 39 | } 40 | 41 | func NewPcapWriter(file *os.File) (PcapWriter, error) { 42 | 43 | s := &pcapWriterImpl{ 44 | file: file, 45 | } 46 | 47 | err := s.writeFileHeader() 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | s.Flush() 53 | 54 | return s, nil 55 | } 56 | 57 | func (s *pcapWriterImpl) WritePacket(data []byte, info *PacketInfo) error { 58 | hdr := &pcapPacketHeader{ 59 | sec: uint32(info.TimeUs / 1000000), 60 | us: uint32(info.TimeUs % 1000000), 61 | capLen: info.CapLen, 62 | len: info.Len, 63 | } 64 | buf := s.packetMeta 65 | 66 | binary.LittleEndian.PutUint32(buf[0:4], hdr.sec) 67 | binary.LittleEndian.PutUint32(buf[4:8], hdr.us) 68 | binary.LittleEndian.PutUint32(buf[8:12], hdr.capLen) 69 | binary.LittleEndian.PutUint32(buf[12:16], hdr.len) 70 | 71 | // fmt.Printf("packet header size %d\n", len(buf)) 72 | _, _ = s.file.Write(buf[:]) 73 | _, _ = s.file.Write(data) 74 | 75 | return nil 76 | } 77 | 78 | func (s *pcapWriterImpl) Flush() { 79 | 80 | _ = s.file.Sync() 81 | } 82 | 83 | func (s *pcapWriterImpl) Close() { 84 | 85 | _ = s.file.Close() 86 | } 87 | 88 | func (s *pcapWriterImpl) writeFileHeader() error { 89 | hdr := &pcapFileHeader{ 90 | magic: 0xA1B2C3D4, 91 | versionMajor: 2, 92 | versionMinor: 4, 93 | thisZone: 0, 94 | sigFigs: 0, 95 | snapLen: 0x40000, 96 | linkType: 1, 97 | } 98 | 99 | data := make([]byte, pcapFileHeaderSize) 100 | 101 | binary.LittleEndian.PutUint32(data[0:4], hdr.magic) 102 | binary.LittleEndian.PutUint16(data[4:6], hdr.versionMajor) 103 | binary.LittleEndian.PutUint16(data[6:8], hdr.versionMinor) 104 | 105 | binary.LittleEndian.PutUint32(data[16:20], hdr.snapLen) 106 | binary.LittleEndian.PutUint32(data[20:24], hdr.linkType) 107 | 108 | // fmt.Printf("pcap header size %d\n", len(data)) 109 | 110 | _, err := s.file.Write(data) 111 | if err != nil { 112 | return err 113 | } 114 | return nil 115 | } 116 | -------------------------------------------------------------------------------- /pkg/util/cmd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package util 17 | 18 | import ( 19 | "bytes" 20 | "fmt" 21 | "io" 22 | "os" 23 | "os/exec" 24 | ) 25 | 26 | var ( 27 | cmdLogger = NewLogger("cmd") 28 | ) 29 | 30 | func BashRaw(cmdStr string) error { 31 | cmd := bashCmd(cmdStr) 32 | cmd.Stdin = os.Stdin 33 | cmd.Stdout = os.Stdout 34 | cmd.Stderr = os.Stderr 35 | return cmd.Run() 36 | } 37 | 38 | func Bash(cmdStr string) error { 39 | var ( 40 | err error 41 | ) 42 | 43 | _, _, err = BashOutput(cmdStr) 44 | 45 | return err 46 | } 47 | 48 | func BashOutput(cmdStr string) (string, string, error) { 49 | var ( 50 | err error 51 | stdout, stderr bytes.Buffer 52 | ) 53 | 54 | cmd := bashCmd(cmdStr) 55 | cmd.Stderr = &stderr 56 | cmd.Stdout = &stdout 57 | err = cmd.Run() 58 | 59 | cmdLogger.Debugf("[stdout] %s", stdout.String()) 60 | if e := stderr.String(); e != "" { 61 | f := cmdLogger.Debugf 62 | if err != nil { 63 | f = cmdLogger.Errorf 64 | } 65 | f("[stderr] %s", e) 66 | } 67 | 68 | return stdout.String(), stderr.String(), err 69 | } 70 | 71 | func runCmd(cmd *exec.Cmd) { 72 | _ = cmd.Run() 73 | } 74 | 75 | func BashPipeInput(cmdStr string, output io.Writer) (io.WriteCloser, error) { 76 | var ( 77 | err error 78 | input io.WriteCloser 79 | ) 80 | 81 | cmd := bashCmd(cmdStr) 82 | defer func() { 83 | if err == nil { 84 | go runCmd(cmd) 85 | } 86 | }() 87 | if output != nil { 88 | cmd.Stdout = output 89 | } 90 | input, err = cmd.StdinPipe() 91 | if err != nil { 92 | return nil, fmt.Errorf("stdin pipe err : %v", err) 93 | } 94 | return input, err 95 | } 96 | 97 | func BashPipe(cmdStr string) (io.WriteCloser, io.ReadCloser, error) { 98 | var ( 99 | err error 100 | input io.WriteCloser 101 | output io.ReadCloser 102 | ) 103 | 104 | cmd := bashCmd(cmdStr) 105 | defer func() { 106 | if err == nil { 107 | go runCmd(cmd) 108 | } 109 | }() 110 | input, err = cmd.StdinPipe() 111 | if err != nil { 112 | return nil, nil, fmt.Errorf("stdin pipe err : %v", err) 113 | } 114 | output, err = cmd.StdoutPipe() 115 | if err != nil { 116 | input.Close() 117 | return nil, nil, fmt.Errorf("stdout pipe err : %v", err) 118 | } 119 | return input, output, err 120 | } 121 | 122 | func bashCmd(cmdStr string) *exec.Cmd { 123 | cmdLogger.Debugf("exec `%v`", cmdStr) 124 | return exec.Command("bash", "-c", cmdStr) 125 | } 126 | -------------------------------------------------------------------------------- /pkg/util/endian.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package util 17 | 18 | import ( 19 | "encoding/binary" 20 | "unsafe" 21 | ) 22 | 23 | var ( 24 | NativeEndian binary.ByteOrder 25 | ) 26 | 27 | func init() { 28 | buf := [2]byte{} 29 | *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) 30 | 31 | switch buf { 32 | case [2]byte{0xCD, 0xAB}: 33 | NativeEndian = binary.LittleEndian 34 | case [2]byte{0xAB, 0xCD}: 35 | NativeEndian = binary.BigEndian 36 | default: 37 | panic("Could not determine native endianness.") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/util/kernel_version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package util 17 | 18 | import ( 19 | "os" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | type KernelVersion struct { 25 | Major int 26 | Minor int 27 | Patch int 28 | } 29 | 30 | func GetKernelVersion() *KernelVersion { 31 | 32 | data, err := os.ReadFile("/proc/version") 33 | if err != nil { 34 | return nil 35 | } 36 | 37 | versionString := string(data) 38 | versionFields := strings.Fields(versionString) 39 | if len(versionFields) < 3 { 40 | return nil 41 | } 42 | 43 | version := strings.Split(versionFields[2], ".") 44 | if len(version) < 3 { 45 | return nil 46 | } 47 | 48 | major, err := strconv.Atoi(version[0]) 49 | if err != nil { 50 | return nil 51 | } 52 | 53 | minor, err := strconv.Atoi(version[1]) 54 | if err != nil { 55 | return nil 56 | } 57 | 58 | patch, err := strconv.Atoi(version[2]) 59 | if err != nil { 60 | return nil 61 | } 62 | 63 | ver := &KernelVersion{ 64 | Major: major, 65 | Minor: minor, 66 | Patch: patch, 67 | } 68 | 69 | return ver 70 | } 71 | -------------------------------------------------------------------------------- /pkg/util/log.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 ByteDance and/or its affiliates. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package util 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | log "github.com/sirupsen/logrus" 23 | ) 24 | 25 | var ( 26 | Logger = NewLogger("generic") 27 | ) 28 | 29 | func NewLogger(mod string) *log.Entry { 30 | return log.WithField("mod", mod) 31 | } 32 | 33 | func SetLogLevel(level string) { 34 | switch level { 35 | case "trace": 36 | log.SetLevel(log.TraceLevel) 37 | case "debug": 38 | log.SetLevel(log.DebugLevel) 39 | case "info": 40 | log.SetLevel(log.InfoLevel) 41 | case "warn": 42 | log.SetLevel(log.WarnLevel) 43 | case "error": 44 | log.SetLevel(log.ErrorLevel) 45 | case "fatal": 46 | log.SetLevel(log.FatalLevel) 47 | case "panic": 48 | log.SetLevel(log.PanicLevel) 49 | } 50 | } 51 | 52 | func Fatalf(format string, a ...interface{}) { 53 | fmt.Printf(format+"\n", a...) 54 | os.Exit(1) 55 | } 56 | 57 | func Fatal(a ...interface{}) { 58 | fmt.Println(a...) 59 | os.Exit(1) 60 | } 61 | --------------------------------------------------------------------------------