2021-01-18 13:13:51 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2021-02-09 18:09:30 +01:00
|
|
|
#include <linux/bpf.h>
|
2021-04-15 14:13:54 +02:00
|
|
|
#include <bpf/bpf_helpers.h>
|
2021-12-08 10:06:30 +01:00
|
|
|
#include <linux/pkt_cls.h>
|
2021-02-09 18:09:30 +01:00
|
|
|
#include <linux/in.h>
|
2021-02-08 20:28:46 +01:00
|
|
|
#include <linux/in6.h>
|
2021-02-09 18:09:30 +01:00
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <linux/ipv6.h>
|
2021-01-26 18:34:23 +01:00
|
|
|
#include <linux/tcp.h>
|
2021-02-09 18:09:30 +01:00
|
|
|
#include <stdbool.h>
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-04-22 17:51:49 +02:00
|
|
|
// overwrite xdp/parsing_helpers.h value to avoid hitting verifier limit
|
|
|
|
#ifdef IPV6_EXT_MAX_CHAIN
|
|
|
|
#undef IPV6_EXT_MAX_CHAIN
|
|
|
|
#endif
|
|
|
|
#define IPV6_EXT_MAX_CHAIN 3
|
|
|
|
|
|
|
|
#include <xdp/parsing_helpers.h>
|
2021-02-08 20:28:46 +01:00
|
|
|
#include "pping.h"
|
2021-01-26 18:34:23 +01:00
|
|
|
|
2021-02-09 13:00:28 +01:00
|
|
|
#define AF_INET 2
|
|
|
|
#define AF_INET6 10
|
2021-01-07 18:30:53 +01:00
|
|
|
#define MAX_TCP_OPTIONS 10
|
|
|
|
|
2021-02-12 18:31:30 +01:00
|
|
|
/*
|
|
|
|
* This struct keeps track of the data and data_end pointers from the xdp_md or
|
|
|
|
* __skb_buff contexts, as well as a currently parsed to position kept in nh.
|
2021-02-16 12:34:19 +01:00
|
|
|
* Additionally, it also keeps the length of the entire packet, which together
|
|
|
|
* with the other members can be used to determine ex. how much data each
|
|
|
|
* header encloses.
|
2021-02-12 18:31:30 +01:00
|
|
|
*/
|
|
|
|
struct parsing_context {
|
2021-04-15 14:13:54 +02:00
|
|
|
void *data; //Start of eth hdr
|
|
|
|
void *data_end; //End of safe acessible area
|
2021-02-12 18:31:30 +01:00
|
|
|
struct hdr_cursor nh; //Position to parse next
|
2021-03-22 12:23:27 +01:00
|
|
|
__u32 pkt_len; //Full packet length (headers+data)
|
|
|
|
bool is_egress; //Is packet on egress or ingress?
|
2021-02-12 18:31:30 +01:00
|
|
|
};
|
|
|
|
|
2021-04-15 14:13:54 +02:00
|
|
|
char _license[] SEC("license") = "GPL";
|
|
|
|
// Global config struct - set from userspace
|
|
|
|
static volatile const struct bpf_config config = {};
|
2021-03-22 12:23:27 +01:00
|
|
|
|
2021-04-15 14:13:54 +02:00
|
|
|
// Map definitions
|
2021-03-02 17:40:51 +01:00
|
|
|
struct {
|
|
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
|
|
__type(key, struct packet_id);
|
|
|
|
__type(value, __u64);
|
|
|
|
__uint(max_entries, 16384);
|
2021-04-15 14:13:54 +02:00
|
|
|
} packet_ts SEC(".maps");
|
2021-03-02 17:40:51 +01:00
|
|
|
|
2021-03-09 19:58:42 +01:00
|
|
|
struct {
|
|
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
|
|
__type(key, struct network_tuple);
|
|
|
|
__type(value, struct flow_state);
|
|
|
|
__uint(max_entries, 16384);
|
|
|
|
} flow_state SEC(".maps");
|
|
|
|
|
2021-04-15 14:13:54 +02:00
|
|
|
struct {
|
|
|
|
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
|
|
|
__uint(key_size, sizeof(__u32));
|
|
|
|
__uint(value_size, sizeof(__u32));
|
2021-06-22 15:22:11 +02:00
|
|
|
} events SEC(".maps");
|
2021-04-15 14:13:54 +02:00
|
|
|
|
|
|
|
// Help functions
|
|
|
|
|
2021-02-08 20:28:46 +01:00
|
|
|
/*
|
2021-02-16 12:34:19 +01:00
|
|
|
* Maps an IPv4 address into an IPv6 address according to RFC 4291 sec 2.5.5.2
|
2021-02-08 20:28:46 +01:00
|
|
|
*/
|
2021-02-09 18:09:30 +01:00
|
|
|
static void map_ipv4_to_ipv6(__be32 ipv4, struct in6_addr *ipv6)
|
2021-01-07 18:30:53 +01:00
|
|
|
{
|
2021-03-22 12:23:27 +01:00
|
|
|
__builtin_memset(&ipv6->in6_u.u6_addr8[0], 0x00, 10);
|
|
|
|
__builtin_memset(&ipv6->in6_u.u6_addr8[10], 0xff, 2);
|
2021-02-08 20:28:46 +01:00
|
|
|
ipv6->in6_u.u6_addr32[3] = ipv4;
|
2021-01-07 18:30:53 +01:00
|
|
|
}
|
2021-01-26 18:34:23 +01:00
|
|
|
|
2021-01-18 18:08:35 +01:00
|
|
|
/*
|
2021-01-26 18:34:23 +01:00
|
|
|
* 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
|
2021-01-18 18:08:35 +01:00
|
|
|
* byte order).
|
|
|
|
* Returns 0 if sucessful and -1 on failure
|
|
|
|
*/
|
2021-02-09 18:09:30 +01:00
|
|
|
static int parse_tcp_ts(struct tcphdr *tcph, void *data_end, __u32 *tsval,
|
|
|
|
__u32 *tsecr)
|
2021-01-07 18:30:53 +01:00
|
|
|
{
|
2021-01-27 12:16:11 +01:00
|
|
|
int len = tcph->doff << 2;
|
2021-01-26 18:34:23 +01:00
|
|
|
void *opt_end = (void *)tcph + len;
|
2021-01-27 12:16:11 +01:00
|
|
|
__u8 *pos = (__u8 *)(tcph + 1); //Current pos in TCP options
|
2021-03-29 20:13:33 +02:00
|
|
|
__u8 i, opt;
|
2021-04-15 14:13:54 +02:00
|
|
|
volatile __u8
|
|
|
|
opt_size; // Seems to ensure it's always read of from stack as u8
|
2021-01-07 18:30:53 +01:00
|
|
|
|
2021-01-27 12:16:11 +01:00
|
|
|
if (tcph + 1 > data_end || len <= sizeof(struct tcphdr))
|
|
|
|
return -1;
|
2021-03-15 18:23:23 +01:00
|
|
|
#pragma unroll //temporary solution until we can identify why the non-unrolled loop gets stuck in an infinite loop
|
2021-01-27 12:16:11 +01:00
|
|
|
for (i = 0; i < MAX_TCP_OPTIONS; i++) {
|
|
|
|
if (pos + 1 > opt_end || pos + 1 > data_end)
|
|
|
|
return -1;
|
2021-01-26 18:34:23 +01:00
|
|
|
|
2021-01-27 12:16:11 +01:00
|
|
|
opt = *pos;
|
|
|
|
if (opt == 0) // Reached end of TCP options
|
|
|
|
return -1;
|
2021-01-26 18:34:23 +01:00
|
|
|
|
2021-01-27 12:16:11 +01:00
|
|
|
if (opt == 1) { // TCP NOP option - advance one byte
|
|
|
|
pos++;
|
|
|
|
continue;
|
|
|
|
}
|
2021-01-26 18:34:23 +01:00
|
|
|
|
2021-01-27 12:16:11 +01:00
|
|
|
// Option > 1, should have option size
|
|
|
|
if (pos + 2 > opt_end || pos + 2 > data_end)
|
|
|
|
return -1;
|
|
|
|
opt_size = *(pos + 1);
|
2021-03-30 19:34:48 +02:00
|
|
|
if (opt_size < 2) // Stop parsing options if opt_size has an invalid value
|
|
|
|
return -1;
|
2021-01-26 18:34:23 +01:00
|
|
|
|
2021-01-27 12:16:11 +01:00
|
|
|
// Option-kind is TCP timestap (yey!)
|
|
|
|
if (opt == 8 && opt_size == 10) {
|
2021-03-29 20:13:33 +02:00
|
|
|
if (pos + 10 > opt_end || pos + 10 > data_end)
|
2021-01-27 12:16:11 +01:00
|
|
|
return -1;
|
|
|
|
*tsval = *(__u32 *)(pos + 2);
|
|
|
|
*tsecr = *(__u32 *)(pos + 6);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-01-07 18:30:53 +01:00
|
|
|
|
2021-01-27 12:16:11 +01:00
|
|
|
// Some other TCP option - advance option-length bytes
|
|
|
|
pos += opt_size;
|
|
|
|
}
|
|
|
|
return -1;
|
2021-01-07 18:30:53 +01:00
|
|
|
}
|
2021-03-09 19:58:42 +01:00
|
|
|
|
2021-02-09 18:09:30 +01:00
|
|
|
/*
|
|
|
|
* Attempts to fetch an identifier for TCP packets, based on the TCP timestamp
|
2021-06-22 15:36:35 +02:00
|
|
|
* option.
|
|
|
|
* If successful, identifier will be set to TSval if is_ingress, or TSecr
|
|
|
|
* otherwise, the port-members of saddr and daddr will be set to the TCP source
|
|
|
|
* and dest, respectively, fei will be filled appropriately (based on
|
|
|
|
* SYN/FIN/RST) and 0 will be returned.
|
|
|
|
* On failure, -1 will be returned.
|
2021-02-09 18:09:30 +01:00
|
|
|
*/
|
2021-03-09 19:58:42 +01:00
|
|
|
static int parse_tcp_identifier(struct parsing_context *ctx, __be16 *sport,
|
2021-06-22 15:36:35 +02:00
|
|
|
__be16 *dport, struct flow_event_info *fei,
|
2021-03-09 19:58:42 +01:00
|
|
|
__u32 *identifier)
|
2021-02-09 18:09:30 +01:00
|
|
|
{
|
|
|
|
__u32 tsval, tsecr;
|
|
|
|
struct tcphdr *tcph;
|
|
|
|
|
2021-02-12 18:31:30 +01:00
|
|
|
if (parse_tcphdr(&ctx->nh, ctx->data_end, &tcph) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Do not timestamp pure ACKs
|
2021-03-09 19:58:42 +01:00
|
|
|
if (ctx->is_egress && ctx->nh.pos - ctx->data >= ctx->pkt_len &&
|
|
|
|
!tcph->syn)
|
2021-02-09 18:09:30 +01:00
|
|
|
return -1;
|
2021-02-12 11:40:43 +01:00
|
|
|
|
2021-06-22 15:36:35 +02:00
|
|
|
// Check if connection is opening/closing
|
|
|
|
if (tcph->syn) {
|
|
|
|
fei->event = FLOW_EVENT_OPENING;
|
|
|
|
fei->reason =
|
|
|
|
tcph->ack ? EVENT_REASON_SYN_ACK : EVENT_REASON_SYN;
|
|
|
|
} else if (tcph->rst) {
|
|
|
|
fei->event = FLOW_EVENT_CLOSING;
|
|
|
|
fei->reason = EVENT_REASON_RST;
|
|
|
|
} else if (!ctx->is_egress && tcph->fin) {
|
|
|
|
fei->event = FLOW_EVENT_CLOSING;
|
|
|
|
fei->reason =
|
|
|
|
tcph->ack ? EVENT_REASON_FIN_ACK : EVENT_REASON_FIN;
|
|
|
|
} else {
|
|
|
|
fei->event = FLOW_EVENT_NONE;
|
|
|
|
}
|
|
|
|
|
2021-02-12 18:31:30 +01:00
|
|
|
if (parse_tcp_ts(tcph, ctx->data_end, &tsval, &tsecr) < 0)
|
2021-02-09 18:09:30 +01:00
|
|
|
return -1; //Possible TODO, fall back on seq/ack instead
|
|
|
|
|
2021-02-16 12:34:19 +01:00
|
|
|
*sport = tcph->source;
|
|
|
|
*dport = tcph->dest;
|
2021-03-09 19:58:42 +01:00
|
|
|
*identifier = ctx->is_egress ? tsval : tsecr;
|
2021-02-09 18:09:30 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attempts to parse the packet limited by the data and data_end pointers,
|
|
|
|
* to retrieve a protocol dependent packet identifier. If sucessful, the
|
2021-06-22 15:36:35 +02:00
|
|
|
* pointed to p_id and fei will be filled with parsed information from the
|
2021-02-09 18:09:30 +01:00
|
|
|
* packet, and 0 will be returned. On failure, -1 will be returned.
|
2021-02-12 11:40:43 +01:00
|
|
|
* If is_egress saddr and daddr will match source and destination of packet,
|
|
|
|
* respectively, and identifier will be set to the identifer for an outgoing
|
|
|
|
* packet. Otherwise, saddr and daddr will be swapped (will match
|
|
|
|
* destination and source of packet, respectively), and identifier will be
|
|
|
|
* set to the identifier of a response.
|
2021-02-09 18:09:30 +01:00
|
|
|
*/
|
2021-03-09 19:58:42 +01:00
|
|
|
static int parse_packet_identifier(struct parsing_context *ctx,
|
2021-06-22 15:22:11 +02:00
|
|
|
struct packet_id *p_id,
|
2021-06-22 15:36:35 +02:00
|
|
|
struct flow_event_info *fei)
|
2021-02-09 18:09:30 +01:00
|
|
|
{
|
2021-02-12 11:40:43 +01:00
|
|
|
int proto, err;
|
2021-02-09 18:09:30 +01:00
|
|
|
struct ethhdr *eth;
|
|
|
|
struct iphdr *iph;
|
|
|
|
struct ipv6hdr *ip6h;
|
2021-02-12 11:40:43 +01:00
|
|
|
struct flow_address *saddr, *daddr;
|
|
|
|
|
|
|
|
// Switch saddr <--> daddr on ingress to match egress
|
2021-03-09 19:58:42 +01:00
|
|
|
if (ctx->is_egress) {
|
2021-02-12 11:40:43 +01:00
|
|
|
saddr = &p_id->flow.saddr;
|
|
|
|
daddr = &p_id->flow.daddr;
|
|
|
|
} else {
|
|
|
|
saddr = &p_id->flow.daddr;
|
|
|
|
daddr = &p_id->flow.saddr;
|
|
|
|
}
|
2021-02-09 18:09:30 +01:00
|
|
|
|
2021-02-12 18:31:30 +01:00
|
|
|
proto = parse_ethhdr(&ctx->nh, ctx->data_end, ð);
|
2021-02-09 18:09:30 +01:00
|
|
|
|
|
|
|
// Parse IPv4/6 header
|
|
|
|
if (proto == bpf_htons(ETH_P_IP)) {
|
|
|
|
p_id->flow.ipv = AF_INET;
|
2021-04-30 11:36:41 +02:00
|
|
|
p_id->flow.proto = parse_iphdr(&ctx->nh, ctx->data_end, &iph);
|
2021-02-09 18:09:30 +01:00
|
|
|
} else if (proto == bpf_htons(ETH_P_IPV6)) {
|
|
|
|
p_id->flow.ipv = AF_INET6;
|
2021-04-30 11:36:41 +02:00
|
|
|
p_id->flow.proto = parse_ip6hdr(&ctx->nh, ctx->data_end, &ip6h);
|
2021-02-12 11:40:43 +01:00
|
|
|
} else {
|
2021-02-09 18:09:30 +01:00
|
|
|
return -1;
|
2021-02-12 11:40:43 +01:00
|
|
|
}
|
2021-02-09 18:09:30 +01:00
|
|
|
|
|
|
|
// Add new protocols here
|
2021-04-30 11:36:41 +02:00
|
|
|
if (p_id->flow.proto == IPPROTO_TCP) {
|
2021-03-09 19:58:42 +01:00
|
|
|
err = parse_tcp_identifier(ctx, &saddr->port, &daddr->port,
|
2021-06-22 15:36:35 +02:00
|
|
|
fei, &p_id->identifier);
|
2021-02-12 11:40:43 +01:00
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
} else {
|
2021-02-09 18:09:30 +01:00
|
|
|
return -1;
|
2021-02-12 11:40:43 +01:00
|
|
|
}
|
2021-02-09 18:09:30 +01:00
|
|
|
|
|
|
|
// Sucessfully parsed packet identifier - fill in IP-addresses and return
|
|
|
|
if (p_id->flow.ipv == AF_INET) {
|
|
|
|
map_ipv4_to_ipv6(iph->saddr, &saddr->ip);
|
|
|
|
map_ipv4_to_ipv6(iph->daddr, &daddr->ip);
|
|
|
|
} else { // IPv6
|
|
|
|
saddr->ip = ip6h->saddr;
|
|
|
|
daddr->ip = ip6h->daddr;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2021-01-07 18:30:53 +01:00
|
|
|
|
2021-05-07 14:54:12 +02:00
|
|
|
/*
|
|
|
|
* Returns the number of unparsed bytes left in the packet (bytes after nh.pos)
|
|
|
|
*/
|
|
|
|
static __u32 remaining_pkt_payload(struct parsing_context *ctx)
|
|
|
|
{
|
|
|
|
// pkt_len - (pos - data) fails because compiler transforms it to pkt_len - pos + data (pkt_len - pos not ok because value - pointer)
|
|
|
|
// data + pkt_len - pos fails on (data+pkt_len) - pos due to math between pkt_pointer and unbounded register
|
|
|
|
__u32 parsed_bytes = ctx->nh.pos - ctx->data;
|
|
|
|
return parsed_bytes < ctx->pkt_len ? ctx->pkt_len - parsed_bytes : 0;
|
|
|
|
}
|
|
|
|
|
2021-06-22 15:22:11 +02:00
|
|
|
/*
|
2021-06-22 15:36:35 +02:00
|
|
|
* Fills in event_type, timestamp, flow, source and reserved.
|
|
|
|
* Does not fill in the flow_info.
|
2021-06-22 15:22:11 +02:00
|
|
|
*/
|
|
|
|
static void fill_flow_event(struct flow_event *fe, __u64 timestamp,
|
2021-06-22 15:36:35 +02:00
|
|
|
struct network_tuple *flow,
|
|
|
|
enum flow_event_source source)
|
2021-06-22 15:22:11 +02:00
|
|
|
{
|
|
|
|
fe->event_type = EVENT_TYPE_FLOW;
|
|
|
|
fe->timestamp = timestamp;
|
|
|
|
__builtin_memcpy(&fe->flow, flow, sizeof(struct network_tuple));
|
2021-06-22 15:36:35 +02:00
|
|
|
fe->source = source;
|
2021-06-22 15:22:11 +02:00
|
|
|
fe->reserved = 0; // Make sure it's initilized
|
|
|
|
}
|
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
/*
|
|
|
|
* Main function for handling the pping egress path.
|
|
|
|
* Parses the packet for an identifer and attemps to store a timestamp for it
|
|
|
|
* in the packet_ts map.
|
|
|
|
*/
|
|
|
|
static void pping_egress(void *ctx, struct parsing_context *pctx)
|
2021-04-15 14:13:54 +02:00
|
|
|
{
|
|
|
|
struct packet_id p_id = { 0 };
|
2021-06-22 15:36:35 +02:00
|
|
|
struct flow_event fe;
|
2021-04-15 14:13:54 +02:00
|
|
|
struct flow_state *f_state;
|
|
|
|
struct flow_state new_state = { 0 };
|
2021-12-08 10:06:30 +01:00
|
|
|
__u64 now;
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
if (parse_packet_identifier(pctx, &p_id, &fe.event_info) < 0)
|
|
|
|
return;
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-06-22 15:22:11 +02:00
|
|
|
now = bpf_ktime_get_ns(); // or bpf_ktime_get_boot_ns
|
|
|
|
f_state = bpf_map_lookup_elem(&flow_state, &p_id.flow);
|
|
|
|
|
|
|
|
// Flow closing - try to delete flow state and push closing-event
|
2021-06-22 15:36:35 +02:00
|
|
|
if (fe.event_info.event == FLOW_EVENT_CLOSING) {
|
2021-06-22 15:22:11 +02:00
|
|
|
if (!f_state) {
|
|
|
|
bpf_map_delete_elem(&flow_state, &p_id.flow);
|
2021-06-22 15:36:35 +02:00
|
|
|
fill_flow_event(&fe, now, &p_id.flow,
|
|
|
|
EVENT_SOURCE_EGRESS);
|
2021-12-08 10:06:30 +01:00
|
|
|
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
|
2021-06-22 15:22:11 +02:00
|
|
|
&fe, sizeof(fe));
|
|
|
|
}
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
2021-04-15 14:13:54 +02:00
|
|
|
}
|
|
|
|
|
2021-06-22 15:22:11 +02:00
|
|
|
// No previous state - attempt to create it and push flow-opening event
|
|
|
|
if (!f_state) {
|
2021-04-15 14:13:54 +02:00
|
|
|
bpf_map_update_elem(&flow_state, &p_id.flow, &new_state,
|
|
|
|
BPF_NOEXIST);
|
|
|
|
f_state = bpf_map_lookup_elem(&flow_state, &p_id.flow);
|
2021-06-22 15:22:11 +02:00
|
|
|
|
|
|
|
if (!f_state) // Creation failed
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
2021-06-22 15:22:11 +02:00
|
|
|
|
2021-06-22 15:36:35 +02:00
|
|
|
if (fe.event_info.event != FLOW_EVENT_OPENING) {
|
|
|
|
fe.event_info.event = FLOW_EVENT_OPENING;
|
|
|
|
fe.event_info.reason = EVENT_REASON_FIRST_OBS_PCKT;
|
2021-06-22 15:22:11 +02:00
|
|
|
}
|
2021-06-22 15:36:35 +02:00
|
|
|
fill_flow_event(&fe, now, &p_id.flow, EVENT_SOURCE_EGRESS);
|
2021-12-08 10:06:30 +01:00
|
|
|
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &fe,
|
2021-06-22 15:22:11 +02:00
|
|
|
sizeof(fe));
|
2021-04-15 14:13:54 +02:00
|
|
|
}
|
|
|
|
|
2021-05-07 14:54:12 +02:00
|
|
|
f_state->sent_pkts++;
|
2021-12-08 10:06:30 +01:00
|
|
|
f_state->sent_bytes += remaining_pkt_payload(pctx);
|
2021-05-07 14:54:12 +02:00
|
|
|
|
2021-04-15 14:13:54 +02:00
|
|
|
// Check if identfier is new
|
|
|
|
if (f_state->last_id == p_id.identifier)
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
2021-04-15 14:13:54 +02:00
|
|
|
f_state->last_id = p_id.identifier;
|
|
|
|
|
|
|
|
// Check rate-limit
|
2021-06-22 15:22:11 +02:00
|
|
|
if (now < f_state->last_timestamp ||
|
|
|
|
now - f_state->last_timestamp < config.rate_limit)
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
2021-04-15 14:13:54 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Updates attempt at creating timestamp, even if creation of timestamp
|
|
|
|
* fails (due to map being full). This should make the competition for
|
|
|
|
* the next available map slot somewhat fairer between heavy and sparse
|
|
|
|
* flows.
|
|
|
|
*/
|
2021-06-22 15:22:11 +02:00
|
|
|
f_state->last_timestamp = now;
|
|
|
|
bpf_map_update_elem(&packet_ts, &p_id, &now, BPF_NOEXIST);
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
2021-04-15 14:13:54 +02:00
|
|
|
}
|
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
/*
|
|
|
|
* Main function for handling the pping ingress path.
|
|
|
|
* Parses the packet for an identifer and tries to lookup a stored timestmap.
|
|
|
|
* If it finds a match, it pushes an rtt_event to the events buffer.
|
|
|
|
*/
|
|
|
|
static void pping_ingress(void *ctx, struct parsing_context *pctx)
|
2021-04-15 14:13:54 +02:00
|
|
|
{
|
|
|
|
struct packet_id p_id = { 0 };
|
2021-06-22 15:36:35 +02:00
|
|
|
struct flow_event fe;
|
2021-06-22 15:22:11 +02:00
|
|
|
struct rtt_event re = { 0 };
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
struct flow_state *f_state;
|
2021-12-08 10:06:30 +01:00
|
|
|
__u64 *p_ts;
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
__u64 now;
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
if (parse_packet_identifier(pctx, &p_id, &fe.event_info) < 0)
|
|
|
|
return;
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-05-07 14:54:12 +02:00
|
|
|
f_state = bpf_map_lookup_elem(&flow_state, &p_id.flow);
|
|
|
|
if (!f_state)
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
2021-05-07 14:54:12 +02:00
|
|
|
|
|
|
|
f_state->rec_pkts++;
|
2021-12-08 10:06:30 +01:00
|
|
|
f_state->rec_bytes += remaining_pkt_payload(pctx);
|
2021-05-07 14:54:12 +02:00
|
|
|
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
now = bpf_ktime_get_ns();
|
2021-04-15 14:13:54 +02:00
|
|
|
p_ts = bpf_map_lookup_elem(&packet_ts, &p_id);
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
if (!p_ts || now < *p_ts)
|
|
|
|
goto validflow_out;
|
2021-04-15 14:13:54 +02:00
|
|
|
|
2021-06-22 15:22:11 +02:00
|
|
|
re.rtt = now - *p_ts;
|
2021-05-06 17:54:31 +02:00
|
|
|
|
|
|
|
// Delete timestamp entry as soon as RTT is calculated
|
2021-04-15 14:13:54 +02:00
|
|
|
bpf_map_delete_elem(&packet_ts, &p_id);
|
|
|
|
|
2021-06-22 15:22:11 +02:00
|
|
|
if (f_state->min_rtt == 0 || re.rtt < f_state->min_rtt)
|
|
|
|
f_state->min_rtt = re.rtt;
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
// Fill event and push to perf-buffer
|
2021-06-22 15:22:11 +02:00
|
|
|
re.event_type = EVENT_TYPE_RTT;
|
|
|
|
re.timestamp = now;
|
|
|
|
re.min_rtt = f_state->min_rtt;
|
|
|
|
re.sent_pkts = f_state->sent_pkts;
|
|
|
|
re.sent_bytes = f_state->sent_bytes;
|
|
|
|
re.rec_pkts = f_state->rec_pkts;
|
|
|
|
re.rec_bytes = f_state->rec_bytes;
|
2021-12-08 10:06:30 +01:00
|
|
|
re.flow = p_id.flow;
|
2021-06-22 15:22:11 +02:00
|
|
|
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &re, sizeof(re));
|
2021-04-15 14:13:54 +02:00
|
|
|
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
validflow_out:
|
|
|
|
// Wait with deleting flow until having pushed final RTT message
|
2021-06-22 15:36:35 +02:00
|
|
|
if (fe.event_info.event == FLOW_EVENT_CLOSING && f_state) {
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
bpf_map_delete_elem(&flow_state, &p_id.flow);
|
2021-06-22 15:36:35 +02:00
|
|
|
fill_flow_event(&fe, now, &p_id.flow, EVENT_SOURCE_INGRESS);
|
2021-06-22 15:22:11 +02:00
|
|
|
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &fe,
|
|
|
|
sizeof(fe));
|
|
|
|
}
|
pping: Add timestamp and min-RTT to output
To add timestamp to output, push the timestamp when packet was
processed from kernel as part of the rtt-event. Also keep track of
minimum encountered RTT for each flow in kernel, and also push that as
part of the RTT-event.
Additionally, avoid pushing RTT messages at all if no flow-state
information can be found (due to ex. being deleted from egress side),
as no valid min-RTT can then be given. Furthermore, no longer delete
flow-information once seeing the FIN-flag on egress in order to keep
useful flow-state around for RTT-messages longer. Due to the
FIN-handshake process, it is sufficient if the ingress program deletes
the flow-state upon seeing FIN. However, still delete flow-state from
either ingress or egress upon seeing RST flag, as RST does not have a
handshake process allowing for delayed deletion.
While minimum RTT could also be tracked from the userspace process,
userspace is not aware of when the flow is closed so would have to add
additional logic to keep track of minimum RTT for each flow and
periodically clean them up. Furthermore, keeping RTT statistics in the
flow-state map is useful for implementing future features, such as an
RTT-based sampling interval. It would also be useful in case pping is
changed to no longer have a long-running userspace process printing
out all the calculated RTTs, but instead simply occasionally looks up
the RTT from the flow-state map.
Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
2021-04-29 18:55:06 +02:00
|
|
|
|
2021-12-08 10:06:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Programs
|
|
|
|
|
|
|
|
// Egress path using TC-BPF
|
2022-01-04 17:08:10 +01:00
|
|
|
SEC("tc")
|
2021-12-08 10:06:30 +01:00
|
|
|
int pping_tc_egress(struct __sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct parsing_context pctx = {
|
|
|
|
.data = (void *)(long)skb->data,
|
|
|
|
.data_end = (void *)(long)skb->data_end,
|
|
|
|
.pkt_len = skb->len,
|
|
|
|
.nh = { .pos = pctx.data },
|
|
|
|
.is_egress = true,
|
|
|
|
};
|
|
|
|
|
|
|
|
pping_egress(skb, &pctx);
|
|
|
|
|
2021-12-17 18:03:00 +01:00
|
|
|
return TC_ACT_UNSPEC;
|
2021-12-08 10:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ingress path using TC-BPF
|
2022-01-04 17:08:10 +01:00
|
|
|
SEC("tc")
|
2021-12-08 10:06:30 +01:00
|
|
|
int pping_tc_ingress(struct __sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct parsing_context pctx = {
|
|
|
|
.data = (void *)(long)skb->data,
|
|
|
|
.data_end = (void *)(long)skb->data_end,
|
|
|
|
.pkt_len = skb->len,
|
|
|
|
.nh = { .pos = pctx.data },
|
|
|
|
.is_egress = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
pping_ingress(skb, &pctx);
|
|
|
|
|
2021-12-17 18:03:00 +01:00
|
|
|
return TC_ACT_UNSPEC;
|
2021-12-08 10:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ingress path using XDP
|
2022-01-04 17:08:10 +01:00
|
|
|
SEC("xdp")
|
2021-12-08 10:06:30 +01:00
|
|
|
int pping_xdp_ingress(struct xdp_md *ctx)
|
|
|
|
{
|
|
|
|
struct parsing_context pctx = {
|
|
|
|
.data = (void *)(long)ctx->data,
|
|
|
|
.data_end = (void *)(long)ctx->data_end,
|
|
|
|
.pkt_len = pctx.data_end - pctx.data,
|
|
|
|
.nh = { .pos = pctx.data },
|
|
|
|
.is_egress = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
pping_ingress(ctx, &pctx);
|
|
|
|
|
2021-04-15 14:13:54 +02:00
|
|
|
return XDP_PASS;
|
|
|
|
}
|