mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2024-11-22 08:16:25 -06:00
Extend the capture to include TCP flags, window and ACK data.
This commit is contained in:
parent
d542bf1660
commit
8288bb3f9b
@ -129,8 +129,18 @@ pub struct PacketHeader {
|
||||
pub dst_port: u16,
|
||||
/// Ip Protocol (see Linux kernel docs)
|
||||
pub ip_protocol: u8,
|
||||
/// Tos to decode
|
||||
pub tos: u8,
|
||||
/// ECN Flag
|
||||
pub ecn: u8,
|
||||
/// DHSCP code
|
||||
pub dscp: u8,
|
||||
/// Packet Size
|
||||
pub size: u32,
|
||||
/// TCP Flag Bitset
|
||||
pub tcp_flags: u8,
|
||||
/// TCP Window Size
|
||||
pub tcp_window: u16,
|
||||
/// TCP TSVal
|
||||
pub tcp_tsval: u32,
|
||||
/// TCP ECR val
|
||||
pub tcp_tsecr: u32,
|
||||
}
|
@ -15,8 +15,24 @@ pub struct HeimdallEvent {
|
||||
pub ip_protocol: u8,
|
||||
pub tos: u8,
|
||||
pub size: u32,
|
||||
pub tcp_flags: u8,
|
||||
pub tcp_window: u16,
|
||||
pub tcp_tsval: u32,
|
||||
pub tcp_tsecr: u32,
|
||||
}
|
||||
|
||||
/*
|
||||
Snippet for tcp_flags decoding
|
||||
if (hdr->fin) flags |= 1;
|
||||
if (hdr->syn) flags |= 2;
|
||||
if (hdr->rst) flags |= 4;
|
||||
if (hdr->psh) flags |= 8;
|
||||
if (hdr->ack) flags |= 16;
|
||||
if (hdr->urg) flags |= 32;
|
||||
if (hdr->ece) flags |= 64;
|
||||
if (hdr->cwr) flags |= 128;
|
||||
*/
|
||||
|
||||
/// Callback for the Heimdall Perf map system. Called whenever Heimdall has
|
||||
/// events for the system to read.
|
||||
///
|
||||
|
@ -1,12 +1,13 @@
|
||||
use std::time::Duration;
|
||||
use dashmap::DashSet;
|
||||
use lqos_bus::PacketHeader;
|
||||
use lqos_bus::{PacketHeader, tos_parser};
|
||||
use lqos_utils::{unix_time::time_since_boot, XdpIpAddress};
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::perf_interface::HeimdallEvent;
|
||||
|
||||
impl HeimdallEvent {
|
||||
fn as_header(&self) -> PacketHeader {
|
||||
let (dscp, ecn) = tos_parser(self.tos);
|
||||
PacketHeader {
|
||||
timestamp: self.timestamp,
|
||||
src: self.src.as_ip().to_string(),
|
||||
@ -14,8 +15,12 @@ impl HeimdallEvent {
|
||||
src_port: self.src_port,
|
||||
dst_port: self.dst_port,
|
||||
ip_protocol: self.ip_protocol,
|
||||
tos: self.tos,
|
||||
ecn, dscp,
|
||||
size: self.size,
|
||||
tcp_flags: self.tcp_flags,
|
||||
tcp_window: self.tcp_window,
|
||||
tcp_tsecr: self.tcp_tsecr,
|
||||
tcp_tsval: self.tcp_tsval,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -538,16 +538,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
function ecn(n) {
|
||||
switch (n) {
|
||||
case 0: return "-";
|
||||
case 1: return "L4S";
|
||||
case 2: return "ECT0";
|
||||
case 3: return "CE";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
function icmpType(n) {
|
||||
switch (n) {
|
||||
case 0: return "ECHO REPLY";
|
||||
|
@ -89,6 +89,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Snippet for tcp_flags decoding
|
||||
if (hdr->fin) flags |= 1;
|
||||
if (hdr->syn) flags |= 2;
|
||||
if (hdr->rst) flags |= 4;
|
||||
if (hdr->psh) flags |= 8;
|
||||
if (hdr->ack) flags |= 16;
|
||||
if (hdr->urg) flags |= 32;
|
||||
if (hdr->ece) flags |= 64;
|
||||
if (hdr->cwr) flags |= 128;
|
||||
*/
|
||||
|
||||
function tcp_flags(n) {
|
||||
let result = "";
|
||||
if (n & 1) result += "FIN-";
|
||||
if (n & 2) result += "SYN-";
|
||||
if (n & 4) result += "RST-";
|
||||
if (n & 8) result += "PSH-";
|
||||
if (n & 16) result += "ACK-";
|
||||
if (n & 32) result += "URG-";
|
||||
if (n & 64) result += "ECE-";
|
||||
if (n & 128) result += "CWR-";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function zoomIn() {
|
||||
PAGE_SIZE /= 2;
|
||||
current_page /= 2;
|
||||
@ -125,7 +151,7 @@
|
||||
console.log("OOps");
|
||||
}
|
||||
let html = "<table class='table table-striped'>";
|
||||
html += "<thead><th>Time (nanos)</th><th>Proto</th><th>Flow</th><th>Bytes</th><th>TOS</th></thead>";
|
||||
html += "<thead><th>Time (nanos)</th><th>Proto</th><th>TCP Flags</th><th>Sequence</th><th>Window</th><th>Flow</th><th>Bytes</th><th>ECN</th><th>DHSCP</th></thead>";
|
||||
let x_axis = [];
|
||||
let y1_axis = [];
|
||||
let y2_axis = [];
|
||||
@ -133,13 +159,23 @@
|
||||
html += "<tr>";
|
||||
html += "<td>" + packets[i].timestamp + "</td>";
|
||||
html += "<td>" + proto(packets[i].ip_protocol) + "</td>";
|
||||
|
||||
if (packets[i].ip_protocol == 6) {
|
||||
html += "<td>" + tcp_flags(packets[i].tcp_flags) + "</td>";
|
||||
html += "<td>" + packets[i].tcp_tsval + "/" + packets[i].tcp_tsecr + "</td>";
|
||||
html += "<td>" + packets[i].tcp_window + "</td>";
|
||||
} else {
|
||||
html += "<td></td><td></td><td></td>";
|
||||
}
|
||||
|
||||
if (packets[i].ip_protocol != 1) {
|
||||
html += "<td>" + packets[i].src + ":" + packets[i].src_port + " -> " + packets[i].dst + ":" + packets[i].dst_port + "</td>";
|
||||
} else {
|
||||
html += "<td>" + packets[i].src + " -> " + packets[i].dst + "</td>";
|
||||
}
|
||||
html += "<td>" + packets[i].size + "</td>";
|
||||
html += "<td>" + packets[i].tos + "</td>";
|
||||
html += "<td>" + ecn(packets[i].ecn) + "</td>";
|
||||
html += "<td>0x" + packets[i].dscp.toString(16) + "</td>";
|
||||
html += "</tr>";
|
||||
x_axis.push(packets[i].timestamp);
|
||||
if (packets[i].src == target) {
|
||||
@ -177,7 +213,7 @@
|
||||
|
||||
target = params.ip;
|
||||
$.get("/api/packet_dump/" + params.ip, (data) => {
|
||||
data.sort((a,b) => a<b);
|
||||
data.sort((a,b) => a.timestamp - b.timestamp);
|
||||
let min_ts = null;
|
||||
for (let i=0; i<data.length; ++i) {
|
||||
if (min_ts == null || min_ts > data[i].timestamp) {
|
||||
|
@ -368,4 +368,14 @@ class RttHistogram {
|
||||
let graph = document.getElementById(target_div);
|
||||
Plotly.newPlot(graph, gData, { margin: { l: 0, r: 0, b: 35, t: 0 }, xaxis: { title: 'TCP Round-Trip Time (ms)' } }, { responsive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function ecn(n) {
|
||||
switch (n) {
|
||||
case 0: return "-";
|
||||
case 1: return "L4S";
|
||||
case 2: return "ECT0";
|
||||
case 3: return "CE";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#include "../common/debug.h"
|
||||
#include "../common/ip_hash.h"
|
||||
#include "../common/bifrost.h"
|
||||
#include "../common/tcp_opts.h"
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/tcp.h>
|
||||
@ -47,6 +48,10 @@ struct dissector_t
|
||||
__u16 src_port;
|
||||
__u16 dst_port;
|
||||
__u8 tos;
|
||||
__u8 tcp_flags;
|
||||
__u16 window;
|
||||
__u32 tsval;
|
||||
__u32 tsecr;
|
||||
};
|
||||
|
||||
// Representation of the VLAN header type.
|
||||
@ -325,6 +330,20 @@ static __always_inline void snoop(struct dissector_t *dissector)
|
||||
}
|
||||
dissector->src_port = hdr->source;
|
||||
dissector->dst_port = hdr->dest;
|
||||
__u8 flags = 0;
|
||||
if (hdr->fin) flags |= 1;
|
||||
if (hdr->syn) flags |= 2;
|
||||
if (hdr->rst) flags |= 4;
|
||||
if (hdr->psh) flags |= 8;
|
||||
if (hdr->ack) flags |= 16;
|
||||
if (hdr->urg) flags |= 32;
|
||||
if (hdr->ece) flags |= 64;
|
||||
if (hdr->cwr) flags |= 128;
|
||||
|
||||
dissector->tcp_flags = flags;
|
||||
dissector->window = hdr->window;
|
||||
|
||||
parse_tcp_ts(hdr, dissector->end, &dissector->tsval, &dissector->tsecr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -50,6 +50,10 @@ struct heimdall_event {
|
||||
__u8 ip_protocol;
|
||||
__u8 tos;
|
||||
__u32 size;
|
||||
__u8 tcp_flags;
|
||||
__u16 tcp_window;
|
||||
__u32 tsval;
|
||||
__u32 tsecr;
|
||||
};
|
||||
|
||||
static __always_inline __u8 get_heimdall_mode()
|
||||
@ -91,6 +95,10 @@ static __always_inline void update_heimdall(struct dissector_t *dissector, __u32
|
||||
event.ip_protocol = dissector->ip_protocol;
|
||||
event.tos = dissector->tos;
|
||||
event.size = size;
|
||||
event.tcp_flags = dissector->tcp_flags;
|
||||
event.tcp_window = dissector->window;
|
||||
event.tsval = dissector->tsval;
|
||||
event.tsecr = dissector->tsecr;
|
||||
long err = bpf_ringbuf_output(&heimdall_events, &event, sizeof(event), 0);
|
||||
if (err != 0) {
|
||||
bpf_debug("Failed to send perf event %d", err);
|
||||
|
75
src/rust/lqos_sys/src/bpf/common/tcp_opts.h
Normal file
75
src/rust/lqos_sys/src/bpf/common/tcp_opts.h
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_TCP_OPTIONS 10
|
||||
|
||||
/*
|
||||
* Parses the TSval and TSecr values from the TCP options field. If sucessful
|
||||
* the TSval and TSecr values will be stored at tsval and tsecr (in network
|
||||
* byte order).
|
||||
* Returns 0 if sucessful and -1 on failure
|
||||
*/
|
||||
static __always_inline int parse_tcp_ts(
|
||||
struct tcphdr *tcph,
|
||||
void *data_end,
|
||||
__u32 *tsval,
|
||||
__u32 *tsecr
|
||||
) {
|
||||
int len = tcph->doff << 2;
|
||||
void *opt_end = (void *)tcph + len;
|
||||
__u8 *pos = (__u8 *)(tcph + 1); // Current pos in TCP options
|
||||
__u8 i, opt;
|
||||
volatile __u8
|
||||
opt_size; // Seems to ensure it's always read of from stack as u8
|
||||
|
||||
if (tcph + 1 > data_end || len <= sizeof(struct tcphdr))
|
||||
return -1;
|
||||
#pragma unroll // temporary solution until we can identify why the non-unrolled loop gets stuck in an infinite loop
|
||||
for (i = 0; i < MAX_TCP_OPTIONS; i++)
|
||||
{
|
||||
if (pos + 1 > opt_end || pos + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
opt = *pos;
|
||||
if (opt == 0) // Reached end of TCP options
|
||||
return -1;
|
||||
|
||||
if (opt == 1)
|
||||
{ // TCP NOP option - advance one byte
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Option > 1, should have option size
|
||||
if (pos + 2 > opt_end || pos + 2 > data_end)
|
||||
return -1;
|
||||
opt_size = *(pos + 1);
|
||||
if (opt_size < 2) // Stop parsing options if opt_size has an invalid value
|
||||
return -1;
|
||||
|
||||
// Option-kind is TCP timestap (yey!)
|
||||
if (opt == 8 && opt_size == 10)
|
||||
{
|
||||
if (pos + 10 > opt_end || pos + 10 > data_end)
|
||||
return -1;
|
||||
*tsval = bpf_ntohl(*(__u32 *)(pos + 2));
|
||||
*tsecr = bpf_ntohl(*(__u32 *)(pos + 6));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some other TCP option - advance option-length bytes
|
||||
pos += opt_size;
|
||||
}
|
||||
return -1;
|
||||
}
|
@ -36,6 +36,7 @@ My modifications are Copyright 2022, Herbert Wolverson
|
||||
#include "debug.h"
|
||||
#include "ip_hash.h"
|
||||
#include "dissector_tc.h"
|
||||
#include "tcp_opts.h"
|
||||
|
||||
#define MAX_MEMCMP_SIZE 128
|
||||
|
||||
@ -226,66 +227,6 @@ static __always_inline struct flow_state *fstate_from_dfkey(
|
||||
return is_dfkey ? &df_state->dir1 : &df_state->dir2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the TSval and TSecr values from the TCP options field. If sucessful
|
||||
* the TSval and TSecr values will be stored at tsval and tsecr (in network
|
||||
* byte order).
|
||||
* Returns 0 if sucessful and -1 on failure
|
||||
*/
|
||||
static __always_inline int parse_tcp_ts(
|
||||
struct tcphdr *tcph,
|
||||
void *data_end,
|
||||
__u32 *tsval,
|
||||
__u32 *tsecr
|
||||
) {
|
||||
int len = tcph->doff << 2;
|
||||
void *opt_end = (void *)tcph + len;
|
||||
__u8 *pos = (__u8 *)(tcph + 1); // Current pos in TCP options
|
||||
__u8 i, opt;
|
||||
volatile __u8
|
||||
opt_size; // Seems to ensure it's always read of from stack as u8
|
||||
|
||||
if (tcph + 1 > data_end || len <= sizeof(struct tcphdr))
|
||||
return -1;
|
||||
#pragma unroll // temporary solution until we can identify why the non-unrolled loop gets stuck in an infinite loop
|
||||
for (i = 0; i < MAX_TCP_OPTIONS; i++)
|
||||
{
|
||||
if (pos + 1 > opt_end || pos + 1 > data_end)
|
||||
return -1;
|
||||
|
||||
opt = *pos;
|
||||
if (opt == 0) // Reached end of TCP options
|
||||
return -1;
|
||||
|
||||
if (opt == 1)
|
||||
{ // TCP NOP option - advance one byte
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Option > 1, should have option size
|
||||
if (pos + 2 > opt_end || pos + 2 > data_end)
|
||||
return -1;
|
||||
opt_size = *(pos + 1);
|
||||
if (opt_size < 2) // Stop parsing options if opt_size has an invalid value
|
||||
return -1;
|
||||
|
||||
// Option-kind is TCP timestap (yey!)
|
||||
if (opt == 8 && opt_size == 10)
|
||||
{
|
||||
if (pos + 10 > opt_end || pos + 10 > data_end)
|
||||
return -1;
|
||||
*tsval = bpf_ntohl(*(__u32 *)(pos + 2));
|
||||
*tsecr = bpf_ntohl(*(__u32 *)(pos + 6));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some other TCP option - advance option-length bytes
|
||||
pos += opt_size;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to fetch an identifier for TCP packets, based on the TCP timestamp
|
||||
* option.
|
||||
|
Loading…
Reference in New Issue
Block a user