mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
WIP - cleanup done, TCP is mostly working on the server-side.
This commit is contained in:
@@ -52,6 +52,8 @@ struct dissector_t
|
|||||||
__u16 window;
|
__u16 window;
|
||||||
__u32 tsval;
|
__u32 tsval;
|
||||||
__u32 tsecr;
|
__u32 tsecr;
|
||||||
|
__u32 sequence;
|
||||||
|
__u32 ack_seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Representation of the VLAN header type.
|
// Representation of the VLAN header type.
|
||||||
@@ -114,6 +116,8 @@ static __always_inline bool dissector_new(
|
|||||||
dissector->src_port = 0;
|
dissector->src_port = 0;
|
||||||
dissector->dst_port = 0;
|
dissector->dst_port = 0;
|
||||||
dissector->tos = 0;
|
dissector->tos = 0;
|
||||||
|
dissector->sequence = 0;
|
||||||
|
dissector->ack_seq = 0;
|
||||||
|
|
||||||
// Check that there's room for an ethernet header
|
// Check that there's room for an ethernet header
|
||||||
if SKB_OVERFLOW (dissector->start, dissector->end, ethhdr)
|
if SKB_OVERFLOW (dissector->start, dissector->end, ethhdr)
|
||||||
@@ -315,6 +319,17 @@ static __always_inline struct icmphdr *get_icmp_header(struct dissector_t *disse
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DIS_TCP_FIN 1
|
||||||
|
#define DIS_TCP_SYN 2
|
||||||
|
#define DIS_TCP_RST 4
|
||||||
|
#define DIS_TCP_PSH 8
|
||||||
|
#define DIS_TCP_ACK 16
|
||||||
|
#define DIS_TCP_URG 32
|
||||||
|
#define DIS_TCP_ECE 64
|
||||||
|
#define DIS_TCP_CWR 128
|
||||||
|
|
||||||
|
#define BITCHECK(flag) (dissector->tcp_flags & flag)
|
||||||
|
|
||||||
static __always_inline void snoop(struct dissector_t *dissector)
|
static __always_inline void snoop(struct dissector_t *dissector)
|
||||||
{
|
{
|
||||||
switch (dissector->ip_protocol)
|
switch (dissector->ip_protocol)
|
||||||
@@ -331,17 +346,19 @@ static __always_inline void snoop(struct dissector_t *dissector)
|
|||||||
dissector->src_port = hdr->source;
|
dissector->src_port = hdr->source;
|
||||||
dissector->dst_port = hdr->dest;
|
dissector->dst_port = hdr->dest;
|
||||||
__u8 flags = 0;
|
__u8 flags = 0;
|
||||||
if (hdr->fin) flags |= 1;
|
if (hdr->fin) flags |= DIS_TCP_FIN;
|
||||||
if (hdr->syn) flags |= 2;
|
if (hdr->syn) flags |= DIS_TCP_SYN;
|
||||||
if (hdr->rst) flags |= 4;
|
if (hdr->rst) flags |= DIS_TCP_RST;
|
||||||
if (hdr->psh) flags |= 8;
|
if (hdr->psh) flags |= DIS_TCP_PSH;
|
||||||
if (hdr->ack) flags |= 16;
|
if (hdr->ack) flags |= DIS_TCP_ACK;
|
||||||
if (hdr->urg) flags |= 32;
|
if (hdr->urg) flags |= DIS_TCP_URG;
|
||||||
if (hdr->ece) flags |= 64;
|
if (hdr->ece) flags |= DIS_TCP_ECE;
|
||||||
if (hdr->cwr) flags |= 128;
|
if (hdr->cwr) flags |= DIS_TCP_CWR;
|
||||||
|
|
||||||
dissector->tcp_flags = flags;
|
dissector->tcp_flags = flags;
|
||||||
dissector->window = hdr->window;
|
dissector->window = hdr->window;
|
||||||
|
dissector->sequence = hdr->seq;
|
||||||
|
dissector->ack_seq = hdr->ack_seq;
|
||||||
|
|
||||||
parse_tcp_ts(hdr, dissector->end, &dissector->tsval, &dissector->tsecr);
|
parse_tcp_ts(hdr, dissector->end, &dissector->tsval, &dissector->tsecr);
|
||||||
}
|
}
|
||||||
@@ -399,6 +416,7 @@ static __always_inline bool dissector_find_ip_header(
|
|||||||
dissector->ip_protocol = dissector->ip_header.iph->protocol;
|
dissector->ip_protocol = dissector->ip_header.iph->protocol;
|
||||||
dissector->tos = dissector->ip_header.iph->tos;
|
dissector->tos = dissector->ip_header.iph->tos;
|
||||||
snoop(dissector);
|
snoop(dissector);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -416,7 +434,7 @@ static __always_inline bool dissector_find_ip_header(
|
|||||||
encode_ipv6(&dissector->ip_header.ip6h->saddr, &dissector->src_ip);
|
encode_ipv6(&dissector->ip_header.ip6h->saddr, &dissector->src_ip);
|
||||||
encode_ipv6(&dissector->ip_header.ip6h->daddr, &dissector->dst_ip);
|
encode_ipv6(&dissector->ip_header.ip6h->daddr, &dissector->dst_ip);
|
||||||
dissector->ip_protocol = dissector->ip_header.ip6h->nexthdr;
|
dissector->ip_protocol = dissector->ip_header.ip6h->nexthdr;
|
||||||
dissector->ip_header.ip6h->flow_lbl[0]; // Is this right?
|
dissector->tos = dissector->ip_header.ip6h->flow_lbl[0]; // Is this right?
|
||||||
snoop(dissector);
|
snoop(dissector);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "dissector.h"
|
#include "dissector.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
|
||||||
#define SECOND_IN_NANOS 1000000000
|
#define SECOND_IN_NANOS 1000000000
|
||||||
#define TIMESTAMP_INTERVAL_NANOS 2000000000
|
#define TIMESTAMP_INTERVAL_NANOS 2000000000
|
||||||
|
|
||||||
@@ -16,15 +17,17 @@
|
|||||||
#define FROM_LOCAL 2
|
#define FROM_LOCAL 2
|
||||||
|
|
||||||
// Defines a TCP connection flow key
|
// Defines a TCP connection flow key
|
||||||
struct tcp_flow_key_t {
|
struct flow_key_t {
|
||||||
struct in6_addr src;
|
struct in6_addr src;
|
||||||
struct in6_addr dst;
|
struct in6_addr dst;
|
||||||
__u16 src_port;
|
__u16 src_port;
|
||||||
__u16 dst_port;
|
__u16 dst_port;
|
||||||
|
__u8 protocol;
|
||||||
|
__u8 pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TCP connection flow entry
|
// TCP connection flow entry
|
||||||
struct tcp_flow_data_t {
|
struct flow_data_t {
|
||||||
// Time (nanos) when the connection was established
|
// Time (nanos) when the connection was established
|
||||||
__u64 start_time;
|
__u64 start_time;
|
||||||
// Time (nanos) when the connection was last seen
|
// Time (nanos) when the connection was last seen
|
||||||
@@ -40,7 +43,7 @@ struct tcp_flow_data_t {
|
|||||||
// Bytes at the next rate estimate
|
// Bytes at the next rate estimate
|
||||||
__u64 next_count_bytes[2];
|
__u64 next_count_bytes[2];
|
||||||
// Rate estimate
|
// Rate estimate
|
||||||
__u64 rate_estimate[2];
|
__u64 rate_estimate_bps[2];
|
||||||
// Sequence number of the last packet
|
// Sequence number of the last packet
|
||||||
__u32 last_sequence[2];
|
__u32 last_sequence[2];
|
||||||
// Acknowledgement number of the last packet
|
// Acknowledgement number of the last packet
|
||||||
@@ -50,10 +53,15 @@ struct tcp_flow_data_t {
|
|||||||
// Timestamp values
|
// Timestamp values
|
||||||
__u32 tsval[2];
|
__u32 tsval[2];
|
||||||
__u32 tsecr[2];
|
__u32 tsecr[2];
|
||||||
|
// When did the timestamp change?
|
||||||
__u64 ts_change_time[2];
|
__u64 ts_change_time[2];
|
||||||
|
// When should we calculate RTT (to avoid flooding)
|
||||||
__u64 ts_calc_time[2];
|
__u64 ts_calc_time[2];
|
||||||
// Most recent RTT
|
// Most recent RTT
|
||||||
__u64 last_rtt[2];
|
__u64 last_rtt[2];
|
||||||
|
// Has the connection ended?
|
||||||
|
// 0 = Alive, 1 = FIN, 2 = RST
|
||||||
|
__u32 end_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map for tracking TCP flow progress.
|
// Map for tracking TCP flow progress.
|
||||||
@@ -61,76 +69,62 @@ struct tcp_flow_data_t {
|
|||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
__type(key, struct tcp_flow_key_t);
|
__type(key, struct flow_key_t);
|
||||||
__type(value, struct tcp_flow_data_t);
|
__type(value, struct flow_data_t);
|
||||||
__uint(max_entries, MAX_FLOWS);
|
__uint(max_entries, MAX_FLOWS);
|
||||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||||
} flowbee SEC(".maps");
|
} flowbee SEC(".maps");
|
||||||
|
|
||||||
static __always_inline struct tcp_flow_key_t build_tcp_flow_key(
|
static __always_inline struct flow_data_t new_flow_data(
|
||||||
|
__u64 now,
|
||||||
|
struct dissector_t *dissector
|
||||||
|
) {
|
||||||
|
struct flow_data_t data = {
|
||||||
|
.start_time = now,
|
||||||
|
.bytes_sent = { 0, 0 },
|
||||||
|
.packets_sent = { 0, 0 },
|
||||||
|
.next_count_time = { now + SECOND_IN_NANOS, now + SECOND_IN_NANOS },
|
||||||
|
.last_count_time = { now, now },
|
||||||
|
.next_count_bytes = { dissector->skb_len, dissector->skb_len },
|
||||||
|
.rate_estimate_bps = { 0, 0 },
|
||||||
|
.last_sequence = { 0, 0 },
|
||||||
|
.last_ack = { 0, 0 },
|
||||||
|
.retries = { 0, 0 },
|
||||||
|
.tsval = { 0, 0 },
|
||||||
|
.tsecr = { 0, 0 },
|
||||||
|
.ts_change_time = { 0, 0 },
|
||||||
|
.ts_calc_time = { now, now }, // Get a first number quickly
|
||||||
|
.last_rtt = { 0, 0 },
|
||||||
|
.end_status = 0
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline struct flow_key_t build_flow_key(
|
||||||
struct dissector_t *dissector, // The packet dissector from the previous step
|
struct dissector_t *dissector, // The packet dissector from the previous step
|
||||||
struct tcphdr *tcp, // The TCP header
|
|
||||||
u_int8_t direction // The direction of the packet (1 = to internet, 2 = to local network)
|
u_int8_t direction // The direction of the packet (1 = to internet, 2 = to local network)
|
||||||
) {
|
) {
|
||||||
if (direction == FROM_INTERNET) {
|
if (direction == FROM_INTERNET) {
|
||||||
return (struct tcp_flow_key_t) {
|
return (struct flow_key_t) {
|
||||||
.src = dissector->src_ip,
|
.src = dissector->src_ip,
|
||||||
.dst = dissector->dst_ip,
|
.dst = dissector->dst_ip,
|
||||||
.src_port = tcp->source,
|
.src_port = dissector->src_port,
|
||||||
.dst_port = tcp->dest,
|
.dst_port = dissector->dst_port,
|
||||||
|
.protocol = dissector->ip_protocol,
|
||||||
|
.pad = 0
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return (struct tcp_flow_key_t) {
|
return (struct flow_key_t) {
|
||||||
.src = dissector->dst_ip,
|
.src = dissector->dst_ip,
|
||||||
.dst = dissector->src_ip,
|
.dst = dissector->src_ip,
|
||||||
.src_port = tcp->dest,
|
.src_port = dissector->dst_port,
|
||||||
.dst_port = tcp->source,
|
.dst_port = dissector->src_port,
|
||||||
|
.protocol = dissector->ip_protocol,
|
||||||
|
.pad = 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void debug_ip(
|
|
||||||
struct in6_addr *ip
|
|
||||||
) {
|
|
||||||
bpf_debug("%d.%d.%d", ip->s6_addr[13], ip->s6_addr[14], ip->s6_addr[15]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline bool get_timestamps(
|
|
||||||
u_int32_t * out_tsval,
|
|
||||||
u_int32_t * out_tsecr,
|
|
||||||
struct tcphdr * tcp,
|
|
||||||
struct dissector_t * dissector,
|
|
||||||
void * end_opts
|
|
||||||
) {
|
|
||||||
u_int8_t *pos = (u_int8_t *)(tcp + 1); // Current pos in TCP options
|
|
||||||
u_int8_t len;
|
|
||||||
|
|
||||||
// This should be 10, but we're running out of space
|
|
||||||
for (u_int8_t i = 0; i<6; i++) {
|
|
||||||
if (pos + 2 > dissector->end) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (*pos) {
|
|
||||||
case 0: return false; // End of options
|
|
||||||
case 1: pos++; break; // NOP
|
|
||||||
case 8: {
|
|
||||||
if (pos + 10 > dissector->end) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out_tsval = bpf_ntohl(*(__u32 *)(pos + 2));
|
|
||||||
*out_tsecr = bpf_ntohl(*(__u32 *)(pos + 6));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
len = *(pos + 1);
|
|
||||||
pos += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Per-Flow ICMP Analysis
|
// Handle Per-Flow ICMP Analysis
|
||||||
static __always_inline void process_icmp(
|
static __always_inline void process_icmp(
|
||||||
struct dissector_t *dissector,
|
struct dissector_t *dissector,
|
||||||
@@ -153,31 +147,17 @@ static __always_inline void process_udp(
|
|||||||
static __always_inline void process_tcp(
|
static __always_inline void process_tcp(
|
||||||
struct dissector_t *dissector,
|
struct dissector_t *dissector,
|
||||||
u_int8_t direction,
|
u_int8_t direction,
|
||||||
struct tcphdr *tcp,
|
|
||||||
u_int64_t now
|
u_int64_t now
|
||||||
) {
|
) {
|
||||||
if ((tcp->syn && !tcp->ack && direction == TO_INTERNET) || (tcp->syn && tcp->ack && direction == FROM_INTERNET)) {
|
if ((BITCHECK(DIS_TCP_SYN) && !BITCHECK(DIS_TCP_ACK) && direction == TO_INTERNET) ||
|
||||||
|
(BITCHECK(DIS_TCP_SYN) && BITCHECK(DIS_TCP_ACK) && direction == FROM_INTERNET)) {
|
||||||
// A customer is requesting a new TCP connection. That means
|
// A customer is requesting a new TCP connection. That means
|
||||||
// we need to start tracking this flow.
|
// we need to start tracking this flow.
|
||||||
|
#ifdef VERBOSE
|
||||||
bpf_debug("[FLOWS] New TCP Connection Detected (%u)", direction);
|
bpf_debug("[FLOWS] New TCP Connection Detected (%u)", direction);
|
||||||
struct tcp_flow_key_t key = build_tcp_flow_key(dissector, tcp, direction);
|
#endif
|
||||||
struct tcp_flow_data_t data = {
|
struct flow_key_t key = build_flow_key(dissector, direction);
|
||||||
.start_time = now,
|
struct flow_data_t data = new_flow_data(now, dissector);
|
||||||
.bytes_sent = { 0, 0 },
|
|
||||||
.packets_sent = { 0, 0 },
|
|
||||||
.next_count_time = { now + SECOND_IN_NANOS, now + SECOND_IN_NANOS },
|
|
||||||
.last_count_time = { now, now },
|
|
||||||
.next_count_bytes = { dissector->skb_len, dissector->skb_len },
|
|
||||||
.rate_estimate = { 0, 0 },
|
|
||||||
.last_sequence = { 0, 0 },
|
|
||||||
.last_ack = { 0, 0 },
|
|
||||||
.retries = { 0, 0 },
|
|
||||||
.tsval = { 0, 0 },
|
|
||||||
.tsecr = { 0, 0 },
|
|
||||||
.ts_change_time = { 0, 0 },
|
|
||||||
.ts_calc_time = { now + TIMESTAMP_INTERVAL_NANOS, now + TIMESTAMP_INTERVAL_NANOS },
|
|
||||||
.last_rtt = { 0, 0 }
|
|
||||||
};
|
|
||||||
if (bpf_map_update_elem(&flowbee, &key, &data, BPF_ANY) != 0) {
|
if (bpf_map_update_elem(&flowbee, &key, &data, BPF_ANY) != 0) {
|
||||||
bpf_debug("[FLOWS] Failed to add new flow to map");
|
bpf_debug("[FLOWS] Failed to add new flow to map");
|
||||||
}
|
}
|
||||||
@@ -185,8 +165,8 @@ static __always_inline void process_tcp(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the flow key
|
// Build the flow key
|
||||||
struct tcp_flow_key_t key = build_tcp_flow_key(dissector, tcp, direction);
|
struct flow_key_t key = build_flow_key(dissector, direction);
|
||||||
struct tcp_flow_data_t *data = bpf_map_lookup_elem(&flowbee, &key);
|
struct flow_data_t *data = bpf_map_lookup_elem(&flowbee, &key);
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
// If it isn't a flow we're tracking, bail out now
|
// If it isn't a flow we're tracking, bail out now
|
||||||
return;
|
return;
|
||||||
@@ -204,8 +184,7 @@ static __always_inline void process_tcp(
|
|||||||
// Calculate the rate estimate
|
// Calculate the rate estimate
|
||||||
__u64 bits = (data->bytes_sent[0] - data->next_count_bytes[0])*8;
|
__u64 bits = (data->bytes_sent[0] - data->next_count_bytes[0])*8;
|
||||||
__u64 time = (now - data->last_count_time[0]) / 1000000000; // Seconds
|
__u64 time = (now - data->last_count_time[0]) / 1000000000; // Seconds
|
||||||
data->rate_estimate[0] = bits/time;
|
data->rate_estimate_bps[0] = bits/time;
|
||||||
//bpf_debug("[FLOWS][%d] Rate Estimate: %u mbits / second", direction, data->rate_estimate[0] / 1000000);
|
|
||||||
data->next_count_time[0] = now + SECOND_IN_NANOS;
|
data->next_count_time[0] = now + SECOND_IN_NANOS;
|
||||||
data->next_count_bytes[0] = data->bytes_sent[0];
|
data->next_count_bytes[0] = data->bytes_sent[0];
|
||||||
data->last_count_time[0] = now;
|
data->last_count_time[0] = now;
|
||||||
@@ -218,8 +197,7 @@ static __always_inline void process_tcp(
|
|||||||
// Calculate the rate estimate
|
// Calculate the rate estimate
|
||||||
__u64 bits = (data->bytes_sent[1] - data->next_count_bytes[1])*8;
|
__u64 bits = (data->bytes_sent[1] - data->next_count_bytes[1])*8;
|
||||||
__u64 time = (now - data->last_count_time[1]) / 1000000000; // Seconds
|
__u64 time = (now - data->last_count_time[1]) / 1000000000; // Seconds
|
||||||
data->rate_estimate[1] = bits/time;
|
data->rate_estimate_bps[1] = bits/time;
|
||||||
//bpf_debug("[FLOWS][%d] Rate Estimate: %u mbits / second", direction, data->rate_estimate[1] / 1000000);
|
|
||||||
data->next_count_time[1] = now + SECOND_IN_NANOS;
|
data->next_count_time[1] = now + SECOND_IN_NANOS;
|
||||||
data->next_count_bytes[1] = data->bytes_sent[1];
|
data->next_count_bytes[1] = data->bytes_sent[1];
|
||||||
data->last_count_time[1] = now;
|
data->last_count_time[1] = now;
|
||||||
@@ -227,12 +205,11 @@ static __always_inline void process_tcp(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sequence and Acknowledgement numbers
|
// Sequence and Acknowledgement numbers
|
||||||
__u32 sequence = bpf_ntohl(tcp->seq);
|
__u32 sequence = bpf_ntohl(dissector->sequence);
|
||||||
__u32 ack_seq = bpf_ntohl(tcp->ack_seq);
|
__u32 ack_seq = bpf_ntohl(dissector->ack_seq);
|
||||||
if (direction == TO_INTERNET) {
|
if (direction == TO_INTERNET) {
|
||||||
if (data->last_sequence[0] != 0 && sequence < data->last_sequence[0]) {
|
if (data->last_sequence[0] != 0 && sequence < data->last_sequence[0]) {
|
||||||
// This is a retransmission
|
// This is a retransmission
|
||||||
//bpf_debug("[FLOWS] Retransmission detected (%u)", direction);
|
|
||||||
data->retries[0]++;
|
data->retries[0]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,33 +218,28 @@ static __always_inline void process_tcp(
|
|||||||
} else {
|
} else {
|
||||||
if (data->last_sequence[1] != 0 && sequence < data->last_sequence[1]) {
|
if (data->last_sequence[1] != 0 && sequence < data->last_sequence[1]) {
|
||||||
// This is a retransmission
|
// This is a retransmission
|
||||||
//bpf_debug("[FLOWS] Retransmission detected (%u)", direction);
|
|
||||||
data->retries[1]++;
|
data->retries[1]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->last_sequence[1] = sequence;
|
data->last_sequence[1] = sequence;
|
||||||
data->last_ack[1] = ack_seq;
|
data->last_ack[1] = ack_seq;
|
||||||
}
|
}
|
||||||
//bpf_debug("[FLOWS][%d] Sequence: %u Ack: %u", direction, sequence, ack_seq);
|
|
||||||
|
|
||||||
// Timestamps to calculate RTT
|
// Timestamps to calculate RTT
|
||||||
u_int32_t tsval = 0;
|
u_int32_t tsval = dissector->tsval;
|
||||||
u_int32_t tsecr = 0;
|
u_int32_t tsecr = dissector->tsecr;
|
||||||
void *end_opts = (tcp + 1) + (tcp->doff << 2);
|
if (BITCHECK(DIS_TCP_ACK) && tsval != 0) {
|
||||||
if (tcp->ack && get_timestamps(&tsval, &tsecr, tcp, dissector, end_opts)) {
|
|
||||||
//bpf_debug("[FLOWS][%d] TSVal %u TSecr %u", direction, tsval, tsecr);
|
|
||||||
if (direction == TO_INTERNET) {
|
if (direction == TO_INTERNET) {
|
||||||
if (tsval != data->tsval[0] || tsecr != data->tsecr[0]) {
|
if (tsval != data->tsval[0] || tsecr != data->tsecr[0]) {
|
||||||
|
|
||||||
if (tsval == data->tsecr[1]) {
|
if (tsval == data->tsecr[1]) {
|
||||||
//bpf_debug("%d Matched!", direction);
|
if (now > data->ts_calc_time[0]) {
|
||||||
__u64 elapsed = now - data->ts_change_time[1];
|
__u64 elapsed = now - data->ts_change_time[1];
|
||||||
data->last_rtt[0] = elapsed;
|
data->ts_calc_time[0] = now + TIMESTAMP_INTERVAL_NANOS;
|
||||||
//bpf_debug("%d TS Change (RTT): %u nanos", direction, elapsed);
|
data->last_rtt[0] = elapsed;
|
||||||
// TODO: Do something with the RTT
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//bpf_debug("%d TSVal Changed", direction);
|
|
||||||
data->ts_change_time[0] = now;
|
data->ts_change_time[0] = now;
|
||||||
data->tsval[0] = tsval;
|
data->tsval[0] = tsval;
|
||||||
data->tsecr[0] = tsecr;
|
data->tsecr[0] = tsecr;
|
||||||
@@ -276,44 +248,25 @@ static __always_inline void process_tcp(
|
|||||||
if (tsval != data->tsval[1] || tsecr != data->tsecr[1]) {
|
if (tsval != data->tsval[1] || tsecr != data->tsecr[1]) {
|
||||||
|
|
||||||
if (tsval == data->tsecr[0]) {
|
if (tsval == data->tsecr[0]) {
|
||||||
//bpf_debug("%d Matched!", direction);
|
if (now > data->ts_calc_time[1]) {
|
||||||
__u64 elapsed = now - data->ts_change_time[0];
|
__u64 elapsed = now - data->ts_change_time[0];
|
||||||
data->last_rtt[1] = elapsed;
|
data->ts_calc_time[1] = now + TIMESTAMP_INTERVAL_NANOS;
|
||||||
//bpf_debug("%d TS Change (RTT): %u nanos", direction, elapsed);
|
data->last_rtt[1] = elapsed;
|
||||||
// TODO: Do something with the RTT
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//bpf_debug("%d TSVal Changed", direction);
|
|
||||||
data->ts_change_time[1] = now;
|
data->ts_change_time[1] = now;
|
||||||
data->tsval[1] = tsval;
|
data->tsval[1] = tsval;
|
||||||
data->tsecr[1] = tsecr;
|
data->tsecr[1] = tsecr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*else {
|
|
||||||
if (tsval == data->tsecr[0]) {
|
|
||||||
//if (tsval == data->tsecr[0] && now > data->ts_calc_time[1]) {
|
|
||||||
__u64 elapsed = now - data->ts_change_time[0];
|
|
||||||
bpf_debug("[FLOWS][%d] TS Change (RTT): %u nanos", direction, elapsed);
|
|
||||||
data->ts_calc_time[1] = now + TIMESTAMP_INTERVAL_NANOS;
|
|
||||||
}
|
|
||||||
if (tsval != data->tsval[1]) {
|
|
||||||
data->ts_change_time[1] = now;
|
|
||||||
}
|
|
||||||
data->tsval[1] = tsval;
|
|
||||||
data->tsecr[1] = tsecr;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has the connection ended?
|
// Has the connection ended?
|
||||||
if (tcp->fin || tcp->rst) {
|
if (BITCHECK(DIS_TCP_FIN)) {
|
||||||
__u64 lifetime = now - data->start_time;
|
data->end_status = 1;
|
||||||
bpf_debug("[FLOWS] TCP Connection Ended [%d / %d]. Lasted %u nanos.", data->bytes_sent[0], data->bytes_sent[1], lifetime);
|
} else if (BITCHECK(DIS_TCP_RST)) {
|
||||||
bpf_debug("[FLOWS] Rate Estimate (Mbps): %u / %u", data->rate_estimate[0] / 1000000, data->rate_estimate[1] / 1000000);
|
data->end_status = 2;
|
||||||
bpf_debug("[FLOWS] Retries: %u / %u", data->retries[0], data->retries[1]);
|
|
||||||
bpf_debug("[FLOWS] RTT: %u / %u (nanos)", data->last_rtt[0], data->last_rtt[1]);
|
|
||||||
bpf_map_delete_elem(&flowbee, &key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,24 +276,12 @@ static __always_inline void track_flows(
|
|||||||
struct dissector_t *dissector, // The packet dissector from the previous step
|
struct dissector_t *dissector, // The packet dissector from the previous step
|
||||||
u_int8_t direction // The direction of the packet (1 = to internet, 2 = to local network)
|
u_int8_t direction // The direction of the packet (1 = to internet, 2 = to local network)
|
||||||
) {
|
) {
|
||||||
//bpf_debug("[FLOWS] Packet detected");
|
__u64 now = bpf_ktime_get_boot_ns();
|
||||||
__u64 now = bpf_ktime_get_ns();
|
|
||||||
|
// Pass to the appropriate protocol handler
|
||||||
switch (dissector->ip_protocol)
|
switch (dissector->ip_protocol)
|
||||||
{
|
{
|
||||||
case IPPROTO_TCP: {
|
case IPPROTO_TCP: process_tcp(dissector, direction, now); break;
|
||||||
struct tcphdr * tcp = get_tcp_header(dissector);
|
|
||||||
if (tcp == NULL) {
|
|
||||||
// Bail out if it's not a TCP packet
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Bail out if we've exceeded the packet size and there is no payload
|
|
||||||
// This keeps the safety checker happy and is generally a good idea
|
|
||||||
if (tcp + 1 >= dissector->end) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//bpf_debug("[FLOWS] TCP packet detected");
|
|
||||||
process_tcp(dissector, direction, tcp, now);
|
|
||||||
} break;
|
|
||||||
case IPPROTO_UDP: {
|
case IPPROTO_UDP: {
|
||||||
struct udphdr *udp = get_udp_header(dissector);
|
struct udphdr *udp = get_udp_header(dissector);
|
||||||
if (udp == NULL) {
|
if (udp == NULL) {
|
||||||
@@ -352,7 +293,6 @@ static __always_inline void track_flows(
|
|||||||
if (udp + 1 >= dissector->end) {
|
if (udp + 1 >= dissector->end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bpf_debug("[FLOWS] UDP packet detected");
|
|
||||||
process_udp(dissector, direction, udp);
|
process_udp(dissector, direction, udp);
|
||||||
} break;
|
} break;
|
||||||
case IPPROTO_ICMP: {
|
case IPPROTO_ICMP: {
|
||||||
@@ -366,189 +306,12 @@ static __always_inline void track_flows(
|
|||||||
if (icmp + 1 >= dissector->end) {
|
if (icmp + 1 >= dissector->end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bpf_debug("[FLOWS] ICMP packet detected");
|
|
||||||
process_icmp(dissector, direction, icmp);
|
process_icmp(dissector, direction, icmp);
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
|
#ifdef VERBOSE
|
||||||
bpf_debug("[FLOWS] Unsupported protocol: %d", dissector->ip_protocol);
|
bpf_debug("[FLOWS] Unsupported protocol: %d", dissector->ip_protocol);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static __always_inline void track_flows(
|
|
||||||
struct dissector_t *dissector, // The packet dissector from the previous step
|
|
||||||
u_int8_t direction // The direction of the packet (1 = to internet, 2 = to local network)
|
|
||||||
) {
|
|
||||||
struct tcphdr * tcp = get_tcp_header(dissector);
|
|
||||||
if (tcp == NULL) {
|
|
||||||
// Bail out if it's not a TCP packet
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bail out if we've exceeded the packet size and there is no payload
|
|
||||||
// This keeps the safety checker happy and is generally a good idea
|
|
||||||
if (tcp + 1 >= dissector->end) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the key for the flow. Since we know direction, there's
|
|
||||||
// no need to consider "reverse keys" and their ilk.
|
|
||||||
struct tcp_flow_key_t key = build_flow_key(dissector, direction);
|
|
||||||
|
|
||||||
// Only care about connections that originate locally
|
|
||||||
__u64 now = bpf_ktime_get_ns();
|
|
||||||
if (tcp->syn && direction == 1) {
|
|
||||||
// SYN packet sent to the Internet. We are establishing a new connection.
|
|
||||||
// We need to add this flow to the tracking table.
|
|
||||||
bpf_debug("New TCP connection detected");
|
|
||||||
struct tcp_flow_data_t data = {
|
|
||||||
.start_time = now,
|
|
||||||
.last_seen_a = now,
|
|
||||||
.last_seen_b = now,
|
|
||||||
.bytes_sent = dissector->skb_len,
|
|
||||||
.bytes_received = 0,
|
|
||||||
.time_a = 0,
|
|
||||||
.time_b = 0,
|
|
||||||
.last_rtt = 0,
|
|
||||||
.packets_sent = 1,
|
|
||||||
.packets_received = 0,
|
|
||||||
.retries_a = 0,
|
|
||||||
.retries_b = 0,
|
|
||||||
.next_count_time = now + SECOND_IN_NANOS,
|
|
||||||
.next_count_bytes = dissector->skb_len,
|
|
||||||
.rate_estimate = 0,
|
|
||||||
.last_count_time = now
|
|
||||||
};
|
|
||||||
bpf_map_update_elem(&flowbee, &key, &data, BPF_ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the flow's last seen time
|
|
||||||
struct tcp_flow_data_t *data = bpf_map_lookup_elem(&flowbee, &key);
|
|
||||||
if (data == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
__u64 last_seen = data->last_seen_a;
|
|
||||||
if (direction == 1) {
|
|
||||||
data->last_seen_a = now;
|
|
||||||
data->bytes_sent += dissector->skb_len;
|
|
||||||
data->packets_sent++;
|
|
||||||
} else {
|
|
||||||
data->last_seen_b = now;
|
|
||||||
data->bytes_received += dissector->skb_len;
|
|
||||||
data->packets_received++;
|
|
||||||
}
|
|
||||||
//bpf_debug("Dir: %d, Sent/Received: [%d]/[%d]", direction, data->bytes_sent, data->bytes_received);
|
|
||||||
|
|
||||||
// Parse the TCP options
|
|
||||||
//__u32 tsval = 0;
|
|
||||||
//__u32 tsecr = 0;
|
|
||||||
void *end_opts = (tcp + 1) + (tcp->doff << 2);
|
|
||||||
bool has_data = end_opts - dissector->start < dissector->skb_len;
|
|
||||||
//if (get_timestamps(&tsval, &tsecr, tcp, dissector, end_opts)) {
|
|
||||||
//bpf_debug("[%d] => TSVal %u TSecr %u", direction, tsval, tsecr);
|
|
||||||
//bpf_debug("[%d] => Seq %u AckSeq %u", direction, tcp->seq, tcp->ack_seq);
|
|
||||||
//}
|
|
||||||
|
|
||||||
if ( tcp->ack && has_data) {
|
|
||||||
//bpf_debug("Direction %d", direction);
|
|
||||||
__u32 sequence = bpf_ntohl(tcp->seq);
|
|
||||||
__u32 ack_seq = bpf_ntohl(tcp->ack_seq);
|
|
||||||
|
|
||||||
if (direction == 1) {
|
|
||||||
// Going TO the Internet. We're acknowledging a packet.
|
|
||||||
// We don't need to record an RTT measurement and check for issues.
|
|
||||||
bpf_debug("%u, A: %u / B: %u", sequence, data->time_a, data->time_b);
|
|
||||||
bpf_debug("%u", ack_seq);
|
|
||||||
|
|
||||||
if (now > data->next_count_time) {
|
|
||||||
// Calculate the rate estimate
|
|
||||||
__u64 bytes = data->bytes_sent + data->bytes_received - data->next_count_bytes;
|
|
||||||
__u64 time = now - data->last_count_time;
|
|
||||||
data->rate_estimate = ((bytes * SECOND_IN_NANOS / time)*8)/1048576;
|
|
||||||
data->next_count_time = now + SECOND_IN_NANOS;
|
|
||||||
data->next_count_bytes = data->bytes_sent + data->bytes_received;
|
|
||||||
data->last_count_time = now;
|
|
||||||
bpf_debug("[1] Rate estimate: %u mbits/sec", data->rate_estimate);
|
|
||||||
|
|
||||||
if (data->rate_estimate > 5 && tcp->ack_seq >= data->time_a) {
|
|
||||||
__u64 rtt = now - last_seen;
|
|
||||||
bpf_debug("RTT: %d nanos (%u - %u)", rtt, tcp->ack_seq, data->time_a);
|
|
||||||
data->last_rtt = rtt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->rate_estimate > 5 && ack_seq >= data->time_b) {
|
|
||||||
__u64 rtt = now - last_seen;
|
|
||||||
bpf_debug("[1] RTT: %d nanos (%u - %u)", rtt, sequence, data->time_b);
|
|
||||||
data->last_rtt = rtt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->time_a != 0 && sequence < data->time_a) {
|
|
||||||
// This is a retransmission
|
|
||||||
//bpf_debug("DIR 1 Retransmission (or out of order) detected");
|
|
||||||
//bpf_debug("to 192.168.66.%d => SEQ %d < %d", dissector->dst_ip.in6_u.u6_addr8[15], sequence, data->time_a);
|
|
||||||
data->retries_a++;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->time_a = sequence;
|
|
||||||
} else {
|
|
||||||
// Coming FROM the Internet. They are acknowledging a packet.
|
|
||||||
// We need to record an RTT measurement, but we can check for issues.
|
|
||||||
//bpf_debug("%d / %d", data->time_a, data->time_b);
|
|
||||||
|
|
||||||
if (now > data->next_count_time) {
|
|
||||||
// Calculate the rate estimate
|
|
||||||
__u64 bytes = data->bytes_sent + data->bytes_received - data->next_count_bytes;
|
|
||||||
__u64 time = now - data->last_count_time;
|
|
||||||
data->rate_estimate = ((bytes * SECOND_IN_NANOS / time)*8)/1000000;
|
|
||||||
data->next_count_time = now + SECOND_IN_NANOS;
|
|
||||||
data->next_count_bytes = data->bytes_sent + data->bytes_received;
|
|
||||||
data->last_count_time = now;
|
|
||||||
bpf_debug("[2] Rate estimate: %u mbits/sec", data->rate_estimate);
|
|
||||||
|
|
||||||
if (data->rate_estimate > 5 && tcp->ack_seq >= data->time_b) {
|
|
||||||
__u64 rtt = now - last_seen;
|
|
||||||
bpf_debug("[2] RTT: %d nanos", rtt);
|
|
||||||
data->last_rtt = rtt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (data->time_b != 0 && sequence < data->time_b) {
|
|
||||||
// This is a retransmission
|
|
||||||
//bpf_debug("DIR 2 Retransmission (or out of order) detected");
|
|
||||||
//bpf_debug("to 192.168.66.%d => SEQ %d > %d", dissector->dst_ip.in6_u.u6_addr8[15], sequence, data->time_b);
|
|
||||||
data->retries_b++;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->time_b = sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//bpf_debug("to 192.168.66.%d => TS %d <-> %d", dissector->dst_ip.in6_u.u6_addr8[15], bpf_ntohs(tsval), bpf_ntohs(tsecr));
|
|
||||||
} else if ( tcp->fin) {
|
|
||||||
// FIN packet. We are closing a connection.
|
|
||||||
// We need to remove this flow from the tracking table.
|
|
||||||
bpf_debug("TCP connection closed");
|
|
||||||
// TODO: Submit the result somewhere
|
|
||||||
bpf_debug(" Flow Lifetime: %u nanos", now - data->start_time);
|
|
||||||
bpf_debug(" BYTES : %d / %d", data->bytes_sent, data->bytes_received);
|
|
||||||
bpf_debug(" PACKETS : %d / %d", data->packets_sent, data->packets_received);
|
|
||||||
bpf_debug(" RTT : %d nanos", data->last_rtt);
|
|
||||||
bpf_debug(" RETRIES : %d / %d", data->retries_a, data->retries_b);
|
|
||||||
// /TODO
|
|
||||||
bpf_map_delete_elem(&flowbee, &key);
|
|
||||||
} else if ( tcp->rst ) {
|
|
||||||
// RST packet. We are resetting a connection.
|
|
||||||
// We need to remove this flow from the tracking table.
|
|
||||||
bpf_debug("TCP connection reset");
|
|
||||||
// TODO: Submit the result somewhere
|
|
||||||
bpf_debug(" Flow Lifetime: %u nanos", now - data->start_time);
|
|
||||||
bpf_debug(" BYTES : %d / %d", data->bytes_sent, data->bytes_received);
|
|
||||||
bpf_debug(" PACKETS : %d / %d", data->packets_sent, data->packets_received);
|
|
||||||
bpf_debug(" RTT : %d nanos", data->last_rtt);
|
|
||||||
bpf_debug(" RETRIES : %d / %d", data->retries_a, data->retries_b);
|
|
||||||
// /TODO
|
|
||||||
bpf_map_delete_elem(&flowbee, &key);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|||||||
@@ -112,9 +112,6 @@ int xdp_prog(struct xdp_md *ctx)
|
|||||||
bpf_debug("(XDP) Spotted VLAN: %u", dissector.current_vlan);
|
bpf_debug("(XDP) Spotted VLAN: %u", dissector.current_vlan);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Per-Flow RTT Tracking
|
|
||||||
track_flows(&dissector, effective_direction);
|
|
||||||
|
|
||||||
// Determine the lookup key by direction
|
// Determine the lookup key by direction
|
||||||
struct ip_hash_key lookup_key;
|
struct ip_hash_key lookup_key;
|
||||||
struct ip_hash_info * ip_info = setup_lookup_key_and_tc_cpu(
|
struct ip_hash_info * ip_info = setup_lookup_key_and_tc_cpu(
|
||||||
@@ -130,6 +127,10 @@ int xdp_prog(struct xdp_md *ctx)
|
|||||||
tc_handle = ip_info->tc_handle;
|
tc_handle = ip_info->tc_handle;
|
||||||
cpu = ip_info->cpu;
|
cpu = ip_info->cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-Flow RTT Tracking
|
||||||
|
track_flows(&dissector, effective_direction);
|
||||||
|
|
||||||
// Update the traffic tracking buffers
|
// Update the traffic tracking buffers
|
||||||
track_traffic(
|
track_traffic(
|
||||||
effective_direction,
|
effective_direction,
|
||||||
|
|||||||
Reference in New Issue
Block a user