Skip to content
Open
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
58 changes: 46 additions & 12 deletions ebpf/redirect.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ struct bpf_map_def policy_map = {
.value_size = sizeof(destination_entry_t),
.max_entries = 10};

#pragma clang section data = "maps"
struct bpf_map_def config_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(uint32_t),
.value_size = sizeof(struct gpa_config_entry),
.max_entries = 1};

#pragma clang section data = "maps"
struct bpf_map_def skip_process_map = {
.type = BPF_MAP_TYPE_HASH,
Expand All @@ -26,6 +33,13 @@ struct bpf_map_def audit_map = {
.value_size = sizeof(sock_addr_audit_entry_t),
.max_entries = 1000};

#pragma clang section data = "maps"
struct bpf_map_def audit_only_map = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(sock_addr_audit_key_t),
.value_size = sizeof(sock_addr_audit_entry_t),
.max_entries = 1000};

/*
check the current pid in the skip_process map.
return 1 if found, otherwise return 0.
Expand All @@ -41,13 +55,21 @@ check_skip_process_map_entry(uint32_t pid)
return (skip_entry != NULL) ? 1 : 0;
}

inline __attribute__((always_inline)) int
local_ip_bind_monitor_only_enabled(void)
{
uint32_t key = GPA_CONFIG_LOCAL_IP_BIND_MONITOR_ONLY;
struct gpa_config_entry *entry = bpf_map_lookup_elem(&config_map, &key);
return entry != NULL && entry->enabled != 0;
}

/*
update audit map entry if not skip redirecting.
return 0 if the entry is updated, otherwise
return 1 if pid found in the skip_process_map.
*/
inline __attribute__((always_inline)) int
update_audit_map_entry(bpf_sock_addr_t *ctx)
update_audit_map_entry(bpf_sock_addr_t *ctx, int audit_only)
{
uint64_t pid_tip = bpf_get_current_pid_tgid();
uint32_t pid = (uint32_t)(pid_tip >> 32);
Expand Down Expand Up @@ -77,6 +99,19 @@ update_audit_map_entry(bpf_sock_addr_t *ctx)
entry.destination_ipv4 = ctx->user_ip4; // we only support ipv4 so far.
entry.destination_port = ctx->user_port;
uint16_t source_port = ctx->msg_src_port;
if (audit_only)
{
sock_addr_audit_key_t key = {0};
key.protocol = ctx->protocol;
key.source_port = source_port != 0 ? source_port : pid;
uint64_t ret = bpf_map_update_elem(&audit_only_map, &key, &entry, 0);
if (ret != 0)
{
bpf_printk("Failed to update audit-only map with results: %u.", ret);
}
return 0;
}

if (source_port == 0)
{
int32_t result = bpf_sock_addr_set_redirect_context(ctx, &entry, sizeof(sock_addr_audit_entry_t));
Expand Down Expand Up @@ -121,23 +156,22 @@ authorize_v4(bpf_sock_addr_t *ctx)
{
bpf_printk("Found v4 proxy entry value: %u, %u", policy->destination_ip.ipv4, policy->destination_port);

uint32_t source_ip = ctx->msg_src_ip4;
int audit_only = local_ip_bind_monitor_only_enabled() && // check the config map for localIPBindMonitorOnly
source_ip != 0 && (source_ip & 0xff) != 0x7f; // check if the source ip is set and not loopback

// update to the audit map before changing the destination ip and port.
if (update_audit_map_entry(ctx) == 1)
if (update_audit_map_entry(ctx, audit_only) == 1)
{
bpf_printk("Found skip process entry, skip the redirection.");
return BPF_SOCK_ADDR_VERDICT_PROCEED_SOFT;
}

// if (ctx->msg_src_ip4 == 0)
// {
// bpf_printk("Local/source ip is not set, redirect to loopback ip.");
// ctx->user_ip4 = policy->destination_ip.ipv4;
// }
// else
// {
// ctx->user_ip4 = ctx->msg_src_ip4;
// bpf_printk("Local/source ip is set, redirect to source ip:%u.", ctx->user_ip4);
// }
if (audit_only)
{
bpf_printk("Source address is explicitly bound, audit without redirecting.");
return BPF_SOCK_ADDR_VERDICT_PROCEED_SOFT;
}

bpf_printk("redirecting to destination loopback ip.");
ctx->user_ip4 = policy->destination_ip.ipv4;
Expand Down
65 changes: 50 additions & 15 deletions linux-ebpf/ebpf_cgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_endian.h>

#include "socket.h"

Expand All @@ -27,13 +28,27 @@ struct {
__uint(max_entries, 10);
} policy_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, struct gpa_config_entry);
__uint(max_entries, 1);
} config_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, struct gpa_audit_key); // source port and protocol
__type(value, struct gpa_audit_event); // audit event (canonical struct)
__uint(max_entries, 200); // LRU evicts oldest on overflow
} audit_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, struct gpa_audit_key);
__type(value, struct gpa_audit_event);
__uint(max_entries, 200);
} audit_only_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, __u64); // pid-tgid or socket cookie
Expand All @@ -57,13 +72,21 @@ check_skip_process_map_entry(__u32 pid)
return (skip_entry != NULL) ? 1 : 0;
}

