Skip to content

NETOBSERV-2198: IPsec support #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .mk/bc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ define PROGRAMS
"tcp_rcv_kprobe":"kprobe",
"kfree_skb":"tracepoint",
"network_events_monitoring":"kprobe",
"track_nat_manip_pkt":"kprobe"
"track_nat_manip_pkt":"kprobe",
"xfrm_input_kprobe": "kprobe",
"xfrm_input_kretprobe": "kretprobe",
"xfrm_output_kprobe": "kprobe",
"xfrm_output_kretprobe": "kretprobe"
}
endef

Expand All @@ -32,7 +36,9 @@ define MAPS
"dns_flows":"hash",
"global_counters":"per_cpu_array",
"filter_map":"lpm_trie",
"peer_filter_map":"lpm_trie"
"peer_filter_map":"lpm_trie",
"ipsec_ingress_map":"hash",
"ipsec_egress_map":"hash"
}
endef

Expand Down
1 change: 1 addition & 0 deletions bpf/configs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ 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_translation_tracking = 0;
volatile const u8 enable_ipsec = 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 @@ -52,6 +52,11 @@
*/
#include "pkt_translation.h"

/*
* Defines ipsec tracker
*/
#include "ipsec.h"

// return 0 on success, 1 if capacity reached
static __always_inline int add_observed_intf(flow_metrics *value, pkt_info *pkt, u32 if_index,
u8 direction) {
Expand Down
190 changes: 190 additions & 0 deletions bpf/ipsec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* IPsec monitoring kretprobe eBPF hook.
*/

#ifndef __IPSEC_H__
#define __IPSEC_H__

#include "utils.h"

static inline int ipsec_lookup_and_update_flow(flow_id *id, int flow_encrypted_ret,
u16 eth_protocol) {
additional_metrics *extra_metrics = bpf_map_lookup_elem(&additional_flow_metrics, id);
if (extra_metrics != NULL) {
extra_metrics->end_mono_time_ts = bpf_ktime_get_ns();
extra_metrics->eth_protocol = eth_protocol;
if (flow_encrypted_ret != 0) {
extra_metrics->flow_encrypted_ret = flow_encrypted_ret;
if (extra_metrics->flow_encrypted) {
extra_metrics->flow_encrypted = false;
}
}
return 0;
}
return -1;
}

static inline int update_flow_with_ipsec_return(int flow_encrypted_ret, direction dir) {
u64 pid_tgid = bpf_get_current_pid_tgid();
u16 eth_protocol = 0;
flow_id *id = NULL;
int ret = 0;

if (dir == INGRESS) {
id = bpf_map_lookup_elem(&ipsec_ingress_map, &pid_tgid);
} else {
id = bpf_map_lookup_elem(&ipsec_egress_map, &pid_tgid);
}

if (!id) {
BPF_PRINTK("ipsec flow id not found in dir: %d", dir);
return 0;
}

if (is_ipv4(id->src_ip)) {
eth_protocol = ETH_P_IP;
} else {
eth_protocol = ETH_P_IPV6;
}

BPF_PRINTK("found encrypted flow dir: %d encrypted: %d\n", dir,
flow_encrypted_ret == 0 ? true : false);

// update flow with ipsec info
ret = ipsec_lookup_and_update_flow(id, flow_encrypted_ret, eth_protocol);
if (ret == 0) {
goto end;
}

u64 current_time = bpf_ktime_get_ns();
additional_metrics new_flow;
__builtin_memset(&new_flow, 0, sizeof(new_flow));
new_flow.start_mono_time_ts = current_time;
new_flow.end_mono_time_ts = current_time;
new_flow.eth_protocol = eth_protocol;
new_flow.flow_encrypted_ret = flow_encrypted_ret;
new_flow.flow_encrypted = flow_encrypted_ret == 0 ? true : false;
ret = bpf_map_update_elem(&additional_flow_metrics, id, &new_flow, BPF_NOEXIST);
if (ret != 0) {
if (ret != -EEXIST) {
BPF_PRINTK("error ipsec creating flow err: %d\n", ret);
}
if (ret == -EEXIST) {
ret = ipsec_lookup_and_update_flow(id, flow_encrypted_ret, eth_protocol);
if (ret != 0) {
BPF_PRINTK("error ipsec updating an existing flow err: %d\n", ret);
}
}
}
end:
if (dir == INGRESS) {
bpf_map_delete_elem(&ipsec_ingress_map, &pid_tgid);
} else {
bpf_map_delete_elem(&ipsec_egress_map, &pid_tgid);
}
return 0;
}

static inline int enter_xfrm_func(struct sk_buff *skb, direction dir) {
u64 pid_tgid = bpf_get_current_pid_tgid();
u16 family = 0, flags = 0, eth_protocol = 0;
u8 dscp = 0, protocol = 0;
flow_id id;
int ret = 0;

__builtin_memset(&id, 0, sizeof(id));

u32 if_index = BPF_CORE_READ(skb, skb_iif);

// 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, NULL, dir);
if (skip) {
return 0;
}

BPF_PRINTK("Enter xfrm dir: %d protocol: %d family: %d if_index: %d \n", dir, protocol, family,
if_index);

if (dir == INGRESS) {
ret = bpf_map_update_elem(&ipsec_ingress_map, &pid_tgid, &id, BPF_NOEXIST);
} else {
ret = bpf_map_update_elem(&ipsec_egress_map, &pid_tgid, &id, BPF_NOEXIST);
}
if (ret != 0) {
if (trace_messages) {
BPF_PRINTK("error creating new ipsec map dir: %d err: %d\n", dir, ret);
}
}
return 0;
}

SEC("kprobe/xfrm_input")
int BPF_KPROBE(xfrm_input_kprobe) {
if (do_sampling == 0 || enable_ipsec == 0) {
return 0;
}
struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
if (!skb) {
return 0;
}
return enter_xfrm_func(skb, INGRESS);
}

SEC("kretprobe/xfrm_input")
int BPF_KRETPROBE(xfrm_input_kretprobe) {
if (do_sampling == 0 || enable_ipsec == 0) {
return 0;
}
int xfrm_ret = PT_REGS_RC(ctx);
return update_flow_with_ipsec_return(xfrm_ret, INGRESS);
}

SEC("kprobe/xfrm_output")
int BPF_KPROBE(xfrm_output_kprobe) {
if (do_sampling == 0 || enable_ipsec == 0) {
return 0;
}
struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM2(ctx);
if (!skb) {
return 0;
}
return enter_xfrm_func(skb, EGRESS);
}

SEC("kretprobe/xfrm_output")
int BPF_KRETPROBE(xfrm_output_kretprobe) {
if (do_sampling == 0 || enable_ipsec == 0) {
return 0;
}
int xfrm_ret = PT_REGS_RC(ctx);
return update_flow_with_ipsec_return(xfrm_ret, EGRESS);
}

#endif /* __IPSEC_H__ */
20 changes: 20 additions & 0 deletions bpf/maps_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,24 @@ struct {
__uint(pinning, LIBBPF_PIN_BY_NAME);
} peer_filter_map SEC(".maps");

// HashMap to store ingress flowid to be able to retrieve them from kretprobe hook
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1 << 20); // Will take around 64MB of space.
__type(key, u64);
__type(value, flow_id);
__uint(map_flags, BPF_F_NO_PREALLOC);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} ipsec_ingress_map SEC(".maps");

