diff --git a/common.h b/common.h index 4a963a0..014f42d 100644 --- a/common.h +++ b/common.h @@ -1,11 +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 diff --git a/exec.sh b/exec.sh new file mode 100755 index 0000000..d3705e0 --- /dev/null +++ b/exec.sh @@ -0,0 +1,5 @@ +#!/usr/bin/sh + +clang-11 -g -O2 -target bpf -c tp_tcp.c -o tp_tcp.o && \ +gcc load_bpf.c -o load_bpf -lbpf && \ +sudo ./load_bpf diff --git a/influxdb.py b/influxdb.py new file mode 100644 index 0000000..4ba02fe --- /dev/null +++ b/influxdb.py @@ -0,0 +1,24 @@ +import influxdb_client +from influxdb_client import InfluxDBClient, Point, WritePrecision +from influxdb_client.client.write_api import SYNCHRONOUS +import os + +#token = os.environ.get("dQV0BbJvy7W9Bool6FGh1ryb_uXBNqZB8BlJqb8yC4yNB8RTDSooT5hixoqMf8cBeXUXTRUdmkwlxnkI9PCsBA==") +token = os.environ.get("sySU58aCfMdTGtBTttduzSS_x_4CBI1twpicYw4Idq9abZWGsAXdbvww2wWmwmLDTtrALAx4Q0wZK9PUIr4ejg==") +org = "gbucchino" +url = "http://192.168.1.68:8086" + +write_client = influxdb_client.InfluxDBClient(url=url, token=token, org=org) + +bucket="tcp" + +write_api = write_client.write_api(write_options=SYNCHRONOUS) + +for value in range(5): + point = ( + Point("measurement1") + .tag("tagname1", "tagvalue1") + .field("field1", value) + ) + write_api.write(bucket=bucket, org="gbucchino", record=point) + time.sleep(1) # separate points by 1 second diff --git a/load_bpf b/load_bpf index c2d9e15..6cb559d 100755 Binary files a/load_bpf and b/load_bpf differ diff --git a/load_bpf.c b/load_bpf.c index 0f5ab34..06f44cd 100644 --- a/load_bpf.c +++ b/load_bpf.c @@ -5,6 +5,7 @@ #include #include #include "common.h" +#include static void clean_obj(struct bpf_object *obj){ printf("Cleaning\n"); @@ -18,8 +19,12 @@ int main(void){ struct reset s_reset; int err; int map_fd; - long long stats; + int map_fd_filter_family; + int map_fd_filter_sport; + int map_fd_index; int keys = 0; + int indexPackets = 0; + int index = 0; obj = bpf_object__open_file(fileObj, NULL); if (!obj){ @@ -27,19 +32,20 @@ int main(void){ return -1; } //LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE); - map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(struct reset), 4096, BPF_ANY); - printf("Create map: %d\n", map_fd); + //map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(struct reset), 4096, BPF_ANY); + //map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(struct reset), 4096, BPF_ANY); err = bpf_object__load(obj); - printf("Object loaded: %d\n", err); if (err){ printf("Failed to load object\n"); + clean_obj(obj); return -1; } program = bpf_object__find_program_by_name(obj, "tcp_retransmit"); if (!program){ printf("Failed to find the program\n"); + clean_obj(obj); return -1; } @@ -51,7 +57,25 @@ int main(void){ } map_fd = bpf_object__find_map_fd_by_name(obj, "tcp_reset_stats"); - printf("Map fd: %d\n", map_fd); + 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){ @@ -59,15 +83,40 @@ int main(void){ 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){ - int e = bpf_map_lookup_elem(map_fd, &keys, &s_reset); - if (e == 0){ - //printf("%lld\n", stats); - struct in_addr *src = (struct in_addr*)&s_reset.saddr; - struct in_addr *dest = (struct in_addr*)&s_reset.daddr; - printf("Sport: %d; dport: %d %s %s\n", s_reset.sport, s_reset.dport, inet_ntoa(*src), inet_ntoa(*dest)); + // Get the index + // and we compare with the local variable + // If it's different, we get the new variable + err = bpf_map_lookup_elem(map_fd_index, &keys, &indexPackets); + + // We have a new packet + if (indexPackets > index){ + index = indexPackets; + printf("Index: %d %d\n", 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 tmp[35]; + memcpy(tmp, s, 35); + char *d = inet_ntoa(*dest); + + printf("Sport: %d; dport: %d %d %d %s - %s\n", s_reset.sport, s_reset.dport, s_reset.family, s_reset.proto, tmp, d); + + } + memset(&s_reset, 0, sizeof(struct reset)); + //sleep(1); } - } return 0; diff --git a/main b/main index dae00eb..a1d94dc 100755 Binary files a/main and b/main differ diff --git a/main.c b/main.c index ef1f3a0..c9bd10a 100644 --- a/main.c +++ b/main.c @@ -1,12 +1,25 @@ #define BPF_NO_GLOBAL_DATA #include #include -#include -#include -#include +#include "ic.h" int main(int argc, char *argv[], char *argp[]){ printf("Hello world\n"); - execve("/usr/bin/ls", argv, argp); + char buf[300]; + + ic_influx_database("192.168.1.68", 8086, "tcp"); + ic_influx_userpw("admin", "Geta,Fte#"); + ic_debug(2); + + snprintf(buf, 300, "host=192.168.1.68"); + ic_tags(buf); + + ic_measure("tcp_reset"); + + ic_string("ipsrc", "192.168.1.1"); + + ic_measureend(); + ic_push(); + return 0; } diff --git a/main.py b/main.py new file mode 100644 index 0000000..59add27 --- /dev/null +++ b/main.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +from bcc import BPF + +with open("tp_tcp.c", 'r') as f: + data = f.read() + +b = BPF(text=data) +#b = BPF(src_file="tp_tcp_py.c") diff --git a/prometheus.py b/prometheus.py new file mode 100644 index 0000000..c5e6eee --- /dev/null +++ b/prometheus.py @@ -0,0 +1,30 @@ +from prometheus_client import start_http_server, Summary, Counter +import random +import time + +# Create a metric to track time spent and requests made. +#REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request', 'foo') +#REQUEST_TIME = Summary('request_processing_seconds', 'foo') + + +# Decorate function with metric. +#@REQUEST_TIME.time() +#def process_request(t): +# """A dummy function that takes some time.""" +# time.sleep(t) + +c = Counter("tcp_reset_stats", "TCP RST stats") +def tcp_rst_stats(): + c.inc() + +if __name__ == '__main__': + # Start up the server to expose the metrics. + start_http_server(8000) + # Generate some requests. + while True: + # process_request(random.random()) + + # Count + print("Inc") + tcp_rst_stats() + time.sleep(30) diff --git a/tp_tcp.c b/tp_tcp.c index 1601494..ece6413 100644 --- a/tp_tcp.c +++ b/tp_tcp.c @@ -1,6 +1,7 @@ #define BPF_NO_GLOBAL_DATA //#define __TARGET_ARCH_x86 #include "vmlinux.h" +#include #include #include #include @@ -9,11 +10,11 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; -struct ctx_reset { - __u16 common_type; - __u8 common_flags; - __u8 common_count; - __s32 pid; +struct ctx_receive_reset { + __u16 common_type; // unsigned short + __u8 common_flags; // unsigned char + __u8 common_count; // unsigned char + __s32 pid; // int const void *skaddr; __u16 sport; @@ -25,17 +26,59 @@ struct ctx_reset { __u8 daddr_v6[16]; __u64 sock_cookie; }; +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(type, BPF_MAP_TYPE_HASH); + __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 4096); - __type(key, int); - __type(value, sizeof(struct reset)); + __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 +/* + * This project do not trace any sniffing ports, because, the tracepoint tcp:tcp_send_reset + * works only for an establish socket, but, if you have a lot of TCP RST, you can have + * an issue with your system + */ + /* * Identify all tracepoint available * - cat /sys/kernel/tracing/available_events @@ -53,33 +96,54 @@ struct { */ //SEC("tp/tcp_retransmit_synack") -SEC("tracepoint/tcp/tcp_receive_reset") -//int tcp_retransmit(struct sock *sk){ -int tcp_retransmit(struct ctx_reset *ctx){ - long long *stats; - struct reset *s_reset; +//SEC("tracepoint/tcp/tcp_receive_reset") +SEC("tracepoint/tcp/tcp_send_reset") +int tcp_retransmit(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; - s_reset = bpf_map_lookup_elem(&tcp_reset_stats, &keys); - if (!s_reset) + 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; - if (!ctx) + index = bpf_map_lookup_elem(&tcp_stats_index, &keys); + if (!index) return 0; - //*stats += 1; - s_reset->saddr[0] = ctx->saddr[0]; - s_reset->saddr[1] = ctx->saddr[1]; - s_reset->saddr[2] = ctx->saddr[2]; - s_reset->saddr[3] = ctx->saddr[3]; - /*s_reset->daddr[0] = ctx->daddr[0]; - s_reset->daddr[1] = ctx->daddr[1]; - s_reset->daddr[2] = ctx->daddr[2]; - s_reset->daddr[3] = ctx->daddr[3];*/ - s_reset->sport = ctx->sport; - s_reset->dport = ctx->dport; + + // Get the family of the socket + bpf_probe_read_kernel(&family, sizeof(family), &sk->__sk_common.skc_family); + if (family != *f_family) + return 0; - //bpf_printk("BPF detected TCP received reset %d - %d %d\n", *stats, dport, sport); - bpf_printk("BPF detected TCP received reset %d %d\n", s_reset->sport, s_reset->dport); + // 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; } diff --git a/tp_tcp.o b/tp_tcp.o index 8fa2941..8dca33c 100644 Binary files a/tp_tcp.o and b/tp_tcp.o differ diff --git a/tp_tcp_py.c b/tp_tcp_py.c new file mode 100644 index 0000000..bcc1143 --- /dev/null +++ b/tp_tcp_py.c @@ -0,0 +1,149 @@ +#define BPF_NO_GLOBAL_DATA +//#define __TARGET_ARCH_x86 +#include "vmlinux.h" +#include +#include +#include +#include +#include "common.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + + +struct ctx_receive_reset { + __u16 common_type; // unsigned short + __u8 common_flags; // unsigned char + __u8 common_count; // unsigned char + __s32 pid; // int + + const void *skaddr; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; + __u64 sock_cookie; +}; +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 + +/* + * This project do not trace any sniffing ports, because, the tracepoint tcp:tcp_send_reset + * works only for an establish socket, but, if you have a lot of TCP RST, you can have + * an issue with your system + */ + +/* + * Identify all tracepoint available + * - cat /sys/kernel/tracing/available_events + * Enable an event: + * - echo 'tcp_receive_reset' >> /sys/kernel/tracing/set_event -> important to add the '>>' + * Docs: https://docs.kernel.org/trace/events.html + * https://events.linuxfoundation.org/wp-content/uploads/2022/10/elena-zannoni-tracing-tutorial-LF-2021.pdf + * https://docs.kernel.org/trace/tracepoints.html + * 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: + * listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes +10:48:28.531295 lo In IP localhost.43961 > localhost.tproxy: Flags [S], seq 2197047013, win 1024, options [mss 1460], length 0 +10:48:28.531306 lo In IP localhost.tproxy > localhost.43961: Flags [R.], seq 0, ack 2197047014, win 0, length 0 + * But we can also block all receive RST: iptables -I INPUT -p tcp --dport -j REJECT --reject-with tcp-reset + */ + +//SEC("tp/tcp_retransmit_synack") +//SEC("tracepoint/tcp/tcp_receive_reset") +//SEC("tracepoint/tcp/tcp_send_reset") +int tcp_retransmit(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; +}