static __always_inline int
local_ip_bind_monitor_only_enabled(void)
{
__u32 key = GPA_CONFIG_LOCAL_IP_BIND_MONITOR_ONLY;
struct gpa_config_entry *entry = bpf_map_lookup_elem(&config_map, &key);
return entry != NULL && entry->enabled != 0;
}

/*
update audit map entry if not skip redirecting.
return 0 if the entry is updated, otherwise
return 1 if pid found in the skip_process_map.
*/
static __always_inline int
update_local_map_entry(struct bpf_sock_addr *ctx)
update_local_map_entry(struct bpf_sock_addr *ctx, __u32 audit_only)
{
__u64 pid_tip = bpf_get_current_pid_tgid();
__u32 pid = (__u32)(pid_tip >> 32);
Expand All @@ -81,6 +104,7 @@ update_local_map_entry(struct bpf_sock_addr *ctx)
entry.destination_ipv4 = ctx->user_ip4; // we only support ipv4 so far.
entry.destination_port = ctx->user_port;
entry.protocol = ctx->protocol;
entry.audit_only = audit_only;

__u64 ret = bpf_map_update_elem(&local_map, &pid_tip, &entry, 0);
if (ret != 0)
Expand Down Expand Up @@ -109,27 +133,30 @@ authorize_v4(struct bpf_sock_addr *ctx)
{
bpf_printk("authorize_v4: Found v4 proxy entry value: %u, %u", policy->destination_ip.ipv4, policy->destination_port);

// At connect4, msg_src_ip4 is not valid; it is only populated for
// UDP sendmsg hooks. A concrete address set by bind(2) is available
// from the socket before TCP performs automatic source selection.
__u32 source_ip = ctx->sk != NULL ? ctx->sk->src_ip4 : 0;
__u32 source_ip_host = bpf_ntohl(source_ip);
__u32 audit_only = local_ip_bind_monitor_only_enabled() &&
source_ip != 0 &&
(source_ip_host & 0xff000000) != 0x7f000000;

// update to the audit map before changing the destination ip and port.
if (update_local_map_entry(ctx) == 1)
if (update_local_map_entry(ctx, audit_only) == 1)
{
bpf_printk("authorize_v4: Found skip process entry, skip the redirection.");
return BPF_SOCK_ADDR_VERDICT_PROCEED;
}

// TODO: check if the local ip is set.
// __u32 local_ip;
// __u64 read = bpf_probe_read_kernel(&local_ip, sizeof(__u32), &ctx->msg_src_ip4);
// if (read == 0 && local_ip != 0)
// {
// // read the local ip from the msg_src_ip4 successfully and ip is set.
// ctx->user_ip4 = local_ip;
// bpf_printk("authorize_v4: Local/source ip is set, redirect to source ip:%u.", local_ip);
// }
// else
if (audit_only)
{
ctx->user_ip4 = policy->destination_ip.ipv4;
bpf_printk("authorize_v4: Local/source ip is not set, redirect to loopback ip.");
bpf_printk("authorize_v4: Source address is explicitly bound, audit without redirecting.");
return BPF_SOCK_ADDR_VERDICT_PROCEED;
}

ctx->user_ip4 = policy->destination_ip.ipv4;
bpf_printk("authorize_v4: Local/source ip is not set, redirect to loopback ip.");
ctx->user_port = policy->destination_port;
}

Expand Down Expand Up @@ -157,7 +184,15 @@ update_audit_map_entry_sk(__u32 local_port, struct gpa_sock_addr_local_entry *lo
entry.destination_ipv4 = local_entry->destination_ipv4;
entry.destination_port = local_entry->destination_port;

__u64 ret = bpf_map_update_elem(&audit_map, &key, &entry, 0);
__u64 ret;
if (local_entry->audit_only)
{
ret = bpf_map_update_elem(&audit_only_map, &key, &entry, 0);
}
else
{
ret = bpf_map_update_elem(&audit_map, &key, &entry, 0);
}
if (ret != 0)
{
bpf_printk("update_audit_map_entry_sk: Failed to update audit map entry with results:%u.", ret);
Expand Down
1 change: 1 addition & 0 deletions proxy_agent/config/GuestProxyAgent.linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"fileLogLevel": "Trace",
"fileLogLevelForEvents": "Info",
"fileLogLevelForSystemEvents": "Info",
"localIPBindMonitorOnly": true,
"canonicalRequestMode": "Shadow"
}
1 change: 1 addition & 0 deletions proxy_agent/config/GuestProxyAgent.windows.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"fileLogLevel": "Trace",
"fileLogLevelForEvents": "Info",
"fileLogLevelForSystemEvents": "Info",
"localIPBindMonitorOnly": true,
"canonicalRequestMode": "Shadow"
}
18 changes: 17 additions & 1 deletion proxy_agent/src/common/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ pub fn get_enable_http_proxy_trace() -> bool {
SYSTEM_CONFIG.enableHttpProxyTrace.unwrap_or(false)
}

pub fn get_local_ip_bind_monitor_only() -> bool {
SYSTEM_CONFIG.get_local_ip_bind_monitor_only()
}

/// Rollout flag for the Innovation 2.1 canonical request pipeline.
///
/// Read from the optional `canonicalRequestMode` key in the GPA config
Expand Down Expand Up @@ -115,6 +119,8 @@ pub struct Config {
/// This is an optional config, mainly for manual debugging purpose
#[serde(skip_serializing_if = "Option::is_none")]
enableHttpProxyTrace: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
localIPBindMonitorOnly: Option<bool>,
/// Innovation 2.1 canonical request rollout flag.
/// Optional; absent or unparseable values resolve to
/// [`crate::proxy::canonical::CanonicalMode::Off`] so production
Expand Down Expand Up @@ -230,6 +236,10 @@ impl Config {
None
}

pub fn get_local_ip_bind_monitor_only(&self) -> bool {
self.localIPBindMonitorOnly.unwrap_or(false)
}

/// Resolve the canonical-request rollout flag.
///
/// Returns [`crate::proxy::canonical::CanonicalMode::Off`] when the
Expand Down Expand Up @@ -278,7 +288,7 @@ mod tests {
Err(err) => panic!("Failed to create folder: {}", err),
}
let config_file_path = temp_test_path.join("test_config.json");
let config = create_config_file(config_file_path);
let mut config = create_config_file(config_file_path);

assert_eq!(
r#"C:\logFolderName"#,
Expand Down Expand Up @@ -331,6 +341,10 @@ mod tests {
);
}

assert!(config.get_local_ip_bind_monitor_only());
config.localIPBindMonitorOnly = None;
assert!(!config.get_local_ip_bind_monitor_only());

assert_eq!(
proxy_agent_shared::logger::LoggerLevel::Info,
config.get_file_log_level_for_events().unwrap(),
Expand Down Expand Up @@ -364,6 +378,7 @@ mod tests {
"hostGAPluginSupport": 1,
"imdsSupport": 1,
"ebpfProgramName": "ebpfProgramName",
"localIPBindMonitorOnly": true,
"fileLogLevelForEvents": "Info",
"fileLogLevelForSystemEvents": "Info"
}"#
Expand All @@ -378,6 +393,7 @@ mod tests {
"hostGAPluginSupport": 1,
"imdsSupport": 1,
"ebpfProgramName": "ebpfProgramName",
"localIPBindMonitorOnly": true,
"fileLogLevelForEvents": "Info",
"fileLogLevelForSystemEvents": "Info"
}"#
Expand Down
Loading
Loading