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 11, 2024
1 parent cbe3140 commit bf898e5
Show file tree
Hide file tree
Showing 26 changed files with 558 additions and 68 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__
5 changes: 5 additions & 0 deletions bpf/flows.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
*/
#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, int dns_errno,
u64 len) {
aggregate_flow->packets += 1;
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
192 changes: 192 additions & 0 deletions bpf/pkt_transformation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* 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, u64 len,
u16 zone_id, u16 family) {
long ret = 0;
u64 current_time = bpf_ktime_get_ns();
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;

flow_metrics *aggregate_flow = bpf_map_lookup_elem(&aggregated_flows, id);
if (aggregate_flow != NULL) {
aggregate_flow->end_mono_time_ts = current_time;
parse_tuple(reply_t, &aggregate_flow->translated_flow, zone_id, family, true);
return ret;
}

// there is no matching flows so lets create new one and add the xlation
flow_metrics new_flow = {
.start_mono_time_ts = current_time,
.end_mono_time_ts = current_time,
.packets = 1,
.bytes = len,
.flags = flags,
};
parse_tuple(reply_t, &new_flow.translated_flow, zone_id, family, true);
ret = bpf_map_update_elem(&aggregated_flows, id, &new_flow, BPF_NOEXIST);
if (ret != 0) {
if (trace_messages && ret != -EEXIST) {
bpf_printk("error packet translation creating new flow %d\n", ret);
}
if (ret == -EEXIST) {
flow_metrics *aggregate_flow = bpf_map_lookup_elem(&aggregated_flows, id);
if (aggregate_flow != NULL) {
aggregate_flow->end_mono_time_ts = current_time;
parse_tuple(reply_t, &aggregate_flow->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;
u8 dscp = 0, protocol = 0;
long ret = 0;
u64 len = 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;

len = BPF_CORE_READ(skb, len);
id.if_index = BPF_CORE_READ(skb, skb_iif);
// read L2 info
core_fill_in_l2(skb, &id, &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);
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, len, 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__ */
11 changes: 11 additions & 0 deletions bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ typedef struct flow_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;
} __attribute__((packed)) translated_flow;
} __attribute__((packed)) flow_metrics;

// Force emitting struct pkt_drops into the ELF.
Expand Down Expand Up @@ -245,4 +253,7 @@ struct filter_value_t {
// Force emitting struct filter_value_t into the ELF.
const struct filter_value_t *unused9 __attribute__((unused));

// Force emitting struct translated_flow_t into the ELF.
const struct translated_flow_t *unused11 __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 @@ -137,6 +137,16 @@ func RecordToMap(fr *model.Record) config.GenericMap {
if len(fr.NetworkMonitorEventsMD) != 0 {
out["NetworkEvents"] = fr.NetworkMonitorEventsMD
}

if !model.AllZeroIP(model.IP(fr.Metrics.TranslatedFlow.Daddr)) &&
!model.AllZeroIP(model.IP(fr.Metrics.TranslatedFlow.Saddr)) {
out["ZoneId"] = fr.Metrics.TranslatedFlow.ZoneId
out["XlatSrcPort"] = fr.Metrics.TranslatedFlow.Sport
out["XlatDstPort"] = fr.Metrics.TranslatedFlow.Dport
out["XlatSrcAddr"] = model.IP(fr.Metrics.TranslatedFlow.Saddr).String()
out["XlatDstAddr"] = model.IP(fr.Metrics.TranslatedFlow.Daddr).String()
out["XlatIcmpId"] = fr.Metrics.TranslatedFlow.IcmpId
}
return out
}

Expand Down
17 changes: 17 additions & 0 deletions pkg/decode/decode_protobuf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ func TestPBFlowToMap(t *testing.T) {
},
},
},
Xlat: &pbflow.Xlat{
SrcAddr: &pbflow.IP{
IpFamily: &pbflow.IP_Ipv4{Ipv4: 0x01020304},
},
DstAddr: &pbflow.IP{
IpFamily: &pbflow.IP_Ipv4{Ipv4: 0x05060708},
},
SrcPort: 1,
DstPort: 2,
ZoneId: 100,
},
}

out := PBFlowToMap(flow)
Expand Down Expand Up @@ -139,6 +150,12 @@ func TestPBFlowToMap(t *testing.T) {
"Direction": "egress",
},
},
"XlatSrcAddr": "1.2.3.4",
"XlatDstAddr": "5.6.7.8",
"XlatSrcPort": uint16(1),
"XlatDstPort": uint16(2),
"ZoneId": uint16(100),
"XlatIcmpId": uint8(0),
}, out)

}
Loading

0 comments on commit bf898e5

Please sign in to comment.