ebpf/redirect.bpf.c (108 lines of code) (raw):

// Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT #include "bpf_helpers.h" #include "socket.h" // SEC("maps") #pragma clang section data = "maps" struct bpf_map_def policy_map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(destination_entry_t), .value_size = sizeof(destination_entry_t), .max_entries = 10}; #pragma clang section data = "maps" struct bpf_map_def skip_process_map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(sock_addr_skip_process_entry), .value_size = sizeof(sock_addr_skip_process_entry), .max_entries = 10}; #pragma clang section data = "maps" struct bpf_map_def audit_map = { .type = BPF_MAP_TYPE_LRU_HASH, // retain the latest records automatically .key_size = sizeof(sock_addr_audit_key_t), // source port and protocol .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. */ inline __attribute__((always_inline)) int check_skip_process_map_entry(uint32_t pid) { sock_addr_skip_process_entry key = {0}; key.pid = pid; // Find the entry in the skip_process map. sock_addr_skip_process_entry *skip_entry = bpf_map_lookup_elem(&skip_process_map, &key); return (skip_entry != NULL) ? 1 : 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) { uint64_t pid_tip = bpf_get_current_pid_tgid(); uint32_t pid = (uint32_t)(pid_tip >> 32); if (check_skip_process_map_entry(pid) == 1) { return 1; } sock_addr_audit_entry_t entry = {0}; entry.process_id = pid; entry.logon_id = bpf_get_current_logon_id(ctx); if (entry.logon_id == 0) { bpf_printk("Failed to get logon id."); } entry.is_admin = bpf_is_current_admin(ctx); if (entry.is_admin < 0) { bpf_printk("Failed to get admin status %u.", entry.is_admin); } 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 (source_port == 0) { int32_t result = bpf_sock_addr_set_redirect_context(ctx, &entry, sizeof(sock_addr_audit_entry_t)); if (result != 0) { bpf_printk("Failed to add audit entry to redirect context with result %u.", result); } else { bpf_printk("Added audit entry to redirect context."); } } else { sock_addr_audit_key_t key = {0}; key.protocol = ctx->protocol; key.source_port = source_port; uint64_t ret = bpf_map_update_elem(&audit_map, &key, &entry, 0); if (ret != 0) { bpf_printk("Failed to update audit map with results: %u.", ret); } else { bpf_printk("Added audit entry with source port: %u", source_port); } } return 0; } inline __attribute__((always_inline)) int authorize_v4(bpf_sock_addr_t *ctx) { destination_entry_t entry = {0}; entry.destination_ip.ipv4 = ctx->user_ip4; entry.destination_port = ctx->user_port; entry.protocol = ctx->protocol; // Find the entry in the policy map. destination_entry_t *policy = bpf_map_lookup_elem(&policy_map, &entry); if (policy != NULL) { bpf_printk("Found v4 proxy entry value: %u, %u", policy->destination_ip.ipv4, policy->destination_port); // update to the audit map before changing the destination ip and port. if (update_audit_map_entry(ctx) == 1) { bpf_printk("Found skip process entry, skip the redirection."); return BPF_SOCK_ADDR_VERDICT_PROCEED; } // 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); // } bpf_printk("redirecting to destination loopback ip."); ctx->user_ip4 = policy->destination_ip.ipv4; ctx->user_port = policy->destination_port; } return BPF_SOCK_ADDR_VERDICT_PROCEED; } // SEC("cgroup/connect4") #pragma clang section text = "cgroup/connect4" int authorize_connect4(bpf_sock_addr_t *ctx) { return authorize_v4(ctx); }