Skip to content

Commit

Permalink
eBPF packet translation tracker
Browse files Browse the repository at this point in the history
Signed-off-by: Mohamed Mahmoud <[email protected]>
  • Loading branch information
msherif1234 committed Dec 12, 2024
1 parent 70003f2 commit 27d9a93
Show file tree
Hide file tree
Showing 27 changed files with 569 additions and 77 deletions.
1 change: 1 addition & 0 deletions bpf/configs.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ volatile const u8 enable_flows_filtering = 0;
volatile const u16 dns_port = 0;
volatile const u8 enable_network_events_monitoring = 0;
volatile const u8 network_events_monitoring_groupid = 0;
volatile const u8 enable_pkt_transformation_tracking = 0;
#endif //__CONFIGS_H__
4 changes: 4 additions & 0 deletions bpf/flows.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
* which runs inside flow_monitor. Is optional.
*/
#include "network_events_monitoring.h"
/*
* Defines packets transformation tracker
*/
#include "pkt_transformation.h"

static inline void update_existing_flow(flow_metrics *aggregate_flow, pkt_info *pkt, u64 len) {
bpf_spin_lock(&aggregate_flow->lock);
Expand Down
13 changes: 7 additions & 6 deletions bpf/headers/vmlinux_amd64.h
Original file line number Diff line number Diff line change
Expand Up @@ -22484,10 +22484,17 @@ union nf_conntrack_proto {

struct nf_ct_ext;

struct nf_conntrack_zone {
u16 id;
u8 flags;
u8 dir;
};

struct nf_conn {
struct nf_conntrack ct_general;
spinlock_t lock;
u32 timeout;
struct nf_conntrack_zone zone;
struct nf_conntrack_tuple_hash tuplehash[2];
long unsigned int status;
possible_net_t ct_net;
Expand All @@ -22499,12 +22506,6 @@ struct nf_conn {
union nf_conntrack_proto proto;
};

struct nf_conntrack_zone {
u16 id;
u8 flags;
u8 dir;
};

struct xt_action_param;

struct xt_mtchk_param;
Expand Down
13 changes: 7 additions & 6 deletions bpf/headers/vmlinux_arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -22582,10 +22582,17 @@ union nf_conntrack_proto {

struct nf_ct_ext;

struct nf_conntrack_zone {
u16 id;
u8 flags;
u8 dir;
};

struct nf_conn {
struct nf_conntrack ct_general;
spinlock_t lock;
u32 timeout;
struct nf_conntrack_zone zone;
struct nf_conntrack_tuple_hash tuplehash[2];
long unsigned int status;
possible_net_t ct_net;
Expand All @@ -22597,12 +22604,6 @@ struct nf_conn {
union nf_conntrack_proto proto;
};

struct nf_conntrack_zone {
u16 id;
u8 flags;
u8 dir;
};

struct icmphdr {
__u8 type;
__u8 code;
Expand Down
13 changes: 7 additions & 6 deletions bpf/headers/vmlinux_ppc64le.h
Original file line number Diff line number Diff line change
Expand Up @@ -67125,10 +67125,17 @@ union nf_conntrack_proto {

struct nf_ct_ext;

struct nf_conntrack_zone {
u16 id;
u8 flags;
u8 dir;
};

struct nf_conn {
struct nf_conntrack ct_general;
spinlock_t lock;
u32 timeout;
struct nf_conntrack_zone zone;
struct nf_conntrack_tuple_hash tuplehash[2];
long unsigned int status;
possible_net_t ct_net;
Expand Down Expand Up @@ -133097,12 +133104,6 @@ struct nf_hook_entries_rcu_head {
void *allocation;
};

struct nf_conntrack_zone {
u16 id;
u8 flags;
u8 dir;
};

struct nf_conntrack_tuple;

struct nf_ct_hook {
Expand Down
180 changes: 180 additions & 0 deletions bpf/pkt_transformation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* Packets Transformations tracker eBPF hooks.
*/

#ifndef __PKT_TRANSFORMATION_H__
#define __PKT_TRANSFORMATION_H__

#include "utils.h"

#define s6_addr in6_u.u6_addr8

static inline void dump_xlated_flow(struct translated_flow_t *flow) {
BPF_PRINTK("zone_id %d sport %d dport %d icmpId %d\n", flow->zone_id, flow->sport, flow->dport,
flow->icmp_id);
int i;
for (i = 0; i < IP_MAX_LEN; i += 4) {
BPF_PRINTK("scrIP[%d]:%d.%d.%d.%d\n", i, flow->saddr[0 + i], flow->saddr[1 + i],
flow->saddr[2 + i], flow->saddr[3 + i]);
}
for (i = 0; i < IP_MAX_LEN; i += 4) {
BPF_PRINTK("dstIP[%d]:%d.%d.%d.%d\n", i, flow->daddr[0 + i], flow->daddr[1 + i],
flow->daddr[2 + i], flow->daddr[3 + i]);
}
}

static inline void parse_tuple(struct nf_conntrack_tuple *t, struct translated_flow_t *flow,
u16 zone_id, u16 family, bool invert) {
__builtin_memset(flow, 0, sizeof(*flow));
if (invert) {
flow->dport = bpf_ntohs(t->src.u.all);
flow->sport = bpf_ntohs(t->dst.u.all);

switch (family) {
case AF_INET:
__builtin_memcpy(flow->saddr, ip4in6, sizeof(ip4in6));
__builtin_memcpy(flow->daddr, ip4in6, sizeof(ip4in6));
bpf_probe_read(flow->daddr + sizeof(ip4in6), sizeof(u32), &t->src.u3.in.s_addr);
bpf_probe_read(flow->saddr + sizeof(ip4in6), sizeof(u32), &t->dst.u3.in.s_addr);
break;

case AF_INET6:
bpf_probe_read(flow->daddr, IP_MAX_LEN, &t->src.u3.in6.s6_addr);
bpf_probe_read(flow->saddr, IP_MAX_LEN, &t->dst.u3.in6.s6_addr);
break;
}
} else {
flow->dport = bpf_ntohs(t->dst.u.all);
flow->sport = bpf_ntohs(t->src.u.all);

switch (family) {
case AF_INET:
__builtin_memcpy(flow->saddr, ip4in6, sizeof(ip4in6));
__builtin_memcpy(flow->daddr, ip4in6, sizeof(ip4in6));
bpf_probe_read(flow->daddr + sizeof(ip4in6), sizeof(u32), &t->dst.u3.in.s_addr);
bpf_probe_read(flow->saddr + sizeof(ip4in6), sizeof(u32), &t->src.u3.in.s_addr);
break;

case AF_INET6:
bpf_probe_read(flow->daddr, IP_MAX_LEN, &t->dst.u3.in6.s6_addr);
bpf_probe_read(flow->saddr, IP_MAX_LEN, &t->src.u3.in6.s6_addr);
break;
}
}
flow->icmp_id = t->src.u.icmp.id;
flow->zone_id = zone_id;
dump_xlated_flow(flow);
}

static inline long translate_lookup_and_update_flow(flow_id *id, u16 flags,
struct nf_conntrack_tuple *orig_t,
struct nf_conntrack_tuple *reply_t, u16 zone_id,
u16 family) {
long ret = 0;
struct translated_flow_t orig;

parse_tuple(orig_t, &orig, zone_id, family, false);

// update id with original flow info
__builtin_memcpy(id->src_ip, orig.saddr, IP_MAX_LEN);
__builtin_memcpy(id->dst_ip, orig.daddr, IP_MAX_LEN);
id->src_port = orig.sport;
id->dst_port = orig.dport;

additional_metrics *extra_metrics = bpf_map_lookup_elem(&additional_flow_metrics, id);
if (extra_metrics != NULL) {
parse_tuple(reply_t, &extra_metrics->translated_flow, zone_id, family, true);
return ret;
}

// there is no matching flows so lets create new one and add the xlation
additional_metrics new_extra_metrics = {};
parse_tuple(reply_t, &new_extra_metrics.translated_flow, zone_id, family, true);
ret = bpf_map_update_elem(&additional_flow_metrics, id, &new_extra_metrics, BPF_NOEXIST);
if (ret != 0) {
if (trace_messages && ret != -EEXIST) {
bpf_printk("error packet translation creating new flow %d\n", ret);
}
if (ret == -EEXIST) {
additional_metrics *extra_metrics = bpf_map_lookup_elem(&additional_flow_metrics, id);
if (extra_metrics != NULL) {
parse_tuple(reply_t, &extra_metrics->translated_flow, zone_id, family, true);
return 0;
}
}
}

return ret;
}

static inline int trace_nat_manip_pkt(struct nf_conn *ct, struct sk_buff *skb) {
struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
u16 family = 0, flags = 0, zone_id = 0, eth_protocol = 0;
;
u8 dscp = 0, protocol = 0;
long ret = 0;
flow_id id;

if (!enable_pkt_transformation_tracking) {
return 0;
}
__builtin_memset(&id, 0, sizeof(id));

bpf_probe_read(&tuplehash, sizeof(tuplehash), &ct->tuplehash);

bpf_probe_read(&zone_id, sizeof(zone_id), &ct->zone.id);
bpf_probe_read(&zone_id, sizeof(zone_id), &ct->zone.id);

struct nf_conntrack_tuple *orig_tuple = &tuplehash[IP_CT_DIR_ORIGINAL].tuple;
struct nf_conntrack_tuple *reply_tuple = &tuplehash[IP_CT_DIR_REPLY].tuple;

// read L2 info
core_fill_in_l2(skb, &eth_protocol, &family);

// read L3 info
core_fill_in_l3(skb, &id, family, &protocol, &dscp);

// read L4 info
switch (protocol) {
case IPPROTO_TCP:
core_fill_in_tcp(skb, &id, &flags);
break;
case IPPROTO_UDP:
core_fill_in_udp(skb, &id);
break;
case IPPROTO_SCTP:
core_fill_in_sctp(skb, &id);
break;
case IPPROTO_ICMP:
core_fill_in_icmpv4(skb, &id);
break;
case IPPROTO_ICMPV6:
core_fill_in_icmpv6(skb, &id);
break;
default:
fill_in_others_protocol(&id, protocol);
}

// check if this packet need to be filtered if filtering feature is enabled
bool skip = check_and_do_flow_filtering(&id, flags, 0, eth_protocol);
if (skip) {
return 0;
}

BPF_PRINTK("Xlat: protocol %d flags 0x%x family %d dscp %d\n", protocol, flags, family, dscp);

bpf_probe_read(&zone_id, sizeof(zone_id), &ct->zone.id);
ret = translate_lookup_and_update_flow(&id, flags, orig_tuple, reply_tuple, zone_id, family);

return ret;
}

SEC("kprobe/nf_nat_manip_pkt")
int BPF_KPROBE(track_nat_manip_pkt) {
struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
struct nf_conn *ct = (struct nf_conn *)PT_REGS_PARM2(ctx);

return trace_nat_manip_pkt(ct, skb);
}

#endif /* __PKT_TRANSFORMATION_H__ */
15 changes: 13 additions & 2 deletions bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ typedef struct additional_metrics_t {
u64 flow_rtt;
u8 network_events_idx;
u8 network_events[MAX_NETWORK_EVENTS][MAX_EVENT_MD];
struct translated_flow_t {
u8 saddr[IP_MAX_LEN];
u8 daddr[IP_MAX_LEN];
u16 sport;
u16 dport;
u16 zone_id;
u8 icmp_id;
} translated_flow;
} additional_metrics;

// Force emitting enums/structs into the ELF
Expand All @@ -132,6 +140,9 @@ const struct dns_record_t *unused4 __attribute__((unused));
// Force emitting enums/structs into the ELF
const struct pkt_drops_t *unused5 __attribute__((unused));

// Force emitting struct translated_flow_t into the ELF.
const struct translated_flow_t *unused6 __attribute__((unused));

// Attributes that uniquely identify a flow
typedef struct flow_id_t {
u8 direction;
Expand Down Expand Up @@ -209,7 +220,7 @@ typedef enum global_counters_key_t {
} global_counters_key;

// Force emitting enums/structs into the ELF
const enum global_counters_key_t *unused12 __attribute__((unused));
const enum global_counters_key_t *unused9 __attribute__((unused));

// filter key used as key to LPM map to filter out flows that are not interesting for the user
struct filter_key_t {
Expand Down Expand Up @@ -255,6 +266,6 @@ struct filter_value_t {
} __attribute__((packed));

// Force emitting enums/structs into the ELF
const struct filter_value_t *unused6 __attribute__((unused));
const struct filter_value_t *unused12 __attribute__((unused));

#endif /* __TYPES_H__ */
1 change: 1 addition & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func FlowsAgent(cfg *Config) (*Flows, error) {
EnableNetworkEventsMonitoring: cfg.EnableNetworkEventsMonitoring,
NetworkEventsMonitoringGroupID: cfg.NetworkEventsMonitoringGroupID,
EnableFlowFilter: cfg.EnableFlowFilter,
EnablePktTransformation: cfg.EnablePktTransformationTracking,
FilterConfig: &tracer.FilterConfig{
FilterAction: cfg.FilterAction,
FilterDirection: cfg.FilterDirection,
Expand Down
2 changes: 2 additions & 0 deletions pkg/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ type Config struct {
EnableNetworkEventsMonitoring bool `env:"ENABLE_NETWORK_EVENTS_MONITORING" envDefault:"false"`
// NetworkEventsMonitoringGroupID to allow ebpf hook to process samples for specific groupID and ignore the rest
NetworkEventsMonitoringGroupID int `env:"NETWORK_EVENTS_MONITORING_GROUP_ID" envDefault:"10"`
// EnablePktTransformationTracking allow tracking packets after transformation for example NAT, default is false.
EnablePktTransformationTracking bool `env:"ENABLE_PKT_TRANSFORMATION" envDefault:"false"`

/* Deprecated configs are listed below this line
* See manageDeprecatedConfigs function for details
Expand Down
10 changes: 10 additions & 0 deletions pkg/decode/decode_protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ func RecordToMap(fr *model.Record) config.GenericMap {
out["PktDropLatestState"] = TCPStateToStr(uint32(fr.Metrics.AdditionalMetrics.PktDrops.LatestState))
out["PktDropLatestDropCause"] = PktDropCauseToStr(fr.Metrics.AdditionalMetrics.PktDrops.LatestDropCause)
}
if !model.AllZeroIP(model.IP(fr.Metrics.AdditionalMetrics.TranslatedFlow.Daddr)) &&
!model.AllZeroIP(model.IP(fr.Metrics.AdditionalMetrics.TranslatedFlow.Saddr)) {
out["ZoneId"] = fr.Metrics.AdditionalMetrics.TranslatedFlow.ZoneId
out["XlatSrcPort"] = fr.Metrics.AdditionalMetrics.TranslatedFlow.Sport
out["XlatDstPort"] = fr.Metrics.AdditionalMetrics.TranslatedFlow.Dport
out["XlatSrcAddr"] = model.IP(fr.Metrics.AdditionalMetrics.TranslatedFlow.Saddr).String()
out["XlatDstAddr"] = model.IP(fr.Metrics.AdditionalMetrics.TranslatedFlow.Daddr).String()
out["XlatIcmpId"] = fr.Metrics.AdditionalMetrics.TranslatedFlow.IcmpId
}
}

if fr.TimeFlowRtt != 0 {
Expand All @@ -140,6 +149,7 @@ func RecordToMap(fr *model.Record) config.GenericMap {
if len(fr.NetworkMonitorEventsMD) != 0 {
out["NetworkEvents"] = fr.NetworkMonitorEventsMD
}

return out
}

Expand Down
Loading

0 comments on commit 27d9a93

Please sign in to comment.