ebpf_ipsec.c (6422B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | // eBPF IPsec // Jack Sangdahl // 28 November 2024 #include <vmlinux.h> #include <bpf/bpf_endian.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> #define TC_ACT_OK 0 #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define UDP_HDR_LEN 8 /* UDP header size */ #define UDP_PORT 12345 /* Port number to filter */ struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u64); } ingress_counter SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u64); } egress_counter SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u32); } shared_key SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u32); } key_ready SEC(".maps"); static __always_inline int handle_packet(struct __sk_buff *ctx, bool ingress); static __always_inline void count_packets(struct __sk_buff *ctx, bool ingress); static __always_inline __u32 generate_private_key(void); static __always_inline __u32 mod_exp(__u32 base, __u32 exp, __u32 mod); static __always_inline __u32 extract_public_key(void *data, void *data_end); static __always_inline void xor_encrypt_decrypt(void *payload, __u32 len, __u32 shared_key); static __always_inline __u32 derive_shared_key(__u32 public_key, __u32 private_key); static __always_inline void replace_with_public_key(void *data, void *data_end, __u32 public_key); static __always_inline void print_udp_content(void *data, void *data_end, struct udphdr *l4); /// @tchook {"ifindex":2, "attach_point":"BPF_TC_INGRESS"} /// @tcopts {"handle":1, "priority":1} SEC("tc") int tc_ingress(struct __sk_buff *ctx) { return handle_packet(ctx, true); } /// @tchook {"ifindex":2, "attach_point":"BPF_TC_EGRESS"} /// @tcopts {"handle":1, "priority":1} SEC("tc") int tc_egress(struct __sk_buff *ctx) { return handle_packet(ctx, false); } static __always_inline int handle_packet(struct __sk_buff *ctx, bool ingress) { void *data_end = (void *)(__u64)ctx->data_end; void *data = (void *)(__u64)ctx->data; struct ethhdr *l2; struct iphdr *l3; struct udphdr *l4; if (ctx->protocol != bpf_htons(ETH_P_IP)) return TC_ACT_OK; l2 = data; if ((void *)(l2 + 1) > data_end) return TC_ACT_OK; l3 = (struct iphdr *)(l2 + 1); if ((void *)(l3 + 1) > data_end) return TC_ACT_OK; if (l3->protocol != IPPROTO_UDP) return TC_ACT_OK; l4 = (struct udphdr *)(l3 + 1); if ((void *)(l4 + 1) > data_end) return TC_ACT_OK; if (bpf_ntohs(l4->source) != UDP_PORT && bpf_ntohs(l4->dest) != UDP_PORT) return TC_ACT_OK; bpf_printk("Received UDP packet on port 12345: src_port: %d, dst_port: %d", bpf_ntohs(l4->source), bpf_ntohs(l4->dest)); __u32 key = 0; __u32 *shared_key_ptr = bpf_map_lookup_elem(&shared_key, &key); __u32 *key_ready_ptr = bpf_map_lookup_elem(&key_ready, &key); if (shared_key_ptr && key_ready_ptr && *key_ready_ptr == 1) { void *payload = (void *)(l4 + 1); __u16 payload_len = bpf_ntohs(l4->len) - UDP_HDR_LEN; if (payload + payload_len <= data_end) if (ingress) { xor_encrypt_decrypt(payload, payload_len, *shared_key_ptr); bpf_printk("Decrypted UDP payload: %s", (char *)payload); } else { xor_encrypt_decrypt(payload, payload_len, *shared_key_ptr); bpf_printk("Encrypted UDP payload: %s", (char *)payload); } } count_packets(ctx, ingress); return TC_ACT_OK; } static __always_inline void count_packets(struct __sk_buff *ctx, bool ingress) { __u32 key = 0; __u64 *count; if (ingress) { count = bpf_map_lookup_elem(&ingress_counter, &key); if (count) { __sync_fetch_and_add(count, 1); bpf_printk("Ingress packet count: %llu", *count); } } else { count = bpf_map_lookup_elem(&egress_counter, &key); if (count) { __sync_fetch_and_add(count, 1); bpf_printk("Egress packet count: %llu", *count); } } } static __always_inline __u32 generate_private_key(void) { return bpf_get_prandom_u32() % 23; } static __always_inline void xor_encrypt_decrypt(void *payload, __u32 len, __u32 shared_key) { __u8 *data = (__u8 *)payload; for (__u32 i = 0; i < len; i++) data[i] ^= ((__u8 *)&shared_key)[i % sizeof(shared_key)]; } static __always_inline __u32 mod_exp(__u32 base, __u32 exp, __u32 mod) { __u32 result = 1; while (exp) { if (exp % 2 == 1) result = (result * base) % mod; base = (base * base) % mod; exp /= 2; } return result; } static __always_inline __u32 extract_public_key(void *data, void *data_end) { char buf[16] = {}; if ((data + 16) > data_end) return 0; bpf_probe_read_kernel_str(buf, sizeof(buf), data); unsigned long result; int ret = bpf_strtoul(buf, result, 10, 0); if (ret < 0) return ret; return result; } static __always_inline __u32 derive_shared_key(__u32 public_key, __u32 private_key) { return mod_exp(public_key, private_key, 23); } static __always_inline void replace_with_public_key(void *data, void *data_end, __u32 public_key) { char buf[16] = {}; int len = bpf_snprintf(buf, sizeof(buf), "%u", (__u64 *)&public_key, sizeof(public_key)); if ((data + len) <= data_end) { bpf_probe_read_kernel_str(data, len, buf); bpf_printk("Replaced payload with public key: %s", buf); } } static __always_inline void print_udp_content(void *data, void *data_end, struct udphdr *l4) { void *payload = (void *)(l4 + 1); if (payload >= data_end) return; __u16 payload_len = bpf_ntohs(l4->len) - UDP_HDR_LEN; if (payload + payload_len > data_end) return; char buf[33] = {}; __u32 len = payload_len < 32 ? payload_len : 32; if (payload + len <= data_end) { bpf_probe_read_kernel(buf, len, payload); buf[len] = '\0'; bpf_printk("UDP payload: %s", buf); } } char __license[] SEC("license") = "GPL"; |