First commit
This commit is contained in:
commit
03b2a0eaef
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
load_bpf
|
||||
tp_tcp.o
|
||||
.**.swp
|
45
README.md
Normal file
45
README.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Introduction
|
||||
This project collect some metrics for TCP. For doing that, I use eBPF.
|
||||
|
||||
## Requirements
|
||||
For executing and loading the eBPF program and to send data to InfluxDB, you need to install some packages:
|
||||
|
||||
```
|
||||
sudo apt install linux-headers-`uname -r` clang-11 gcc gcc-multilib libbpf-dev libbpfcc bpfcc-tools
|
||||
```
|
||||
|
||||
For installing bpftool command:
|
||||
```
|
||||
sudo apt install linux-tools-common linux-tools-generic
|
||||
```
|
||||
|
||||
## Compile eBPF program
|
||||
First, you need to dump the vmlinux header file, which contains all definitions codes of your Linux kernel:
|
||||
|
||||
```
|
||||
sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
|
||||
```
|
||||
|
||||
After that, you can compile the eBPF code:
|
||||
|
||||
```
|
||||
$ clang-11 -g -O2 -target bpf -c tp_tcp.c -o tp_tcp.o
|
||||
```
|
||||
|
||||
Now, I made a C script which can load the eBPF program and attach it:
|
||||
|
||||
```
|
||||
$ gcc load_bpf.c -o load_bpf -lbpf
|
||||
```
|
||||
|
||||
And you can execute it, but, you need to have the root privileges:
|
||||
|
||||
```
|
||||
$ sudo ./load_bpf
|
||||
```
|
||||
|
||||
|
||||
## InfluxDB
|
||||
I use this [project](https://github.com/nigelargriffiths/InfluxDB-C-client) for sending data to InfluxDB.
|
||||
|
||||
The documentation of that project is [here](https://www.influxdata.com/blog/influxdb-c-client-library-for-capturing-statistics/)
|
18
common.h
Normal file
18
common.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef H_COMMON
|
||||
#define H_COMMON
|
||||
|
||||
typedef unsigned char __u8;
|
||||
typedef unsigned short __u16;
|
||||
typedef unsigned int __u32;
|
||||
typedef unsigned long long __u64;
|
||||
|
||||
struct reset {
|
||||
__u8 saddr[4];
|
||||
__u8 daddr[4];
|
||||
__u16 sport;
|
||||
__u16 dport;
|
||||
__u16 family;
|
||||
__u16 proto;
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif
|
15
exec.sh
Executable file
15
exec.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/sh
|
||||
|
||||
use_influxdb=true
|
||||
influxdb_host="<ip>"
|
||||
influxdb_orgid="<org ID>"
|
||||
influxdb_token="<token>"
|
||||
influxdb_bucket="tcp_metrics"
|
||||
|
||||
clang-11 -g -O2 -target bpf -c tp_tcp.c -o tp_tcp.o && \
|
||||
gcc ic.c load_bpf.c -o load_bpf -lbpf && \
|
||||
if [ "$use_influxdb" = true ]; then
|
||||
sudo ./load_bpf $influxdb_host $influxdb_orgid $influxdb_token $influxdb_bucket
|
||||
else
|
||||
sudo ./load_bpf --no-influxdb
|
||||
fi
|
361
ic.c
Normal file
361
ic.c
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Influx C (ic) client for data capture
|
||||
* Developer: Nigel Griffiths.
|
||||
* (C) Copyright 2021 Nigel Griffiths
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the gnu general public license as published by
|
||||
the free software foundation, either version 3 of the license, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but without any warranty; without even the implied warranty of
|
||||
merchantability or fitness for a particular purpose. see the
|
||||
gnu general public license for more details.
|
||||
|
||||
You should have received a copy of the gnu general public license
|
||||
along with this program. if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Compile: cc ic.c -g -O3 -o ic
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define DEBUG if(debug)
|
||||
#define MEGABYTE ( 1024 * 1024 ) /* USed as the default buffer sizes */
|
||||
|
||||
int debug = 0; /* 0=off, 1=on basic, 2=trace like output */
|
||||
|
||||
char influx_hostname[1024 + 1] = { 0 };/* details of the influxdb server or telegraf */
|
||||
char influx_ip[16 + 1] = { 0 };
|
||||
long influx_port = 0;
|
||||
|
||||
char influx_database[256+1]; /* the influxdb database */
|
||||
char influx_username[64+1]; /* optional for influxdb access */
|
||||
char influx_password[64+1]; /* optional for influxdb access */
|
||||
char influx_token[128+1];
|
||||
char influx_orgID[64+1];
|
||||
|
||||
char *output; /* all the stats must fit in this buffer */
|
||||
long output_size = 0;
|
||||
long output_char = 0;
|
||||
|
||||
char *influx_tags; /* saved tags for every influxdb line protocol mesurement */
|
||||
|
||||
int subended = 0; /* stop ic_subend and ic_measureend both enig the measure */
|
||||
int first_sub = 0; /* need to remove the ic_measure measure before adding ic_sub measure */
|
||||
char saved_section[64];
|
||||
char saved_sub[64];
|
||||
|
||||
int sockfd; /* file desciptor for socket connection */
|
||||
|
||||
void error(char *buf)
|
||||
{
|
||||
fprintf(stderr, "error: \"%s\" errno=%d meaning=\"%s\"\n", buf, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
sleep(2); /* this can help the socket close cleanly at the remote end */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void ic_debug(int level)
|
||||
{
|
||||
debug = level;
|
||||
}
|
||||
|
||||
/* ic_tags() argument is the measurement tags for influddb */
|
||||
/* example: "host=vm1234" note:the comma & hostname of the virtual machine sending the data */
|
||||
/* complex: "host=lpar42,serialnum=987654,arch=power9" note:the comma separated list */
|
||||
void ic_tags(char *t)
|
||||
{
|
||||
DEBUG fprintf(stderr,"ic_tags(%s)\n",t);
|
||||
if( influx_tags == (char *) 0) {
|
||||
if( (influx_tags = (char *)malloc(MEGABYTE)) == (char *)-1)
|
||||
error("failed to malloc() tags buffer");
|
||||
}
|
||||
|
||||
strncpy(influx_tags,t,256);
|
||||
}
|
||||
|
||||
void ic_influx_database(char *host, long port, char *db) /* note: converts influxdb hostname to ip address */
|
||||
{
|
||||
struct hostent *he;
|
||||
char errorbuf[1024 +1 ];
|
||||
|
||||
influx_port = port;
|
||||
strncpy(influx_database,db,256);
|
||||
|
||||
if(host[0] <= '0' && host[0] <='9') {
|
||||
DEBUG fprintf(stderr,"ic_influx(ipaddr=%s,port=%ld,database=%s))\n",host,port,db);
|
||||
strncpy(influx_ip,host,16);
|
||||
} else {
|
||||
DEBUG fprintf(stderr,"ic_influx_by_hostname(host=%s,port=%ld,database=%s))\n",host,port,db);
|
||||
strncpy(influx_hostname,host,1024);
|
||||
if (isalpha(host[0])) {
|
||||
|
||||
he = gethostbyname(host);
|
||||
if (he == NULL) {
|
||||
sprintf(errorbuf, "influx host=%s to ip address convertion failed gethostbyname(), bailing out\n", host);
|
||||
error(errorbuf);
|
||||
}
|
||||
/* this could return multiple ip addresses but we assume its the first one */
|
||||
if (he->h_addr_list[0] != NULL) {
|
||||
strcpy(influx_ip, inet_ntoa(*(struct in_addr *) (he->h_addr_list[0])));
|
||||
DEBUG fprintf(stderr,"ic_influx_by_hostname hostname=%s converted to ip address %s))\n",host,influx_ip);
|
||||
} else {
|
||||
sprintf(errorbuf, "influx host=%s to ip address convertion failed (empty list), bailing out\n", host);
|
||||
error(errorbuf);
|
||||
}
|
||||
} else {
|
||||
strcpy( influx_ip, host); /* perhaps the hostname is actually an ip address */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ic_influx_token(char *token){
|
||||
DEBUG fprintf(stderr, "ic_influx_token(token=%s))\n", token);
|
||||
strncpy(influx_token, token, 128);
|
||||
}
|
||||
void ic_influx_orgID(char *orgID){
|
||||
DEBUG fprintf(stderr, "ic_influx_orgID(ordID=%s))\n", orgID);
|
||||
strncpy(influx_orgID, orgID, 64);
|
||||
}
|
||||
void ic_influx_userpw(char *user, char *pw)
|
||||
{
|
||||
DEBUG fprintf(stderr,"ic_influx_userpw(username=%s,pssword=%s))\n",user,pw);
|
||||
strncpy(influx_username,user,64);
|
||||
strncpy(influx_password,pw,64);
|
||||
}
|
||||
|
||||
int create_socket() /* returns 1 for error and 0 for ok */
|
||||
{
|
||||
int i;
|
||||
static char buffer[4096];
|
||||
static struct sockaddr_in serv_addr;
|
||||
|
||||
if(debug) DEBUG fprintf(stderr, "socket: trying to connect to \"%s\":%ld\n", influx_ip, influx_port);
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
error("socket() call failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = inet_addr(influx_ip);
|
||||
serv_addr.sin_port = htons(influx_port);
|
||||
|
||||
/* connect tot he socket offered by the web server */
|
||||
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
|
||||
DEBUG fprintf(stderr, " connect() call failed errno=%d", errno);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ic_check(long adding) /* Check the buffer space */
|
||||
{
|
||||
if(output == (char *)0) { /* First time create the buffer *
|
||||
if( (output = (char *)malloc(MEGABYTE)) == (char *)-1)
|
||||
error("failed to malloc() output buffer");
|
||||
}
|
||||
if(output_char + (2*adding) > output_size) /* When near the end of the output buffer, extend it*/
|
||||
if( (output = (char *)realloc(output, output_size + MEGABYTE)) == (char *)-1)
|
||||
error("failed to realloc() output buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void remove_ending_comma_if_any()
|
||||
{
|
||||
if (output[output_char - 1] == ',') {
|
||||
output[output_char - 1] = 0; /* remove the char */
|
||||
output_char--;
|
||||
}
|
||||
}
|
||||
|
||||
void ic_measure(char *section)
|
||||
{
|
||||
ic_check( strlen(section) + strlen(influx_tags) + 3);
|
||||
|
||||
output_char += sprintf(&output[output_char], "%s,%s ", section, influx_tags);
|
||||
strcpy(saved_section, section);
|
||||
first_sub = 1;
|
||||
subended = 0;
|
||||
DEBUG fprintf(stderr, "ic_measure(\"%s\") count=%ld\n", section, output_char);
|
||||
}
|
||||
|
||||
void ic_measureend()
|
||||
{
|
||||
ic_check( 4 );
|
||||
remove_ending_comma_if_any();
|
||||
if (!subended) {
|
||||
output_char += sprintf(&output[output_char], " \n");
|
||||
}
|
||||
subended = 0;
|
||||
DEBUG fprintf(stderr, "ic_measureend()\n");
|
||||
}
|
||||
|
||||
/* Note this added a further tag to the measurement of the "resource_name" */
|
||||
/* measurement might be "disks" */
|
||||
/* sub might be "sda1", "sdb1", etc */
|
||||
void ic_sub(char *resource)
|
||||
{
|
||||
int i;
|
||||
|
||||
ic_check( strlen(saved_section) + strlen(influx_tags) +strlen(saved_sub) + strlen(resource) + 9);
|
||||
|
||||
/* remove previously added section */
|
||||
if (first_sub) {
|
||||
for (i = output_char - 1; i > 0; i--) {
|
||||
if (output[i] == '\n') {
|
||||
output[i + 1] = 0;
|
||||
output_char = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
first_sub = 0;
|
||||
|
||||
/* remove the trailing s */
|
||||
strcpy(saved_sub, saved_section);
|
||||
if (saved_sub[strlen(saved_sub) - 1] == 's') {
|
||||
saved_sub[strlen(saved_sub) - 1] = 0;
|
||||
}
|
||||
output_char += sprintf(&output[output_char], "%s,%s,%s_name=%s ", saved_section, influx_tags, saved_sub, resource);
|
||||
subended = 0;
|
||||
DEBUG fprintf(stderr, "ic_sub(\"%s\") count=%ld\n", resource, output_char);
|
||||
}
|
||||
|
||||
void ic_subend()
|
||||
{
|
||||
ic_check( 4 );
|
||||
remove_ending_comma_if_any();
|
||||
output_char += sprintf(&output[output_char], " \n");
|
||||
subended = 1;
|
||||
DEBUG fprintf(stderr, "ic_subend()\n");
|
||||
}
|
||||
|
||||
void ic_long(char *name, long long value)
|
||||
{
|
||||
ic_check( strlen(name) + 16 + 4 );
|
||||
output_char += sprintf(&output[output_char], "%s=%lldi,", name, value);
|
||||
DEBUG fprintf(stderr, "ic_long(\"%s\",%lld) count=%ld\n", name, value, output_char);
|
||||
}
|
||||
|
||||
void ic_double(char *name, double value)
|
||||
{
|
||||
ic_check( strlen(name) + 16 + 4 );
|
||||
if (isnan(value) || isinf(value)) { /* not-a-number or infinity */
|
||||
DEBUG fprintf(stderr, "ic_double(%s,%.1f) - nan error\n", name, value);
|
||||
} else {
|
||||
output_char += sprintf(&output[output_char], "%s=%.3f,", name, value);
|
||||
DEBUG fprintf(stderr, "ic_double(\"%s\",%.1f) count=%ld\n", name, value, output_char);
|
||||
}
|
||||
}
|
||||
|
||||
void ic_string(char *name, char *value)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
|
||||
ic_check( strlen(name) + strlen(value) + 4 );
|
||||
len = strlen(value);
|
||||
for (i = 0; i < len; i++) /* replace problem characters and with a space */
|
||||
if (value[i] == '\n' || iscntrl(value[i]))
|
||||
value[i] = ' ';
|
||||
output_char += sprintf(&output[output_char], "%s=\"%s\",", name, value);
|
||||
DEBUG fprintf(stderr, "ic_string(\"%s\",\"%s\") count=%ld\n", name, value, output_char);
|
||||
}
|
||||
|
||||
void ic_push()
|
||||
{
|
||||
char header[1024];
|
||||
char result[1024];
|
||||
char buffer[1024 * 8];
|
||||
int ret;
|
||||
int i;
|
||||
int total;
|
||||
int sent;
|
||||
int code;
|
||||
|
||||
if (output_char == 0) /* nothing to send so skip this operation */
|
||||
return;
|
||||
if (influx_port) {
|
||||
DEBUG fprintf(stderr, "ic_push() size=%ld\n", output_char);
|
||||
if (create_socket() == 1) {
|
||||
sprintf(buffer, "POST /api/v2/write?bucket=%s&orgID=%s HTTP/1.1\r\nHost: %s:%ld\r\nContent-Length: %ld\r\nAuthorization: Token %s\r\n\r\n",
|
||||
influx_database, influx_orgID, influx_hostname, influx_port, output_char, influx_token);
|
||||
DEBUG fprintf(stderr, "buffer size=%ld\nbuffer=<%s>\n", strlen(buffer), buffer);
|
||||
if ((ret = write(sockfd, buffer, strlen(buffer))) != strlen(buffer)) {
|
||||
fprintf(stderr, "warning: \"write post to sockfd failed.\" errno=%d\n", errno);
|
||||
}
|
||||
total = output_char;
|
||||
sent = 0;
|
||||
if (debug == 2)
|
||||
fprintf(stderr, "output size=%d output=\n<%s>\n", total, output);
|
||||
while (sent < total) {
|
||||
ret = write(sockfd, &output[sent], total - sent);
|
||||
DEBUG fprintf(stderr, "written=%d bytes sent=%d total=%d\n", ret, sent, total);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "warning: \"write body to sockfd failed.\" errno=%d\n", errno);
|
||||
break;
|
||||
}
|
||||
sent = sent + ret;
|
||||
}
|
||||
for (i = 0; i < 1024; i++) /* empty the buffer */
|
||||
result[i] = 0;
|
||||
if ((ret = read(sockfd, result, sizeof(result))) > 0) {
|
||||
result[ret] = 0;
|
||||
DEBUG fprintf(stderr, "received bytes=%d data=<%s>\n", ret, result);
|
||||
sscanf(result, "HTTP/1.1 %d", &code);
|
||||
for (i = 13; i < 1024; i++)
|
||||
if (result[i] == '\r')
|
||||
result[i] = 0;
|
||||
if (debug == 2)
|
||||
fprintf(stderr, "http-code=%d text=%s [204=Success]\n", code, &result[13]);
|
||||
if (code != 204)
|
||||
fprintf(stderr, "code %d -->%s<--\n", code, result);
|
||||
}
|
||||
close(sockfd);
|
||||
sockfd = 0;
|
||||
DEBUG fprintf(stderr, "ic_push complete\n");
|
||||
} else {
|
||||
DEBUG fprintf(stderr, "socket create failed\n");
|
||||
}
|
||||
} else error("influx port is not set, bailing out");
|
||||
|
||||
output[0] = 0;
|
||||
output_char = 0;
|
||||
}
|
||||
int ic_read(const char *ipsrc, const char *ipdst){
|
||||
int value;
|
||||
int ret;
|
||||
char buffer[1024 * 8];
|
||||
char request[1024];
|
||||
char result[1024];
|
||||
int len = 0;
|
||||
|
||||
sprintf(request, "from(bucket: \"tcp_metrics\")|> range(start: -1m)|> filter(fn: (r) => r[\"_measurement\"] == \"tcp_reset\")|> filter(fn: (r) => r[\"host\"] == \"%s\")|> filter(fn: (r) => r[\"_field\"] == \"%s\")|> last()", ipsrc, ipdst);
|
||||
len = strlen(request);
|
||||
|
||||
if (create_socket() == 1) {
|
||||
sprintf(buffer, "POST /api/v2/query?bucket=%s&orgID=%s HTTP/1.1\r\nHost: %s:%ld\r\nContent-Type: application/vnd.flux\r\nAccept: application/csv\r\nContent-Length: %d\r\nAuthorization: Token %s\r\n\r\n%s",
|
||||
influx_database, influx_orgID, influx_hostname, influx_port, len, influx_token, request);
|
||||
DEBUG fprintf(stderr, "buffer size=%ld\nbuffer=<%s>\n", strlen(buffer), buffer);
|
||||
printf("%s\n", buffer);
|
||||
if ((ret = write(sockfd, buffer, strlen(buffer))) != strlen(buffer)) {
|
||||
fprintf(stderr, "warning: \"write post to sockfd failed.\" errno=%d\n", errno);
|
||||
}
|
||||
if ((ret = read(sockfd, result, sizeof(result))) > 0) {
|
||||
printf("%s\n", result);
|
||||
}
|
||||
close(sockfd);
|
||||
sockfd = 0;
|
||||
}
|
||||
return value;
|
||||
}
|
25
ic.h
Normal file
25
ic.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Influx C (ic) client for data capture header file
|
||||
* Developer: Nigel Griffiths.
|
||||
* (C) Copyright 2021 Nigel Griffiths
|
||||
*/
|
||||
void ic_influx_database(char *host, long port, char *db);
|
||||
void ic_influx_userpw(char *user, char *pw);
|
||||
void ic_influx_orgID(char *orgID);
|
||||
void ic_influx_token(char *token);
|
||||
void ic_tags(char *tags);
|
||||
|
||||
void ic_measure(char *section);
|
||||
void ic_measureend();
|
||||
|
||||
void ic_sub(char *sub_name);
|
||||
void ic_subend();
|
||||
|
||||
void ic_long(char *name, long long value);
|
||||
void ic_double(char *name, double value);
|
||||
void ic_string(char *name, char *value);
|
||||
|
||||
void ic_push();
|
||||
int ic_read(const char *, const char *);
|
||||
void ic_debug(int level);
|
||||
|
190
load_bpf.c
Normal file
190
load_bpf.c
Normal file
@ -0,0 +1,190 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "common.h"
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "ic.h"
|
||||
|
||||
#define BUF_SIZE 300
|
||||
#define BUCKET_SIZE 16 // Bucket size for InfluxDB
|
||||
#define INFLUXDB_SIZE 64 // Host and ordID size for InfluxDB
|
||||
#define TOKEN_SIZE 128 // Token size for InfluxDB
|
||||
#define CNT_ARGS 5 // Number of args
|
||||
|
||||
|
||||
static void clean_obj(struct bpf_object *obj){
|
||||
printf("Cleaning\n");
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
static void usage(char *app){
|
||||
printf("Usage: %s <host> <ordID> <token> <bucket>\n", app);
|
||||
}
|
||||
static int check_arguments(int argc, char *argv[]){
|
||||
if (argc < 1){
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "--no-influxdb") == 0)
|
||||
return 0;
|
||||
|
||||
if (argc < CNT_ARGS){
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return argc; // Return the number of arguments
|
||||
}
|
||||
int main(int argc, char *argv[]){
|
||||
const char *fileObj = "tp_tcp.o";
|
||||
struct bpf_object *obj;
|
||||
struct bpf_program *program;
|
||||
struct bpf_map *map;
|
||||
struct reset s_reset;
|
||||
int err;
|
||||
int map_fd;
|
||||
int map_fd_filter_family;
|
||||
int map_fd_filter_sport;
|
||||
int map_fd_index;
|
||||
int keys = 0;
|
||||
int indexPackets = 0;
|
||||
int index = 0;
|
||||
char buf[BUF_SIZE];
|
||||
char host[INFLUXDB_SIZE];
|
||||
char orgID[INFLUXDB_SIZE];
|
||||
char token[TOKEN_SIZE];
|
||||
char bucket[BUCKET_SIZE];
|
||||
int use_influxdb = 0;
|
||||
int debug = 1;
|
||||
|
||||
// We get args
|
||||
err = check_arguments(argc, argv);
|
||||
if (err == -1)
|
||||
return -1;
|
||||
|
||||
if (err == CNT_ARGS){
|
||||
strncpy(host, argv[1], INFLUXDB_SIZE);
|
||||
strncpy(orgID, argv[2], INFLUXDB_SIZE);
|
||||
strncpy(token, argv[3], TOKEN_SIZE);
|
||||
strncpy(bucket, argv[4], BUCKET_SIZE);
|
||||
use_influxdb = 1;
|
||||
}
|
||||
|
||||
// Connect to InfluxDB
|
||||
if (use_influxdb) {
|
||||
ic_influx_database(host, 8086, bucket);
|
||||
ic_influx_orgID(orgID);
|
||||
ic_influx_token(token);
|
||||
}
|
||||
|
||||
obj = bpf_object__open_file(fileObj, NULL);
|
||||
if (!obj){
|
||||
printf("Failed to open %s\n", fileObj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(struct reset), 4096, BPF_ANY);
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (err){
|
||||
printf("%s\n", strerror(errno));
|
||||
printf("Failed to load object\n");
|
||||
clean_obj(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
program = bpf_object__find_program_by_name(obj, "tcp_rst_stats");
|
||||
if (!program){
|
||||
printf("Failed to find the program\n");
|
||||
clean_obj(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, "tcp_reset_stats");
|
||||
if (!map){
|
||||
printf("Failed to get the map\n");
|
||||
clean_obj(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
map_fd = bpf_object__find_map_fd_by_name(obj, "tcp_reset_stats");
|
||||
if (map_fd < 0){
|
||||
printf("Failed to find the map 'tcp_reset_stats'\n");
|
||||
clean_obj(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
map_fd_filter_family = bpf_object__find_map_fd_by_name(obj, "filter_family");
|
||||
if (map_fd_filter_family < 0){
|
||||
printf("Failed to find the map 'filter_family'\n");
|
||||
clean_obj(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
map_fd_index = bpf_object__find_map_fd_by_name(obj, "tcp_stats_index");
|
||||
if (map_fd_index < 0){
|
||||
printf("Failed to find the map 'tcp_stats_index'\n");
|
||||
clean_obj(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct bpf_link *link = bpf_program__attach(program);
|
||||
if (!link){
|
||||
printf("Failed to attach the program\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Sepcify our filters
|
||||
/*
|
||||
* IPv4: AF_INET -> 2
|
||||
* IPv6: AF_INET6 -> 10
|
||||
*/
|
||||
__s16 f = AF_INET;
|
||||
err = bpf_map_update_elem(map_fd_filter_family, &keys, &f, BPF_ANY);
|
||||
|
||||
while(1){
|
||||
err = bpf_map_lookup_elem(map_fd_index, &keys, &indexPackets);
|
||||
|
||||
// We have a new packet
|
||||
if (indexPackets > index){
|
||||
index = indexPackets;
|
||||
|
||||
err = bpf_map_lookup_elem(map_fd, &keys, &s_reset);
|
||||
if (err == 0){
|
||||
struct in_addr *src = (struct in_addr*)&s_reset.saddr;
|
||||
struct in_addr *dest = (struct in_addr*)&s_reset.daddr;
|
||||
char *s = inet_ntoa(*src);
|
||||
char *d;
|
||||
char tmp[35];
|
||||
int lastValue;
|
||||
memcpy(tmp, s, 35);
|
||||
d = inet_ntoa(*dest);
|
||||
|
||||
if (debug)
|
||||
printf("Sport: %d; dport: %d %d %d %s - %s\n", s_reset.sport, s_reset.dport, s_reset.family, s_reset.proto, tmp, d);
|
||||
|
||||
if (use_influxdb) {
|
||||
printf("Send data to influx\n");
|
||||
// Send data to InfluxDB
|
||||
snprintf(buf, BUF_SIZE, "host=%s", s);
|
||||
ic_tags(buf);
|
||||
|
||||
ic_measure("tcp_reset");
|
||||
ic_long("value", 1);
|
||||
ic_measureend();
|
||||
ic_push();
|
||||
|
||||
memset(buf, 0, BUF_SIZE);
|
||||
}
|
||||
}
|
||||
memset(&s_reset, 0, sizeof(struct reset));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
23
tcp_server.py
Normal file
23
tcp_server.py
Normal file
@ -0,0 +1,23 @@
|
||||
import socket
|
||||
import struct
|
||||
|
||||
def server(host, port):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
l_onoff = 1
|
||||
l_linger = 0
|
||||
s.setsockopt(
|
||||
socket.SOL_SOCKET,
|
||||
socket.SO_LINGER,
|
||||
struct.pack('ii', l_onoff, l_linger)
|
||||
)
|
||||
s.bind(('', port))
|
||||
s.listen(5)
|
||||
|
||||
while 1:
|
||||
skclt, addrclt = s.accept()
|
||||
print(f"New connection from client: {addrclt}")
|
||||
|
||||
|
||||
skclt.close()
|
||||
|
||||
server('0.0.0.0', 8080)
|
12
tests/read.sh
Executable file
12
tests/read.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
curl -i -X POST "http://192.168.1.68:8086/api/v2/query?bucket=tcp_metrics&orgID=f32d493484526abc" \
|
||||
--header "Authorization: Token $1" \
|
||||
--header "Accept: application/csv" \
|
||||
--header "Content-Type: application/vnd.flux" \
|
||||
--data 'from(bucket: "tcp_metrics")
|
||||
|> range(start: -5m)
|
||||
|> filter(fn: (r) => r["_measurement"] == "tcp_reset")
|
||||
|> filter(fn: (r) => r["host"] == "127.0.0.1")
|
||||
|> filter(fn: (r) => r["_field"] == "value")
|
||||
|> yield(name: "mean")'
|
5
tests/write.sh
Normal file
5
tests/write.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
curl -i -X POST "http://localhost:8086/api/v2/write?bucket=tcp_metrics&orgID=392fcb79fc296d8e" \
|
||||
--header "Authorization: Token $1" \
|
||||
--data "cpu_load_short,host=server01, value=1"
|
115
tp_tcp.c
Normal file
115
tp_tcp.c
Normal file
@ -0,0 +1,115 @@
|
||||
#define BPF_NO_GLOBAL_DATA
|
||||
//#define __TARGET_ARCH_x86
|
||||
#include "vmlinux.h"
|
||||
#include <string.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include "common.h"
|
||||
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
|
||||
|
||||
// Format: /sys/kernel/debug/tracing/events/tcp/tcp_send_reset/format
|
||||
struct ctx_send_reset {
|
||||
__u16 common_type; // unsigned short
|
||||
__u8 common_flags; // unsigned char
|
||||
__u8 common_count; // unsigned char
|
||||
__s32 pid; // int
|
||||
|
||||
const void *skbaddr;
|
||||
const void *skaddr;
|
||||
__s32 state; // int
|
||||
__u16 sport;
|
||||
__u16 dport;
|
||||
__u8 saddr[4];
|
||||
__u8 daddr[4];
|
||||
__u8 saddr_v6[16];
|
||||
__u8 daddr_v6[16];
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 4096);
|
||||
__type(key, 1);
|
||||
__type(value, __s32);
|
||||
} tcp_stats_index SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 4096);
|
||||
__type(key, 4);
|
||||
__type(value, struct reset);
|
||||
} tcp_reset_stats SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __s32);
|
||||
__type(value, __u16);
|
||||
} filter_family SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __s32);
|
||||
__type(value, __u16);
|
||||
} filter_sport SEC(".maps");
|
||||
|
||||
// sudo tcpdump -i any 'tcp[13] & 4 != 0' -n -> filter TCP reset flags
|
||||
|
||||
/*
|
||||
* Why we need to detect RST:
|
||||
* When we scan the port, the scanner send an SYN flag and if the port is block, we receive a RST flag:
|
||||
* But we can also block all receive RST: iptables -I INPUT -p tcp --dport <port> -j REJECT --reject-with tcp-reset
|
||||
*/
|
||||
|
||||
SEC("tracepoint/tcp/tcp_send_reset")
|
||||
int tcp_rst_stats(struct ctx_send_reset *ctx){
|
||||
struct reset s_reset = {};
|
||||
int *index;
|
||||
int keys = 0;
|
||||
struct sock *sk;
|
||||
__u16 family;
|
||||
__s16 *f_family;
|
||||
__u16 proto;
|
||||
int err;
|
||||
|
||||
memset(&s_reset, 0, sizeof(struct reset));
|
||||
|
||||
// Get filter
|
||||
sk = (struct sock*)ctx->skaddr;
|
||||
f_family = bpf_map_lookup_elem(&filter_family, &keys);
|
||||
if (!f_family)
|
||||
return 0;
|
||||
|
||||
index = bpf_map_lookup_elem(&tcp_stats_index, &keys);
|
||||
if (!index)
|
||||
return 0;
|
||||
|
||||
// Get the family of the socket
|
||||
bpf_probe_read_kernel(&family, sizeof(family), &sk->__sk_common.skc_family);
|
||||
if (family != *f_family)
|
||||
return 0;
|
||||
|
||||
// Get and update the index in the map
|
||||
*index += 1;
|
||||
|
||||
// Proto type: here it's 6 (TCP)
|
||||
bpf_probe_read_kernel(&proto, sizeof(proto), &sk->sk_protocol);
|
||||
|
||||
memcpy(s_reset.saddr, ctx->saddr, 4);
|
||||
memcpy(s_reset.daddr, ctx->daddr, 4);
|
||||
|
||||
//bpf_probe_read_kernel(&s_reset.saddr, 4, &ctx->saddr);
|
||||
//bpf_probe_read_kernel(&s_reset.daddr, 4, &ctx->daddr);
|
||||
|
||||
s_reset.sport = ctx->sport;
|
||||
s_reset.dport = ctx->dport;
|
||||
s_reset.family = family;
|
||||
s_reset.proto = proto;
|
||||
|
||||
bpf_printk("BPF detected TCP send reset %d %d", s_reset.sport, s_reset.dport);
|
||||
bpf_map_update_elem(&tcp_reset_stats, &keys, &s_reset, BPF_ANY);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user