├── .gitignore
├── COPYING
├── Dockerfile
├── Makefile.in
├── README.md
├── autogen.sh
├── configure.ac
├── container_info.cpp
├── container_info.h
├── ebpf_flow.cpp
├── ebpf_flow.h
├── ebpf_types.h
├── ebpflow_code.ebpf
├── ebpflow_header.ebpf
├── ebpflowexport.cpp
├── ebpflowexport.go
├── examples
└── c++
│ ├── README.md
│ └── usage_libebpfflow.cpp
├── go
└── ebpf_flow.go
├── pid2veth
├── Makefile
├── README.md
└── pid2veth.c
├── utils
├── README.md
├── docker_show_veth.sh
└── kubectl_show_veth.sh
└── wireshark
├── Makefile
├── README.md
├── ebpf.lua
├── ebpfdump.c
├── pcapio.c
└── pcapio.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.a
3 | ebpflow.ebpf.enc
4 | ebpftest
5 | toolebpflow
6 | autom4te.cache/
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04
2 |
3 | RUN echo "deb [trusted=yes] http://repo.iovisor.org/apt/bionic bionic-nightly main" | \
4 | tee /etc/apt/sources.list.d/iovisor.list && \
5 | apt-get update -y && \
6 | DEBIAN_FRONTEND=noninteractive apt-get install -y bcc-tools
7 |
8 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libcurl4-openssl-dev libjson-c-dev libzmq3-dev libbpfcc-dev
9 |
10 | COPY ebpflowexport /usr/share/
11 |
12 | ENTRYPOINT ["/usr/share/ebpflowexport"]
13 |
14 |
--------------------------------------------------------------------------------
/Makefile.in:
--------------------------------------------------------------------------------
1 | #
2 | # (C) 2018-22 - ntop.org
3 | #
4 |
5 | ###############################################################
6 |
7 | HAS_JSON=$(shell pkg-config --exists json-c; echo $$?)
8 | ifeq ($(HAS_JSON), 0)
9 | JSON_INC = $(shell pkg-config --cflags json-c) -DHAVE_JSONC
10 | JSON_LIB = $(shell pkg-config --libs json-c)
11 | endif
12 |
13 | ###############################################################
14 |
15 | HAS_LIBCURL=$(shell pkg-config --exists libcurl; echo $$?)
16 | ifeq ($(HAS_LIBCURL), 0)
17 | LIBCURL_INC = $(shell pkg-config --cflags libcurl) -DHAVE_LIBCURL
18 | LIBCURL_LIB = $(shell pkg-config --libs libcurl)
19 | endif
20 |
21 | ###############################################################
22 |
23 | CFLAGS=-std=c++11 -g -Wall $(JSON_INC) $(LIBCURL_INC) -fPIC
24 | LIBS=-lbcc -lzmq $(JSON_LIB) $(LIBCURL_LIB)
25 | GOROOT=
26 |
27 | all: ebpflowexport
28 |
29 | container_info.o: container_info.cpp container_info.h
30 | g++ -c $(CFLAGS) container_info.cpp -o container_info.o
31 |
32 | libebpfflow.a: ebpf_flow.cpp ebpf_flow.h container_info.o ebpflow.ebpf.enc
33 | g++ -c $(CFLAGS) ebpf_flow.cpp -o ebpf_flow.o
34 | ar rvs $@ ebpf_flow.o container_info.o
35 |
36 | ebpflowexport: ebpflowexport.cpp libebpfflow.a Makefile
37 | g++ $(CFLAGS) ebpflowexport.cpp -o $@ libebpfflow.a $(LIBS)
38 |
39 | ebpflow.ebpf.enc: ebpflow_header.ebpf ebpf_types.h ebpflow_code.ebpf Makefile
40 | echo -n "const char * ebpf_code = R\"(" > ebpflow.ebpf.enc
41 | cat ebpflow_header.ebpf ebpf_types.h ebpflow_code.ebpf | base64 -w 0 >> ebpflow.ebpf.enc
42 | echo ")\";" >> ebpflow.ebpf.enc
43 |
44 | go_ebpflowexport: ebpflowexport.go Makefile libebpfflow.a
45 | go build -o go_ebpflowexport ebpflowexport.go
46 |
47 | clean:
48 | /bin/rm -f *~ container_info.a libebpfflow.a *.o ebpflow.ebpf.enc
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # libebpfflow
2 | Traffic visibility library based on eBPF
3 |
4 | ### Introduction
5 | libebpfflow is a traffic visibility library based on eBPF able to compute network flows. It can be used to:
6 | * enable network visibility
7 | * create a packet-less network probe
8 | * inspect host and container communications for different container runtimes
9 |
10 | ### Main features
11 | * Ability to inspect TCP and UDP traffic
12 | * Container visibility
13 | * TCP latency computation
14 | * Process and user visibility
15 |
16 | ### Supported Languages
17 | * Golang
18 | * C/C++
19 |
20 | ### Requirements
21 | You need a modern eBPF-enabled Linux distribution.
22 |
23 | On Ubuntu 16.04/18.04/20.04 Server LTS you can install the prerequisites (we assume that the compiler is already installed) as follows:
24 | ```sh
25 | $ sudo apt-get install build-essential autoconf automake autogen libjson-c-dev pkg-config libzmq3-dev libcurl4-openssl-dev libbpfcc-dev
26 | ```
27 |
28 | ### Build
29 | Generate makefile
30 | ```sh
31 | $ ./autogen.sh
32 | ```
33 |
34 | Now build averything
35 | ```sh
36 | $ make
37 | ```
38 | Go testing tool
39 | ```sh
40 | make go_ebpflowexport
41 | ```
42 |
43 | ### Testing
44 | The library comes with two different tools: *ebpflowexport* and *go\_ebpflowexport*. In the _Build_ section is reported how to build the tools. Although both tools were developed to show potential library usage and to provide guidance on how to use the library, *ebpflowexport* displays all the information provided by *libebpfflow* and provides some options for filtering flow events while *go\_ebpflowexport* displays only basic information concerning events.
45 | ```sh
46 | $ sudo ./ebpflowexport -h
47 | ebpflowexport: Traffic visibility tool based on libebpfflow. By default all events will be shown
48 | Usage: ebpflow [ OPTIONS ]
49 | -h, --help display this message
50 | -t, --tcp TCP events
51 | -u, --udp UDP events
52 | -i, --in incoming events (i.e. TCP accept and UDP receive)
53 | -o, --on outgoing events (i.e. TCP connect and UDP send)
54 | -r, --retr retransmissions events
55 | -c, --tcpclose TCP close events
56 | -d, --docker gather additional information concerning containers (default: enabled)
57 | -v, --verbose vebose formatting (default: every event is shown)
58 | Note: please run as root
59 | ```
60 | What follows is a demostration of the execution of *ebpflowexport* in a system where both minikube with containerd as runtime and docker containers are running at the same time.
61 | ```sh
62 | $ sudo ./ebpflowexport -tio
63 | Welcome to ebpflowexport v.1.0.190407
64 | (C) 2018-19 ntop.org
65 | Initializing eBPF [Legacy API]...
66 | eBPF initializated successfully
67 | 1554803923.684786 [lo][Sent][IPv4/TCP][pid/tid: 1446/496 [/usr/bin/kubelet], uid/gid: 0/0][father pid/tid: 1/0 [/lib/systemd/systemd], uid/gid: 0/0][addr: 127.0.0.1:53790 <-> 127.0.0.1:10252][latency: 0.10 msec]
68 | 1554803923.685139 [lo][Rcvd][IPv4/TCP][pid/tid: 2554/2329 [/usr/local/bin/kube-controller-manager], uid/gid: 0/0][father pid/tid: 2295/0 [/usr/local/bin/containerd-shim], uid/gid: 0/0][addr: 127.0.0.1:53790 <-> 127.0.0.1:10252][containerID: 275d71585e03][runtime: containerd][kube_pod: kube-controller-manager-minikube][kube_ns: kube-system][latency: 0.00 msec]
69 | 1554803924.781354 [eth0][Sent][IPv4/TCP][pid/tid: 30197/30197 [/usr/bin/curl], uid/gid: 0/0][father pid/tid: 26219/0 [/bin/bash], uid/gid: 0/0][addr: 172.17.0.2:54348 <-> 216.58.205.46:80][containerID: cbd2540ec5be][runtime: docker][docker_name: sleepy_haibt][latency: 0.22 msec]
70 | 1554803929.257494 [enp0s3][Sent][IPv4/TCP][pid/tid: 30221/30221 [/usr/lib/apt/methods/http], uid/gid: 104/65534][father pid/tid: 30216/0 [/usr/bin/apt], uid/gid: 0/0][addr: 10.0.2.15:37140 <-> 91.189.88.162:80][latency: 0.17 msec]
71 | ```
72 | A basic example of usage in c++ can be found in the directory */examples* whereas for the Go language the example provided is the one in */go/ebpf_flow.go*. More details on how to use the library you can be found in the [ntopng](https://github.com/ntop/ntopng) code or by inspecting the code of the tool ebpflowexport application.
73 |
74 | ### Export eBPF Information to ntopng
75 | Supposing to start both ebpflowexport and ntopng on the same host do
76 |
77 | - ntopng -i tcp://127.0.0.1:1234
78 | - ebpflowexport -z tcp://127.0.0.1:1234
79 |
80 |
81 | ### Start as a Docker container
82 | To use ebpflowexport as a Docker container first you have to build the tool. Once the tool has been built, build the docker image from the project root:
83 | ```sh
84 | $ docker build -t ebpflowexport .
85 | ```
86 | The container can then be run
87 | ```sh
88 | $ docker run -it --rm --privileged \
89 | -v /lib/modules:/lib/modules:ro \
90 | -v /usr/src:/usr/src:ro \
91 | -v /etc/localtime:/etc/localtime:ro \
92 | -v /sys/kernel/debug:/sys/kernel/debug \
93 | -v /var/run/docker.sock:/var/run/docker.sock \
94 | -v /snap/bin/microk8s.ctr:/snap/bin/microk8s.ctr \
95 | ebpflowexport
96 | ```
97 |
98 | ### Open Issues
99 | While the library is already usable in production, we plan to add some additional features including:
100 | * Implement periodic flow stats exports including bytes/packets/retransmissions
101 |
102 |
--------------------------------------------------------------------------------
/autogen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rm -f config.h config.h.in *~ #*
4 |
5 | echo "Wait please..."
6 | autoreconf -if
7 | echo ""
8 | echo "Now running ./configure"
9 | ./configure
10 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | odnl> Do not add anything above
2 | AC_INIT([libebpfflow],[1.0.1])
3 | dnl> Do not add anything above
4 |
5 | AC_PROG_CPP
6 | AC_PROG_CXX
7 |
8 | # Use C++ language for tests
9 | AC_LANG(C++)
10 |
11 | # libbpfcc-dev
12 |
13 | # AC_CHECK_LIB([bcc], [printf])
14 | #if test "x$ac_cv_lib_bcc_printf" != x""yes; then
15 | # echo "Please install the bcc-dev(el) package and try again"
16 | # exit
17 | #fi
18 |
19 | # Libs required for compilation test below
20 | #LIBS="${LIBS} -lbcc"
21 |
22 | AC_MSG_CHECKING([eBPF library new API version])
23 | AC_TRY_COMPILE([
24 | #include
25 | ], [ ebpf::BPF *bpf;
26 | bpf->attach_kprobe((const char*)"", NULL, 0, BPF_PROBE_ENTRY);
27 | ], [ AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED(HAVE_NEW_EBPF, 1, [new BPF API]) ], [AC_MSG_RESULT(no)])
28 |
29 | DATE=`date +"%y%m%d"`
30 | VERSION="1.0.${DATE}"
31 |
32 | AC_DEFINE_UNQUOTED(EBPF_FLOW_VERSION, "${VERSION}", "Library version")
33 |
34 | AC_CHECK_LIB([json-c], [json_object_new_object])
35 | if test "x$ac_cv_lib_json_c_json_object_new_object" != "xyes"; then
36 | echo "Please install the json-c package and try again"
37 | exit
38 | fi
39 |
40 | AC_CHECK_LIB([curl], [curl_easy_init])
41 | if test "x$ac_cv_lib_curl_curl_easy_init" != "xyes"; then
42 | echo "Please install the libcurl (libcurl4-openssl-dev) package and try again"
43 | exit
44 | fi
45 |
46 | AC_CHECK_LIB([zmq], [zmq_socket_monitor])
47 | if test "x$ac_cv_lib_zmq_zmq_socket_monitor" != "xyes"; then :
48 | echo "Please install the ZMQ package and try again"
49 | exit
50 | fi
51 |
52 | if ! which pkg-config >/dev/null ; then
53 | AC_MSG_ERROR([Missing pkg-config: please check README.md])
54 | fi
55 |
56 |
57 | AC_CHECK_LIB([json-c], json_object_new_double_s, AC_DEFINE_UNQUOTED(HAVE_DOUBLES, 1, [json-c has json_object_new_double_s]))
58 |
59 | AC_SUBST(EBPF_LIBRARY_VERSION)
60 | AC_CONFIG_HEADERS(config.h)
61 | AC_CONFIG_FILES(Makefile)
62 |
63 | AC_OUTPUT
64 |
--------------------------------------------------------------------------------
/container_info.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #include "config.h"
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include
35 | #include
36 |
37 | #include "container_info.h"
38 |
39 | #define DOCKERD_SOCK_PATH "/var/run/docker.sock"
40 | #define MICROK8S_CTR_PATH "/snap/bin/microk8s.ctr"
41 |
42 |
43 | // Used to store libcurl partial results
44 | struct response_buffer {
45 | char *memory;
46 | size_t size;
47 | };
48 |
49 | // #define DEBUG
50 |
51 | /* ************************************************* */
52 | // ===== ===== INITIALIZER AND DESTROYER ===== ===== //
53 | /* ************************************************* */
54 | ContainerInfo::ContainerInfo() {
55 | struct stat s;
56 |
57 | gQueryCache.clear();
58 |
59 | if(stat(MICROK8S_CTR_PATH, &s) == 0)
60 | strcpy(ctr_path, MICROK8S_CTR_PATH);
61 | else
62 | strcpy(ctr_path, "ctr");
63 |
64 | update_namespaces();
65 | }
66 |
67 | ContainerInfo::~ContainerInfo() {
68 | gQueryCache.clear();
69 | namespaces.clear();
70 | }
71 |
72 | /* ********************************************** */
73 | // ===== ===== QUERY TO DOCKER DAEMON ===== ===== //
74 | /* ********************************************** */
75 | static size_t WriteMemoryCallback (void *contents, size_t size, size_t nmemb, void *userp) {
76 | size_t realsize = size * nmemb;
77 | struct response_buffer *mem = (struct response_buffer *) userp;
78 |
79 | char *ptr = (char*) realloc(mem->memory, mem->size + realsize + 1);
80 | if(ptr == NULL) {
81 | /* out of memory! */
82 | #ifdef DEBUG
83 | printf("not enough memory (realloc returned NULL)\n");
84 | #endif
85 | return(0);
86 | }
87 |
88 | mem->memory = ptr;
89 | memcpy(&(mem->memory[mem->size]), contents, realsize);
90 | mem->size += realsize;
91 | mem->memory[mem->size] = 0;
92 |
93 | return realsize;
94 | }
95 |
96 | /* **************************************************** */
97 |
98 | /* parse_response - fill a container_info data structure with the information returned by a query to
99 | * the docker daemon
100 | * return(0) if no error occurred -1 otherwise
101 | */
102 | int ContainerInfo::parse_response(const char* buff, ssize_t buffsize, struct container_info *entry) {
103 | int res_found = 0; // 1 if some info has been found
104 | struct json_object *jobj=NULL, *jlabel=NULL;
105 | struct json_object *jdockername, *jconfig, *jpodname, *jcname, *jkubens;
106 | struct json_tokener *jtok;
107 |
108 | #ifdef DEBUG
109 | printf("[%s:%u] %s(%s)\n", __FILE__, __LINE__, __FUNCTION__, buff);
110 | #endif
111 |
112 | //memset(entry, 0, sizeof(struct container_info));
113 |
114 | // Parsing req to json
115 | jtok = json_tokener_new();
116 | jobj = json_tokener_parse_ex(jtok, buff, buffsize);
117 | // We're not using it anymore, let's cleanup
118 | json_tokener_free(jtok);
119 |
120 | if(jobj == NULL)
121 | goto fail;
122 |
123 | // Docker name
124 | if(json_object_object_get_ex(jobj, "Name", &jdockername)) {
125 | res_found = 1;
126 | entry->docker.name = std::string(json_object_get_string(jdockername)+1);
127 | }
128 |
129 | /*
130 | "Labels": {
131 | "io.cri-containerd.kind": "container",
132 | "io.kubernetes.container.name": "dnsmasq",
133 | "io.kubernetes.pod.name": "kube-dns-6bfbdd666c-5jbmx",
134 | "io.kubernetes.pod.namespace": "kube-system",
135 | "io.kubernetes.pod.uid": "5528e13d-5df8-11e9-a377-001c427c953a"
136 | },
137 | */
138 |
139 | // Container labels
140 | if(json_object_object_get_ex(jobj, "Config", &jconfig)) /* json from docker api */
141 | json_object_object_get_ex(jconfig, "Labels", &jlabel);
142 | else /* json from containerd api */
143 | json_object_object_get_ex(jobj, "Labels", &jlabel);
144 |
145 | // Kubernetes info (when available)
146 | if(jlabel != NULL) {
147 | // Extracting kube info
148 | if(json_object_object_get_ex(jlabel, "io.kubernetes.pod.name", &jpodname)) {
149 | res_found = 1;
150 | entry->kube.pod = std::string(json_object_get_string(jpodname));
151 | }
152 |
153 | if(json_object_object_get_ex(jlabel, "io.kubernetes.container.name", &jcname)) {
154 | res_found = 1;
155 | entry->kube.name = std::string(json_object_get_string(jcname));
156 | }
157 |
158 | if(json_object_object_get_ex(jlabel, "io.kubernetes.pod.namespace", &jkubens)) {
159 | res_found = 1;
160 | entry->kube.ns = std::string(json_object_get_string(jkubens));
161 | }
162 | }
163 |
164 | if(!res_found)
165 | goto fail;
166 |
167 | json_object_put(jobj);
168 | return(0);
169 |
170 | fail:
171 | if(jobj)
172 | json_object_put(jobj);
173 |
174 | // memset(entry, 0, sizeof(struct container_info));
175 | return(-1);
176 | }
177 |
178 | /* **************************************************** */
179 |
180 | int ContainerInfo::dockerd_update_query_cache(char* t_containerid,
181 | struct container_info **t_dqr) {
182 | int rc = 0;
183 | CURL *curl_handle;
184 | CURLcode res;
185 | char url[101];
186 | struct response_buffer chunk;
187 | std::string cgroupid(t_containerid);
188 | struct stat s;
189 | struct cache_entry ce;
190 |
191 | #ifdef DEBUG
192 | printf("[%s:%u] %s()\n", __FILE__, __LINE__, __FUNCTION__);
193 | #endif
194 |
195 | (*t_dqr) = NULL;
196 |
197 | if(stat(DOCKERD_SOCK_PATH, &s) == -1)
198 | return(-1); /* Docker not found */
199 |
200 | // Crafting query
201 | snprintf(url, sizeof(url), "http://localhost/containers/%s/json", t_containerid);
202 |
203 | // Performing query ----- //
204 | // Initializing memory buffer
205 | chunk.memory = (char*) malloc(1);
206 | chunk.size = 0;
207 | // Preparing libcurl
208 | curl_handle = curl_easy_init();
209 | // URL
210 | curl_easy_setopt(curl_handle, CURLOPT_URL, url);
211 | // Callback && Callback arguments
212 | curl_easy_setopt(curl_handle, CURLOPT_UNIX_SOCKET_PATH, DOCKERD_SOCK_PATH);
213 | curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
214 | curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
215 |
216 | res = curl_easy_perform(curl_handle);
217 |
218 | // Checking for errors
219 | if(res == CURLE_OK) {
220 | rc = parse_response(chunk.memory, chunk.size, &ce.content);
221 | } else {
222 | #ifdef DEBUG
223 | printf("curl_easy_perform(%s) failed: %s\n", url, curl_easy_strerror(res));
224 | #endif
225 | }
226 |
227 | // Adding entry to table and pointing argument to entry
228 | ce.num_uses = 0;
229 | gQueryCache[t_containerid] = ce;
230 |
231 | *t_dqr = &(gQueryCache[t_containerid].content);
232 |
233 | // Cleaning up
234 | curl_easy_cleanup(curl_handle);
235 | free(chunk.memory);
236 | curl_global_cleanup();
237 |
238 | return(rc);
239 | }
240 |
241 | /* **************************************************** */
242 |
243 | int ContainerInfo::containerd_update_query_cache (char* t_containerid,
244 | struct container_info **t_dqr) {
245 | FILE *fp;
246 | char *ns;
247 | char comm[256];
248 | char buff[LINE_MAX];
249 | std::string cgroupid(t_containerid);
250 | std::set::iterator s;
251 | std::string result;
252 | struct cache_entry ce;
253 |
254 | #ifdef DEBUG
255 | printf("[%s:%u] %s()\n", __FILE__, __LINE__, __FUNCTION__);
256 | #endif
257 |
258 | (*t_dqr) = NULL;
259 |
260 | try {
261 | regex_match(cgroupid, std::regex("^([0-9a-zA-Z\\.\\_\\-])*$"));
262 | } catch (std::regex_error& e) {
263 | #ifdef DEBUG
264 | printf("[%s:%u] %s()\n", __FILE__, __LINE__, __FUNCTION__);
265 | #endif
266 | return(-1);
267 | }
268 |
269 | for(s = namespaces.begin(); s != namespaces.end(); ++s) {
270 | ns = (char*) (*s).c_str();
271 |
272 | /* ***** ***** SANITIZE THE INPUT ***** ***** */
273 | // The container id and namespace MUST be sanitized
274 | // otherwise there's a risk of command injection
275 | // cgroupid has been already sanitized
276 | /* ***** ***** ****************** ***** ***** */
277 | try {
278 | regex_match(*s, std::regex("^([0-9a-zA-Z\\.\\_\\-])*$"));
279 | } catch (std::regex_error& e) {
280 | #ifdef DEBUG
281 | printf("[%s:%u] %s()\n", __FILE__, __LINE__, __FUNCTION__);
282 | #endif
283 | return(-1);
284 | }
285 |
286 | snprintf(comm, sizeof(comm), "%s --namespace=%s c info %s 2>/dev/null",
287 | ctr_path, ns, t_containerid);
288 |
289 | #ifdef DEBUG
290 | printf("[%s:%u] %s\n", __FILE__, __LINE__, comm);
291 | #endif
292 |
293 | // piping to the command
294 | fp = popen(comm, "r");
295 | if(fp == NULL) {
296 | #ifdef DEBUG
297 | printf("containerd interaction failed \n");
298 | #endif
299 | return -1;
300 | }
301 |
302 | while(fgets(buff, sizeof(buff), fp)) {
303 | result += buff;
304 | }
305 |
306 | pclose(fp);
307 |
308 | // handling json
309 | if(parse_response(result.c_str(), result.size(), &ce.content) == 0)
310 | break;
311 | }
312 |
313 | ce.num_uses = 0;
314 | gQueryCache[t_containerid] = ce;
315 | *t_dqr = &(gQueryCache[t_containerid].content);
316 |
317 | return(0);
318 | }
319 |
320 | /* **************************************************** */
321 |
322 | int ContainerInfo::update_namespaces() {
323 | FILE *fp;
324 | int i = 0;
325 | char ns[LINE_MAX];
326 | char buf[90];
327 |
328 | #ifdef DEBUG
329 | printf("[%s:%u] %s()\n", __FILE__, __LINE__, __FUNCTION__);
330 | #endif
331 |
332 | namespaces.clear();
333 |
334 | snprintf(buf, sizeof(buf), "%s namespace ls 2>/dev/null", ctr_path);
335 |
336 | #ifdef DEBUG
337 | printf("[%s:%u] %s\n", __FILE__, __LINE__, buf);
338 | #endif
339 |
340 | if((fp = popen(buf, "r")) == NULL)
341 | return(-1);
342 |
343 | while(fgets(ns, sizeof(ns), fp) != NULL) {
344 | char *space;
345 |
346 | if(i == 0) /* Fs line is the title */ {
347 | i++;
348 | continue;
349 | }
350 |
351 | if((space = strchr(ns, ' ')) != NULL)
352 | space[0] = '\0';
353 |
354 | #ifdef DEBUG
355 | printf("[%s:%u] Found namespace %s\n", __FILE__, __LINE__, space);
356 | #endif
357 |
358 | namespaces.insert(ns);
359 | }
360 |
361 | pclose(fp);
362 | return(0);
363 | }
364 |
365 | /* **************************************************** */
366 |
367 | /* *********************************** */
368 | // ===== ===== CACHE CHECK ===== ===== //
369 | /* *********************************** */
370 | /*
371 | * container_id_find_in_cache - check if containers info have been cached
372 | * returns -1 if the query has not been cached 0 if some info are available
373 | * 1 for dummy keys
374 | */
375 | int ContainerInfo::container_id_find_in_cache(char* t_containerid,
376 | struct container_info **t_dqs) {
377 | std::string cgroupid(t_containerid);
378 | std::unordered_map::iterator res = gQueryCache.find(cgroupid);
379 |
380 | if(res != gQueryCache.end()) {
381 | *t_dqs = &(res->second.content);
382 | res->second.num_uses++;
383 |
384 | return(0);
385 | } else
386 | return(-1);
387 | }
388 |
389 | /* **************************************************** */
390 |
391 | void ContainerInfo::clean_cache() {
392 | std::unordered_map::iterator it;
393 |
394 | for(it = gQueryCache.begin(); it != gQueryCache.end();) {
395 | struct cache_entry ce = it->second;
396 |
397 | if(ce.num_uses == 0)
398 | it = gQueryCache.erase(it);
399 | else {
400 | ce.num_uses = 0;
401 | it++;
402 | }
403 | }
404 | }
405 |
406 | /* **************************************************** */
407 |
408 | int ContainerInfo::get_container_info(char* t_containerId, struct container_info **t_dqr) {
409 | int res;
410 | static time_t last = time(NULL);
411 | time_t now;
412 |
413 | #ifdef DEBUG
414 | printf("[%s:%u] %s(%s)\n", __FILE__, __LINE__, __FUNCTION__, t_containerId);
415 | #endif
416 |
417 | if((t_containerId[0] == '\0') || (strcmp(t_containerId, "/") == 0))
418 | return(-1);
419 |
420 | now = time(NULL);
421 |
422 | if(difftime(now, last) > REFRESH_TIME /* Seconds */ ) {
423 | int rc;
424 | clean_cache();
425 | namespaces.clear();
426 | rc = update_namespaces();
427 | last = now;
428 |
429 | if(rc == -1) return(rc);
430 | }
431 |
432 | res = container_id_find_in_cache(t_containerId, t_dqr);
433 | if(res != -1) /* Item is cached */
434 | return(res);
435 |
436 | // Dockerd
437 | res = dockerd_update_query_cache(t_containerId, t_dqr);
438 |
439 | // Containerd interaction
440 | if(res != 0)
441 | res = containerd_update_query_cache(t_containerId, t_dqr);
442 |
443 | return(res);
444 | }
445 |
--------------------------------------------------------------------------------
/container_info.h:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #ifndef __CONTAINER_INFO_HPP__
22 | #define __CONTAINER_INFO_HPP__ 1
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | struct container_info {
32 | struct {
33 | std::string name;
34 | } docker;
35 |
36 | struct {
37 | std::string name, pod, ns;
38 | } kube;
39 | };
40 |
41 | // Cache cleaning and namespace update interval in seconds
42 | #define REFRESH_TIME 30
43 |
44 | struct cache_entry {
45 | int num_uses;
46 | struct container_info content;
47 | };
48 |
49 | class ContainerInfo {
50 | private:
51 | // Cache where to store the queries results
52 | std::unordered_map gQueryCache;
53 |
54 | // Namespace cache
55 | std::set namespaces;
56 |
57 | char ctr_path[64];
58 |
59 | /*
60 | * Gather namespaces from ctr or docker-containerd-ctr
61 | */
62 | int update_namespaces();
63 |
64 | /*
65 | * Removes from the cache all those entries that have
66 | * been accessed less than MIN_VISITS times
67 | */
68 | void clean_cache();
69 |
70 | /*
71 | * Create the entry if it does not exist, otherwise updates the content
72 | */
73 | void update_cache_entry(char* t_containerid, struct cache_entry *t_dqr);
74 |
75 | /* parse_response - fill a container_info data structure with the information returned by a query to
76 | * the docker daemon
77 | * buff: if NULL a dummy entry will be created (not added to the cache)
78 | * return 0 if no error occurred -1 otherwise
79 | */
80 | int parse_response(const char* buff, ssize_t buffsize, struct container_info *entry);
81 |
82 | /*
83 | * update_query_cache - query to docker api from docker socket (/var/run/docker.sock) and caches the result.
84 | * @t_containerid: full length cgroup id
85 | * @t_dqr: filled with the information gathered if no error occurred
86 | * returns 0 if no error occurres and container info has been found, otherwise -1
87 | * note: the same operation can be done using
88 | * `$ curl --unix-socket /var/run/docker.sock http://localhost/containers//json`
89 | */
90 | int dockerd_update_query_cache(char* t_containerid, struct container_info **t_dqr);
91 |
92 | /*
93 | * update_query_cache - exec ctr (i.e. containerd cli) to retrieve information
94 | * concerning the container. If ctr is not available it will be tried: docker-containerd-ctr
95 | * @t_containerid: full length cgroup id
96 | * @t_ns: target namespace
97 | * @t_dqr: filled with the information gathered if no error occurred
98 | * returns 0 if no error occurres and container info has been found, otherwise -1
99 | * note: the same operation can be done using
100 | * `$ sudo ctr --namespace= containers info `
101 | */
102 | int containerd_update_query_cache (char* t_containerid, struct container_info **t_dqr);
103 |
104 | /* *********************************** */
105 | // ===== ===== CACHE CHECK ===== ===== //
106 | /* *********************************** */
107 | /*
108 | * container_id_cached - check if containers info have been cached
109 | * and if some info are available stores them in *t_dqs
110 | * @t_containerid: docker container ID
111 | * @t_dqs: will point to the cache entry if no error occurs (returns != -1)
112 | * returns 0 if the cache contains information concerning the container
113 | * -1 if there no entry corresponding to the ID provided. 1 if
114 | * there is an entry associated with the ID but there isn't
115 | * information available
116 | */
117 | int container_id_find_in_cache(char *t_containerid, struct container_info **t_dqs);
118 |
119 | public:
120 | ContainerInfo();
121 | ~ContainerInfo();
122 |
123 | /*
124 | * Retrieves container information
125 | * @t_containerId: container ID
126 | * @t_dqs: will point to the container informations if no error occurs (returns != -1)
127 | * returns 0 if some info has been found, -1 otherwise
128 | */
129 | int get_container_info(char* t_containerId, container_info **t_dqr);
130 | };
131 |
132 | #endif /* __CONTAINER_INFO_HPP__ */
133 |
--------------------------------------------------------------------------------
/ebpf_flow.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #include "config.h"
22 |
23 | #include "ebpf_flow.h"
24 | #include "container_info.h"
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | #include "ebpflow.ebpf.enc"
38 |
39 | static ContainerInfo cinfo;
40 |
41 | /* ******************************************* */
42 |
43 | std::string b64decode(const void* data, const size_t len) {
44 | unsigned char* p = (unsigned char*)data;
45 | int pad = len > 0 && (len % 4 || p[len - 1] == '=');
46 | const size_t L = ((len + 3) / 4 - pad) * 4;
47 | const size_t strsize = L / 4 * 3 + pad;
48 | if(strsize <= 0)
49 | return std::string();
50 |
51 | std::string str(strsize, '\0');
52 | const int B64index[256] = {
53 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
56 | 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
57 | 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
58 | 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
59 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
60 |
61 | for(size_t i = 0, j = 0; i < L; i += 4) {
62 | int n = B64index[p[i]] << 18 |
63 | B64index[p[i + 1]] << 12 |
64 | B64index[p[i + 2]] << 6 |
65 | B64index[p[i + 3]];
66 | str[j++] = n >> 16;
67 | str[j++] = n >> 8 & 0xFF;
68 | str[j++] = n & 0xFF;
69 | }
70 |
71 | if(pad) {
72 | int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
73 | str[str.size() - 1] = n >> 16;
74 |
75 | if(len > L + 2 && p[L + 2] != '=') {
76 | n |= B64index[p[L + 2]] << 6;
77 | str.push_back(n >> 8 & 0xFF);
78 | }
79 | }
80 |
81 | return str;
82 | }
83 |
84 | /* ******************************************* */
85 | /* ******************************************* */
86 |
87 | static int attachEBPFTracepoint(ebpf::BPF *bpf,
88 | const char *tracepoint, const char *probe_func) {
89 | ebpf::StatusTuple rc = bpf->attach_tracepoint(tracepoint, probe_func);
90 |
91 | #ifdef DEBUG
92 | if(rc.code() != 0)
93 | printf("ERROR: %s/%s: %d/%s\n", tracepoint, probe_func, rc.code(), rc.msg().c_str());
94 | #endif
95 |
96 | return(rc.code());
97 | }
98 |
99 | /* ******************************************* */
100 |
101 | static int attachEBPFKernelProbe(ebpf::BPF *bpf, const char *queue_name,
102 | const char *entry_point,
103 | bpf_probe_attach_type attach_type) {
104 | int rc = bpf->attach_kprobe(queue_name, entry_point,
105 | #if defined HAVE_NEW_EBPF || LINUX_VERSION_CODE <= KERNEL_VERSION(3,15,0)
106 | 0,
107 | #endif
108 | attach_type).code();
109 |
110 | #ifdef DEBUG
111 | if(rc != 0)
112 | printf("ERROR: %s/%s: %d\n", queue_name, entry_point, rc);
113 | #endif
114 |
115 | return(rc);
116 | }
117 |
118 | /* ******************************************* */
119 |
120 | extern "C" {
121 | void* init_ebpf_flow(void *priv_ptr, eBPFHandler ebpfHandler,
122 | ebpfRetCode *rc, u_int16_t flags) {
123 | ebpf::BPF *bpf = NULL;
124 | std::string code = b64decode(ebpf_code, strlen(ebpf_code));
125 | ebpf::StatusTuple open_res(0);
126 |
127 | // Default value is 0
128 | flags = (flags == 0) ? 0xFFFF : flags;
129 |
130 | if(code == "") {
131 | *rc = ebpf_unable_to_load_kernel_probe;
132 | goto init_failed;
133 | }
134 |
135 | try {
136 | bpf = new ebpf::BPF;
137 | } catch(std::bad_alloc& ba) {
138 | *rc = ebpf_out_of_memory;
139 | goto init_failed;
140 | }
141 |
142 | if(bpf->init(code).code() != 0) {
143 | *rc = ebpf_initialization_failed;
144 | goto init_failed;
145 | }
146 |
147 | // attaching probes ----- //
148 | if((flags & LIBEBPF_TCP) && (flags & LIBEBPF_OUTCOMING)) {
149 | if(attachEBPFKernelProbe(bpf,"tcp_v4_connect",
150 | "trace_connect_entry", BPF_PROBE_ENTRY)
151 | || attachEBPFKernelProbe(bpf, "tcp_v4_connect",
152 | "trace_connect_v4_return", BPF_PROBE_RETURN)
153 | || attachEBPFKernelProbe(bpf, "tcp_v6_connect",
154 | "trace_connect_entry", BPF_PROBE_ENTRY)
155 | || attachEBPFKernelProbe(bpf, "tcp_v6_connect",
156 | "trace_connect_v6_return", BPF_PROBE_RETURN)
157 | ) {
158 | *rc = ebpf_kprobe_attach_error;
159 | goto init_failed;
160 | }
161 | }
162 |
163 | if((flags & LIBEBPF_TCP) && (flags & LIBEBPF_INCOMING)) {
164 | if(attachEBPFKernelProbe(bpf, "inet_csk_accept",
165 | "trace_tcp_accept", BPF_PROBE_RETURN)) {
166 | *rc = ebpf_kprobe_attach_error;
167 | goto init_failed;
168 | }
169 | }
170 |
171 | if((flags & LIBEBPF_UDP) && (flags & LIBEBPF_OUTCOMING)) {
172 | if(attachEBPFTracepoint(bpf, "net:net_dev_queue",
173 | "trace_netif_tx_entry")) {
174 | *rc = ebpf_kprobe_attach_error;
175 | goto init_failed;
176 | }
177 | }
178 |
179 | if((flags & LIBEBPF_UDP) && (flags & LIBEBPF_INCOMING)) {
180 | if(attachEBPFTracepoint(bpf, "net:netif_receive_skb",
181 | "trace_netif_rx_entry")) {
182 | *rc = ebpf_kprobe_attach_error;
183 | goto init_failed;
184 | }
185 | }
186 |
187 | if(flags & LIBEBPF_TCP_CLOSE) {
188 | if(attachEBPFKernelProbe(bpf, "tcp_set_state",
189 | "trace_tcp_set_state", BPF_PROBE_ENTRY)) {
190 | *rc = ebpf_kprobe_attach_error;
191 | goto init_failed;
192 | }
193 | }
194 |
195 | if(flags & LIBEBPF_TCP_RETR) {
196 | if(attachEBPFKernelProbe(bpf, "tcp_retransmit_skb",
197 | "trace_tcp_retransmit_skb", BPF_PROBE_ENTRY)) {
198 | *rc = ebpf_kprobe_attach_error;
199 | goto init_failed;
200 | }
201 | }
202 |
203 | // opening output buffer ----- //
204 | open_res = bpf->open_perf_buffer("ebpf_events", ebpfHandler, NULL, (void*)priv_ptr);
205 | if(open_res.code() != 0) { *rc = ebpf_events_open_error; goto init_failed; }
206 |
207 | *rc = ebpf_no_error;
208 | return((void*)bpf);
209 |
210 | init_failed:
211 | if(bpf) delete bpf;
212 | return(NULL);
213 | };
214 |
215 | /* ******************************************* */
216 |
217 | static int is_kernel_thread(__u32 pid) {
218 | char pathname[64];
219 | struct stat statbuf;
220 |
221 | snprintf(pathname, sizeof(pathname), "/proc/%u", pid);
222 |
223 | /* The process exists... */
224 | if(stat(pathname, &statbuf) == 0) {
225 | snprintf(pathname, sizeof(pathname), "/proc/%u/exe", pid);
226 |
227 | if(stat(pathname, &statbuf) == -1)
228 | return(1); /* It looks like a kernel thread */
229 | }
230 |
231 | return(0);
232 | }
233 |
234 | /* ******************************************* */
235 |
236 | static void check_pid(struct taskInfo *task) {
237 | if((task->pid != 0) && is_kernel_thread(task->pid))
238 | memset(task, 0, sizeof(struct taskInfo));
239 | }
240 |
241 | /* ******************************************* */
242 |
243 | /* Fill cmdline arguments */
244 | static void fill_exe_cmdline(struct taskInfo *task) {
245 | if(task->pid != 0) {
246 | FILE *f;
247 | char what[256];
248 |
249 | sprintf(what, "/proc/%u/cmdline", task->pid);
250 |
251 | if((f = fopen(what, "r")) != NULL) {
252 | char *line, cmdbuf[512] = { '\0' };
253 |
254 | if((line = fgets(cmdbuf, sizeof(cmdbuf), f)) != NULL) {
255 | char *delimiter = strchr(line, '\0');
256 |
257 | if(delimiter[1] != '\0')
258 | task->cmdline = strdup(&delimiter[1]);
259 | }
260 |
261 | fclose(f);
262 | }
263 | }
264 | }
265 |
266 | /* ******************************************* */
267 |
268 | void fill_exe_takinfo(struct taskInfo *task) {
269 | task->cmdline = NULL, task->full_task_path = NULL;
270 |
271 | if(task->pid != 0) {
272 | char what[256], sym[256];
273 | int l;
274 |
275 | /* Fill full path */
276 | snprintf(what, sizeof(what), "/proc/%u/exe", task->pid);
277 | if((l = readlink(what, sym, sizeof(sym))) != -1) {
278 | char *space;
279 |
280 | sym[l] = '\0';
281 |
282 | if((space = strchr(sym, ' ')) != NULL) {
283 | if(space[1] == '(') /* (deleted) */
284 | space[0] = '\0';
285 | }
286 |
287 | task->full_task_path = strdup(sym);
288 |
289 | fill_exe_cmdline(task);
290 | }
291 | }
292 | }
293 |
294 | /* ******************************************* */
295 |
296 | void ebpf_preprocess_event(eBPFevent *event) {
297 | struct container_info *container_info;
298 |
299 | gettimeofday(&event->event_time, NULL);
300 | check_pid(&event->proc), check_pid(&event->father);
301 |
302 | fill_exe_takinfo(&event->proc);
303 | fill_exe_takinfo(&event->father);
304 |
305 | if((event->container_id[0] == '\0')
306 | || ((event->container_id[0] == '/') && (event->container_id[1] == '\0')))
307 | event->container_id[0] = '\0';
308 | else {
309 | event->docker.name = event->kube.name = event->kube.pod = event->kube.ns = NULL;
310 |
311 | // Docker names cgroup as follows: "docker-.scope"
312 | if(strstr(event->container_id, "docker-") != NULL) {
313 | event->container_id[71] = '\0';
314 | memmove(event->container_id, event->container_id+7, strlen(event->container_id)-6);
315 | }
316 |
317 | if(cinfo.get_container_info(event->container_id, &container_info) == 0) {
318 | if(container_info->docker.name[0] != '\0') /* Docker info available */
319 | event->docker.name = strdup(container_info->docker.name.c_str());
320 |
321 | if(container_info->kube.name[0] != '\0') /* Kubernetes info available */ {
322 | event->kube.name = strdup(container_info->kube.name.c_str());
323 | event->kube.pod = strdup(container_info->kube.pod.c_str());
324 | event->kube.ns = strdup(container_info->kube.ns.c_str());
325 | }
326 | }
327 | }
328 | }
329 |
330 | /* ******************************************* */
331 |
332 | void ebpf_free_event(eBPFevent *event) {
333 | if(event->proc.full_task_path != NULL) free(event->proc.full_task_path);
334 | if(event->father.full_task_path != NULL) free(event->father.full_task_path);
335 | if(event->proc.cmdline != NULL) free(event->proc.cmdline);
336 | if(event->father.cmdline != NULL) free(event->father.cmdline);
337 |
338 | if(event->docker.name != NULL) free(event->docker.name);
339 | if(event->kube.name != NULL) free(event->kube.name);
340 | if(event->kube.pod != NULL) free(event->kube.pod);
341 | if(event->kube.ns != NULL) free(event->kube.ns);
342 | }
343 |
344 | /* ******************************************* */
345 |
346 | void term_ebpf_flow(void *ebpfHook) {
347 | ebpf::BPF *bpf = (ebpf::BPF*)ebpfHook;
348 |
349 | if(bpf != NULL)
350 | delete bpf;
351 | }
352 |
353 | /* ******************************************* */
354 |
355 | int ebpf_poll_event(void *ebpfHook, u_int ms_timeout) {
356 | ebpf::BPF *bpf = (ebpf::BPF*)ebpfHook;
357 |
358 | /* NOTE: some library versions do not return a value [TODO] */
359 | /* int rc = */ bpf->poll_perf_buffer("ebpf_events", ms_timeout);
360 |
361 | return(1);
362 | }
363 |
364 | /* ******************************************* */
365 |
366 | const char* ebpf_print_error(ebpfRetCode rc) {
367 | switch(rc) {
368 | case ebpf_no_error:
369 | return("ebpf_no_error");
370 |
371 | case ebpf_initialization_failed:
372 | return("ebpf_initialization_failed");
373 |
374 | case ebpf_unable_to_load_kernel_probe:
375 | return("ebpf_unable_to_load_kernel_probe");
376 |
377 | case ebpf_out_of_memory:
378 | return("ebpf_out_of_memory");
379 |
380 | case ebpf_kprobe_attach_error:
381 | return("ebpf_kprobe_attach_error");
382 |
383 | case ebpf_events_open_error:
384 | return("ebpf_events_open_error");
385 | }
386 |
387 | return("Unknown error");
388 | }
389 |
390 | /* ******************************************* */
391 |
392 | void* ebpf_get_cinfo_handler() { return((void*)&cinfo); }
393 |
394 | /* ******************************************* */
395 |
396 | const char* ebpf_flow_version() {
397 | return(EBPF_FLOW_VERSION);
398 | }
399 |
400 | };
401 |
--------------------------------------------------------------------------------
/ebpf_flow.h:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #ifndef __EBPF_FLOW_H__
22 | #define __EBPF_FLOW_H__ 1
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #ifdef __linux__
29 | #include
30 | #include
31 | #endif
32 | #include
33 |
34 | #define ktime_t u_int64_t
35 | #define u8 u_int8_t
36 | #define u16 u_int16_t
37 | #define u32 u_int32_t
38 | #define u64 u_int64_t
39 |
40 | #include "ebpf_types.h"
41 |
42 | /* ******************************************* */
43 |
44 | typedef enum {
45 | ebpf_no_error = 0,
46 | ebpf_initialization_failed,
47 | ebpf_unable_to_load_kernel_probe,
48 | ebpf_out_of_memory,
49 | ebpf_kprobe_attach_error,
50 | ebpf_events_open_error,
51 | } ebpfRetCode;
52 |
53 | /*
54 | * Supported flags to filter events when initializating libebpfflow
55 | * Combinations of this flags allow selecte events to be to captured
56 | */
57 | typedef enum {
58 | LIBEBPF_TCP = 1 << 0,
59 | LIBEBPF_UDP = 1 << 1,
60 | LIBEBPF_INCOMING = 1 << 2,
61 | LIBEBPF_OUTCOMING = 1 << 3,
62 | LIBEBPF_TCP_CLOSE = 1 << 4,
63 | LIBEBPF_TCP_RETR = 1 << 5,
64 | } libebpflow_flag;
65 |
66 | /* ******************************************* */
67 |
68 | #ifdef __cplusplus
69 | extern "C" {
70 | #endif // __cplusplus
71 |
72 | typedef void (*eBPFHandler)(void* t_bpfctx, void* t_data, int t_datasize);
73 |
74 | /*
75 | * init_ebpf_flow - Initializes the library with a target event handler
76 | * @ebpfHandler: the function used to handle events
77 | * @rc: pointer to the variable in which to store the return code
78 | * @flags: restrict the number of events to generate by
79 | * not tracing certain functions. Use default (i.e. 0 or 0xFFFF) to capture
80 | * all events. Supported events are combinations of libebpflow_flag enum type
81 | * returns a pointer to an ebpf::BPF object upon success NULL otherwise
82 | */
83 | void* init_ebpf_flow(void *priv_ptr, eBPFHandler ebpfHandler,
84 | ebpfRetCode *rc,
85 | u_int16_t flags /* default 0 to capture all events */);
86 |
87 | /*
88 | * term_ebpf_flow - Cleans the resources used by the library
89 | * @ebpfHook: a pointer to an ebpf::BPF, that is the one returned by init_ebpf_flow
90 | */
91 | void term_ebpf_flow(void *ebpfHook);
92 |
93 | /*
94 | * ebpf_poll_event: Pools an event from an ebpf::BPF object
95 | * @ms_timeout: maximum time to wait for an event
96 | * @ebpfHook: reference to the result of init_ebpf_flow invocation
97 | * return 1 if an event has been processed, 0 in case of timeout.
98 | */
99 | int ebpf_poll_event(void *ebpfHook, u_int ms_timeout);
100 |
101 | /*
102 | * Collect further information wrt the one contained in an eBPF event
103 | */
104 | void ebpf_preprocess_event(eBPFevent *event);
105 |
106 | const char* ebpf_print_error(ebpfRetCode rc);
107 |
108 | /*
109 | * Cleans the resources used by an eBPFevent data structure
110 | */
111 | void ebpf_free_event(eBPFevent *event);
112 |
113 |
114 | /* ******************************************* */
115 | /*
116 | * Returns the handler used by this library to retrieve container information
117 | * to be casted to ContainerInfo* to avoid mixinc C with C++
118 | */
119 | void* ebpf_get_cinfo_handler();
120 |
121 | const char* ebpf_flow_version();
122 |
123 | /* ******************************************* */
124 | /* Helper */
125 | void fill_exe_takinfo(struct taskInfo *task);
126 |
127 | #ifdef __cplusplus
128 | };
129 | #endif // __cplusplus
130 |
131 | #endif /* __EBPF_FLOW_H__ */
132 |
--------------------------------------------------------------------------------
/ebpf_types.h:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #define CONTAINER_ID_LEN 64 /* 128 */ // max is in dcache.h > DNAME_INLINE_LEN
22 |
23 | #define COMMAND_LEN 16 // defined in sched.h
24 |
25 | /*
26 | * Events types are forged as follows:
27 | * I_digit (=1): init events (e.g. connection creation)
28 | * (=2): update events on existing connection
29 | * (=3): connection closing
30 | * (=5): operation failed
31 | * II_digit (=0): tcp events
32 | * (=1): udp events
33 | * III_digit: discriminate the single event
34 | */
35 | typedef enum {
36 | eTCP_ACPT = 100,
37 | eTCP_CONN = 101,
38 | eTCP_RETR = 200,
39 | eUDP_RECV = 210,
40 | eUDP_SEND = 211,
41 | eTCP_CLOSE = 300,
42 | eTCP_CONN_FAIL = 500,
43 | } event_type;
44 |
45 | struct taskInfo {
46 | u32 pid; /* Process Id */
47 | u32 tid; /* Thread Id */
48 | u32 uid; /* User Id */
49 | u32 gid; /* Group Id */
50 | char task[COMMAND_LEN];
51 | char *full_task_path, *cmdline;
52 | };
53 |
54 | // separate data structs for ipv4 and ipv6
55 | struct ipv4_addr_t {
56 | u64 saddr;
57 | u64 daddr;
58 | };
59 |
60 | struct ipv6_addr_t {
61 | unsigned __int128 saddr;
62 | unsigned __int128 daddr;
63 | };
64 |
65 | #ifndef IFNAMSIZ
66 | #define IFNAMSIZ 16
67 | #endif
68 |
69 | typedef struct {
70 | ktime_t ktime;
71 | char ifname[IFNAMSIZ];
72 | struct timeval event_time;
73 | u_int8_t ip_version, sent_packet;
74 | u16 etype;
75 |
76 | union {
77 | struct ipv4_addr_t v4;
78 | struct ipv6_addr_t v6;
79 | } addr;
80 |
81 | u8 proto;
82 | u16 sport, dport;
83 | u32 latency_usec;
84 | u16 retransmissions;
85 |
86 | struct taskInfo proc, father;
87 | char container_id[CONTAINER_ID_LEN];
88 |
89 | struct {
90 | char *name;
91 | } docker;
92 |
93 | struct {
94 | char *name;
95 | char *pod;
96 | char *ns;
97 | } kube;
98 | } eBPFevent;
99 |
100 |
--------------------------------------------------------------------------------
/ebpflow_code.ebpf:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 |
22 | struct udp_info {
23 | struct taskInfo proc, father;
24 | char container_id[CONTAINER_ID_LEN];
25 | };
26 | BPF_HASH(udpinfo, u16, struct udp_info);
27 |
28 | static void fill_ifname(eBPFevent *ev, struct sock *sk);
29 |
30 | /* ******************************************* */
31 |
32 | static void update_socket_hash(struct pt_regs *ctx, struct sock *sk) {
33 | u32 tid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF;
34 | struct sock_stats s = { .sk = sk, .ts = bpf_ktime_get_ns() };
35 |
36 | // stash the sock ptr for lookup on returns
37 | currsock.update(&tid, &s);
38 | };
39 |
40 | /* ******************************************* */
41 |
42 | int trace_connect_entry(struct pt_regs *ctx, struct sock *sk) {
43 | update_socket_hash(ctx, sk);
44 |
45 | // bpf_override_return(ctx, -ENOMEM);
46 | return(0);
47 | };
48 |
49 | /* ******************************************* */
50 |
51 | static void fill_father_task_info(struct taskInfo *task) {
52 | // Parent basic info ----- //
53 | struct task_struct *t = (struct task_struct *)bpf_get_current_task();
54 | struct task_struct *parent;
55 | struct cred *fcredential;
56 |
57 | // Grabbing father pointer
58 | // bpf_probe_read(&parent, sizeof(struct task_struct *), &t->real_parent);
59 | parent = t->real_parent;
60 |
61 | // Reading father credential
62 | // bpf_probe_read(&fcredential, sizeof(struct cred *), &parent->real_cred);
63 | fcredential = (struct cred *)(parent->real_cred);
64 |
65 | task->pid = (u32)parent->pid;
66 | task->uid = (u32)fcredential->uid.val;
67 | task->gid = (u32)fcredential->gid.val;
68 |
69 | if(task->pid == 0)
70 | task->task[0] = '\0';
71 | else
72 | bpf_probe_read(&task->task, sizeof(task->task), parent->comm);
73 | }
74 |
75 | /* ******************************************* */
76 |
77 | static void fill_container_id(char *container_id) {
78 | struct task_struct *curr_task;
79 | struct css_set *css;
80 | struct cgroup_subsys_state *sbs;
81 | struct cgroup *cg;
82 | struct kernfs_node *knode, *pknode;
83 | char *name;
84 | int name_shift = 0;
85 |
86 | // Initializing to root cgroup
87 | memcpy(container_id, "/\0", 2);
88 |
89 | curr_task = (struct task_struct *) bpf_get_current_task();
90 | css = curr_task->cgroups;
91 | bpf_probe_read(&sbs, sizeof(void *), &css->subsys[0]);
92 | bpf_probe_read(&cg, sizeof(void *), &sbs->cgroup);
93 |
94 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,15,0)
95 | bpf_probe_read(&name, sizeof(void *), &cg->name);
96 | // Docker name cgroup as follows: "docker-"
97 | bpf_probe_read(container_id, CONTAINER_ID_LEN, name+sizeof(struct cgroup_name));
98 | container_id[CONTAINER_ID_LEN-1] = '\0';
99 | #else
100 | // Reading fspath
101 | bpf_probe_read(&knode, sizeof(void *), &cg->kn);
102 | bpf_probe_read(&pknode, sizeof(void *), &knode->parent);
103 |
104 | if(pknode != NULL) {
105 | char *aus;
106 |
107 | bpf_probe_read(&aus, sizeof(void *), &knode->name);
108 | bpf_probe_read_str(container_id, CONTAINER_ID_LEN, aus);
109 | }
110 | #endif
111 | }
112 |
113 | /* ******************************************* */
114 |
115 | static void fill_task_info(char *container_id, struct taskInfo *task, struct taskInfo *father) {
116 | struct task_struct *curr_task = (struct task_struct *)bpf_get_current_task();
117 | u64 tgid = bpf_get_current_pid_tgid();
118 | u64 ugid = bpf_get_current_uid_gid();
119 | u32 pid = tgid & 0xFFFFFFFF, tid = (tgid >> 32) & 0xFFFFFFFF;
120 | u32 uid = ugid & 0xFFFFFFFF, gid = (ugid >> 32) & 0xFFFFFFFF;
121 |
122 | task->pid = pid;
123 | task->tid = tid;
124 | task->uid = uid;
125 | task->gid = gid;
126 |
127 | if(pid == 0)
128 | task->task[0] = '\0';
129 | else {
130 | bpf_get_current_comm(&task->task, sizeof(task->task));
131 | fill_father_task_info(father);
132 | }
133 |
134 | container_id[0] = '\0';
135 | fill_container_id(container_id);
136 | }
137 |
138 | /* ******************************************* */
139 |
140 | static void swap_event_peers(eBPFevent *ev) {
141 | if(ev->ip_version == 4) {
142 | u32 tmp;
143 | u16 tmp16;
144 |
145 | tmp16 = ev->sport;
146 | ev->sport = ev->dport;
147 | ev->dport = tmp16;
148 |
149 | tmp = ev->addr.v4.daddr;
150 | ev->addr.v4.daddr = ev->addr.v4.saddr;
151 | ev->addr.v4.saddr = tmp;
152 | } else {
153 | u16 tmp16;
154 | unsigned __int128 tmp;
155 |
156 | tmp16 = ev->sport;
157 | ev->sport = ev->dport;
158 | ev->dport = tmp16;
159 |
160 | memcpy(&tmp, &ev->addr.v6.saddr, sizeof(tmp));
161 | memcpy(&ev->addr.v6.saddr, &ev->addr.v6.daddr, sizeof(ev->addr.v6.saddr));
162 | memcpy(&ev->addr.v6.daddr, &tmp, sizeof(ev->addr.v6.daddr));
163 | }
164 | }
165 |
166 | /* ******************************************* */
167 |
168 | static int fill_event(struct pt_regs *ctx, eBPFevent *ev,
169 | struct sock *sk,
170 | void *msg,
171 | u64 begin_ts,
172 | u8 proto, u8 swap_peers) {
173 | u16 sport = 0, dport = 0;
174 | u16 family;
175 | u64 delta;
176 | u32 pid = bpf_get_current_pid_tgid() & 0xFFFFFFFF;
177 | u32 saddr = 0, daddr = 0;
178 | ktime_t kt = { bpf_ktime_get_ns() };
179 |
180 | ev->sent_packet = (swap_peers == 0) ? 1 : 0;
181 |
182 | bpf_probe_read(&family, sizeof(family), &sk->__sk_common.skc_family);
183 | if((family != AF_INET) && (family != AF_INET6)) return(-1);
184 |
185 | bpf_probe_read(&sport, sizeof(u16), &sk->__sk_common.skc_num);
186 | bpf_probe_read(&dport, sizeof(u16), &sk->__sk_common.skc_dport);
187 |
188 | if(msg) {
189 | struct sockaddr_in usin;
190 |
191 | bpf_probe_read(&usin, sizeof(usin), msg);
192 | family = usin.sin_family;
193 |
194 | if(usin.sin_family == AF_INET) {
195 | daddr = usin.sin_addr.s_addr;
196 | dport = usin.sin_port;
197 | }
198 | }
199 |
200 | if(begin_ts > 0) {
201 | delta = bpf_ktime_get_ns() - begin_ts;
202 | delta /= 1000;
203 | } else
204 | delta = 0;
205 |
206 | dport = ntohs(dport); /* This has to be done all the time */
207 |
208 | if((sport == 0) && (dport == 0))
209 | return(-1);
210 |
211 | ev->proc.pid = pid;
212 |
213 | if(family == AF_INET) {
214 | ev->ip_version = 4;
215 |
216 | if(saddr == 0)
217 | bpf_probe_read(&ev->addr.v4.saddr, sizeof(u32), &sk->__sk_common.skc_rcv_saddr);
218 | else
219 | ev->addr.v4.saddr = saddr;
220 |
221 | if(daddr == 0)
222 | bpf_probe_read(&ev->addr.v4.daddr, sizeof(u32), &sk->__sk_common.skc_daddr);
223 | else
224 | ev->addr.v4.daddr = daddr;
225 | } else /* (family == AF_INET6) */ {
226 | ev->ip_version = 6;
227 |
228 | bpf_probe_read(&ev->addr.v6.saddr, sizeof(ev->addr.v6.saddr),
229 | sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
230 | bpf_probe_read(&ev->addr.v6.daddr, sizeof(ev->addr.v6.daddr), sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
231 |
232 | if(/* Implement in a better way */
233 | (((ev->addr.v6.saddr) & 0xFFFFFFFF) == 0)
234 | && (((ev->addr.v6.saddr >> 32) & 0xFFFFFFFF) == 0)
235 | ) {
236 | ev->ip_version = 4;
237 | ev->proc.pid = pid;
238 | ev->addr.v4.saddr = ev->addr.v6.saddr >> 96;
239 | ev->sport = sport;
240 | ev->addr.v4.daddr = ev->addr.v6.daddr >> 96;
241 | }
242 | }
243 |
244 | ev->dport = dport;
245 | ev->sport = sport;
246 | ev->latency_usec = delta;
247 | ev->proto = proto;
248 | bpf_get_current_comm(&ev->proc.task, sizeof(ev->proc.task));
249 | ev->proc.pid = pid;
250 |
251 | fill_task_info((char*)ev->container_id, &ev->proc, &ev->father);
252 |
253 | if(swap_peers) swap_event_peers(ev);
254 |
255 | fill_ifname(ev, sk);
256 |
257 | ev->ktime = kt;
258 | return(0);
259 | }
260 |
261 | /* ******************************************* */
262 |
263 | static int trace_connect_return(struct pt_regs *ctx) {
264 | int ret = PT_REGS_RC(ctx); // return value
265 | struct sock_stats *s;
266 | u32 tid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF;
267 | eBPFevent event = { .etype = eTCP_CONN, .ip_version = 4 };
268 |
269 | s = currsock.lookup(&tid);
270 | if(s == NULL)
271 | return(0); // missed entry
272 |
273 |
274 | fill_event(ctx, &event, s->sk, NULL, s->ts, IPPROTO_TCP, 0 /* don't swap */);
275 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
276 |
277 | currsock.delete(&tid);
278 |
279 | return(0);
280 | }
281 |
282 | /* ******************************************* */
283 |
284 | int trace_connect_v4_return(struct pt_regs *ctx) {
285 | return trace_connect_return(ctx);
286 | }
287 |
288 | /* ******************************************* */
289 |
290 | int trace_connect_v6_return(struct pt_regs *ctx) {
291 | return trace_connect_return(ctx);
292 | }
293 |
294 | /* ******************************************* */
295 |
296 | int trace_tcp_accept(struct pt_regs *ctx) {
297 | struct sock *newsk = (struct sock *)PT_REGS_RC(ctx);
298 |
299 | if(newsk != NULL) {
300 | eBPFevent event = { .etype = eTCP_ACPT, .ip_version = 4 };
301 |
302 | fill_event(ctx, &event, newsk, NULL, 0, IPPROTO_TCP, 1 /* swap */);
303 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
304 | }
305 |
306 | return(0);
307 | }
308 |
309 | /* ******************************************* */
310 |
311 | // Fired when the state changes and check if the state is CLOSE
312 | int trace_tcp_set_state(struct pt_regs *ctx, struct sock *sk, int state) {
313 | unsigned char old_state;
314 | eBPFevent event = {};
315 |
316 | if((state != TCP_CLOSE) && (state != EINPROGRESS))
317 | return 0;
318 | else {
319 | // Reading old state
320 | // bpf_probe_read(&old_state, sizeof(unsigned char), (unsigned char*) &sk->__sk_common.skc_state);
321 | old_state = sk->__sk_common.skc_state;
322 | }
323 |
324 | fill_event(ctx, &event, sk, NULL, 0, IPPROTO_TCP, 0);
325 |
326 | // Connection refused if we move from SYN_SENT to TCP_CLOSE
327 | if (((int)old_state == TCP_SYN_SENT) && (state == TCP_CLOSE))
328 | event.etype = eTCP_CONN_FAIL;
329 | else
330 | event.etype = eTCP_CLOSE;
331 |
332 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
333 | return 0;
334 | }
335 |
336 | /* ******************************************* */
337 |
338 | int trace_tcp_retransmit_skb(struct pt_regs *ctx, struct sock *sk) {
339 | u32 tid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF;
340 | eBPFevent event = { .etype = eTCP_RETR, .retransmissions = 1 };
341 |
342 | fill_event(ctx, &event, sk, NULL, 0, IPPROTO_TCP, 0);
343 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
344 |
345 | return 0;
346 | }
347 |
348 | /* *********************** UDP *************************** */
349 | /* *********************** UDP *************************** */
350 | /* *********************** UDP *************************** */
351 |
352 | /* key is IPs+sport+dport, value = bpf_ktime_get_ns() */
353 | #define BPF_LRU_HASH3(_name, _key_type, _leaf_type) BPF_TABLE("lru_hash", _key_type, _leaf_type, _name, 10240)
354 | BPF_LRU_HASH3(udpmsglru, u64, u64);
355 |
356 | /* ******************************************* */
357 |
358 | static u8 is_cached_entry(eBPFevent *ev) {
359 | u64 hash_idx;
360 | u64 *when, now;
361 |
362 | /* NOTE: implemented asymmetric hash to make sure we see both flow directions */
363 | if(ev->ip_version == 4)
364 | hash_idx = ev->addr.v4.saddr + ev->addr.v4.daddr + ev->sport + ev->dport + ev->proc.pid;
365 | else
366 | hash_idx = ev->addr.v6.saddr + ev->addr.v6.daddr + ev->sport + ev->dport + ev->proc.pid;
367 |
368 | when = udpmsglru.lookup(&hash_idx);
369 | now = bpf_ktime_get_ns();
370 |
371 | if(when == NULL) {
372 | /* not found so not cached */
373 |
374 | udpmsglru.update(&hash_idx, &now);
375 | return(0);
376 | } else {
377 | u64 diff = now - *when;
378 |
379 | if(diff > 1000000000 /* 1 sec */) {
380 | /* or it was cached more than one second ago */
381 | udpmsglru.update(&hash_idx, &now);
382 | return(0);
383 | }
384 |
385 | return(1);
386 | }
387 |
388 | return(0);
389 | }
390 |
391 | /* ******************************************* */
392 | /* ******************************************* */
393 |
394 | /* https://blog.yadutaf.fr/2017/07/28/tracing-a-packet-journey-using-linux-tracepoints-perf-ebpf/ */
395 |
396 | #define ETHERTYPE_IP 0x0800 /* IP */
397 | #define ETHERTYPE_IPV6 0x86DD /* IP protocol version 6 */
398 | #define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging */
399 |
400 | #define MAC_HEADER_SIZE 14;
401 | #define member_address(source_struct, source_member) \
402 | ({ \
403 | void* __ret; \
404 | __ret = (void*) (((char*)source_struct) + offsetof(typeof(*source_struct), source_member)); \
405 | __ret; \
406 | })
407 | #define member_read(destination, source_struct, source_member) \
408 | do{ \
409 | bpf_probe_read( \
410 | destination, \
411 | sizeof(source_struct->source_member), \
412 | member_address(source_struct, source_member) \
413 | ); \
414 | } while(0)
415 |
416 | static inline int udp_packet_trace(void *ctx, struct sk_buff* skb, u_int8_t sent_packet) {
417 | // Compute MAC header address
418 | char* head;
419 | u16 mac_header;
420 | eBPFevent event = { .etype = eUDP_SEND, .sent_packet = sent_packet };
421 | u8 offset, l4proto, ip_version;
422 | char* ip_header_address;
423 | struct udphdr *udphdr;
424 | u16 eth_proto;
425 | struct net_device *dev;
426 |
427 | member_read(&head, skb, head);
428 | member_read(&mac_header, skb, mac_header);
429 |
430 | head = head + mac_header;
431 |
432 | bpf_probe_read(ð_proto, sizeof(u16), &head[12]);
433 |
434 | // Compute IP Header address
435 | ip_header_address = head + MAC_HEADER_SIZE;
436 |
437 | // Load IP protocol version
438 | bpf_probe_read(&ip_version, sizeof(u8), ip_header_address);
439 | event.ip_version = ip_version >> 4 & 0xf;
440 |
441 | /* TODO; ADD VLAN support */
442 |
443 | if(eth_proto == htons(ETHERTYPE_IP)) {
444 | struct iphdr iphdr;
445 |
446 | event.ip_version = 4;
447 | bpf_probe_read(&iphdr, sizeof(iphdr), ip_header_address);
448 |
449 | // Load protocol and address
450 | offset = iphdr.ihl * 4;
451 |
452 | l4proto = iphdr.protocol;
453 |
454 | // Discard non UDP traffic
455 | if(l4proto != IPPROTO_UDP) return 0;
456 |
457 | event.addr.v4.saddr = iphdr.saddr;
458 | event.addr.v4.daddr = iphdr.daddr;
459 | udphdr = (struct udphdr*)(&ip_header_address[offset]);
460 | bpf_probe_read(&event.sport, sizeof(u16), &udphdr->source);
461 | bpf_probe_read(&event.dport, sizeof(u16), &udphdr->dest);
462 | event.sport = htons(event.sport);
463 | event.dport = htons(event.dport);
464 | } else if(eth_proto == htons(ETHERTYPE_IPV6)) {
465 | // Assume no option header --> fixed size header
466 | struct ipv6hdr* ipv6hdr = (struct ipv6hdr*)ip_header_address;
467 |
468 | event.ip_version = 6;
469 | bpf_probe_read(&l4proto, sizeof(ipv6hdr->nexthdr),
470 | (char*)ipv6hdr + offsetof(struct ipv6hdr, nexthdr));
471 |
472 | // Discard non UDP traffic
473 | if(l4proto != IPPROTO_UDP) return 0;
474 |
475 | bpf_probe_read(&event.addr.v6.saddr, sizeof(ipv6hdr->saddr),
476 | (char*)ipv6hdr + offsetof(struct ipv6hdr, saddr));
477 | bpf_probe_read(&event.addr.v6.daddr, sizeof(ipv6hdr->daddr),
478 | (char*)ipv6hdr + offsetof(struct ipv6hdr, daddr));
479 | offset = sizeof(*ipv6hdr);
480 | udphdr = (struct udphdr*)(&ip_header_address[offset]);
481 | bpf_probe_read(&event.sport, sizeof(u16), &udphdr->source);
482 | bpf_probe_read(&event.dport, sizeof(u16), &udphdr->dest);
483 | event.sport = htons(event.sport);
484 | event.dport = htons(event.dport);
485 | } else {
486 | #if 0
487 | event.ip_version = 6;
488 | event.sport = ntohs(eth_proto);
489 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
490 | #endif
491 | return(0);
492 | }
493 |
494 | event.proto = IPPROTO_UDP;
495 | event.latency_usec = 0;
496 |
497 | if(sent_packet)
498 | fill_task_info((char*)event.container_id, &event.proc, &event.father);
499 | else {
500 | event.container_id[0] = '\0';
501 | memset(&event.proc, 0, sizeof(event.proc));
502 | memset(&event.father, 0, sizeof(event.father));
503 | }
504 |
505 | member_read(&dev, skb, dev);
506 | bpf_probe_read(&event.ifname, IFNAMSIZ, dev->name);
507 |
508 | if(!is_cached_entry(&event))
509 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
510 |
511 | return 0;
512 | }
513 |
514 | /* ******************************************* */
515 |
516 | static void fill_ifname(eBPFevent *ev, struct sock *sk) {
517 | struct net_device *dev;
518 | struct dst_entry *dst;
519 |
520 | member_read(&dst, sk, sk_dst_cache);
521 | member_read(&dev, dst, dev);
522 | bpf_probe_read(&ev->ifname, IFNAMSIZ, dev->name);
523 | }
524 |
525 | /* ******************************************* */
526 |
527 | /**
528 | * Attach to Kernel Tracepoints
529 | */
530 | /*
531 | cat /sys/kernel/debug/tracing/events/net/netif_rx/format
532 |
533 | field:unsigned short common_type;offset:0;size:2;signed:0;
534 | field:unsigned char common_flags;offset:2;size:1;signed:0;
535 | field:unsigned char common_preempt_count;offset:3;size:1;signed:0;
536 | field:int common_pid;offset:4;size:4;signed:1;
537 |
538 | field:void * skbaddr;offset:8;size:8;signed:0;
539 | field:unsigned int len;offset:16;size:4;signed:0;
540 | field:__data_loc char[] name;offset:20;size:4;signed:1;
541 |
542 | */
543 | struct netif_rx_read_args {
544 | u64 __unused__;
545 | void * skbaddr;
546 | u_int16_t len;
547 | char name[];
548 | };
549 |
550 | /*
551 | cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_bind/format
552 |
553 | field:int __syscall_nr;offset:8;size:4;signed:1;
554 | field:int fd;offset:16;size:8;signed:0;
555 | field:struct sockaddr * umyaddr;offset:24;size:8;signed:0;
556 | field:int addrlen;offset:32;size:8;signed:0;
557 | */
558 | struct sys_bind_args {
559 | u64 __unused__;
560 | int __syscall_nr;
561 | int fd;
562 | struct sockaddr *umyaddr;
563 | int addrlen;
564 | };
565 |
566 | /*
567 | * When a packet is received the skb has not yet hit the system and thus
568 | * we don't know (yet) the process that will handle it
569 | */
570 | int trace_netif_rx_entry(struct netif_rx_read_args *args) {
571 | return udp_packet_trace(args, (struct sk_buff*)(args->skbaddr), 0);
572 | }
573 |
574 | int trace_netif_tx_entry(struct netif_rx_read_args *args) {
575 | return udp_packet_trace(args, (struct sk_buff*)(args->skbaddr), 1);
576 | }
577 |
578 | int trace_receive_v4(struct pt_regs *ctx, struct sock *sk) {
579 | eBPFevent event = { .etype = eUDP_RECV, .ip_version = 4 };
580 |
581 | if(fill_event(ctx, &event, sk, NULL, bpf_ktime_get_ns(), IPPROTO_UDP, 0 /* don't swap */) == 0)
582 | ebpf_events.perf_submit(ctx, &event, sizeof(eBPFevent));
583 |
584 | return(0);
585 | }
586 |
--------------------------------------------------------------------------------
/ebpflow_header.ebpf:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #pragma clang diagnostic push
30 | #pragma clang diagnostic ignored "-Wtautological-compare"
31 | #include
32 | #pragma clang diagnostic pop
33 |
34 | struct sock_stats {
35 | struct sock *sk;
36 | u64 ts;
37 | };
38 |
39 | // hash map currsock
40 | BPF_HASH(currsock, u32, struct sock_stats);
41 |
42 | BPF_PERF_OUTPUT(ebpf_events);
43 |
44 |
--------------------------------------------------------------------------------
/ebpflowexport.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-22 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 |
46 | #include "ebpf_flow.h"
47 |
48 |
49 | void help();
50 | static char* intoaV4(unsigned int addr, char* buf, u_short bufLen);
51 | char* intoaV6(unsigned __int128 addr, char* buf, u_short bufLen);
52 | static void handleTermination(int t_s=0);
53 | static void ebpfHandler(void* t_bpfctx, void* t_data, int t_datasize);
54 | static void zmqHandler(void* t_bpfctx, void* t_data, int t_datasize);
55 | static u_int8_t verbose = 0;
56 | static int gRUNNING = 1;
57 | void *gZMQsocket = NULL;
58 |
59 | /* **************************** */
60 | // ===== ===== MAIN ===== ===== //
61 | /* **************************** */
62 |
63 | struct zmq_msg_hdr {
64 | char url[32];
65 | u_int32_t version;
66 | u_int32_t size;
67 | };
68 |
69 | static const struct option long_opts[] = {
70 | { "retr", 0, NULL, 'r' },
71 | { "tcpclose", 0, NULL, 'c' },
72 | { "udp", 0, NULL, 'u' },
73 | { "tcp", 0, NULL, 't' },
74 | { "in", 0, NULL, 'i' },
75 | { "out", 0, NULL, 'o' },
76 | { "help", 0, NULL, 'h' },
77 | { "zmq", 0, NULL, 'z' },
78 | { "verbose", 0, NULL, 'v' },
79 | { NULL, 0, NULL, 0 }
80 | };
81 |
82 | int main(int argc, char **argv) {
83 | int ch;
84 | char* zmq_endpoint = NULL;
85 | void *context = NULL, *ebpf = NULL;
86 | short flags = 0;
87 | ebpfRetCode rc = ebpf_no_error;
88 | eBPFHandler handler = ebpfHandler;
89 |
90 | signal(SIGINT, handleTermination);
91 |
92 | // Argument Parsing ----- //
93 | while ((ch = getopt_long(argc, argv, "z:rcutiohv", long_opts, NULL)) != EOF) {
94 | switch (ch) {
95 | case 'u':
96 | flags |= LIBEBPF_UDP;
97 | break;
98 | case 't':
99 | flags |= LIBEBPF_TCP;
100 | break;
101 | case 'i':
102 | flags |= LIBEBPF_INCOMING;
103 | break;
104 | case 'o':
105 | flags |= LIBEBPF_OUTCOMING;
106 | break;
107 | case 'c':
108 | flags |= LIBEBPF_TCP_CLOSE;
109 | break;
110 | case 'r':
111 | flags |= LIBEBPF_TCP_RETR;
112 | break;
113 | case 'z':
114 | zmq_endpoint = strdup(optarg);
115 | handler = zmqHandler;
116 | break;
117 | case 'v':
118 | verbose = 1;
119 | break;
120 | default:
121 | help();
122 | return 0;
123 | }
124 | }
125 | // Setting defaults
126 | if(flags == 0)
127 | flags = 0xffff;
128 |
129 | if(!(flags & LIBEBPF_INCOMING) && !(flags & LIBEBPF_OUTCOMING))
130 | flags += LIBEBPF_INCOMING | LIBEBPF_OUTCOMING;
131 |
132 | if(!(flags & LIBEBPF_TCP) && !(flags & LIBEBPF_UDP)
133 | && !(flags & LIBEBPF_TCP_CLOSE) && !(flags & eTCP_RETR))
134 | flags += (LIBEBPF_UDP | LIBEBPF_TCP) | LIBEBPF_TCP_CLOSE;
135 |
136 | // Checking root ----- //
137 | if(getuid() != 0) {
138 | //printf("Please run as root user \n");
139 | help();
140 | return 0;
141 | }
142 |
143 | printf("Welcome to ebpflowexport v.%s\n(C) 2018-22 ntop.org\n",
144 | ebpf_flow_version());
145 |
146 | if(zmq_endpoint) {
147 | context = zmq_ctx_new();
148 | if(context == NULL) {
149 | printf("Unable to initialize ZMQ context");
150 | goto close;
151 | }
152 |
153 | gZMQsocket = zmq_socket(context, ZMQ_PUB);
154 | if(gZMQsocket == NULL) {
155 | printf("Unable to create ZMQ socket");
156 | goto close;
157 | }
158 |
159 | if(zmq_endpoint[strlen(zmq_endpoint) - 1] == 'c') {
160 | /* Collector mode */
161 | if(zmq_connect(gZMQsocket, zmq_endpoint) != 0)
162 | printf("Unable to connect to ZMQ socket %s: %s\n", zmq_endpoint, strerror(errno));
163 | } else {
164 | /* Probe mode */
165 | if(zmq_bind(gZMQsocket, zmq_endpoint) != 0) {
166 | printf("Unable to bind to ZMQ socket %s: %s\n", zmq_endpoint, strerror(errno));
167 | goto close;
168 | }
169 | }
170 | }
171 |
172 | // Activating libebpflow ----- //
173 | printf("Initializing eBPF [%s]...\n",
174 | #ifdef NEW_EBF
175 | "New API"
176 | #else
177 | "Legacy API"
178 | #endif
179 | );
180 | ebpf = init_ebpf_flow(NULL, handler, &rc, flags);
181 |
182 | if(!ebpf) {
183 | printf("Unable to initialize libebpfflow: %s\n", ebpf_print_error(rc));
184 | goto close;
185 | }
186 |
187 | printf("eBPF initializated successfully\n");
188 |
189 | // Polling event ----- //
190 | while(gRUNNING) {
191 | ebpf_poll_event(ebpf, 10);
192 | }
193 |
194 | // Cleaning and terminating ----- //
195 | close:
196 | if(gZMQsocket != NULL)
197 | zmq_close(gZMQsocket);
198 | if(context != NULL)
199 | zmq_ctx_destroy(context);
200 | if(zmq_endpoint)
201 | free(zmq_endpoint);
202 |
203 | term_ebpf_flow(ebpf);
204 | printf("eBPF terminated\n");
205 |
206 | return(rc);
207 | }
208 |
209 | void help() {
210 | printf(
211 | "(C) 2018-22 - ntop.org\n"
212 | "ebpflowexport: Traffic visibility tool based on libebpfflow. By default all events will be shown\n"
213 | "Termination: CTRL-C\n"
214 | "Usage: ebpflowexport [ OPTIONS ]\n"
215 | " -h, --help display this message\n"
216 | " -v Verbose\n"
217 | " -t, --tcp TCP events\n"
218 | " -u, --udp UDP events\n"
219 | " -i, --in Incoming events (i.e. TCP accept and UDP receive)\n"
220 | " -o, --on Outgoing events (i.e. TCP connect and UDP send)\n"
221 | " -r, --retr Retransmissions events\n"
222 | " -c, --tcpclose TCP connection refused and socket close \n"
223 | " -z, --zmq Publish JSON events as a ZeroMQ publisher with envelope 'ebpfflow'\n"
224 | " Example:\n"
225 | " - ebpflowexport -z tcp://127.0.0.1:1234\n"
226 | " - ebpflowexport -z tcp://127.0.0.1:6789c [for Wireshark]\n\n"
227 | "IMPORTANT: please run this tool as root\n"
228 | );
229 | }
230 |
231 |
232 | /* ******************************************** */
233 | // ===== ===== IP ADDRESS TO STRING ===== ===== //
234 | /* ******************************************** */
235 | static char* intoaV4(unsigned int addr, char* buf, u_short bufLen) {
236 | char *cp, *retStr;
237 | int n;
238 |
239 | cp = &buf[bufLen];
240 | *--cp = '\0';
241 |
242 | n = 4;
243 | do {
244 | u_int byte = addr & 0xff;
245 |
246 | *--cp = byte % 10 + '0';
247 | byte /= 10;
248 | if(byte > 0) {
249 | *--cp = byte % 10 + '0';
250 | byte /= 10;
251 | if(byte > 0)
252 | *--cp = byte + '0';
253 | }
254 | *--cp = '.';
255 | addr >>= 8;
256 | } while (--n > 0);
257 |
258 | /* Convert the string to lowercase */
259 | retStr = (char*)(cp+1);
260 |
261 | return(retStr);
262 | }
263 |
264 | static char* intoaV6(void *addr, char* buf, u_short bufLen) {
265 | char *ret = (char*)inet_ntop(AF_INET6, addr, buf, bufLen);
266 |
267 | if(ret == NULL)
268 | buf[0] = '\0';
269 |
270 | return(buf);
271 | }
272 |
273 |
274 | /* ***************************************** */
275 | // ===== ===== CALLBACK HANDLERS ===== ===== //
276 | /* ***************************************** */
277 |
278 | const char* event_summary(eBPFevent* e) {
279 | switch(e->etype) {
280 | case eTCP_ACPT:
281 | return("ACCEPT");
282 | break;
283 | case eTCP_CONN:
284 | return("CONNECT");
285 | break;
286 | case eTCP_CONN_FAIL:
287 | return("CONNECT_FAILED");
288 | break;
289 | case eTCP_CLOSE:
290 | return("CLOSE");
291 | break;
292 | case eTCP_RETR:
293 | return("RETRANSMIT");
294 | break;
295 | case eUDP_SEND:
296 | return("SEND");
297 | break;
298 | case eUDP_RECV:
299 | return("RECV");
300 | break;
301 | }
302 |
303 | return("???");
304 | }
305 |
306 | /* ***************************************************** */
307 | /* ***************************************************** */
308 |
309 | static void IPV4Handler(void* t_bpfctx, eBPFevent *e, struct ipv4_addr_t *event) {
310 | char buf1[32], buf2[32];
311 |
312 | printf("[addr: %s:%u <-> %s:%u]",
313 | intoaV4(htonl(event->saddr), buf1, sizeof(buf1)), e->sport,
314 | intoaV4(htonl(event->daddr), buf2, sizeof(buf2)), e->dport);
315 | }
316 |
317 | static void IPV6Handler(void* t_bpfctx, eBPFevent *e, struct ipv6_addr_t *event) {
318 | char buf1[128], buf2[128];
319 |
320 | printf("[addr: %s:%u <-> %s:%u]",
321 | intoaV6(&event->saddr, buf1, sizeof(buf1)), e->sport,
322 | intoaV6(&event->daddr, buf2, sizeof(buf2)), e->dport);
323 | }
324 |
325 | /* ***************************************************************** */
326 |
327 | static void ebpfHandler(void* t_bpfctx, void* t_data, int t_datasize) {
328 | eBPFevent *e = (eBPFevent*)t_data;
329 | eBPFevent event;
330 | struct timespec tp;
331 |
332 | memcpy(&event, e, sizeof(eBPFevent)); /* Copy needed as ebpf_preprocess_event will modify the memory */
333 |
334 | ebpf_preprocess_event(&event);
335 |
336 | clock_gettime(CLOCK_MONOTONIC, &tp);
337 |
338 | #if 0
339 | printf("[latency %.1f usec] ",
340 | (float)(tp.tv_nsec-(event.ktime % 1000000000))/(float)1000);
341 | #endif
342 |
343 | printf("%u.%06u ", (unsigned int)event.event_time.tv_sec, (unsigned int)event.event_time.tv_usec);
344 |
345 | printf("[%s][%s][IPv4/%s][pid/tid: %u/%u [%s%s%s], uid/gid: %u/%u][father pid/tid: %u/%u [%s%s%s], uid/gid: %u/%u]",
346 | event.ifname, event.sent_packet ? "Sent" : "Rcvd",
347 | (event.proto == IPPROTO_TCP) ? "TCP" : "UDP",
348 | event.proc.pid, event.proc.tid,
349 | (event.proc.full_task_path == NULL) ? event.proc.task : event.proc.full_task_path,
350 | (event.proc.cmdline == NULL) ? "" : " ",
351 | (event.proc.cmdline == NULL) ? "" : event.proc.cmdline,
352 | event.proc.uid, event.proc.gid,
353 | event.father.pid, event.father.tid,
354 | (event.father.full_task_path == NULL) ? event.father.task : event.father.full_task_path,
355 | (event.father.cmdline == NULL) ? "" : "",
356 | (event.father.cmdline == NULL) ? "": event.father.cmdline,
357 | event.father.uid, event.father.gid);
358 |
359 | if(event.ip_version == 4)
360 | IPV4Handler(t_bpfctx, &event, &event.addr.v4);
361 | else
362 | IPV6Handler(t_bpfctx, &event, &event.addr.v6);
363 |
364 | if(event.proto == IPPROTO_TCP) {
365 | printf("[%s]", event_summary(&event));
366 |
367 | if(event.etype == eTCP_CONN)
368 | printf("[latency: %.2f msec]", ((float)event.latency_usec)/(float)1000);
369 | }
370 |
371 | // Container ----- /'/
372 | if(event.container_id[0] != '\0') {
373 | printf("[containerID: %s]", event.container_id);
374 |
375 | if(event.docker.name != NULL)
376 | printf("[docker_name: %s]", event.docker.name);
377 |
378 | if(event.kube.ns) printf("[kube_name: %s]", event.kube.name);
379 | if(event.kube.pod) printf("[kube_pod: %s]", event.kube.pod);
380 | if(event.kube.ns) printf("[kube_ns: %s]", event.kube.ns);
381 | }
382 |
383 | printf("\n");
384 | ebpf_free_event(&event);
385 | }
386 |
387 | /* ******************************************* */
388 |
389 | void task2json(struct taskInfo *t, struct json_object **t_res) {
390 | struct json_object *j = json_object_new_object();
391 | struct passwd *uid_info;
392 | struct group *gg;
393 |
394 | json_object_object_add(j, "PID", json_object_new_int(t->pid));
395 |
396 | json_object_object_add(j, "UID", json_object_new_int(t->uid));
397 | if((uid_info = getpwuid(t->pid)) != NULL)
398 | json_object_object_add(j, "UID_NAME", json_object_new_string(uid_info->pw_name));
399 |
400 | json_object_object_add(j, "GID", json_object_new_int(t->gid));
401 |
402 | if((gg = getgrgid(t->gid)) != NULL)
403 | json_object_object_add(j, "GID_NAME", json_object_new_string(gg->gr_name));
404 |
405 | json_object_object_add(j, "TID", json_object_new_int(t->tid));
406 |
407 | json_object_object_add(j, "PROCESS_PATH",
408 | json_object_new_string(t->full_task_path != NULL ? t->full_task_path : t->task));
409 |
410 | json_object_object_add(j, "PROCESS_CMDLINE",
411 | json_object_new_string(t->cmdline != NULL ? t->cmdline : ""));
412 |
413 | *t_res = j;
414 | }
415 |
416 | /* ******************************************* */
417 |
418 | void event2json(eBPFevent *t_event, struct json_object **t_res) {
419 | char buf1[128], buf2[128];
420 | char *saddr, *daddr;
421 | const char *t_saddr, *t_daddr;
422 | struct json_object *j = json_object_new_object(), *k, *docker_json, *kube_json;
423 | struct json_object *proc, *father;
424 |
425 | snprintf(buf1, sizeof(buf1), "%u.%06u",
426 | (unsigned int)t_event->event_time.tv_sec,
427 | (unsigned int)t_event->event_time.tv_usec);
428 | json_object_object_add(j, "timestamp", json_object_new_string(buf1));
429 |
430 | // json_object_object_add(j, "ktime", json_object_new_int64(t_event->ktime));
431 |
432 | json_object_object_add(j, "INTERFACE_NAME", json_object_new_string(t_event->ifname));
433 | json_object_object_add(j, "IP_PROTOCOL_VERSION", json_object_new_int(t_event->ip_version));
434 |
435 | json_object_object_add(j,
436 | (t_event->proto == IPPROTO_TCP) ? "TCP_EVENT_TYPE" : "UDP_EVENT_TYPE",
437 | json_object_new_string(event_summary(t_event)));
438 |
439 | if(t_event->ip_version == 4) {
440 | saddr = intoaV4(htonl(t_event->addr.v4.saddr), buf1, sizeof(buf1));
441 | daddr = intoaV4(htonl(t_event->addr.v4.daddr), buf2, sizeof(buf2));
442 | t_saddr = "IPV4_SRC_ADDR", t_daddr = "IPV4_DST_ADDR";
443 | } else {
444 | saddr = intoaV6(&t_event->addr.v6.saddr, buf1, sizeof(buf1));
445 | daddr = intoaV6(&t_event->addr.v6.daddr, buf2, sizeof(buf2));
446 | t_saddr = "IPV6_SRC_ADDR", t_daddr = "IPV6_DST_ADDR";
447 | }
448 |
449 | json_object_object_add(j, t_saddr, json_object_new_string(saddr));
450 | json_object_object_add(j, t_daddr, json_object_new_string(daddr));
451 |
452 | json_object_object_add(j, "PROTOCOL", json_object_new_int(t_event->proto));
453 | json_object_object_add(j, "L4_SRC_PORT", json_object_new_int(t_event->sport));
454 | json_object_object_add(j, "L4_DST_PORT", json_object_new_int(t_event->dport));
455 |
456 | if(t_event->latency_usec > 0) {
457 | double v = t_event->latency_usec/(double)1000;
458 |
459 | snprintf(buf1, sizeof(buf1), "%.3f", v);
460 |
461 | #ifdef HAVE_DOUBLES
462 | json_object_object_add(j, "NW_LATENCY_MS", json_object_new_double_s(v, buf1));
463 | #else
464 | json_object_object_add(j, "NW_LATENCY_MS", json_object_new_string(buf1));
465 | #endif
466 | }
467 |
468 | if(t_event->retransmissions > 0)
469 | json_object_object_add(j, "RETRAN_PKTS", json_object_new_int(t_event->retransmissions));
470 |
471 | if(t_event->proc.task[0] != '\0') {
472 | task2json(&t_event->proc, &proc);
473 | json_object_object_add(j, "LOCAL_PROCESS", proc);
474 | }
475 |
476 | if(t_event->father.task[0] != '\0') {
477 | task2json(&t_event->father, &father);
478 | json_object_object_add(j, "LOCAL_FATHER_PROCESS", father);
479 | }
480 |
481 | if(t_event->docker.name != NULL) {
482 | if((k = json_object_new_object()) != NULL) {
483 | if((docker_json = json_object_new_object()) != NULL) {
484 | if(t_event->container_id[0] != '\0')
485 | json_object_object_add(docker_json, "ID", json_object_new_string(t_event->container_id));
486 |
487 | json_object_object_add(docker_json, "NAME", json_object_new_string(t_event->docker.name));
488 | json_object_object_add(k, "DOCKER", docker_json);
489 | json_object_object_add(j, "LOCAL_CONTAINER", k);
490 | } else
491 | json_object_put(k);
492 | }
493 | }
494 |
495 | if(t_event->kube.pod) {
496 | if((k = json_object_new_object()) != NULL) {
497 | if((kube_json = json_object_new_object()) != NULL) {
498 | if(t_event->container_id[0] != '\0')
499 | json_object_object_add(kube_json, "ID", json_object_new_string(t_event->container_id));
500 |
501 | if(t_event->kube.name != NULL)
502 | json_object_object_add(kube_json, "NAME", json_object_new_string(t_event->kube.name));
503 |
504 | if(t_event->kube.pod != NULL)
505 | json_object_object_add(kube_json, "POD", json_object_new_string(t_event->kube.pod));
506 |
507 | if(t_event->kube.ns != NULL)
508 | json_object_object_add(kube_json, "NS", json_object_new_string(t_event->kube.ns));
509 |
510 | json_object_object_add(k, "K8S", kube_json);
511 | json_object_object_add(j, "LOCAL_CONTAINER", k);
512 | } else
513 | json_object_put(k);
514 | }
515 | }
516 |
517 | *t_res = j;
518 | }
519 |
520 | /* ******************************************* */
521 |
522 | static void zmqHandler(void* t_bpfctx, void* t_data, int t_datasize) {
523 | struct json_object *json_event;
524 | char *json_str;
525 | eBPFevent *e = (eBPFevent*)t_data;
526 | eBPFevent event;
527 |
528 | memcpy(&event, e, sizeof(eBPFevent));
529 | ebpf_preprocess_event(&event);
530 | event2json(&event, &json_event);
531 | json_str = (char*) json_object_get_string(json_event);
532 |
533 | if(verbose) printf("%s\n", json_str);
534 |
535 | // writing event ----- //
536 | struct zmq_msg_hdr msg_hdr;
537 |
538 | /* 1 Send the event in JSON format */
539 | strncpy(msg_hdr.url, "flow", sizeof(msg_hdr.url));
540 | msg_hdr.version = 0;
541 | msg_hdr.size = strlen(json_str);
542 | zmq_send(gZMQsocket, &msg_hdr, sizeof(msg_hdr), ZMQ_SNDMORE);
543 | zmq_send(gZMQsocket, json_str, msg_hdr.size, 0);
544 |
545 | /* 2 Send the event in binary format */
546 | strncpy(msg_hdr.url, "ebpf", sizeof(msg_hdr.url));
547 | msg_hdr.version = 0;
548 | msg_hdr.size = sizeof(eBPFevent);
549 | zmq_send(gZMQsocket, &msg_hdr, sizeof(msg_hdr), ZMQ_SNDMORE);
550 | zmq_send(gZMQsocket, &event, msg_hdr.size, 0);
551 |
552 | json_object_put(json_event);
553 | ebpf_free_event(&event);
554 | }
555 |
556 | /* ******************************************* */
557 |
558 | static void handleTermination(int t_s) {
559 | if(!gRUNNING) return;
560 |
561 | printf("\r* Terminating * \n");
562 | gRUNNING = 0;
563 | }
564 |
--------------------------------------------------------------------------------
/ebpflowexport.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | ebpf_flow "./go"
5 | "os"
6 | "syscall"
7 | "os/signal"
8 | "fmt"
9 | )
10 |
11 | var gRUNNING bool = true
12 |
13 | func main () {
14 | // Open ebpflow
15 | ebpf := ebpf_flow.NewEbpflow(event_handler, 0)
16 | fmt.Println("Initialzed")
17 |
18 | // Handle interruption
19 | c := make(chan os.Signal)
20 | signal.Notify(c, os.Interrupt, syscall.SIGTERM)
21 | go func() {
22 | <-c
23 | gRUNNING = false
24 | }()
25 |
26 | // Poll events
27 | for gRUNNING == true {
28 | ebpf.PollEvent(10)
29 | }
30 |
31 | // Clean resources
32 | ebpf.Close()
33 | }
34 |
35 | func event_handler (event ebpf_flow.EBPFevent) {
36 | fmt.Printf("[pid:%d][etype:%d][%s][task:%s][path:%s]",
37 | event.Proc.Pid, event.EType, event.Ifname,
38 | event.Proc.Task, event.Proc.Full_Task_Path)
39 | fmt.Printf("[%s:%d <-> %s:%d]",
40 | event.Saddr.String(), event.Sport, event.Daddr.String(), event.Dport)
41 |
42 | if (event.Docker != nil) {
43 | fmt.Printf("[container_id: %s][name: %s]", event.Container_id[:16], event.Docker.Name)
44 | } else if (event.Kube != nil) {
45 | fmt.Printf("[container_id: %s][name: %s][ns: %s][pod: %s]",
46 | event.Container_id[:16], event.Kube.Name, event.Kube.Ns, event.Kube.Pod)
47 | }
48 | fmt.Printf("\n")
49 | }
50 |
--------------------------------------------------------------------------------
/examples/c++/README.md:
--------------------------------------------------------------------------------
1 | ### Build
2 | Before building the example libebpfflow.a must be built from the project root.
3 | The example can be built with g++.
4 | ```sh
5 | $ g++ usage_libebpfflow.cpp -o example ../../libebpfflow.a -lbcc -ljson-c -lcurl
6 | ```
7 |
8 | ### Usage
9 | ```
10 | $ sudo ./example
11 | ```
12 |
--------------------------------------------------------------------------------
/examples/c++/usage_libebpfflow.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "../../ebpf_flow.h"
3 |
4 | int gRUNNING = 1;
5 |
6 | static void event_handler(void* t_bpfctx, void* t_data, int t_datasize);
7 | static void handl_signint(int t_s);
8 |
9 | int main(int argc, char **argv) {
10 | // Return code
11 | ebpfRetCode rc;
12 | // This will store the object from which to poll
13 | void *ebpf;
14 |
15 | // Initialize the library to capture TCP events
16 | ebpf = init_ebpf_flow(NULL, event_handler, &rc,
17 | LIBEBPF_TCP|LIBEBPF_INCOMING|LIBEBPF_OUTCOMING);
18 |
19 | if(!ebpf) {
20 | printf("Error: %s\n", ebpf_print_error(rc));
21 | return(rc);
22 | }
23 |
24 | printf("Initialized, start polling events");
25 |
26 | // Polling event with a timeout of 10ms
27 | while(gRUNNING) {
28 | ebpf_poll_event(ebpf, 10);
29 | }
30 |
31 | // Cleaning environment
32 | term_ebpf_flow(ebpf);
33 |
34 | return 0;
35 | }
36 |
37 |
38 | static void event_handler(void* t_bpfctx, void* t_data, int t_datasize) {
39 | eBPFevent *e = (eBPFevent*)t_data;
40 | eBPFevent event;
41 |
42 | // Copy needed as ebpf_preprocess_event will modify the memory
43 | memcpy(&event, e, sizeof(eBPFevent));
44 | ebpf_preprocess_event(&event, 1, NULL);
45 |
46 | printf("[pid: %lu][%s]",
47 | (long unsigned int)event.proc.pid, event.proc.task);
48 |
49 | // Cleaning environment
50 | ebpf_free_event(&event);
51 | }
52 |
53 |
54 | static void handl_signint(int t_s) {
55 | printf("Terminating");
56 | gRUNNING = 0;
57 | }
58 |
--------------------------------------------------------------------------------
/go/ebpf_flow.go:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2018-19 - ntop.org
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software Foundation,
17 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 | *
19 | */
20 |
21 | /*
22 | * This package implements an interface to capture network events
23 | * by using eBPF probes attached to Linux kernel functions.
24 | * Note: only one event handler at a time is supported
25 | */
26 | package goebpf_flow
27 |
28 | import (
29 | "fmt"
30 | "net"
31 | "encoding/binary"
32 | "unsafe"
33 | )
34 |
35 | /*
36 | #cgo CFLAGS: -I.
37 | #cgo LDFLAGS: -L.. -lebpfflow -lcurl -lbcc -ljson-c -lstdc++
38 | #include "../ebpf_flow.h"
39 |
40 | #include
41 |
42 | void go_handleEvent(void *t_bpfctx, void *t_data, int t_datalen);
43 |
44 | static void* wrapper_init_ebpf_flow(__u16 flags) {
45 | ebpfRetCode rc;
46 | return init_ebpf_flow(NULL, go_handleEvent, &rc, flags);
47 | }
48 |
49 | static eBPFevent* preprocess(eBPFevent *e) {
50 | eBPFevent *event = malloc(sizeof(eBPFevent));
51 | memcpy(event, e, sizeof(eBPFevent));
52 | ebpf_preprocess_event(event);
53 | return event;
54 | }
55 | */
56 | import "C"
57 |
58 |
59 | /* ****************************************** */
60 | // ===== ===== KERNEL->USER EVENT ===== ===== //
61 | /* ****************************************** */
62 | const COMMAND_LEN = 16 // defined in sched.h
63 | const IFNAMSIZ = 16 // max is in limits.h -> NAME_MAX
64 | const CGROUP_ID_LEN = 64
65 |
66 | /*
67 | * Events types are forged as follows:
68 | * I_digit (=1): init events (e.g. connection creation)
69 | * (=2): update events on existing connection
70 | * (=3): connection closing
71 | * (=5): operation failed
72 | * II_digit (=0): tcp events
73 | * (=1): udp events
74 | * III_digit: discriminate the single event
75 | * The type is reported in eEBPFevent->etype
76 | */
77 | type etype uint32
78 | const (
79 | eTCP_ACPT = 100
80 | eTCP_CONN = 101
81 | eTCP_CONN_FAIL = 500
82 | eUDP_RECV = 210
83 | eUDP_SEND = 211
84 | eTCP_RETR = 200
85 | eTCP_CLOSE = 300
86 | )
87 |
88 | /*
89 | * Supported flags to filter events when initializating libebpfflow
90 | * Combinations of this flags allow to capture only subsets of events
91 | */
92 | type libebpflow_flag uint16
93 | const (
94 | LIBEBPF_TCP = 1 << 0
95 | LIBEBPF_UDP = 1 << 1
96 | LIBEBPF_INCOMING = 1 << 2
97 | LIBEBPF_OUTCOMING = 1 << 3
98 | LIBEBPF_TCP_CLOSE = 1 << 4
99 | LIBEBPF_TCP_RETR = 1 << 5
100 | )
101 |
102 | type TaskInfo struct {
103 | Pid uint32
104 | Uid uint32
105 | Gid uint32
106 | Task string // task name
107 | Full_Task_Path string
108 | }
109 |
110 | type DockerInfo struct {
111 | Name string // Docker container name
112 | }
113 |
114 | type KubeInfo struct {
115 | Name string // Container name
116 | Pod string // Container pod
117 | Ns string // Kubernetes namespace
118 | }
119 |
120 | type EBPFevent struct {
121 | Ktime uint64 // Absolute kernel time
122 | Ifname string // net-dev name
123 | Event_time uint64 // Event time, filled during event preprocessing
124 | Ip_version uint8
125 | Sent_packet uint8
126 | EType etype // event type, supported events are listed in event_type enum
127 |
128 | Saddr net.IP
129 | Daddr net.IP
130 |
131 | Proto uint8
132 | Sport, Dport uint16
133 | Latency_usec uint32
134 | Retransmissions uint16
135 |
136 | Proc TaskInfo
137 | Father TaskInfo
138 |
139 | Container_id string // Container identifier
140 | // Both next fields are initializated to nil and populated only during preprocessing
141 | Docker *DockerInfo
142 | Kube *KubeInfo
143 | }
144 |
145 | /*
146 | * Implements the methods to manage eBPF events. The correct usage requires:
147 | * 1. Initialization with NewEbpflow
148 | * 2. Event polling by using the funcion PollEvent
149 | * 3. Invocation of Term to clean resources
150 | */
151 | type Ebpflow struct {
152 | ebpf unsafe.Pointer
153 | }
154 |
155 | var gHandler func(EBPFevent)
156 |
157 | /*
158 | * Creates a new Ebpflow object from which start capuring events
159 | * Args:
160 | * handler - function handler, called whenever a new event is captured
161 | * and the function Poll is invoked
162 | * flags - filter the events based on which bits are active. If the value
163 | * is zero all events are captured. Check libebpflow_flag for more details
164 | */
165 | func NewEbpflow (handler func(EBPFevent), flags uint16) *Ebpflow {
166 | ebpfp, _ := C.wrapper_init_ebpf_flow(0)
167 | if ebpfp == nil {
168 | fmt.Println("Error, unable to initialize libebpfflow")
169 | return nil
170 | }
171 |
172 | gHandler = handler
173 | return &Ebpflow{ ebpf: ebpfp }
174 | }
175 |
176 | /*
177 | * Frees the resources used
178 | */
179 | func (e Ebpflow) Close () {
180 | C.term_ebpf_flow(e.ebpf);
181 | }
182 |
183 | /*
184 | * If a new event has been capured the handler provided on creation is
185 | * invoked with the new event as argument.
186 | * Args:
187 | * timeout - waits at most timeout milliseconds if no new event is captured
188 | */
189 | func (e Ebpflow) PollEvent(timeout int) {
190 | C.ebpf_poll_event(e.ebpf, (C.uint)(timeout))
191 | }
192 |
193 | /*
194 | * Translates information concerning a task from a C structure to a Go struct
195 | */
196 | func c2TaskInfo (p C.struct_taskInfo) TaskInfo {
197 | return TaskInfo {
198 | Pid: (uint32)(p.pid),
199 | Uid: (uint32)(p.uid),
200 | Gid: (uint32)(p.gid),
201 | Task: C.GoString(&p.task[0]),
202 | Full_Task_Path: C.GoString(p.full_task_path),
203 | }
204 | }
205 |
206 | //export go_handleEvent
207 | func go_handleEvent(t_bpfctx unsafe.Pointer, t_data unsafe.Pointer, t_datalen C.int) {
208 | event := (* C.eBPFevent)(t_data)
209 | filled_event := C.preprocess(event);
210 |
211 | goevent := EBPFevent {
212 | Ktime: (uint64)(filled_event.ktime),
213 | Ifname: C.GoString(&filled_event.ifname[0]),
214 | Proto: (uint8)(filled_event.proto),
215 | Latency_usec: (uint32)(filled_event.latency_usec),
216 | Retransmissions: (uint16)(filled_event.retransmissions),
217 | Ip_version: (uint8)(filled_event.ip_version),
218 | Sent_packet: (uint8)(filled_event.sent_packet),
219 | Sport: (uint16)(filled_event.sport),
220 | Dport: (uint16)(filled_event.dport),
221 | Proc: c2TaskInfo(filled_event.proc),
222 | Father: c2TaskInfo(filled_event.father),
223 | Container_id: C.GoString(&filled_event.container_id[0]),
224 | }
225 | if(filled_event.ip_version == 4) {
226 | ipv4 := (*C.struct_ipv4_addr_t)(unsafe.Pointer(&filled_event.addr[0]))
227 | goevent.Saddr = make(net.IP, 4)
228 | binary.LittleEndian.PutUint32(goevent.Saddr, (uint32)(ipv4.saddr))
229 | goevent.Daddr = make(net.IP, 4)
230 | binary.LittleEndian.PutUint32(goevent.Daddr, (uint32)(ipv4.daddr))
231 | } else {
232 | ipv6 := (*C.struct_ipv6_addr_t)(unsafe.Pointer(&filled_event.addr[0]))
233 | saddr := ([16]byte)(ipv6.saddr)
234 | goevent.Saddr = net.ParseIP(string(saddr[:]))
235 | daddr := ([16]byte)(ipv6.daddr)
236 | goevent.Daddr = net.ParseIP(string(daddr[:]))
237 | }
238 |
239 | if(filled_event.docker.name != nil) {
240 | goevent.Docker = &DockerInfo {
241 | Name: C.GoString(filled_event.docker.name),
242 | }
243 | } else {
244 | goevent.Docker = nil
245 | }
246 |
247 | if(filled_event.kube.pod != nil) {
248 | goevent.Kube = &KubeInfo {
249 | Name: C.GoString(filled_event.kube.name),
250 | Pod: C.GoString(filled_event.kube.pod),
251 | Ns: C.GoString(filled_event.kube.ns),
252 | }
253 | } else {
254 | goevent.Kube = nil
255 | }
256 |
257 | gHandler(goevent)
258 | C.ebpf_free_event(filled_event)
259 | }
260 |
261 |
262 |
263 |
264 |
265 |
--------------------------------------------------------------------------------
/pid2veth/Makefile:
--------------------------------------------------------------------------------
1 | pid2veth: pid2veth.c Makefile
2 | gcc -g pid2veth.c -o pid2veth
3 |
4 |
5 | clean:
6 | /bin/rm -f *~ pid2veth
7 |
--------------------------------------------------------------------------------
/pid2veth/README.md:
--------------------------------------------------------------------------------
1 | # pid2veth
2 | print the name of the virtual Ethernet associated with a container given the pid of a task in it.
3 | ### Build
4 | ```sh
5 | $ make
6 | ```
7 | ### Testing
8 | Start a container and get the pid of a process in it.
9 | ```sh
10 | $ sudo docker run --name=pid2veth_test -id ubuntu
11 | 2c93a6cf1e9719ef1c842981ae4efaae37b684fe67f4f3eace4e09cba2c6c2d3
12 | $ docker ps -q | xargs docker inspect --format '[Name:{{.Name}}][Pid:{{.State.Pid}}][ID:{{.ID}}]'
13 | [Name:/pid2veth_test][Pid:13033][ID:2c93a6cf1e97]
14 | ```
15 | Build and execute pid2veth.
16 | ```sh
17 | $ sudo ./pid2veth 13033
18 | veth0eb3759
19 | ```
20 |
21 |
22 |
--------------------------------------------------------------------------------
/pid2veth/pid2veth.c:
--------------------------------------------------------------------------------
1 | #define _GNU_SOURCE
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 |
16 |
17 | #ifndef NETLINK_GENERIC
18 | #define NETLINK_GENERIC 16
19 | #endif
20 |
21 | #ifndef SIOCETHTOOL
22 | #define SIOCETHTOOL 0x8946
23 | #endif
24 |
25 | #define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */
26 |
27 | struct ethtool_stats {
28 | __u32 cmd;
29 | __u32 n_stats;
30 | __u64 data[10];
31 | };
32 |
33 | /* ************************************************** */
34 |
35 | /*
36 | * Returns the peer of a veth network interface
37 | */
38 | int getp(const char *ifname) {
39 | struct ifreq ifr;
40 | struct ethtool_stats *stats;
41 | int err, fd, res;
42 |
43 | // Opening socket fd
44 | //(more details at http://man7.org/linux/man-pages/man7/netdevice.7.html)
45 | fd = socket(AF_INET, SOCK_DGRAM, 0);
46 | if(fd < 0)
47 | fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
48 |
49 | // Setting target interface
50 | strcpy(ifr.ifr_name, ifname);
51 | // Setting request args
52 | //(more details at https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ethtool.h#L662)
53 | stats =(struct ethtool_stats *) calloc(1, sizeof(struct ethtool_stats) +(sizeof(char) * 100));
54 | stats->cmd = ETHTOOL_GSTATS;
55 | ifr.ifr_data =(char*) stats;
56 |
57 | // Sending request to driver
58 | err = ioctl(fd, SIOCETHTOOL, &ifr);
59 | if(err < 0) {
60 | perror("> Cannot get stats information");
61 | res = -1;
62 | goto clean;
63 | }
64 | res =(int)(stats->data[0]);
65 |
66 | clean:
67 | if(fd != -1)
68 | close(fd);
69 | if(stats != NULL)
70 | free(stats);
71 | return res;
72 | }
73 |
74 | /* ************************************************** */
75 |
76 | /*
77 | * Returns the namespace filescritor in which the process with @pid is in
78 | */
79 | int getnsfd(int pid, FILE **f) {
80 | char buff[120];
81 | int fp;
82 |
83 | // Getting user ns file desc
84 | snprintf(buff, sizeof(buff), "/proc/%d/ns/net", pid);
85 | *f = fopen(buff, "r");
86 | if(*f == NULL) {
87 | printf("%s\n", strerror(errno));
88 | fp = -1;
89 | }
90 | else {
91 | fp = fileno(*f);
92 | }
93 |
94 | return fp;
95 | }
96 |
97 | /* ************************************************** */
98 |
99 | /*
100 | * Given a container's pid, store in buff the veth interface's name
101 | * assigned to the container. The buffer must allow for the storage of at least IF_NAMESIZE bytes
102 | */
103 | int pid2ifname(int pid, char *t_buff) {
104 | FILE *usrf, *containerf;
105 | int usrns_fd, containerns_fd;
106 | int if_idx, res = 0;
107 |
108 | // Getting ns file desc
109 | usrns_fd = getnsfd(1, &usrf);
110 | containerns_fd = getnsfd(pid, &containerf);
111 |
112 | // Jumping to ns
113 | setns(containerns_fd, CLONE_NEWNET);
114 |
115 | // Getting veth peer
116 | if_idx = getp("eth0");
117 | if(if_idx == -1) {
118 | res = -1;
119 | goto clean;
120 | }
121 |
122 | // Jumping back
123 | setns(usrns_fd, CLONE_NEWNET);
124 |
125 | // Converting
126 | if_indextoname(if_idx, t_buff);
127 |
128 | clean:
129 | if(usrf != NULL)
130 | fclose(usrf);
131 |
132 | if(containerf != NULL)
133 | fclose(containerf);
134 |
135 | return res;
136 | }
137 |
138 | /* ************************************************** */
139 |
140 | void help() {
141 | printf(
142 | "pid2veth: print the name of the virtual Ethernet associated with a container \n"
143 | "given the pid of a task in it \n"
144 | "Usage: ./pid2veth \n"
145 | );
146 | }
147 |
148 | /* ************************************************** */
149 |
150 | int main(int argc, char **argv) {
151 | char buff[IF_NAMESIZE] = { '\0' };
152 | int pid;
153 |
154 | if(argc < 2) {
155 | printf("> Please provide the pid of a task inside a container \n\n");
156 | help();
157 | return(-1);
158 | }
159 |
160 | if(getuid() != 0) {
161 | printf("> Please run as root \n\n");
162 | help();
163 | return 0;
164 | }
165 |
166 | pid = atoi(argv[1]);
167 | if(pid2ifname(pid, buff) == 0) {
168 | printf("%s\n", buff);
169 | }
170 |
171 | return 0;
172 | }
173 |
--------------------------------------------------------------------------------
/utils/README.md:
--------------------------------------------------------------------------------
1 | kubectl_show_veth.sh
2 | Kubernetes tool that shows veth and container names
3 |
4 | docker_show_veth.sh
5 | Docket tool that shows veth and container names
6 |
7 |
8 |
--------------------------------------------------------------------------------
/utils/docker_show_veth.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Prints the veth used by running docker containers
5 | #
6 |
7 | if [ "$EUID" -ne 0 ]
8 | then echo "Please run as root"
9 | exit
10 | fi
11 |
12 | echo "veth containerId"
13 | echo "-----------------------"
14 |
15 | for container in $(docker ps --format '{{.Names}}'); do
16 | iflink=$(docker exec -it $container bash -c 'cat /sys/class/net/eth*/iflink')
17 | for net in $iflink; do
18 | net=$(echo $net|tr -d '\r')
19 | veth=$(grep -l $net /sys/class/net/veth*/ifindex)
20 | veth=$(echo $veth|sed -e 's;^.*net/\(.*\)/ifindex$;\1;')
21 | echo $veth $container
22 | done
23 | done
24 |
--------------------------------------------------------------------------------
/utils/kubectl_show_veth.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$EUID" -ne 0 ]
4 | then echo "Please run as root"
5 | exit
6 | fi
7 |
8 | if [ -x "/snap/bin/microk8s.kubectl" ]; then
9 | kubectl="/snap/bin/microk8s.kubectl"
10 | else
11 | kubectl=$(which kubectl)
12 | fi
13 |
14 | for listNamespace in $($kubectl get namespace -o jsonpath='{.items[*].metadata.name}'); do
15 | for listPod in $($kubectl get pod --namespace=$listNamespace -o jsonpath='{.items[*].metadata.name}'); do
16 |
17 | id=`$kubectl exec $listPod -- cat /sys/class/net/eth0/iflink 2>1 /dev/null`
18 | if test "$id" != ""; then
19 | ifname=`ip -o link|grep ^$id:|cut -d ':' -f 2|cut -d '@' -f 1|tr -d '[:blank:]'`
20 | echo "$ifname $listPod"
21 | fi
22 | done
23 | done
24 |
--------------------------------------------------------------------------------
/wireshark/Makefile:
--------------------------------------------------------------------------------
1 |
2 | EXTCAP_PATH_LINUX=/usr/lib/x86_64-linux-gnu/wireshark/extcap/ebpfdump
3 |
4 | ###############################################################
5 |
6 | HAS_JSON=$(shell pkg-config --exists json-c; echo $$?)
7 | ifeq ($(HAS_JSON), 0)
8 | JSON_INC = $(shell pkg-config --cflags json-c) -DHAVE_JSONC
9 | JSON_LIB = $(shell pkg-config --libs json-c)
10 | endif
11 |
12 | ###############################################################
13 |
14 | HAS_LIBCURL=$(shell pkg-config --exists libcurl; echo $$?)
15 | ifeq ($(HAS_LIBCURL), 0)
16 | LIBCURL_INC = $(shell pkg-config --cflags libcurl) -DHAVE_LIBCURL
17 | LIBCURL_LIB = $(shell pkg-config --libs libcurl)
18 | endif
19 |
20 | ###############################################################
21 |
22 | CFLAGS=-g -I.. $(JSON_INC) $(LIBCURL_INC) -Wformat-truncation
23 | BASE_LIBS=$(JSON_LIB) $(LIBCURL_LIB) -lpcap -lzmq
24 |
25 | OS := $(shell uname -s)
26 | ifeq ($(OS),Linux)
27 | LIBS=-L .. -lebpfflow -lbcc $(BASE_LIBS)
28 | else
29 | LIBS=$(BASE_LIBS)
30 | endif
31 |
32 | ebpfdump: ebpfdump.c Makefile
33 | g++ $(CFLAGS) -g ebpfdump.c -o ebpfdump $(LIBS)
34 |
35 |
36 | install: ebpfdump
37 | sudo cp ebpfdump $(EXTCAP_PATH_LINUX)
38 | sudo chown root:root $(EXTCAP_PATH_LINUX)
39 | sudo chmod gou+s ebpfdump $(EXTCAP_PATH_LINUX)
40 |
41 | clean:
42 | /bin/rm -f ebpfdump
43 |
44 |
--------------------------------------------------------------------------------
/wireshark/README.md:
--------------------------------------------------------------------------------
1 | ### Introduction
2 | This is an extcap plugin that allows wireshark to capture system-generated events
3 |
4 | # Installation
5 | Once you compiled this plugin you need to install it in the 'Extcap path' as specified in the wireshark Help menu
6 |
7 | As of the ebpf.lua you need to copy it into ~/.wireshark/plugins/ to interpret eBPF events
8 |
9 | # Usage
10 | Starting wireshark you will see a new interface named 'eBPF interface'. Select it, and start the packet capture.
11 |
12 |
--------------------------------------------------------------------------------
/wireshark/ebpf.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- (C) 2019 - ntop.org
3 | --
4 | -- This plugin is part of libebpflow (https://github.com/ntop/libebpfflow)
5 | --
6 | -- This program is free software; you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation; either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program; if not, write to the Free Software Foundation,
18 | -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 | --
20 |
21 | local ebpfflow_proto = Proto("ebpfflow", "ebpfflow Protocol Interpreter")
22 |
23 | ebpfflow_proto.fields = {}
24 |
25 | local f_frame_len = Field.new("frame.len")
26 |
27 | local ebpfflow_fds = ebpfflow_proto.fields
28 | ebpfflow_fds.ktime_sec = ProtoField.new("Kernel time (sec)", "ebpfflow.ktime.sec", ftypes.UINT32)
29 | ebpfflow_fds.ktime_usec = ProtoField.new("Kernel time (usec)", "ebpfflow.ktime.sec", ftypes.UINT32)
30 | ebpfflow_fds.ifname = ProtoField.new("Interface name", "ebpfflow.ifname", ftypes.STRING)
31 | ebpfflow_fds.evttime_sec = ProtoField.uint64("ebpfflow.evttime.sec", "Event time (sec)")
32 | ebpfflow_fds.evttime_usec = ProtoField.uint64("ebpfflow.evttime.sec", "Event time (usec)")
33 | ebpfflow_fds.ip_version = ProtoField.uint8("ebpfflow.ip_version", "Event IP protocol version")
34 | ebpfflow_fds.direction = ProtoField.new("Event direction", "ebpfflow.direction", ftypes.STRING)
35 | ebpfflow_fds.etype = ProtoField.new("Event type", "ebpfflow.etype", ftypes.STRING)
36 | ebpfflow_fds.sipaddr4 = ProtoField.new("IPv4 src address", "ebpfflow.srcipv4", ftypes.IPv4)
37 | ebpfflow_fds.dipaddr4 = ProtoField.new("IPv4 dst address", "ebpfflow.dstipv4", ftypes.IPv4)
38 | ebpfflow_fds.sipaddr6 = ProtoField.new("IPv6 src address", "ebpfflow.srcipv6", ftypes.IPv6)
39 | ebpfflow_fds.dipaddr6 = ProtoField.new("IPv6 dst address", "ebpfflow.dstipv6", ftypes.IPv6)
40 | ebpfflow_fds.proto = ProtoField.new("Event protocol", "ebpfflow.proto", ftypes.STRING)
41 | ebpfflow_fds.sport = ProtoField.uint16("ebpfflow.sport", "Event source port")
42 | ebpfflow_fds.dport = ProtoField.uint16("ebpfflow.dport", "Event destination port")
43 | ebpfflow_fds.latency = ProtoField.uint32("ebpfflow.latency", "Event latency (usec)")
44 | ebpfflow_fds.retr = ProtoField.uint16("ebpfflow.retr", "Event retransmissions")
45 |
46 | ebpfflow_fds.proc_pid = ProtoField.uint32("ebpfflow.proc_pid", "Event Process PID")
47 | ebpfflow_fds.proc_tid = ProtoField.uint32("ebpfflow.proc_tid", "Event Process TID")
48 | ebpfflow_fds.proc_uid = ProtoField.uint32("ebpfflow.proc_uid", "Event Process UID")
49 | ebpfflow_fds.proc_gid = ProtoField.uint32("ebpfflow.proc_gid", "Event Process GID")
50 | ebpfflow_fds.proc_task = ProtoField.new("Event Process Task", "ebpfflow.proc_task", ftypes.STRING)
51 |
52 | ebpfflow_fds.father_pid = ProtoField.uint32("ebpfflow.father_pid", "Event Father PID")
53 | ebpfflow_fds.father_tid = ProtoField.uint32("ebpfflow.father_tid", "Event Father TID")
54 | ebpfflow_fds.father_uid = ProtoField.uint32("ebpfflow.father_uid", "Event Father UID")
55 | ebpfflow_fds.father_gid = ProtoField.uint32("ebpfflow.father_gid", "Event Father GID")
56 | ebpfflow_fds.father_task = ProtoField.new("Event Father Task", "ebpfflow.father_task", ftypes.STRING)
57 |
58 | ebpfflow_fds.container_id = ProtoField.new("Event Container Id", "ebpfflow.container_id", ftypes.STRING)
59 |
60 | local f_eth_trailer = Field.new("eth.trailer")
61 | local f_eth_type = Field.new("eth.type")
62 |
63 | local ebpfflow_event = "eBPFFlow Event"
64 |
65 | -- ebpfflow_fds.application_protocol = ProtoField.new("ebpfflow Application Protocol", "ebpfflow.protocol.application", ftypes.UINT8, nil, base.DEC)
66 | -- ebpfflow_fds.name = ProtoField.new("ebpfflow Protocol Name", "ebpfflow.protocol.name", ftypes.STRING)
67 |
68 | -- ###############################################
69 |
70 | local f_null_type = Field.new("null.type")
71 |
72 | local debug = false
73 |
74 | -- ###############################################
75 |
76 | function ebpfflow_proto.init()
77 |
78 | end
79 |
80 | -- ###############################################
81 |
82 | -- Print contents of `tbl`, with indentation.
83 | -- You can call it as tprint(mytable)
84 | -- The other two parameters should not be set
85 | function tprint(s, l, i)
86 | l = (l) or 1000; i = i or "";-- default item limit, indent string
87 | if (l<1) then io.write("ERROR: Item limit reached.\n"); return l-1 end;
88 | local ts = type(s);
89 | if (ts ~= "table") then io.write(i..' '..ts..' '..tostring(s)..'\n'); return l-1 end
90 | io.write(i..' '..ts..'\n');
91 | for k,v in pairs(s) do
92 | local indent = ""
93 |
94 | if(i ~= "") then
95 | indent = i .. "."
96 | end
97 | indent = indent .. tostring(k)
98 |
99 | l = tprint(v, l, indent);
100 | if (l < 0) then break end
101 | end
102 |
103 | return l
104 | end
105 |
106 | -- ###############################################
107 |
108 | local function direction2str(d)
109 | if(d == 1) then
110 | return("Sent")
111 | else
112 | return("Received")
113 | end
114 | end
115 |
116 | -- ###############################################
117 |
118 | local function proto2str(_p)
119 | p = tonumber(_p)
120 | if(p == 6) then return("TCP")
121 | elseif(p == 17) then return("UDP")
122 | else return(_p)
123 | end
124 | end
125 |
126 | -- ###############################################
127 |
128 | local function event2str(e)
129 | if(e == 100) then return("TCP_Accept")
130 | elseif(e == 101) then return("TCP_Connect")
131 | elseif(e == 200) then return("TCP_Retransmission")
132 | elseif(e == 210) then return("UDP_Receive")
133 | elseif(e == 211) then return("UDP_Send")
134 | elseif(e == 300) then return("TCP_Close")
135 | elseif(e == 500) then return("TCP_ConnectionFailed")
136 | else
137 | return(""..e)
138 | end
139 | end
140 |
141 | -- ###############################################
142 |
143 | local function getstring(finfo)
144 | local ok, val = pcall(tostring, finfo)
145 | if not ok then val = "(unknown)" end
146 | return val
147 | end
148 |
149 | local function getval(finfo)
150 | local ok, val = pcall(tostring, finfo)
151 | if not ok then val = nil end
152 | return val
153 | end
154 |
155 | function dump_pinfo(pinfo)
156 | local fields = { all_field_infos() }
157 | for ix, finfo in ipairs(fields) do
158 | -- output = output .. "\t[" .. ix .. "] " .. finfo.name .. " = " .. getstring(finfo) .. "\n"
159 | --print(finfo.name .. "\n")
160 | print("\t[" .. ix .. "] " .. finfo.name .. " = " .. getstring(finfo) .. "\n")
161 | end
162 | end
163 |
164 | -- ###############################################
165 |
166 | function ebpfflow_parse_extended_ebpf_data(pinfo, tvb, tree, offset)
167 | local ebpf_subtree = tree:add(ebpfflow_proto, tvb(), "eBPFFlow Protocol")
168 |
169 | pinfo.cols.protocol:set("eBPF")
170 | pinfo.cols.info:set(ebpfflow_event)
171 |
172 | ebpf_subtree:add_le(ebpfflow_fds.ktime_sec, tvb:range(offset,4))
173 | offset = offset + 4
174 |
175 | ebpf_subtree:add_le(ebpfflow_fds.ktime_usec, tvb:range(offset,4))
176 | offset = offset + 4
177 |
178 | ebpf_subtree:add(ebpfflow_fds.ifname, tvb:range(offset,16):stringz())
179 | offset = offset + 16
180 |
181 | ebpf_subtree:add_le(ebpfflow_fds.evttime_sec, tvb:range(offset,8))
182 | offset = offset + 8
183 |
184 | ebpf_subtree:add_le(ebpfflow_fds.evttime_usec, tvb:range(offset,8))
185 | offset = offset + 8
186 |
187 | r = tvb:range(offset,1)
188 | ip_version = r:le_uint()
189 | ebpf_subtree:add_le(ebpfflow_fds.ip_version, r, ip_version)
190 | offset = offset + 1
191 |
192 | r = tvb:range(offset,1)
193 | direction = r:le_uint()
194 | ebpf_subtree:add_le(ebpfflow_fds.direction, r, direction2str(direction))
195 | offset = offset + 1
196 |
197 | etype_r = tvb:range(offset,2)
198 | etype = etype_r:le_uint(etype_r)
199 | evt = event2str(etype)
200 | pinfo.cols.info:set(evt)
201 | ebpf_subtree:add(ebpfflow_fds.etype, evt)
202 | offset = offset + 1
203 |
204 | if(ip_version == 4) then
205 | offset = offset + 5
206 | ebpf_subtree:add(ebpfflow_fds.sipaddr4, tvb:range(offset,4))
207 | offset = offset + 8
208 | ebpf_subtree:add(ebpfflow_fds.dipaddr4, tvb:range(offset,4))
209 | offset = offset + 19
210 | else
211 | ebpf_subtree:add(ebpfflow_fds.sipaddr6, tvb:range(offset,16))
212 | offset = offset + 16
213 | --
214 | ebpf_subtree:add(ebpfflow_fds.dipaddr6, tvb:range(offset,16))
215 | offset = offset + 16
216 | end
217 |
218 | offset = offset + 5 -- padding
219 |
220 | r = tvb:range(offset,1)
221 | proto = r:le_uint()
222 | ebpf_subtree:add(ebpfflow_fds.proto, proto2str(proto))
223 | offset = offset + 1
224 |
225 | offset = offset + 1 -- pad
226 |
227 | ebpf_subtree:add_le(ebpfflow_fds.sport, tvb:range(offset,2))
228 | offset = offset + 2
229 |
230 | ebpf_subtree:add_le(ebpfflow_fds.dport, tvb:range(offset,2))
231 | offset = offset + 2
232 |
233 | offset = offset + 2 -- padding
234 | if(proto == 6) then
235 | -- TCP
236 | ebpf_subtree:add_le(ebpfflow_fds.latency, tvb:range(offset,4))
237 | offset = offset + 4
238 |
239 | ebpf_subtree:add_le(ebpfflow_fds.retr, tvb:range(offset,2))
240 | offset = offset + 2
241 | else
242 | offset = offset + 6
243 | end
244 |
245 | offset = offset + 2 -- pad
246 |
247 | -- Tasks
248 | ebpf_subtree:add_le(ebpfflow_fds.proc_pid, tvb:range(offset,4))
249 | offset = offset + 4
250 |
251 | ebpf_subtree:add_le(ebpfflow_fds.proc_tid, tvb:range(offset,4))
252 | offset = offset + 4
253 |
254 | ebpf_subtree:add_le(ebpfflow_fds.proc_uid, tvb:range(offset,4))
255 | offset = offset + 4
256 |
257 | ebpf_subtree:add_le(ebpfflow_fds.proc_gid, tvb:range(offset,4))
258 | offset = offset + 4
259 |
260 | ebpf_subtree:add(ebpfflow_fds.proc_task, tvb:range(offset,16):stringz())
261 | offset = offset + 16
262 |
263 | offset = offset + 8 -- ptr
264 |
265 | -- Father Task
266 | ebpf_subtree:add_le(ebpfflow_fds.father_pid, tvb:range(offset,4))
267 | offset = offset + 4
268 |
269 | ebpf_subtree:add_le(ebpfflow_fds.father_tid, tvb:range(offset,4))
270 | offset = offset + 4
271 |
272 | ebpf_subtree:add_le(ebpfflow_fds.father_uid, tvb:range(offset,4))
273 | offset = offset + 4
274 |
275 | ebpf_subtree:add_le(ebpfflow_fds.father_gid, tvb:range(offset,4))
276 | offset = offset + 4
277 |
278 | ebpf_subtree:add(ebpfflow_fds.father_task, tvb:range(offset,16):stringz())
279 | offset = offset + 16
280 |
281 | offset = offset + 8 -- ptr
282 |
283 | -- Container
284 | ebpf_subtree:add(ebpfflow_fds.container_id, tvb:range(offset,128))
285 | offset = offset + 128
286 | end
287 |
288 | -- ###############################################
289 |
290 | function ebpfflow_parse_ebpf_data(pinfo, tvb, tree, offset)
291 | local ebpf_subtree = tree:add(ebpfflow_proto, tvb(), "eBPFFlow Protocol")
292 |
293 | ebpf_subtree:add_le(ebpfflow_fds.proc_pid, tvb:range(offset,4))
294 | offset = offset + 4
295 |
296 | ebpf_subtree:add_le(ebpfflow_fds.proc_tid, tvb:range(offset,4))
297 | offset = offset + 4
298 |
299 | ebpf_subtree:add_le(ebpfflow_fds.proc_uid, tvb:range(offset,4))
300 | offset = offset + 4
301 |
302 | ebpf_subtree:add_le(ebpfflow_fds.proc_gid, tvb:range(offset,4))
303 | offset = offset + 4
304 |
305 | ebpf_subtree:add_le(ebpfflow_fds.proc_task, tvb:range(offset,8))
306 | offset = offset + 8
307 |
308 | ebpf_subtree:add_le(ebpfflow_fds.container_id, tvb:range(offset,12))
309 | offset = offset + 12
310 | end
311 |
312 | -- ###############################################
313 |
314 | -- the dissector function callback
315 | function ebpfflow_proto.dissector(tvb, pinfo, tree)
316 | -- Wireshark dissects the packet twice. We ignore the first
317 | -- run as on that step the packet is still undecoded
318 | -- The trick below avoids to process the packet twice
319 |
320 | if(pinfo.visited == true) then
321 | local null_type = f_null_type()
322 | local eth_trailer = f_eth_trailer()
323 | local eth_type = f_eth_type()
324 |
325 | if(eth_type ~= nil) then
326 | local eth_type = getval(eth_type)
327 | if(eth_type == "0x00000000") then
328 | ebpfflow_parse_extended_ebpf_data(pinfo, tvb, tree, 14)
329 | end
330 | end
331 |
332 | if(eth_trailer ~= nil) then
333 | local eth_trailer = getval(eth_trailer)
334 | local magic = string.sub(eth_trailer, 1, 5)
335 |
336 | if(magic == "19:68") then
337 | local frame_len = getval(f_frame_len())
338 |
339 | ebpfflow_parse_ebpf_data(pinfo, tvb, tree, frame_len - 36)
340 | end
341 | end
342 |
343 | if(null_type ~= nil) then
344 | local null_type = getval(null_type)
345 |
346 | if(null_type == "0x000007e3") then
347 | ebpfflow_parse_extended_ebpf_data(pinfo, tvb, tree, 4)
348 | end
349 | end
350 | end
351 |
352 | -- ###########################################
353 |
354 | -- As we do not need to add fields to the dissection
355 | -- there is no need to process the packet multiple times
356 | if(pinfo.visited == true) then return end
357 |
358 | num_pkts = num_pkts + 1
359 | if((num_pkts > 1) and (pinfo.number == 1)) then return end
360 |
361 | ebpfflow_dissector(tvb, pinfo, tree)
362 | end
363 |
364 | register_postdissector(ebpfflow_proto)
365 |
--------------------------------------------------------------------------------
/wireshark/ebpfdump.c:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * (C) 2019 - ntop.org
4 | *
5 | *
6 | * This program is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU Lessed General Public License as published by
8 | * the Free Software Foundation; either version 2.1 of the License, or
9 | * (at your option) any later version.
10 | *
11 | */
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #ifdef __linux__
30 | #include
31 | #include
32 | #define th_sport source
33 | #define th_dport dest
34 | #define uh_sport source
35 | #define uh_dport dest
36 | #else
37 | #include
38 | #include
39 | #define s6_addr32 __u6_addr.__u6_addr32
40 | #endif
41 | #include "pcapio.c"
42 | #include "ebpf_flow.h"
43 |
44 | struct ebpf_event {
45 | u_int32_t pid, tid, uid, gid;
46 | char process_name[8];
47 | char container_id[12];
48 | };
49 |
50 | #define EBPFDUMP_INTERFACE "ebpf"
51 |
52 | #define SOCKET_LIBEBPF 2019
53 | #define EXIT_SUCCESS 0
54 |
55 | #define EBPFDUMP_MAX_NBPF_LEN 8192
56 | #define EBPFDUMP_MAX_DATE_LEN 26
57 | #define EBPFDUMP_MAX_NAME_LEN 4096
58 |
59 | #define EBPFDUMP_VERSION_MAJOR "0"
60 | #define EBPFDUMP_VERSION_MINOR "1"
61 | #define EBPFDUMP_VERSION_RELEASE "0"
62 |
63 | #define EXTCAP_OPT_LIST_INTERFACES 'l'
64 | #define EXTCAP_OPT_VERSION 'v'
65 | #define EXTCAP_OPT_LIST_DLTS 'L'
66 | #define EXTCAP_OPT_INTERFACE 'i'
67 | #define EXTCAP_OPT_CONFIG 'c'
68 | #define EXTCAP_OPT_CAPTURE 'C'
69 | #define EXTCAP_OPT_FIFO 'F'
70 | #define EXTCAP_OPT_DEBUG 'D'
71 | #define EBPFDUMP_OPT_HELP 'h'
72 | #define EBPFDUMP_OPT_IFNAME 'n'
73 | #define EBPFDUMP_OPT_CUSTOM_NAME 'N'
74 |
75 | #define EBPFDUMP_EBPFEVENTS_NAME "ebpfevents"
76 | #define EBPFDUMP_EBPFZMQEVENTS_NAME "ebpfzmqevents"
77 | #define EBPFDUMP_ZMQ_ADDRESS "tcp://0.0.0.0:6789"
78 | #define EBPFDUMP_ZMQ_TOPIC "ebpf"
79 |
80 | static struct option longopts[] = {
81 | /* mandatory extcap options */
82 | { "extcap-interfaces", no_argument, NULL, EXTCAP_OPT_LIST_INTERFACES },
83 | { "extcap-version", optional_argument, NULL, EXTCAP_OPT_VERSION },
84 | { "extcap-dlts", no_argument, NULL, EXTCAP_OPT_LIST_DLTS },
85 | { "extcap-interface", required_argument, NULL, EXTCAP_OPT_INTERFACE },
86 | { "extcap-config", no_argument, NULL, EXTCAP_OPT_CONFIG },
87 | { "capture", no_argument, NULL, EXTCAP_OPT_CAPTURE },
88 | { "fifo", required_argument, NULL, EXTCAP_OPT_FIFO },
89 | { "debug", optional_argument, NULL, EXTCAP_OPT_DEBUG },
90 |
91 | /* custom extcap options */
92 | { "help", no_argument, NULL, EBPFDUMP_OPT_HELP },
93 | { "ifname", required_argument, NULL, EBPFDUMP_OPT_IFNAME },
94 | { "custom-name", required_argument, NULL, EBPFDUMP_OPT_CUSTOM_NAME },
95 |
96 | {0, 0, 0, 0}
97 | };
98 |
99 | typedef struct _extcap_interface {
100 | const char * interface;
101 | const char * description;
102 | u_int16_t dlt;
103 | const char * dltname;
104 | const char * dltdescription;
105 | } extcap_interface;
106 |
107 | #define DLT_EN10MB 1
108 |
109 | static extcap_interface extcap_interfaces[] = {
110 | { EBPFDUMP_INTERFACE, "eBPF interface", DLT_EN10MB, NULL, "The EN10MB Ethernet2 DLT" },
111 | };
112 |
113 | struct zmq_msg_hdr {
114 | char url[32];
115 | u_int32_t version;
116 | u_int32_t size;
117 | };
118 |
119 | struct zmq_info {
120 | u_int8_t initialized;
121 | void *z_socket, *z_context;
122 | };
123 |
124 | struct zmq_info zmq;
125 |
126 | #define MAX_NUM_INT 32
127 |
128 | static size_t extcap_interfaces_num = sizeof(extcap_interfaces) / sizeof(extcap_interface);
129 | static char *extcap_selected_interface = NULL;
130 | static char *pcap_selected_interface = NULL;
131 | static char *extcap_capture_fifo = NULL;
132 | static FILE *fp = NULL;
133 | static FILE *log_fp = NULL;
134 | static char *all_interfaces[MAX_NUM_INT] = { NULL };
135 | static u_int8_t num_all_interfaces = 0;
136 | static int32_t thiszone;
137 | static char *containerId = NULL;
138 |
139 | /* ***************************************************** */
140 | /* ***************************************************** */
141 |
142 | /* LRU cache */
143 |
144 | #define NUM_LRU_ENTRIES 256
145 |
146 | struct lru_cache_entry {
147 | u_int32_t key;
148 | u_int8_t is_full;
149 | struct ebpf_event value;
150 | };
151 |
152 | struct lru_cache {
153 | struct lru_cache_entry entries[NUM_LRU_ENTRIES];
154 | };
155 |
156 | void lru_cache_init(struct lru_cache *c) {
157 | memset(c, 0, sizeof(lru_cache));
158 | }
159 |
160 | u_int8_t lru_find_cache(struct lru_cache *c, u_int32_t key,
161 | struct ebpf_event *value) {
162 | u_int32_t slot = key % NUM_LRU_ENTRIES;
163 |
164 | if(c->entries[slot].is_full) {
165 | memcpy(value, &c->entries[slot].value, sizeof(struct ebpf_event));
166 | return(1);
167 | } else
168 | return(0);
169 | }
170 |
171 | void lru_add_to_cache(struct lru_cache *c, u_int32_t key, struct ebpf_event *value) {
172 | u_int32_t slot = key % NUM_LRU_ENTRIES;
173 |
174 | c->entries[slot].is_full = 1, c->entries[slot].key = key;
175 | memcpy(&c->entries[slot].value, value, sizeof(struct ebpf_event));
176 | }
177 |
178 | struct lru_cache received_events;
179 |
180 | /* ***************************************************** */
181 | /* ***************************************************** */
182 |
183 | inline u_int min(u_int a, u_int b) { return((a < b) ? a : b); }
184 |
185 | void sigproc(int sig) {
186 | fprintf(stdout, "Exiting...");
187 | fflush(stdout);
188 | exit(0);
189 | }
190 |
191 | /* ***************************************************** */
192 |
193 | static int initZMQ() {
194 | int val = 1;
195 |
196 | zmq.z_context = zmq_ctx_new();
197 | zmq.z_socket = zmq_socket(zmq.z_context, ZMQ_SUB);
198 | zmq_setsockopt(zmq.z_socket, ZMQ_TCP_KEEPALIVE, &val, sizeof(val));
199 |
200 | if(zmq_bind(zmq.z_socket, EBPFDUMP_ZMQ_ADDRESS) != 0) {
201 | printf("Unable to bind to ZMQ socket %s: %s\n",
202 | EBPFDUMP_ZMQ_ADDRESS, strerror(errno));
203 | return(-1);
204 | } else {
205 | if(log_fp) fprintf(log_fp, "Listening on %s for ZMQ events\n", EBPFDUMP_ZMQ_ADDRESS);
206 | }
207 |
208 | if(zmq_setsockopt(zmq.z_socket, ZMQ_SUBSCRIBE, EBPFDUMP_ZMQ_TOPIC, strlen(EBPFDUMP_ZMQ_TOPIC)) != 0)
209 | return(-1);
210 |
211 | zmq.initialized = 1;
212 |
213 | return(0);
214 | }
215 |
216 | /* ***************************************************** */
217 |
218 | static void termZMQ() {
219 | if(zmq.initialized) {
220 | zmq_close(zmq.z_socket);
221 | zmq_ctx_destroy(zmq.z_context);
222 | }
223 | }
224 |
225 | /* ***************************************************** */
226 |
227 | void extcap_version() {
228 | /* Print version */
229 | printf("extcap {version=%s.%s.%s}\n",
230 | EBPFDUMP_VERSION_MAJOR, EBPFDUMP_VERSION_MINOR,
231 | EBPFDUMP_VERSION_RELEASE);
232 | }
233 |
234 | /* ***************************************************** */
235 |
236 | int docker_list_interfaces() {
237 | FILE *fd;
238 | int rc, found = 0;
239 | struct stat statbuf;
240 | const char *dcmd = "/usr/bin/docker";;
241 | char cmd[256];
242 |
243 | snprintf(cmd, sizeof(cmd), "%s ps --format '{{.Names}}'", dcmd);
244 |
245 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
246 |
247 | if((fd = popen(cmd, "r")) != NULL) {
248 | char line[1024];
249 |
250 | if(fgets(line, sizeof(line)-1, (FILE*) fd)) {
251 | char *tmp, *container = strtok_r(line, " ", &tmp);
252 |
253 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, line);
254 |
255 | while(container) {
256 | FILE *fd1;
257 |
258 | container[strlen(container)-1] = '\0'; /* Remove trailing \r */
259 | snprintf(cmd, sizeof(cmd), "%s exec %s bash -c 'cat /sys/class/net/eth0/iflink'", dcmd, container);
260 |
261 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
262 |
263 | if((fd1 = popen(cmd, "r")) != NULL) {
264 | char netId[64];
265 |
266 | if(fgets(netId, sizeof(netId)-1, (FILE*)fd1)) {
267 | FILE *fd2;
268 |
269 | netId[strlen(netId)-1] ='\0';
270 |
271 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, netId);
272 |
273 | snprintf(cmd, sizeof(cmd), "/bin/grep -l %s /sys/class/net/veth*/ifindex", netId);
274 |
275 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
276 |
277 | if((fd2 = popen(cmd, "r")) != NULL) {
278 | char ifname[128];
279 |
280 | if(fgets(ifname, sizeof(ifname)-1, (FILE*)fd2)) {
281 | char *veth = &ifname[15];
282 |
283 | veth[11] = '\0';
284 |
285 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, veth);
286 |
287 | printf("value {arg=0}{value=%s@%s}{display=Container %s}\n", veth, container, container);
288 | found = 1;
289 | }
290 |
291 | fclose(fd2);
292 | }
293 | } else
294 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] No output read :-(\n", __FILE__, __LINE__);
295 |
296 | fclose(fd1);
297 | } else {
298 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Command failed :-(\n", __FILE__, __LINE__);
299 | }
300 |
301 | container = strtok_r(NULL, " ", &tmp);
302 | }
303 | }
304 |
305 | fclose(fd);
306 | }
307 |
308 |
309 | return(found);
310 | }
311 |
312 | /* ***************************************************** */
313 |
314 | void kubectl_list_interfaces() {
315 | FILE *fd;
316 | int rc ;
317 | struct stat statbuf;
318 | const char *kcmd;
319 | char cmd[256];
320 |
321 | if(stat("/snap/bin/microk8s.kubectl", &statbuf) == 0)
322 | kcmd = "/snap/bin/microk8s.kubectl";
323 | else if(stat("/usr/bin/kubectl", &statbuf) == 0)
324 | kcmd = "/usr/bin/kubectl";
325 | else
326 | return; /* No kubectk */
327 |
328 | snprintf(cmd, sizeof(cmd), "%s get namespace -o 'jsonpath={.items[*].metadata.name}'",
329 | kcmd);
330 |
331 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
332 |
333 | if((fd = popen(cmd, "r")) != NULL) {
334 | char line[1024];
335 |
336 | if(fgets(line, sizeof(line)-1, (FILE*) fd)) {
337 | char *tmp, *ns = strtok_r(line, " ", &tmp);
338 |
339 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, line);
340 |
341 | while(ns) {
342 | FILE *fd1;
343 |
344 | snprintf(cmd, sizeof(cmd), "%s get pod --namespace=%s -o jsonpath='{.items[*].metadata.name}'",
345 | kcmd, ns);
346 |
347 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
348 |
349 | if((fd1 = popen(cmd, "r")) != NULL) {
350 | char pod[512];
351 |
352 | while(fgets(pod, sizeof(pod)-1, (FILE*)fd1)) {
353 | char *tmp, *ns1;
354 | FILE *fd2;
355 |
356 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, pod);
357 |
358 | ns1 = strtok_r(pod, " ", &tmp);
359 |
360 | while(ns1 != NULL) {
361 | snprintf(cmd, sizeof(cmd),
362 | "%s exec %s --namespace=%s -- cat /sys/class/net/eth0/iflink 2>1 /dev/null",
363 | kcmd, ns1, ns);
364 |
365 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
366 |
367 | if((fd2 = popen(cmd, "r")) != NULL) {
368 | char ids[32];
369 |
370 | while(fgets(ids, sizeof(ids)-1, (FILE*) fd2)) {
371 | FILE *fd3;
372 |
373 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, ids);
374 |
375 | snprintf(cmd, sizeof(cmd), "ip -o link|grep ^%d:|cut -d ':' -f 2|cut -d '@' -f 1|tr -d '[:blank:]' | sed 's/\\n//g'",
376 | atoi(ids));
377 |
378 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Executing %s\n", __FILE__, __LINE__, cmd);
379 |
380 | if((fd3 = popen(cmd, "r")) != NULL) {
381 | char ifname[32];
382 |
383 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Pipe open\n", __FILE__, __LINE__);
384 |
385 | while(fgets(ifname, sizeof(ifname)-1, (FILE*) fd3)) {
386 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Read %s\n", __FILE__, __LINE__, ifname);
387 |
388 | ifname[strlen(ifname)-1] = '\0';
389 | printf("value {arg=0}{value=%s@%s}{display=Pod %s, Namespace %s}\n", ifname, ns1, ns1, ns);
390 |
391 | if(num_all_interfaces < MAX_NUM_INT)
392 | all_interfaces[num_all_interfaces++] = strdup(ifname);
393 | }
394 |
395 | fclose(fd3);
396 | } else {
397 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] popen failed\n", __FILE__, __LINE__);
398 | }
399 | }
400 |
401 | fclose(fd2);
402 | } else {
403 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] popen failed\n", __FILE__, __LINE__);
404 | }
405 |
406 | ns1 = strtok_r(NULL, " ", &tmp);
407 |
408 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] Next NS %s\n", __FILE__, __LINE__, ns1 ? ns1 : "");
409 | }
410 | }
411 |
412 | fclose(fd1);
413 | } else {
414 | if(log_fp) fprintf(log_fp, "[DEBUG][%s:%u] popen failed\n", __FILE__, __LINE__);
415 | }
416 |
417 | ns = strtok_r(NULL, " ", &tmp);
418 | }
419 | }
420 |
421 | fclose(fd);
422 | }
423 | }
424 |
425 | /* ***************************************************** */
426 |
427 | void print_pcap_interfaces() {
428 | char errbuf[PCAP_ERRBUF_SIZE];
429 | pcap_if_t *devpointer;
430 |
431 | if(pcap_findalldevs(&devpointer, errbuf) == 0) {
432 | int i = 0;
433 |
434 | while(devpointer) {
435 | if(devpointer->description == NULL) {
436 | u_int8_t found = 0, i;
437 |
438 | for(i=0; iname) == 0) {
440 | found = 1;
441 | break;
442 | }
443 |
444 | if(!found)
445 | printf("value {arg=0}{value=%s}{display=%s}\n", devpointer->name, devpointer->name);
446 | }
447 |
448 | devpointer = devpointer->next;
449 | }
450 | }
451 | }
452 |
453 | /* ***************************************************** */
454 |
455 | void extcap_list_all_interfaces() {
456 | u_int i;
457 |
458 | /* Add eBPF-only events */
459 | #ifdef __linux__
460 | printf("value {arg=0}{value=%s}{display=eBPF Events}\n", EBPFDUMP_EBPFEVENTS_NAME);
461 | #endif
462 |
463 | printf("value {arg=0}{value=%s}{display=eBPF Remote Events (ZMQ)}\n", EBPFDUMP_EBPFZMQEVENTS_NAME);
464 |
465 | #ifdef __linux__
466 | /* Print kubernetes containers only if there are no docker containers */
467 | i = docker_list_interfaces();
468 |
469 | // if(i == 0)
470 | kubectl_list_interfaces();
471 |
472 | /* Print additional interfaces */
473 | print_pcap_interfaces();
474 |
475 | for(i=0; i\n");
490 | printf("--extcap-config\n");
491 | printf("--capture\n");
492 | printf("--fifo \n");
493 | printf("--debug\n");
494 | printf("--name \n");
495 | printf("--custom-name \n");
496 | printf("--help\n");
497 |
498 | return(0);
499 | }
500 |
501 | /* ***************************************************** */
502 |
503 | void extcap_config() {
504 | u_int argidx = 0;
505 |
506 | if(!extcap_selected_interface) {
507 | extcap_print_help();
508 | return;
509 | }
510 |
511 | printf("arg {number=0}{call=--ifname}{display=Interface Name}"
512 | "{type=selector}{tooltip=Network Interface from which packets will be captured}\n");
513 |
514 | extcap_list_all_interfaces();
515 | }
516 |
517 | /* ***************************************************** */
518 |
519 | void extcap_list_interfaces() {
520 | u_int i;
521 |
522 | for(i = 0; i < extcap_interfaces_num; i++)
523 | printf("interface {value=%s}{display=%s}\n",
524 | extcap_interfaces[i].interface,
525 | extcap_interfaces[i].description);
526 | }
527 |
528 | /* ***************************************************** */
529 |
530 | void extcap_dlts() {
531 | int i;
532 |
533 | if(!extcap_selected_interface) return;
534 | for(i = 0; i < extcap_interfaces_num; i++) {
535 | extcap_interface *eif = &extcap_interfaces[i];
536 |
537 | if(!strncmp(extcap_selected_interface, eif->interface, strlen(eif->interface))) {
538 | printf("dlt {number=%u}{name=%s}{display=%s}\n",
539 | eif->dlt, eif->interface, eif->dltdescription);
540 | break;
541 | }
542 | }
543 | }
544 |
545 | /* ***************************************************** */
546 |
547 | int exec_head(const char *bin, char *line, size_t line_len) {
548 | FILE *fp;
549 |
550 | fp = popen(bin, "r");
551 |
552 | if(fp == NULL)
553 | return -1;
554 |
555 | if(fgets(line, line_len-1, fp) == NULL) {
556 | pclose(fp);
557 | return -1;
558 | }
559 |
560 | pclose(fp);
561 | return 0;
562 | }
563 |
564 | /* ***************************************************** */
565 |
566 | float wireshark_version() {
567 | char line[1035];
568 | char *version, *rev;
569 | float v = 0;
570 |
571 | if(exec_head("/usr/bin/wireshark -v", line, sizeof(line)) != 0 &&
572 | exec_head("/usr/local/bin/wireshark -v", line, sizeof(line)) != 0)
573 | return 0;
574 |
575 | version = strchr(line, ' ');
576 | if(version == NULL) return 0;
577 | version++;
578 | rev = strchr(version, '.');
579 | if(rev == NULL) return 0;
580 | rev++;
581 | rev = strchr(rev, '.');
582 | if(rev == NULL) return 0;
583 | *rev = '\0';
584 |
585 | sscanf(version, "%f", &v);
586 |
587 | return v;
588 | }
589 |
590 | /* ***************************************************** */
591 |
592 | /* ******************************************** */
593 | // ===== ===== IP ADDRESS TO STRING ===== ===== //
594 | /* ******************************************** */
595 |
596 | static char* intoaV4(unsigned int addr, char* buf, u_short bufLen) {
597 | char *cp, *retStr;
598 | int n;
599 |
600 | cp = &buf[bufLen];
601 | *--cp = '\0';
602 |
603 | n = 4;
604 | do {
605 | u_int byte = addr & 0xff;
606 |
607 | *--cp = byte % 10 + '0';
608 | byte /= 10;
609 | if(byte > 0) {
610 | *--cp = byte % 10 + '0';
611 | byte /= 10;
612 | if(byte > 0)
613 | *--cp = byte + '0';
614 | }
615 | *--cp = '.';
616 | addr >>= 8;
617 | } while (--n > 0);
618 |
619 | /* Convert the string to lowercase */
620 | retStr = (char*)(cp+1);
621 |
622 | return(retStr);
623 | }
624 |
625 | /* ***************************************************** */
626 |
627 | static char* intoaV6(void *addr, char* buf, u_short bufLen) {
628 | char *ret = (char*)inet_ntop(AF_INET6, addr, buf, bufLen);
629 |
630 | if(ret == NULL)
631 | buf[0] = '\0';
632 |
633 | return(buf);
634 | }
635 |
636 | /* ***************************************************** */
637 |
638 | static void IPV4Handler(eBPFevent *e, struct ipv4_addr_t *event, u_int32_t *hashval) {
639 | if(log_fp) {
640 | char buf1[32], buf2[32];
641 |
642 | fprintf(log_fp, "[addr: %s:%u <-> %s:%u]\n",
643 | intoaV4(htonl(event->saddr), buf1, sizeof(buf1)), e->sport,
644 | intoaV4(htonl(event->daddr), buf2, sizeof(buf2)), e->dport);
645 | }
646 |
647 | *hashval = e->proto + ntohl(event->saddr) + ntohl(event->daddr) + e->sport + e->dport;
648 | }
649 |
650 | /* ***************************************************** */
651 |
652 | static void IPV6Handler(eBPFevent *e, struct ipv6_addr_t *event, u_int32_t *hashval) {
653 | u_int32_t *s = (u_int32_t*)&event->saddr;
654 | u_int32_t *d = (u_int32_t*)&event->daddr;
655 |
656 | if(log_fp) {
657 | char buf1[128], buf2[128];
658 |
659 | fprintf(log_fp, "[addr: %s:%u <-> %s:%u]\n",
660 | intoaV6(&event->saddr, buf1, sizeof(buf1)), e->sport,
661 | intoaV6(&event->daddr, buf2, sizeof(buf2)), e->dport);
662 | }
663 |
664 | *hashval = e->proto + e->sport + e->dport;
665 |
666 | *hashval += ntohl(s[0]) + ntohl(s[1]) + ntohl(s[2]) + ntohl(s[3])
667 | + ntohl(d[0]) + ntohl(d[1]) + ntohl(d[2]) + ntohl(d[3]);
668 | }
669 |
670 | /* ***************************************************** */
671 |
672 | static void ebpf_process_event(void* t_bpfctx, void* t_data, int t_datasize) {
673 | eBPFevent *e = (eBPFevent*)t_data;
674 | u_int len = sizeof(eBPFevent)+32;
675 | char buf[len];
676 | struct timespec tp;
677 | struct timeval now;
678 | u_int64_t bytes_written = 0;
679 | int err;
680 | u_int32_t *null_sock_type = (u_int32_t*)buf;
681 | eBPFevent event;
682 |
683 | memcpy(&event, e, sizeof(eBPFevent)); /* Copy needed as ebpf_preprocess_event will modify the memory */
684 |
685 | #ifdef __linux__
686 | if(t_bpfctx) ebpf_preprocess_event(&event);
687 | #endif
688 |
689 | gettimeofday(&now, NULL);
690 |
691 | *null_sock_type = htonl(SOCKET_LIBEBPF);
692 |
693 | if(log_fp)
694 | fprintf(log_fp, "[ifname: %s][extcap: %s/pcap: %s]\n",
695 | event.ifname,
696 | extcap_selected_interface ? extcap_selected_interface : "",
697 | pcap_selected_interface ? pcap_selected_interface : "");
698 |
699 | if(/* extcap_selected_interface || */
700 | pcap_selected_interface
701 | || (containerId && (strstr(event.container_id, containerId)))
702 | || (containerId && (!strcmp(event.kube.pod, containerId)))
703 | ) {
704 | /*
705 | We are capturing from a physical interface and here we need
706 | to glue events with packets
707 | */
708 |
709 | if(log_fp) fprintf(log_fp, "==> [%s][%s][%s][%s][%s][%s]\n",
710 | event.container_id, containerId, event.docker.name,
711 | event.kube.name, event.kube.pod, event.kube.ns);
712 |
713 | if(
714 | (extcap_selected_interface && (strcmp(event.ifname, extcap_selected_interface) == 0))
715 | || (pcap_selected_interface && (strcmp(event.ifname, pcap_selected_interface) == 0))
716 | || (containerId && event.docker.name && (!strcmp(event.docker.name, containerId)))
717 | || (containerId && event.kube.pod && (!strcmp(event.kube.pod, containerId)))
718 | ) {
719 | u_int32_t hashval = 0;
720 | struct ebpf_event evt;
721 |
722 | if(log_fp) {
723 | printf("[%s][%s][IPv4/%s][pid/tid: %u/%u [%s], uid/gid: %u/%u]"
724 | "[father pid/tid: %u/%u [%s], uid/gid: %u/%u]",
725 | event.ifname, event.sent_packet ? "Sent" : "Rcvd",
726 | (event.proto == IPPROTO_TCP) ? "TCP" : "UDP",
727 | event.proc.pid, event.proc.tid,
728 | (event.proc.full_task_path == NULL) ? event.proc.task : event.proc.full_task_path,
729 | event.proc.uid, event.proc.gid,
730 | event.father.pid, event.father.tid,
731 | (event.father.full_task_path == NULL) ? event.father.task : event.father.full_task_path,
732 | event.father.uid, event.father.gid);
733 |
734 | if(event.ip_version == 4)
735 | IPV4Handler(&event, &event.addr.v4, &hashval);
736 | else
737 | IPV6Handler(&event, &event.addr.v6, &hashval);
738 |
739 | if(event.container_id[0] != '\0') {
740 | printf("[containerID: %s]", event.container_id);
741 |
742 | if(event.docker.name != NULL)
743 | printf("[docker_name: %s]", event.docker.name);
744 |
745 | if(event.kube.ns) printf("[kube_name: %s]", event.kube.name);
746 | if(event.kube.pod) printf("[kube_pod: %s]", event.kube.pod);
747 | if(event.kube.ns) printf("[kube_ns: %s]", event.kube.ns);
748 | }
749 |
750 | printf("[hashval: %u]\n", hashval);
751 | }
752 |
753 | if(!lru_find_cache(&received_events, hashval, &evt)) {
754 | u_int l; /* Trick to avoid silly compiler warnings */
755 |
756 | memset(&evt, 0, sizeof(evt));
757 |
758 | evt.pid = event.proc.pid, evt.tid = event.proc.tid,
759 | evt.uid = event.proc.uid, evt.gid = event.proc.gid;
760 |
761 | l = min(sizeof(evt.process_name), strlen(event.proc.task));
762 | memcpy(evt.process_name, event.proc.task, l);
763 |
764 | l = min(sizeof(evt.container_id), strlen(event.container_id));
765 | memcpy(evt.container_id, event.container_id, l);
766 |
767 | if(log_fp)
768 | fprintf(log_fp, "========>>>>>> Adding %u [process_name: %s][container_id: %s][pid: %u][tid: %u][uid: %u][gid: %u]\n",
769 | hashval, evt.process_name, evt.container_id,
770 | evt.pid, evt.tid, evt.uid, evt.gid);
771 |
772 | lru_add_to_cache(&received_events, hashval, &evt);
773 | // printf("++++ Adding %u\n", hashval);
774 | }
775 |
776 | /* ************************************************* */
777 |
778 | /* Uncomment for dumping events with packets */
779 | #if 0
780 | memset(buf, 0, 14);
781 | memcpy(&buf[14], &event, sizeof(eBPFevent));
782 | if(!libpcap_write_packet(fp, now.tv_sec, now.tv_usec, len, len,
783 | (const u_int8_t*)buf, &bytes_written, &err)) {
784 | time_t now = time(NULL);
785 | fprintf(stderr, "Error while writing packet @ %s", ctime(&now));
786 | } else
787 | fflush(fp); /* Flush buffer */
788 | #endif
789 |
790 | /* ************************************************* */
791 | } else {
792 | if(log_fp)
793 | printf("Skipping event for interface %s\n", event.ifname);
794 | }
795 | } else {
796 | memcpy(&buf[4], &event, sizeof(eBPFevent));
797 |
798 | if(!libpcap_write_packet(fp, now.tv_sec, now.tv_usec, len, len,
799 | (const u_int8_t*)buf, &bytes_written, &err)) {
800 | time_t now = time(NULL);
801 | fprintf(stderr, "Error while writing packet @ %s", ctime(&now));
802 | } else
803 | fflush(fp); /* Flush buffer */
804 | }
805 |
806 | #ifdef __linux__
807 | if(t_bpfctx) ebpf_free_event(&event);
808 | #endif
809 | }
810 |
811 | /* ****************************************************** */
812 |
813 | /*
814 | * A faster replacement for inet_ntoa().
815 | */
816 | char* __intoa(unsigned int addr, char* buf, u_short bufLen) {
817 | char *cp, *retStr;
818 | u_int byte;
819 | int n;
820 |
821 | cp = &buf[bufLen];
822 | *--cp = '\0';
823 |
824 | n = 4;
825 | do {
826 | byte = addr & 0xff;
827 | *--cp = byte % 10 + '0';
828 | byte /= 10;
829 | if (byte > 0) {
830 | *--cp = byte % 10 + '0';
831 | byte /= 10;
832 | if (byte > 0)
833 | *--cp = byte + '0';
834 | }
835 | *--cp = '.';
836 | addr >>= 8;
837 | } while (--n > 0);
838 |
839 | /* Convert the string to lowercase */
840 | retStr = (char*)(cp+1);
841 |
842 | return(retStr);
843 | }
844 |
845 | /* ************************************ */
846 |
847 | static char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
848 |
849 | char* intoa(unsigned int addr) {
850 | return(__intoa(addr, buf, sizeof(buf)));
851 | }
852 |
853 | /* ************************************ */
854 |
855 | static inline char* in6toa(struct in6_addr addr6) {
856 | snprintf(buf, sizeof(buf),
857 | "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
858 | addr6.s6_addr[0], addr6.s6_addr[1], addr6.s6_addr[2],
859 | addr6.s6_addr[3], addr6.s6_addr[4], addr6.s6_addr[5], addr6.s6_addr[6],
860 | addr6.s6_addr[7], addr6.s6_addr[8], addr6.s6_addr[9], addr6.s6_addr[10],
861 | addr6.s6_addr[11], addr6.s6_addr[12], addr6.s6_addr[13], addr6.s6_addr[14],
862 | addr6.s6_addr[15]);
863 |
864 | return(buf);
865 | }
866 |
867 | /* *************************************** */
868 |
869 | int32_t gmt_to_local(time_t t) {
870 | int dt, dir;
871 | struct tm *gmt, *loc;
872 | struct tm sgmt;
873 |
874 | if (t == 0)
875 | t = time(NULL);
876 | gmt = &sgmt;
877 | *gmt = *gmtime(&t);
878 | loc = localtime(&t);
879 | dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
880 | (loc->tm_min - gmt->tm_min) * 60;
881 |
882 | /*
883 | * If the year or julian day is different, we span 00:00 GMT
884 | * and must add or subtract a day. Check the year first to
885 | * avoid problems when the julian day wraps.
886 | */
887 | dir = loc->tm_year - gmt->tm_year;
888 | if (dir == 0)
889 | dir = loc->tm_yday - gmt->tm_yday;
890 | dt += dir * 24 * 60 * 60;
891 |
892 | return (dt);
893 | }
894 |
895 | /* ****************************************************** */
896 |
897 | const char* proto2str(u_short proto) {
898 | static char protoName[8];
899 |
900 | switch(proto) {
901 | case IPPROTO_TCP: return("TCP");
902 | case IPPROTO_UDP: return("UDP");
903 | case IPPROTO_ICMP: return("ICMP");
904 | default:
905 | snprintf(protoName, sizeof(protoName), "%d", proto);
906 | return(protoName);
907 | }
908 | }
909 |
910 | /* ****************************************************** */
911 |
912 | static char hex[] = "0123456789ABCDEF";
913 |
914 | char* etheraddr_string(const u_char *ep, char *buf) {
915 | u_int i, j;
916 | char *cp;
917 |
918 | cp = buf;
919 | if ((j = *ep >> 4) != 0)
920 | *cp++ = hex[j];
921 | else
922 | *cp++ = '0';
923 |
924 | *cp++ = hex[*ep++ & 0xf];
925 |
926 | for(i = 5; (int)--i >= 0;) {
927 | *cp++ = ':';
928 | if ((j = *ep >> 4) != 0)
929 | *cp++ = hex[j];
930 | else
931 | *cp++ = '0';
932 |
933 | *cp++ = hex[*ep++ & 0xf];
934 | }
935 |
936 | *cp = '\0';
937 | return (buf);
938 | }
939 |
940 | /* ***************************************************** */
941 |
942 | void pcap_processs_packet(u_char *_deviceId,
943 | const struct pcap_pkthdr *h,
944 | const u_char *pkt) {
945 | struct ether_header ehdr;
946 | u_short eth_type, vlan_id;
947 | const u_char *p = pkt;
948 | char buf1[32], buf2[32];
949 | int s;
950 | struct tcphdr *tp;
951 | struct udphdr *up;
952 | u_int8_t proto = 0;
953 | u_int32_t hashval = 0;
954 | u_int64_t bytes_written = 0;
955 | int err;
956 |
957 | s = (h->ts.tv_sec + thiszone) % 86400;
958 |
959 | if(log_fp)
960 | fprintf(log_fp, "%02d:%02d:%02d.%06u ",
961 | s / 3600, (s % 3600) / 60, s % 60,
962 | (unsigned)h->ts.tv_usec);
963 |
964 | memcpy(&ehdr, p, sizeof(struct ether_header));
965 | eth_type = ntohs(ehdr.ether_type);
966 |
967 | if(log_fp)
968 | fprintf(log_fp, "[%s -> %s] ",
969 | etheraddr_string(ehdr.ether_shost, buf1),
970 | etheraddr_string(ehdr.ether_dhost, buf2));
971 |
972 | if(eth_type == 0x8100) {
973 | vlan_id = (p[14] & 15)*256 + p[15];
974 | eth_type = (p[16])*256 + p[17];
975 |
976 | if(log_fp)
977 | fprintf(log_fp, "[vlan %u] ", vlan_id);
978 |
979 | p += 4;
980 | }
981 |
982 | p += sizeof(ehdr);
983 |
984 | if(eth_type == 0x0800) {
985 | struct ip *ip = (struct ip*)p;
986 |
987 | proto = ip->ip_p;
988 |
989 | if(log_fp) {
990 | fprintf(log_fp, "[%s]", proto2str(ip->ip_p));
991 | fprintf(log_fp, "[%s ", intoa(ntohl(ip->ip_src.s_addr)));
992 | fprintf(log_fp, "-> %s]", intoa(ntohl(ip->ip_dst.s_addr)));
993 | }
994 |
995 | hashval = proto + ntohl(ip->ip_src.s_addr) + ntohl(ip->ip_dst.s_addr);
996 |
997 | p += ((u_int16_t)ip->ip_hl * 4);
998 | } else if(eth_type == 0x86DD) {
999 | struct ip6_hdr *ip6 = (struct ip6_hdr*)p;
1000 |
1001 | proto = ip6->ip6_nxt;
1002 |
1003 | if(log_fp) {
1004 | fprintf(log_fp, "[%s ", in6toa(ip6->ip6_src));
1005 | fprintf(log_fp, "-> %s]", in6toa(ip6->ip6_dst));
1006 | }
1007 |
1008 | hashval = proto
1009 | + ntohl(ip6->ip6_src.s6_addr32[0])
1010 | + ntohl(ip6->ip6_src.s6_addr32[1])
1011 | + ntohl(ip6->ip6_src.s6_addr32[2])
1012 | + ntohl(ip6->ip6_src.s6_addr32[3])
1013 | + ntohl(ip6->ip6_dst.s6_addr32[0])
1014 | + ntohl(ip6->ip6_dst.s6_addr32[1])
1015 | + ntohl(ip6->ip6_dst.s6_addr32[2])
1016 | + ntohl(ip6->ip6_dst.s6_addr32[3]);
1017 |
1018 | p += sizeof(struct ip6_hdr)+htons(ip6->ip6_plen);
1019 | } else if(eth_type == 0x0806) {
1020 | if(log_fp)
1021 | fprintf(log_fp, "[ARP]");
1022 | } else {
1023 | if(log_fp)
1024 | fprintf(log_fp, "[eth_type=0x%04X]", eth_type);
1025 | }
1026 |
1027 | if(proto) {
1028 | if(log_fp) fprintf(log_fp, "[%s]", proto2str(proto));
1029 |
1030 | switch(proto) {
1031 | case IPPROTO_TCP:
1032 | {
1033 | struct tcphdr *t = (struct tcphdr*)p;
1034 |
1035 | if(log_fp)
1036 | fprintf(log_fp, "[%u -> %u]", ntohs(t->th_sport), ntohs(t->th_dport));
1037 |
1038 | hashval += ntohs(t->th_sport) + ntohs(t->th_dport);
1039 | }
1040 | break;
1041 | case IPPROTO_UDP:
1042 | {
1043 | struct udphdr *u = (struct udphdr*)p;;
1044 |
1045 | if(log_fp)
1046 | fprintf(log_fp, "[%u -> %u]", ntohs(u->uh_sport), ntohs(u->uh_dport));
1047 |
1048 | hashval += ntohs(u->uh_sport) + ntohs(u->uh_dport);
1049 | }
1050 | break;
1051 | }
1052 | }
1053 |
1054 | if(log_fp)
1055 | fprintf(log_fp, "[caplen=%u][len=%u][hashval=%u]\n", h->caplen, h->len, hashval);
1056 |
1057 | // if(log_fp) fprintf(log_fp, "[caplen=%u][len=%u][hashval=%u]\n", h->caplen, h->len, hashval);
1058 |
1059 | if(fp) {
1060 | struct ebpf_event evt;
1061 |
1062 | if(lru_find_cache(&received_events, hashval, &evt)) {
1063 | char *packet;
1064 | u_int new_len = h->caplen + sizeof(struct ebpf_event) + 2;
1065 |
1066 | // fprintf(log_fp, "++++ Found %u\n", hashval);
1067 |
1068 | packet = (char*)malloc(new_len);
1069 |
1070 | if(packet) {
1071 | if(log_fp)
1072 | fprintf(log_fp, "========>>>>>> Reading %u [process_name: %s][container_id: %s][pid: %u][tid: %u][uid: %u][gid: %u][len: %u -> %u]\n",
1073 | hashval, evt.process_name, evt.container_id,
1074 | evt.pid, evt.tid, evt.uid, evt.gid,
1075 | h->caplen, new_len);
1076 |
1077 | memcpy(packet, pkt, h->caplen);
1078 | packet[h->caplen] = 0x19;
1079 | packet[h->caplen+1] = 0x68;
1080 | memcpy(&packet[h->caplen+2], &evt, sizeof(evt));
1081 |
1082 | if(!libpcap_write_packet(fp, h->ts.tv_sec, h->ts.tv_usec, new_len, new_len,
1083 | (const u_int8_t*)packet, &bytes_written, &err)) {
1084 | time_t now = time(NULL);
1085 | fprintf(stderr, "Error while writing packet @ %s", ctime(&now));
1086 | } else
1087 | fflush(fp); /* Flush buffer */
1088 |
1089 | free(packet);
1090 | }
1091 | } else {
1092 | if(!libpcap_write_packet(fp, h->ts.tv_sec, h->ts.tv_usec, h->caplen, h->len,
1093 | (const u_int8_t*)pkt, &bytes_written, &err)) {
1094 | time_t now = time(NULL);
1095 | fprintf(stderr, "Error while writing packet @ %s", ctime(&now));
1096 | } else
1097 | fflush(fp); /* Flush buffer */
1098 | }
1099 | }
1100 | }
1101 |
1102 | /* ***************************************************** */
1103 |
1104 | void extcap_capture() {
1105 | ebpfRetCode rc;
1106 | void *ebpf;
1107 | u_int num = 0;
1108 | u_int64_t bytes_written = 0;
1109 | int err;
1110 | u_int8_t success;
1111 | pcap_t *pd = NULL;
1112 | int promisc = 1;
1113 | int snaplen = 1600;
1114 | char errbuf[PCAP_ERRBUF_SIZE];
1115 |
1116 | if(log_fp)
1117 | fprintf(log_fp, "[DEBUG][%s:%u] Capturing [ifname: %s][fifo: %s]\n",
1118 | __FILE__, __LINE__,
1119 | pcap_selected_interface ? pcap_selected_interface : "",
1120 | extcap_capture_fifo ? extcap_capture_fifo : "");
1121 |
1122 | if(log_fp)
1123 | fprintf(log_fp, "[DEBUG][%s:%u] Capturing [ifname: %s][fifo: %s]\n",
1124 | __FILE__, __LINE__,
1125 | pcap_selected_interface ? pcap_selected_interface : "",
1126 | extcap_capture_fifo ? extcap_capture_fifo : "");
1127 |
1128 | if(pcap_selected_interface && (strcmp(pcap_selected_interface, EBPFDUMP_EBPFZMQEVENTS_NAME) == 0)) {
1129 | if(initZMQ() != 0)
1130 | return;
1131 | else
1132 | pcap_selected_interface = NULL; /* Trick to semplify the rest of the code */
1133 | } else {
1134 | #ifdef __linux__
1135 | ebpf = init_ebpf_flow(NULL, ebpf_process_event, &rc, 0xFFFF);
1136 |
1137 | if(ebpf == NULL) {
1138 | fprintf(stderr, "Unable to initialize libebpfflow\n");
1139 | return;
1140 | }
1141 | #endif
1142 | }
1143 |
1144 | if(pcap_selected_interface) {
1145 | char *at = strchr(pcap_selected_interface, '@');
1146 |
1147 | if(at) {
1148 | at[0] = '\0';
1149 | containerId = &at[1];
1150 | }
1151 | }
1152 |
1153 | if((fp = fopen(extcap_capture_fifo, "wb")) == NULL) {
1154 | fprintf(stderr, "Unable to create file %s", extcap_capture_fifo);
1155 | return;
1156 | }
1157 |
1158 | if(!libpcap_write_file_header(fp,
1159 | pcap_selected_interface ? DLT_EN10MB : 0 /* DLT_NULL */,
1160 | pcap_selected_interface ? 2048: sizeof(eBPFevent), FALSE, &bytes_written, &err)) {
1161 | fprintf(stderr, "Unable to write file %s header", extcap_capture_fifo);
1162 | return;
1163 | }
1164 |
1165 | if((signal(SIGINT, sigproc) == SIG_ERR)
1166 | || (signal(SIGTERM, sigproc) == SIG_ERR)
1167 | || (signal(SIGQUIT, sigproc) == SIG_ERR)) {
1168 | fprintf(stderr, "Unable to install SIGINT/SIGTERM signal handler");
1169 | return;
1170 | }
1171 |
1172 | if(pcap_selected_interface) {
1173 | if((pd = pcap_open_live(pcap_selected_interface, snaplen, promisc, 1, errbuf)) == NULL) {
1174 | printf("pcap_open_live: %s\n", errbuf);
1175 | return;
1176 | }
1177 |
1178 | if(log_fp) fprintf(log_fp, "Reading packets from %s\n", pcap_selected_interface);
1179 |
1180 | while(1) {
1181 | if(pcap_dispatch(pd, 1, pcap_processs_packet, NULL) < 0) break;
1182 | #ifdef __linux__
1183 | ebpf_poll_event(ebpf, 1);
1184 | #endif
1185 | }
1186 |
1187 | pcap_close(pd);
1188 | } else {
1189 | /* eBPF-only capture */
1190 | if(zmq.initialized) {
1191 | /* We need to poll events via ZMQ */
1192 |
1193 | while(1) {
1194 | struct zmq_msg_hdr h;
1195 | int size;
1196 |
1197 | #if 0
1198 | char json_str[2048];
1199 |
1200 | size = zmq_recv(zmq.z_socket, &h, sizeof(h), 0);
1201 | zmq_recv(zmq.z_socket, json_str, h.size, 0);
1202 | if(log_fp) fprintf(log_fp, "%s\n", json_str);
1203 | json_str[h.size] = '\0';
1204 | printf("%s\n", json_str);
1205 | #else
1206 | eBPFevent event;
1207 |
1208 | size = zmq_recv(zmq.z_socket, &h, sizeof(h), 0);
1209 | zmq_recv(zmq.z_socket, &event, h.size, 0);
1210 | // printf("Received %u bytes event\n", h.size);
1211 | ebpf_process_event(NULL, (void*)&event, h.size);
1212 | #endif
1213 | }
1214 | } else {
1215 | #ifdef __linux__
1216 | while(1) {
1217 | /* fprintf(stderr, "%u\n", ++num); */
1218 | ebpf_poll_event(ebpf, 10);
1219 | }
1220 | #endif
1221 | }
1222 | }
1223 |
1224 | #ifdef __linux__
1225 | if(!zmq.initialized)
1226 | term_ebpf_flow(ebpf);
1227 | #endif
1228 |
1229 | fclose(fp);
1230 | }
1231 |
1232 | /* ***************************************************** */
1233 |
1234 | int main(int argc, char *argv[]) {
1235 | int option_idx = 0, result;
1236 | time_t epoch;
1237 | char date_str[EBPFDUMP_MAX_DATE_LEN];
1238 | struct tm* tm_info;
1239 |
1240 | memset(&zmq, 0, sizeof(zmq));
1241 |
1242 | thiszone = gmt_to_local(0);
1243 | lru_cache_init(&received_events);
1244 |
1245 | log_fp = fopen("/tmp/ebpfdump.log", "w");
1246 |
1247 | #if 0
1248 | /* test code */
1249 | if(0) {
1250 | eBPFevent x;
1251 |
1252 | printf("%d\n", offsetof(eBPFevent, proc));
1253 | printf("%d\n", offsetof(eBPFevent, father));
1254 |
1255 | return(0);
1256 | }
1257 | #endif
1258 |
1259 | if(argc == 1) {
1260 | extcap_print_help();
1261 | return EXIT_SUCCESS;
1262 | }
1263 |
1264 | u_int defer_dlts = 0, defer_config = 0, defer_capture = 0;
1265 | while((result = getopt_long(argc, argv, "h", longopts, &option_idx)) != -1) {
1266 | // fprintf(stderr, "OPT: '%c' VAL: '%s' \n", result, optarg != NULL ? optarg : "");
1267 |
1268 | switch(result) {
1269 | /* mandatory extcap options */
1270 | case EXTCAP_OPT_DEBUG:
1271 | break;
1272 | case EXTCAP_OPT_LIST_INTERFACES:
1273 | extcap_version();
1274 | extcap_list_interfaces();
1275 | defer_dlts = defer_config = defer_capture = 0;
1276 | break;
1277 | case EXTCAP_OPT_VERSION:
1278 | extcap_version();
1279 | defer_dlts = defer_config = defer_capture = 0;
1280 | break;
1281 | case EXTCAP_OPT_LIST_DLTS:
1282 | defer_dlts = 1; defer_config = defer_capture = 0;
1283 | break;
1284 | case EXTCAP_OPT_INTERFACE:
1285 | extcap_selected_interface = strndup(optarg, EBPFDUMP_MAX_NAME_LEN);
1286 | break;
1287 | case EXTCAP_OPT_CONFIG:
1288 | defer_config = 1; defer_dlts = defer_capture = 0;
1289 | break;
1290 | case EXTCAP_OPT_CAPTURE:
1291 | defer_capture = 1; defer_dlts = defer_config = 0;
1292 | break;
1293 | break;
1294 | case EXTCAP_OPT_FIFO:
1295 | extcap_capture_fifo = strdup(optarg);
1296 | break;
1297 |
1298 | /* custom ebpfdump options */
1299 | case EBPFDUMP_OPT_IFNAME:
1300 | if(strcmp(optarg, "ebpfevents") != 0)
1301 | pcap_selected_interface = strdup(optarg);
1302 | break;
1303 |
1304 | case EBPFDUMP_OPT_HELP:
1305 | extcap_print_help();
1306 | return EXIT_SUCCESS;
1307 | break;
1308 | }
1309 | }
1310 |
1311 | if(defer_dlts) extcap_dlts();
1312 | else if(defer_config) extcap_config();
1313 | else if(defer_capture) extcap_capture();
1314 |
1315 | if(extcap_selected_interface) free(extcap_selected_interface);
1316 | if(extcap_capture_fifo) free(extcap_capture_fifo);
1317 |
1318 | if(log_fp) fclose(log_fp);
1319 |
1320 | termZMQ();
1321 |
1322 | return EXIT_SUCCESS;
1323 | }
1324 |
--------------------------------------------------------------------------------
/wireshark/pcapio.c:
--------------------------------------------------------------------------------
1 | /* pcapio.c
2 | * Our own private code for writing libpcap files when capturing.
3 | *
4 | * We have these because we want a way to open a stream for output given
5 | * only a file descriptor. libpcap 0.9[.x] has "pcap_dump_fopen()", which
6 | * provides that, but
7 | *
8 | * 1) earlier versions of libpcap doesn't have it
9 | *
10 | * and
11 | *
12 | * 2) WinPcap doesn't have it, because a file descriptor opened
13 | * by code built for one version of the MSVC++ C library
14 | * can't be used by library routines built for another version
15 | * (e.g., threaded vs. unthreaded).
16 | *
17 | * Libpcap's pcap_dump() also doesn't return any error indications.
18 | *
19 | * Wireshark - Network traffic analyzer
20 | * By Gerald Combs
21 | * Copyright 1998 Gerald Combs
22 | *
23 | * Derived from code in the Wiretap Library
24 | * Copyright (c) 1998 by Gilbert Ramirez
25 | *
26 | * Modified by ntop - September 2019
27 | *
28 | * SPDX-License-Identifier: GPL-2.0-or-later
29 | */
30 |
31 | #ifdef __linux__
32 | #include
33 | #endif
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | #ifdef _WIN32
41 | #include
42 | #endif
43 |
44 | #include "pcapio.h"
45 |
46 | /* Magic numbers in "libpcap" files.
47 |
48 | "libpcap" file records are written in the byte order of the host that
49 | writes them, and the reader is expected to fix this up.
50 |
51 | PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC
52 | is a byte-swapped version of that.
53 |
54 | PCAP_NSEC_MAGIC is for Ulf Lamping's modified "libpcap" format,
55 | which uses the same common file format as PCAP_MAGIC, but the
56 | timestamps are saved in nanosecond resolution instead of microseconds.
57 | PCAP_SWAPPED_NSEC_MAGIC is a byte-swapped version of that. */
58 | #define PCAP_MAGIC 0xa1b2c3d4
59 | #define PCAP_SWAPPED_MAGIC 0xd4c3b2a1
60 | #define PCAP_NSEC_MAGIC 0xa1b23c4d
61 | #define PCAP_SWAPPED_NSEC_MAGIC 0x4d3cb2a1
62 |
63 | /* "libpcap" file header. */
64 | struct pcap_hdr {
65 | u_int32_t magic; /* magic number */
66 | u_int16_t version_major; /* major version number */
67 | u_int16_t version_minor; /* minor version number */
68 | u_int32_t thiszone; /* GMT to local correction */
69 | u_int32_t sigfigs; /* accuracy of timestamps */
70 | u_int32_t snaplen; /* max length of captured packets, in octets */
71 | u_int32_t network; /* data link type */
72 | };
73 |
74 | /* "libpcap" record header. */
75 | struct pcaprec_hdr {
76 | u_int32_t ts_sec; /* timestamp seconds */
77 | u_int32_t ts_usec; /* timestamp microseconds (nsecs for PCAP_NSEC_MAGIC) */
78 | u_int32_t incl_len; /* number of octets of packet saved in file */
79 | u_int32_t orig_len; /* actual length of packet */
80 | };
81 |
82 | /* Magic numbers in ".pcapng" files.
83 | *
84 | * .pcapng file records are written in the byte order of the host that
85 | * writes them, and the reader is expected to fix this up.
86 | * PCAPNG_MAGIC is the magic number, in host byte order;
87 | * PCAPNG_SWAPPED_MAGIC is a byte-swapped version of that.
88 | */
89 | #define PCAPNG_MAGIC 0x1A2B3C4D
90 | #define PCAPNG_SWAPPED_MAGIC 0x4D3C2B1A
91 |
92 | /* Currently we are only supporting the initial version of
93 | the file format. */
94 | #define PCAPNG_MAJOR_VERSION 1
95 | #define PCAPNG_MINOR_VERSION 0
96 |
97 | /* Section Header Block without options and trailing Block Total Length */
98 | struct shb {
99 | u_int32_t block_type;
100 | u_int32_t block_total_length;
101 | u_int32_t byte_order_magic;
102 | u_int16_t major_version;
103 | u_int16_t minor_version;
104 | u_int64_t section_length;
105 | };
106 | #define SECTION_HEADER_BLOCK_TYPE 0x0A0D0D0A
107 |
108 | /* Interface Description Block without options and trailing Block Total Length */
109 | struct idb {
110 | u_int32_t block_type;
111 | u_int32_t block_total_length;
112 | u_int16_t link_type;
113 | u_int16_t reserved;
114 | u_int32_t snap_len;
115 | };
116 | #define INTERFACE_DESCRIPTION_BLOCK_TYPE 0x00000001
117 |
118 | /* Interface Statistics Block without actual packet, options, and trailing
119 | Block Total Length */
120 | struct isb {
121 | u_int32_t block_type;
122 | u_int32_t block_total_length;
123 | u_int32_t interface_id;
124 | u_int32_t timestamp_high;
125 | u_int32_t timestamp_low;
126 | };
127 | #define INTERFACE_STATISTICS_BLOCK_TYPE 0x00000005
128 |
129 | /* Enhanced Packet Block without actual packet, options, and trailing
130 | Block Total Length */
131 | struct epb {
132 | u_int32_t block_type;
133 | u_int32_t block_total_length;
134 | u_int32_t interface_id;
135 | u_int32_t timestamp_high;
136 | u_int32_t timestamp_low;
137 | u_int32_t captured_len;
138 | u_int32_t packet_len;
139 | };
140 | #define ENHANCED_PACKET_BLOCK_TYPE 0x00000006
141 |
142 | struct pcap_option {
143 | u_int16_t type;
144 | u_int16_t value_length;
145 | };
146 | #define OPT_ENDOFOPT 0
147 | #define OPT_COMMENT 1
148 | #define EPB_FLAGS 2
149 | #define SHB_HARDWARE 2 /* currently not used */
150 | #define SHB_OS 3
151 | #define SHB_USERAPPL 4
152 | #define IDB_NAME 2
153 | #define IDB_DESCRIPTION 3
154 | #define IDB_IF_SPEED 8
155 | #define IDB_TSRESOL 9
156 | #define IDB_FILTER 11
157 | #define IDB_OS 12
158 | #define ISB_STARTTIME 2
159 | #define ISB_ENDTIME 3
160 | #define ISB_IFRECV 4
161 | #define ISB_IFDROP 5
162 | #define ISB_FILTERACCEPT 6
163 | #define ISB_OSDROP 7
164 | #define ISB_USRDELIV 8
165 | #define ADD_PADDING(x) ((((x) + 3) >> 2) << 2)
166 |
167 | #ifndef TRUE
168 | #define TRUE 1
169 | #endif
170 |
171 | #ifndef FALSE
172 | #define FALSE 0
173 | #endif
174 |
175 | #ifndef G_MAXUINT16
176 | #define G_MAXUINT16 ((u_int16_t)-1)
177 | #endif
178 |
179 | #ifndef G_MAXUINT64
180 | #define G_MAXUINT64 ((u_int64_t)-1)
181 | #endif
182 |
183 | /* Write to capture file */
184 | static u_int8_t
185 | write_to_file(FILE* pfile, const u_int8_t* data, size_t data_length,
186 | u_int64_t *bytes_written, int *err)
187 | {
188 | size_t nwritten;
189 |
190 | nwritten = fwrite(data, data_length, 1, pfile);
191 | if (nwritten != 1) {
192 | if (ferror(pfile)) {
193 | *err = errno;
194 | } else {
195 | *err = 0;
196 | }
197 | return FALSE;
198 | }
199 |
200 | (*bytes_written) += data_length;
201 | return TRUE;
202 | }
203 |
204 | /* Writing pcap files */
205 |
206 | /* Write the file header to a dump file.
207 | Returns TRUE on success, FALSE on failure.
208 | Sets "*err" to an error code, or 0 for a short write, on failure*/
209 | u_int8_t
210 | libpcap_write_file_header(FILE* pfile, int linktype, int snaplen, u_int8_t ts_nsecs, u_int64_t *bytes_written, int *err)
211 | {
212 | struct pcap_hdr file_hdr;
213 |
214 | file_hdr.magic = ts_nsecs ? PCAP_NSEC_MAGIC : PCAP_MAGIC;
215 | /* current "libpcap" format is 2.4 */
216 | file_hdr.version_major = 2;
217 | file_hdr.version_minor = 4;
218 | file_hdr.thiszone = 0; /* XXX - current offset? */
219 | file_hdr.sigfigs = 0; /* unknown, but also apparently unused */
220 | file_hdr.snaplen = snaplen;
221 | file_hdr.network = linktype;
222 |
223 | return write_to_file(pfile, (const u_int8_t*)&file_hdr, sizeof(file_hdr), bytes_written, err);
224 | }
225 |
226 | /* Write a record for a packet to a dump file.
227 | Returns TRUE on success, FALSE on failure. */
228 | u_int8_t
229 | libpcap_write_packet(FILE* pfile,
230 | time_t sec, u_int32_t usec,
231 | u_int32_t caplen, u_int32_t len,
232 | const u_int8_t *pd,
233 | u_int64_t *bytes_written, int *err)
234 | {
235 | struct pcaprec_hdr rec_hdr;
236 |
237 | rec_hdr.ts_sec = (u_int32_t)sec; /* Y2.038K issue in pcap format.... */
238 | rec_hdr.ts_usec = usec;
239 | rec_hdr.incl_len = caplen;
240 | rec_hdr.orig_len = len;
241 | if (!write_to_file(pfile, (const u_int8_t*)&rec_hdr, sizeof(rec_hdr), bytes_written, err))
242 | return FALSE;
243 |
244 | return write_to_file(pfile, pd, caplen, bytes_written, err);
245 | }
246 |
247 | /* Writing pcapng files */
248 |
249 | static u_int32_t
250 | pcapng_count_string_option(const char *option_value)
251 | {
252 | if ((option_value != NULL) && (strlen(option_value) > 0) && (strlen(option_value) < G_MAXUINT16)) {
253 | /* There's a value to write; get its length */
254 | return (u_int32_t)(sizeof(struct pcap_option) +
255 | (u_int16_t)ADD_PADDING(strlen(option_value)));
256 | }
257 | return 0; /* nothing to write */
258 | }
259 |
260 | static u_int8_t
261 | pcapng_write_string_option(FILE* pfile,
262 | u_int16_t option_type, const char *option_value,
263 | u_int64_t *bytes_written, int *err)
264 | {
265 | size_t option_value_length;
266 | struct pcap_option option;
267 | const u_int32_t padding = 0;
268 |
269 | if (option_value == NULL)
270 | return TRUE; /* nothing to write */
271 | option_value_length = strlen(option_value);
272 | if ((option_value_length > 0) && (option_value_length < G_MAXUINT16)) {
273 | /* something to write */
274 | option.type = option_type;
275 | option.value_length = (u_int16_t)option_value_length;
276 |
277 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
278 | return FALSE;
279 |
280 | if (!write_to_file(pfile, (const u_int8_t*)option_value, (int) option_value_length, bytes_written, err))
281 | return FALSE;
282 |
283 | if (option_value_length % 4) {
284 | if (!write_to_file(pfile, (const u_int8_t*)&padding, 4 - option_value_length % 4, bytes_written, err))
285 | return FALSE;
286 | }
287 | }
288 | return TRUE;
289 | }
290 |
291 | /* Write a pre-formatted pcapng block directly to the output file */
292 | u_int8_t
293 | pcapng_write_block(FILE* pfile,
294 | const u_int8_t *data,
295 | u_int32_t length,
296 | u_int64_t *bytes_written,
297 | int *err)
298 | {
299 | u_int32_t block_length, end_length;
300 | /* Check
301 | * - length and data are aligned to 4 bytes
302 | * - block_total_length field is the same at the start and end of the block
303 | *
304 | * The block_total_length is not checked against the provided length but
305 | * getting the trailing block_total_length from the length argument gives
306 | * us an implicit check of correctness without needing to do an endian swap
307 | */
308 | if (((length & 3) != 0) || ((data[0] & 3) != 0)) {
309 | *err = EINVAL;
310 | return FALSE;
311 | }
312 | block_length = *(const u_int32_t *) (data+sizeof(u_int32_t));
313 | end_length = *(const u_int32_t *) (data+length-sizeof(u_int32_t));
314 | if (block_length != end_length) {
315 | *err = EBADMSG;
316 | return FALSE;
317 | }
318 | return write_to_file(pfile, data, length, bytes_written, err);
319 | }
320 |
321 | u_int8_t
322 | pcapng_write_section_header_block(FILE* pfile,
323 | const char *comment,
324 | const char *hw,
325 | const char *os,
326 | const char *appname,
327 | u_int64_t section_length,
328 | u_int64_t *bytes_written,
329 | int *err)
330 | {
331 | struct shb shb;
332 | struct pcap_option option;
333 | u_int32_t block_total_length;
334 | u_int32_t options_length;
335 |
336 | /* Size of base header */
337 | block_total_length = sizeof(struct shb) +
338 | sizeof(u_int32_t);
339 | options_length = 0;
340 | options_length += pcapng_count_string_option(comment);
341 | options_length += pcapng_count_string_option(hw);
342 | options_length += pcapng_count_string_option(os);
343 | options_length += pcapng_count_string_option(appname);
344 | /* If we have options add size of end-of-options */
345 | if (options_length != 0) {
346 | options_length += (u_int32_t)sizeof(struct pcap_option);
347 | }
348 | block_total_length += options_length;
349 |
350 | /* write shb header */
351 | shb.block_type = SECTION_HEADER_BLOCK_TYPE;
352 | shb.block_total_length = block_total_length;
353 | shb.byte_order_magic = PCAPNG_MAGIC;
354 | shb.major_version = PCAPNG_MAJOR_VERSION;
355 | shb.minor_version = PCAPNG_MINOR_VERSION;
356 | shb.section_length = section_length;
357 |
358 | if (!write_to_file(pfile, (const u_int8_t*)&shb, sizeof(struct shb), bytes_written, err))
359 | return FALSE;
360 |
361 | if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment,
362 | bytes_written, err))
363 | return FALSE;
364 | if (!pcapng_write_string_option(pfile, SHB_HARDWARE, hw,
365 | bytes_written, err))
366 | return FALSE;
367 | if (!pcapng_write_string_option(pfile, SHB_OS, os,
368 | bytes_written, err))
369 | return FALSE;
370 | if (!pcapng_write_string_option(pfile, SHB_USERAPPL, appname,
371 | bytes_written, err))
372 | return FALSE;
373 | if (options_length != 0) {
374 | /* write end of options */
375 | option.type = OPT_ENDOFOPT;
376 | option.value_length = 0;
377 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
378 | return FALSE;
379 | }
380 |
381 | /* write the trailing block total length */
382 | return write_to_file(pfile, (const u_int8_t*)&block_total_length, sizeof(u_int32_t), bytes_written, err);
383 | }
384 |
385 | u_int8_t
386 | pcapng_write_interface_description_block(FILE* pfile,
387 | const char *comment, /* OPT_COMMENT 1 */
388 | const char *name, /* IDB_NAME 2 */
389 | const char *descr, /* IDB_DESCRIPTION 3 */
390 | const char *filter, /* IDB_FILTER 11 */
391 | const char *os, /* IDB_OS 12 */
392 | int link_type,
393 | int snap_len,
394 | u_int64_t *bytes_written,
395 | u_int64_t if_speed, /* IDB_IF_SPEED 8 */
396 | u_int8_t tsresol, /* IDB_TSRESOL 9 */
397 | int *err)
398 | {
399 | struct idb idb;
400 | struct pcap_option option;
401 | u_int32_t block_total_length;
402 | u_int32_t options_length;
403 | const u_int32_t padding = 0;
404 |
405 | block_total_length = (u_int32_t)(sizeof(struct idb) + sizeof(u_int32_t));
406 | options_length = 0;
407 | /* 01 - OPT_COMMENT */
408 | options_length += pcapng_count_string_option(comment);
409 |
410 | /* 02 - IDB_NAME */
411 | options_length += pcapng_count_string_option(name);
412 |
413 | /* 03 - IDB_DESCRIPTION */
414 | options_length += pcapng_count_string_option(descr);
415 |
416 | /* 08 - IDB_IF_SPEED */
417 | if (if_speed != 0) {
418 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
419 | sizeof(u_int64_t));
420 | }
421 |
422 | /* 09 - IDB_TSRESOL */
423 | if (tsresol != 0) {
424 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
425 | sizeof(struct pcap_option));
426 | }
427 |
428 | /* 11 - IDB_FILTER */
429 | if ((filter != NULL) && (strlen(filter) > 0) && (strlen(filter) < G_MAXUINT16)) {
430 | /* No, this isn't a string, it has an extra type byte */
431 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
432 | (u_int16_t)(ADD_PADDING(strlen(filter)+ 1)));
433 | }
434 |
435 | /* 12 - IDB_OS */
436 | options_length += pcapng_count_string_option(os);
437 |
438 | /* If we have options add size of end-of-options */
439 | if (options_length != 0) {
440 | options_length += (u_int32_t)sizeof(struct pcap_option);
441 | }
442 | block_total_length += options_length;
443 |
444 | /* write block header */
445 | idb.block_type = INTERFACE_DESCRIPTION_BLOCK_TYPE;
446 | idb.block_total_length = block_total_length;
447 | idb.link_type = link_type;
448 | idb.reserved = 0;
449 | idb.snap_len = snap_len;
450 | if (!write_to_file(pfile, (const u_int8_t*)&idb, sizeof(struct idb), bytes_written, err))
451 | return FALSE;
452 |
453 | /* 01 - OPT_COMMENT - write comment string if applicable */
454 | if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment,
455 | bytes_written, err))
456 | return FALSE;
457 |
458 | /* 02 - IDB_NAME - write interface name string if applicable */
459 | if (!pcapng_write_string_option(pfile, IDB_NAME, name,
460 | bytes_written, err))
461 | return FALSE;
462 |
463 | /* 03 - IDB_DESCRIPTION */
464 | /* write interface description string if applicable */
465 | if (!pcapng_write_string_option(pfile, IDB_DESCRIPTION, descr,
466 | bytes_written, err))
467 | return FALSE;
468 |
469 | /* 08 - IDB_IF_SPEED */
470 | if (if_speed != 0) {
471 | option.type = IDB_IF_SPEED;
472 | option.value_length = sizeof(u_int64_t);
473 |
474 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
475 | return FALSE;
476 |
477 | if (!write_to_file(pfile, (const u_int8_t*)&if_speed, sizeof(u_int64_t), bytes_written, err))
478 | return FALSE;
479 | }
480 |
481 | /* 09 - IDB_TSRESOL */
482 | if (tsresol != 0) {
483 | option.type = IDB_TSRESOL;
484 | option.value_length = sizeof(u_int8_t);
485 |
486 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
487 | return FALSE;
488 |
489 | if (!write_to_file(pfile, (const u_int8_t*)&tsresol, sizeof(u_int8_t), bytes_written, err))
490 | return FALSE;
491 |
492 | if (!write_to_file(pfile, (const u_int8_t*)&padding, 3, bytes_written, err))
493 | return FALSE;
494 | }
495 |
496 | /* 11 - IDB_FILTER - write filter string if applicable
497 | * We only write version 1 of the filter, pcapng string
498 | */
499 | if ((filter != NULL) && (strlen(filter) > 0) && (strlen(filter) < G_MAXUINT16 - 1)) {
500 | option.type = IDB_FILTER;
501 | option.value_length = (u_int16_t)(strlen(filter) + 1 );
502 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
503 | return FALSE;
504 |
505 | /* The first byte of the Option Data keeps a code of the filter used, 0 = lipbpcap filter string */
506 | if (!write_to_file(pfile, (const u_int8_t*)&padding, 1, bytes_written, err))
507 | return FALSE;
508 | if (!write_to_file(pfile, (const u_int8_t*)filter, (int) strlen(filter), bytes_written, err))
509 | return FALSE;
510 | if ((strlen(filter) + 1) % 4) {
511 | if (!write_to_file(pfile, (const u_int8_t*)&padding, 4 - (strlen(filter) + 1) % 4, bytes_written, err))
512 | return FALSE;
513 | }
514 | }
515 |
516 | /* 12 - IDB_OS - write os string if applicable */
517 | if (!pcapng_write_string_option(pfile, IDB_OS, os,
518 | bytes_written, err))
519 | return FALSE;
520 |
521 | if (options_length != 0) {
522 | /* write end of options */
523 | option.type = OPT_ENDOFOPT;
524 | option.value_length = 0;
525 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
526 | return FALSE;
527 | }
528 |
529 | /* write the trailing Block Total Length */
530 | return write_to_file(pfile, (const u_int8_t*)&block_total_length, sizeof(u_int32_t), bytes_written, err);
531 | }
532 |
533 | /* Write a record for a packet to a dump file.
534 | Returns TRUE on success, FALSE on failure. */
535 | u_int8_t
536 | pcapng_write_enhanced_packet_block(FILE* pfile,
537 | const char *comment,
538 | time_t sec, u_int32_t usec,
539 | u_int32_t caplen, u_int32_t len,
540 | u_int32_t interface_id,
541 | u_int32_t ts_mul,
542 | const u_int8_t *pd,
543 | u_int32_t flags,
544 | u_int64_t *bytes_written,
545 | int *err)
546 | {
547 | struct epb epb;
548 | struct pcap_option option;
549 | u_int32_t block_total_length;
550 | u_int64_t timestamp;
551 | u_int32_t options_length;
552 | const u_int32_t padding = 0;
553 | u_int8_t buff[8];
554 | u_int8_t i;
555 | u_int8_t pad_len = 0;
556 |
557 | block_total_length = (u_int32_t)(sizeof(struct epb) +
558 | ADD_PADDING(caplen) +
559 | sizeof(u_int32_t));
560 | options_length = 0;
561 | options_length += pcapng_count_string_option(comment);
562 | if (flags != 0) {
563 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
564 | sizeof(u_int32_t));
565 | }
566 | /* If we have options add size of end-of-options */
567 | if (options_length != 0) {
568 | options_length += (u_int32_t)sizeof(struct pcap_option);
569 | }
570 | block_total_length += options_length;
571 | timestamp = (u_int64_t)sec * ts_mul + (u_int64_t)usec;
572 | epb.block_type = ENHANCED_PACKET_BLOCK_TYPE;
573 | epb.block_total_length = block_total_length;
574 | epb.interface_id = interface_id;
575 | epb.timestamp_high = (u_int32_t)((timestamp>>32) & 0xffffffff);
576 | epb.timestamp_low = (u_int32_t)(timestamp & 0xffffffff);
577 | epb.captured_len = caplen;
578 | epb.packet_len = len;
579 | if (!write_to_file(pfile, (const u_int8_t*)&epb, sizeof(struct epb), bytes_written, err))
580 | return FALSE;
581 | if (!write_to_file(pfile, pd, caplen, bytes_written, err))
582 | return FALSE;
583 | /* Use more efficient write in case of no "extras" */
584 | if(caplen % 4) {
585 | pad_len = 4 - (caplen % 4);
586 | }
587 | /*
588 | * If we have no options to write, just write out the padding and
589 | * the block total length with one fwrite() call.
590 | */
591 | if(!comment && flags == 0 && options_length==0){
592 | /* Put padding in the buffer */
593 | for (i = 0; i < pad_len; i++) {
594 | buff[i] = 0;
595 | }
596 | /* Write the total length */
597 | memcpy(&buff[i], &block_total_length, sizeof(u_int32_t));
598 | i += sizeof(u_int32_t);
599 | return write_to_file(pfile, (const u_int8_t*)&buff, i, bytes_written, err);
600 | }
601 | if (pad_len) {
602 | if (!write_to_file(pfile, (const u_int8_t*)&padding, pad_len, bytes_written, err))
603 | return FALSE;
604 | }
605 | if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment,
606 | bytes_written, err))
607 | return FALSE;
608 | if (flags != 0) {
609 | option.type = EPB_FLAGS;
610 | option.value_length = sizeof(u_int32_t);
611 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
612 | return FALSE;
613 | if (!write_to_file(pfile, (const u_int8_t*)&flags, sizeof(u_int32_t), bytes_written, err))
614 | return FALSE;
615 | }
616 | if (options_length != 0) {
617 | /* write end of options */
618 | option.type = OPT_ENDOFOPT;
619 | option.value_length = 0;
620 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
621 | return FALSE;
622 | }
623 |
624 | return write_to_file(pfile, (const u_int8_t*)&block_total_length, sizeof(u_int32_t), bytes_written, err);
625 | }
626 |
627 | u_int8_t
628 | pcapng_write_interface_statistics_block(FILE* pfile,
629 | u_int32_t interface_id,
630 | u_int64_t *bytes_written,
631 | const char *comment, /* OPT_COMMENT 1 */
632 | u_int64_t isb_starttime, /* ISB_STARTTIME 2 */
633 | u_int64_t isb_endtime, /* ISB_ENDTIME 3 */
634 | u_int64_t isb_ifrecv, /* ISB_IFRECV 4 */
635 | u_int64_t isb_ifdrop, /* ISB_IFDROP 5 */
636 | int *err)
637 | {
638 | struct isb isb;
639 | #ifdef _WIN32
640 | FILETIME now;
641 | #else
642 | struct timeval now;
643 | #endif
644 | struct pcap_option option;
645 | u_int32_t block_total_length;
646 | u_int32_t options_length;
647 | u_int64_t timestamp;
648 |
649 | #ifdef _WIN32
650 | /*
651 | * Current time, represented as 100-nanosecond intervals since
652 | * January 1, 1601, 00:00:00 UTC.
653 | *
654 | * I think DWORD might be signed, so cast both parts of "now"
655 | * to u_int32_t so that the sign bit doesn't get treated specially.
656 | *
657 | * Windows 8 provides GetSystemTimePreciseAsFileTime which we
658 | * might want to use instead.
659 | */
660 | GetSystemTimeAsFileTime(&now);
661 | timestamp = (((u_int64_t)(u_int32_t)now.dwHighDateTime) << 32) +
662 | (u_int32_t)now.dwLowDateTime;
663 |
664 | /*
665 | * Convert to same thing but as 1-microsecond, i.e. 1000-nanosecond,
666 | * intervals.
667 | */
668 | timestamp /= 10;
669 |
670 | /*
671 | * Subtract difference, in microseconds, between January 1, 1601
672 | * 00:00:00 UTC and January 1, 1970, 00:00:00 UTC.
673 | */
674 | timestamp -= G_U_INT64_T_CONSTANT(11644473600000000);
675 | #else
676 | /*
677 | * Current time, represented as seconds and microseconds since
678 | * January 1, 1970, 00:00:00 UTC.
679 | */
680 | gettimeofday(&now, NULL);
681 |
682 | /*
683 | * Convert to delta in microseconds.
684 | */
685 | timestamp = (u_int64_t)(now.tv_sec) * 1000000 +
686 | (u_int64_t)(now.tv_usec);
687 | #endif
688 | block_total_length = (u_int32_t)(sizeof(struct isb) + sizeof(u_int32_t));
689 | options_length = 0;
690 | if (isb_ifrecv != G_MAXUINT64) {
691 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
692 | sizeof(u_int64_t));
693 | }
694 | if (isb_ifdrop != G_MAXUINT64) {
695 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
696 | sizeof(u_int64_t));
697 | }
698 | /* OPT_COMMENT */
699 | options_length += pcapng_count_string_option(comment);
700 | if (isb_starttime !=0) {
701 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
702 | sizeof(u_int64_t)); /* ISB_STARTTIME */
703 | }
704 | if (isb_endtime !=0) {
705 | options_length += (u_int32_t)(sizeof(struct pcap_option) +
706 | sizeof(u_int64_t)); /* ISB_ENDTIME */
707 | }
708 | /* If we have options add size of end-of-options */
709 | if (options_length != 0) {
710 | options_length += (u_int32_t)sizeof(struct pcap_option);
711 | }
712 | block_total_length += options_length;
713 |
714 | isb.block_type = INTERFACE_STATISTICS_BLOCK_TYPE;
715 | isb.block_total_length = block_total_length;
716 | isb.interface_id = interface_id;
717 | isb.timestamp_high = (u_int32_t)((timestamp>>32) & 0xffffffff);
718 | isb.timestamp_low = (u_int32_t)(timestamp & 0xffffffff);
719 | if (!write_to_file(pfile, (const u_int8_t*)&isb, sizeof(struct isb), bytes_written, err))
720 | return FALSE;
721 |
722 | /* write comment string if applicable */
723 | if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment,
724 | bytes_written, err))
725 | return FALSE;
726 |
727 | if (isb_starttime !=0) {
728 | u_int32_t high, low;
729 |
730 | option.type = ISB_STARTTIME;
731 | option.value_length = sizeof(u_int64_t);
732 | high = (u_int32_t)((isb_starttime>>32) & 0xffffffff);
733 | low = (u_int32_t)(isb_starttime & 0xffffffff);
734 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
735 | return FALSE;
736 |
737 | if (!write_to_file(pfile, (const u_int8_t*)&high, sizeof(u_int32_t), bytes_written, err))
738 | return FALSE;
739 |
740 | if (!write_to_file(pfile, (const u_int8_t*)&low, sizeof(u_int32_t), bytes_written, err))
741 | return FALSE;
742 | }
743 | if (isb_endtime !=0) {
744 | u_int32_t high, low;
745 |
746 | option.type = ISB_ENDTIME;
747 | option.value_length = sizeof(u_int64_t);
748 | high = (u_int32_t)((isb_endtime>>32) & 0xffffffff);
749 | low = (u_int32_t)(isb_endtime & 0xffffffff);
750 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
751 | return FALSE;
752 |
753 | if (!write_to_file(pfile, (const u_int8_t*)&high, sizeof(u_int32_t), bytes_written, err))
754 | return FALSE;
755 |
756 | if (!write_to_file(pfile, (const u_int8_t*)&low, sizeof(u_int32_t), bytes_written, err))
757 | return FALSE;
758 | }
759 | if (isb_ifrecv != G_MAXUINT64) {
760 | option.type = ISB_IFRECV;
761 | option.value_length = sizeof(u_int64_t);
762 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
763 | return FALSE;
764 |
765 | if (!write_to_file(pfile, (const u_int8_t*)&isb_ifrecv, sizeof(u_int64_t), bytes_written, err))
766 | return FALSE;
767 | }
768 | if (isb_ifdrop != G_MAXUINT64) {
769 | option.type = ISB_IFDROP;
770 | option.value_length = sizeof(u_int64_t);
771 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
772 | return FALSE;
773 |
774 | if (!write_to_file(pfile, (const u_int8_t*)&isb_ifdrop, sizeof(u_int64_t), bytes_written, err))
775 | return FALSE;
776 | }
777 | if (options_length != 0) {
778 | /* write end of options */
779 | option.type = OPT_ENDOFOPT;
780 | option.value_length = 0;
781 | if (!write_to_file(pfile, (const u_int8_t*)&option, sizeof(struct pcap_option), bytes_written, err))
782 | return FALSE;
783 | }
784 |
785 | return write_to_file(pfile, (const u_int8_t*)&block_total_length, sizeof(u_int32_t), bytes_written, err);
786 | }
787 |
788 | /*
789 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
790 | *
791 | * Local variables:
792 | * c-basic-offset: 8
793 | * tab-width: 8
794 | * indent-tabs-mode: nil
795 | * End:
796 | *
797 | * vi: set shiftwidth=8 tabstop=8 expandtab:
798 | * :indentSize=8:tabSize=8:noTabs=true:
799 | */
800 |
--------------------------------------------------------------------------------
/wireshark/pcapio.h:
--------------------------------------------------------------------------------
1 | /* pcapio.h
2 | * Declarations of our own routines for writing libpcap files.
3 | *
4 | * Wireshark - Network traffic analyzer
5 | * By Gerald Combs
6 | * Copyright 1998 Gerald Combs
7 | *
8 | * Derived from code in the Wiretap Library
9 | * Copyright (c) 1998 by Gilbert Ramirez
10 | *
11 | * SPDX-License-Identifier: GPL-2.0-or-later
12 | */
13 |
14 | /* Writing pcap files */
15 |
16 | /** Write the file header to a dump file.
17 | Returns TRUE on success, FALSE on failure.
18 | Sets "*err" to an error code, or 0 for a short write, on failure*/
19 | extern u_int8_t
20 | libpcap_write_file_header(FILE* pfile, int linktype, int snaplen,
21 | u_int8_t ts_nsecs, u_int64_t *bytes_written, int *err);
22 |
23 | /** Write a record for a packet to a dump file.
24 | Returns TRUE on success, FALSE on failure. */
25 | extern u_int8_t
26 | libpcap_write_packet(FILE* pfile,
27 | time_t sec, u_int32_t usec,
28 | u_int32_t caplen, u_int32_t len,
29 | const u_int8_t *pd,
30 | u_int64_t *bytes_written, int *err);
31 |
32 | /* Writing pcapng files */
33 |
34 | /* Write a pre-formatted pcapng block */
35 | extern u_int8_t
36 | pcapng_write_block(FILE* pfile,
37 | const u_int8_t *data,
38 | u_int32_t block_total_length,
39 | u_int64_t *bytes_written,
40 | int *err);
41 |
42 | /** Write a section header block (SHB)
43 | *
44 | */
45 | extern u_int8_t
46 | pcapng_write_section_header_block(FILE* pfile, /**< Write information */
47 | const char *comment, /**< Comment on the section, Optinon 1 opt_comment
48 | * A UTF-8 string containing a comment that is associated to the current block.
49 | */
50 | const char *hw, /**< HW, Optinon 2 shb_hardware
51 | * An UTF-8 string containing the description of the hardware used to create this section.
52 | */
53 | const char *os, /**< Operating system name, Optinon 3 shb_os
54 | * An UTF-8 string containing the name of the operating system used to create this section.
55 | */
56 | const char *appname, /**< Application name, Optinon 4 shb_userappl
57 | * An UTF-8 string containing the name of the application used to create this section.
58 | */
59 | u_int64_t section_length, /**< Length of section */
60 | u_int64_t *bytes_written, /**< Number of written bytes */
61 | int *err /**< Error type */
62 | );
63 |
64 | extern u_int8_t
65 | pcapng_write_interface_description_block(FILE* pfile,
66 | const char *comment, /* OPT_COMMENT 1 */
67 | const char *name, /* IDB_NAME 2 */
68 | const char *descr, /* IDB_DESCRIPTION 3 */
69 | const char *filter, /* IDB_FILTER 11 */
70 | const char *os, /* IDB_OS 12 */
71 | int link_type,
72 | int snap_len,
73 | u_int64_t *bytes_written,
74 | u_int64_t if_speed, /* IDB_IF_SPEED 8 */
75 | u_int8_t tsresol, /* IDB_TSRESOL 9 */
76 | int *err);
77 |
78 | extern u_int8_t
79 | pcapng_write_interface_statistics_block(FILE* pfile,
80 | u_int32_t interface_id,
81 | u_int64_t *bytes_written,
82 | const char *comment, /* OPT_COMMENT 1 */
83 | u_int64_t isb_starttime, /* ISB_STARTTIME 2 */
84 | u_int64_t isb_endtime, /* ISB_ENDTIME 3 */
85 | u_int64_t isb_ifrecv, /* ISB_IFRECV 4 */
86 | u_int64_t isb_ifdrop, /* ISB_IFDROP 5 */
87 | int *err);
88 |
89 | extern u_int8_t
90 | pcapng_write_enhanced_packet_block(FILE* pfile,
91 | const char *comment,
92 | time_t sec, u_int32_t usec,
93 | u_int32_t caplen, u_int32_t len,
94 | u_int32_t interface_id,
95 | u_int32_t ts_mul,
96 | const u_int8_t *pd,
97 | u_int32_t flags,
98 | u_int64_t *bytes_written,
99 | int *err);
100 |
101 | /*
102 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
103 | *
104 | * Local variables:
105 | * c-basic-offset: 4
106 | * tab-width: 8
107 | * indent-tabs-mode: nil
108 | * End:
109 | *
110 | * vi: set shiftwidth=4 tabstop=8 expandtab:
111 | * :indentSize=4:tabSize=8:noTabs=true:
112 | */
113 |
--------------------------------------------------------------------------------