// HashMap to store egress flowid to be able to retrieve them from kretprobe hook
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1 << 20); // Will take around 64MB of space.
__type(key, u64);
__type(value, flow_id);
__uint(map_flags, BPF_F_NO_PREALLOC);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} ipsec_egress_map SEC(".maps");

#endif //__MAPS_DEFINITION_H__
2 changes: 2 additions & 0 deletions bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ typedef struct additional_metrics_t {
} translated_flow;
u16 eth_protocol;
u8 network_events_idx;
bool flow_encrypted;
u8 flow_encrypted_ret;
} additional_metrics;

// Force emitting enums/structs into the ELF
Expand Down
8 changes: 8 additions & 0 deletions bpf/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,12 @@ static inline bool is_transport_protocol(u8 protocol) {
return false;
}

static inline bool is_ipv4(u8 *ip) {
for (int i = 0; i < IP_MAX_LEN; i++) {
if (ip[i] == 255) {
return true;
}
}
return false;
}
#endif // __UTILS_H__
1 change: 1 addition & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ func FlowsAgent(cfg *Config) (*Flows, error) {
EnablePktTranslation: cfg.EnablePktTranslationTracking,
UseEbpfManager: cfg.EbpfProgramManagerMode,
BpfManBpfFSPath: cfg.BpfManBpfFSPath,
EnableIPsecTracker: cfg.EnableIPsecTracking,
FilterConfig: filterRules,
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ type Config struct {
BpfManBpfFSPath string `env:"BPFMAN_BPF_FS_PATH" envDefault:"/run/netobserv/maps"`
// EnableUDNMapping to allow mapping pod's interface to udn label
EnableUDNMapping bool `env:"ENABLE_UDN_MAPPING" envDefault:"false"`
// EnableIPsecTracking enable tracking IPsec flows encryption
EnableIPsecTracking bool `env:"ENABLE_IPSEC_TRACKING" envDefault:"false"`
/* Deprecated configs are listed below this line
* See manageDeprecatedConfigs function for details
*/
Expand Down
4 changes: 4 additions & 0 deletions pkg/decode/decode_protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ func RecordToMap(fr *model.Record) config.GenericMap {
out["XlatSrcAddr"] = model.IP(fr.Metrics.AdditionalMetrics.TranslatedFlow.Saddr).String()
out["XlatDstAddr"] = model.IP(fr.Metrics.AdditionalMetrics.TranslatedFlow.Daddr).String()
}
if fr.Metrics.AdditionalMetrics.FlowEncrypted || fr.Metrics.AdditionalMetrics.FlowEncryptedRet != 0 {
out["IPSecSuccess"] = fr.Metrics.AdditionalMetrics.FlowEncrypted
out["IPSecRetCode"] = fr.Metrics.AdditionalMetrics.FlowEncryptedRet
}
}

if fr.TimeFlowRtt != 0 {
Expand Down
14 changes: 9 additions & 5 deletions pkg/decode/decode_protobuf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ func TestPBFlowToMap(t *testing.T) {
DstPort: 2,
ZoneId: 100,
},
FlowEncrypted: 1,
FlowEncryptedRet: 0,
}

out := PBFlowToMap(flow)
Expand Down Expand Up @@ -146,10 +148,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),
"XlatSrcAddr": "1.2.3.4",
"XlatDstAddr": "5.6.7.8",
"XlatSrcPort": uint16(1),
"XlatDstPort": uint16(2),
"ZoneId": uint16(100),
"IPSecSuccess": true,
"IPSecRetCode": uint8(0),
}, out)
}
Loading