diff options
author | Jack Sangdahl <[email protected]> | 2024-12-15 22:37:30 -0700 |
---|---|---|
committer | Jack Sangdahl <[email protected]> | 2024-12-15 22:37:30 -0700 |
commit | 9c5b2396436398a3e205831c007ecc4e2b2c604e (patch) | |
tree | 763566660faf574ab04fba71b82e3c32c0daac0d | |
parent | d3b273cbebd8a4d6b890d11bd4ae505b45abf739 (diff) | |
download | ecen5273-master.tar.gz ecen5273-master.zip |
Signed-off-by: Jack Sangdahl <[email protected]>
-rw-r--r-- | pa4_ipsec/.gitignore | 4 | ||||
-rw-r--r-- | pa4_ipsec/ebpf_ipsec.c | 194 | ||||
-rw-r--r-- | pa4_ipsec/img/example.png | bin | 0 -> 85517 bytes | |||
-rw-r--r-- | pa4_ipsec/img/p1.png | bin | 0 -> 298436 bytes | |||
-rw-r--r-- | pa4_ipsec/img/p2.png | bin | 0 -> 363381 bytes | |||
-rw-r--r-- | pa4_ipsec/img/p3.png | bin | 0 -> 448512 bytes | |||
-rw-r--r-- | pa4_ipsec/ingress.c | 36 | ||||
-rw-r--r-- | pa4_ipsec/readme.md | 102 | ||||
-rw-r--r-- | readme.md | 2 |
9 files changed, 338 insertions, 0 deletions
diff --git a/pa4_ipsec/.gitignore b/pa4_ipsec/.gitignore new file mode 100644 index 0000000..11edefe --- /dev/null +++ b/pa4_ipsec/.gitignore @@ -0,0 +1,4 @@ +*.o +*.json +ecc +ecli diff --git a/pa4_ipsec/ebpf_ipsec.c b/pa4_ipsec/ebpf_ipsec.c new file mode 100644 index 0000000..1a2800a --- /dev/null +++ b/pa4_ipsec/ebpf_ipsec.c @@ -0,0 +1,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"; diff --git a/pa4_ipsec/img/example.png b/pa4_ipsec/img/example.png Binary files differnew file mode 100644 index 0000000..2a90248 --- /dev/null +++ b/pa4_ipsec/img/example.png diff --git a/pa4_ipsec/img/p1.png b/pa4_ipsec/img/p1.png Binary files differnew file mode 100644 index 0000000..6c887ce --- /dev/null +++ b/pa4_ipsec/img/p1.png diff --git a/pa4_ipsec/img/p2.png b/pa4_ipsec/img/p2.png Binary files differnew file mode 100644 index 0000000..8b218b4 --- /dev/null +++ b/pa4_ipsec/img/p2.png diff --git a/pa4_ipsec/img/p3.png b/pa4_ipsec/img/p3.png Binary files differnew file mode 100644 index 0000000..d7d2f3a --- /dev/null +++ b/pa4_ipsec/img/p3.png diff --git a/pa4_ipsec/ingress.c b/pa4_ipsec/ingress.c new file mode 100644 index 0000000..1021687 --- /dev/null +++ b/pa4_ipsec/ingress.c @@ -0,0 +1,36 @@ +// ingress.c +#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 */ + +/// @tchook {"ifindex":2, "attach_point":"BPF_TC_INGRESS"} +/// @tcopts {"handle":1, "priority":1} +SEC("tc") +int tc_ingress(struct __sk_buff *ctx) { + void *data_end = (void *)(__u64)ctx->data_end; + void *data = (void *)(__u64)ctx->data; + struct ethhdr *l2; + struct iphdr *l3; + + 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; + + bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), + l3->ttl); + return TC_ACT_OK; +} + +char __license[] SEC("license") = "GPL"; diff --git a/pa4_ipsec/readme.md b/pa4_ipsec/readme.md new file mode 100644 index 0000000..3b06864 --- /dev/null +++ b/pa4_ipsec/readme.md @@ -0,0 +1,102 @@ +## PA4 IPsec + +### Introduction +In today's digital world, secure communication is vital to protect +sensitive information from malicious actors. This assignment explores +the implementation of IPsec (internet protocol security) using eBPF +(extended Berkeley Packet Filter) to safeguard the communication +between Alice and Bob. The goal is to prevent Eve, an eavesdropper, +from intercepting and sniffing their communication. This approach +ensures that the transmitted data remains confidential, meaning Eve +cannot see the message content. As an example, without IPsec, Eve can +indeed see a conversation between Alice and Bob as below: + +![example](img/example.png) + +### Development setup using Multipass +- Follow instructions here to install Multipass: +[multipass.run](https://multipass.run/install) +- Eve will be your local machine (eg. laptop), Alice and Bob are two +different Multipass VMs. +```shell +## Launch Alice VM +$ multipass launch 22.04 --name alice --disk 10G --memory 4G --cpus 2 +## Launch Bob VM +$ multipass launch 22.04 --name bob --disk 10G --memory 4G --cpus 2 +## Get information (e.g., IP addresses) about Alice and Bob VMs +$ multipass info +## Open shell on Alice machine +$ multipass shell alice +## Open shell on Bob machine +$ multipass shell bob +``` +- Recreate the scenario in the introduction: + - On Eve machine (`localhost`): + `$ sudo tcpdump -i <broadcast interface> udp port 12345 -A`, + broadcast interface can be obtained on local machine with IP + `10.62.70.1`. + - On Alice VM: `$ nc -ul 12345` + - On Bob VM: `$ nc -u -p 12345 <Alice IP> 12345` + +### Key Components +1. Capture ingress & egress UDP packets port 12345 +2. Public key exchange share share key derive +3. Packet encryption and decryption +4. Message statistics + +### Capture ingress & egress UDP packets +Your very first task is to modify the provided [`ingress.c`](ingress.c) +file so that it can do the following. +- Capture both ingress and egress packets +- Only process UDP packets using port 12345 +- Print the UDP payload content to `trace_pipe` + +The expected result of this section is as follows: +![p1](img/p1.png) + +### Public key exchange and share key derive +In this assignment, we will use the Diffie-Hellman key exchange +algorithm. +- To simplify the problem, we can keep the prime modulus `p = 23` and +generator `g = 5` as fixed values. However, Alice and Bob's private +number `a` and `b`, respecitvely, need to be randomly generated every +time the eBPF program is run. +- Since eBPF cannot generate the UDP packet on its own but rather +capture and manipulate packets passing through a network interface, we +need a way to exchange their public key `A` and `B` via the first +message sent from Alice and Bob. For example: + - From `netcat`, Alice and Bob send a message with a blank space at + the beginning of the conversation. + - From `eBPF`, you can replace the first egress message content + with their public key. +- After receiving the public key from their partner, Alice and Bob need +to derive the shared key using their partner's public key and their +private key. + +The expected result of this section is as follows: +![p2](img/p2.png) + +### Message encryption and decryption +After Alice and Bob derive the shared key, meaning from the second +message onward, XOR encryption/decryption can be applied to keep Eve +from sniffing the message content. +- Encrypt egress messages +- Decrypt ingress messages + +The expected result of this section is as follows. Note that Alice and +Bob send a message with a blank space/line to initiate the key exchange +process at the beginning of their conversation. +![p3](img/p3.png) + +### Message statistics +We want to know between Alice and Bob, who is more talkative? +- Count the number of egress messages and show the log at `trace_pipe` +- Count the number of ingress messages and show the log at `trace_pipe` + +### Usage +``` +$ ecc ebpf_ipsec.c +# ecli run package.json +# echo 1 > /sys/kernel/debug/tracing/tracing_on +# cat /sys/kernel/debug/tracing/trace_pipe +``` @@ -2,3 +2,5 @@ ### Labs 1. File Transfer 2. Web Server +3. Caching Proxy +4. eBPF IPsec |