aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Sangdahl <[email protected]>2024-12-15 22:37:30 -0700
committerJack Sangdahl <[email protected]>2024-12-15 22:37:30 -0700
commit9c5b2396436398a3e205831c007ecc4e2b2c604e (patch)
tree763566660faf574ab04fba71b82e3c32c0daac0d
parentd3b273cbebd8a4d6b890d11bd4ae505b45abf739 (diff)
downloadecen5273-9c5b2396436398a3e205831c007ecc4e2b2c604e.tar.gz
ecen5273-9c5b2396436398a3e205831c007ecc4e2b2c604e.zip
Programming Assignment 4: eBPF IPsecHEADmaster
Signed-off-by: Jack Sangdahl <[email protected]>
-rw-r--r--pa4_ipsec/.gitignore4
-rw-r--r--pa4_ipsec/ebpf_ipsec.c194
-rw-r--r--pa4_ipsec/img/example.pngbin0 -> 85517 bytes
-rw-r--r--pa4_ipsec/img/p1.pngbin0 -> 298436 bytes
-rw-r--r--pa4_ipsec/img/p2.pngbin0 -> 363381 bytes
-rw-r--r--pa4_ipsec/img/p3.pngbin0 -> 448512 bytes
-rw-r--r--pa4_ipsec/ingress.c36
-rw-r--r--pa4_ipsec/readme.md102
-rw-r--r--readme.md2
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
new file mode 100644
index 0000000..2a90248
--- /dev/null
+++ b/pa4_ipsec/img/example.png
Binary files differ
diff --git a/pa4_ipsec/img/p1.png b/pa4_ipsec/img/p1.png
new file mode 100644
index 0000000..6c887ce
--- /dev/null
+++ b/pa4_ipsec/img/p1.png
Binary files differ
diff --git a/pa4_ipsec/img/p2.png b/pa4_ipsec/img/p2.png
new file mode 100644
index 0000000..8b218b4
--- /dev/null
+++ b/pa4_ipsec/img/p2.png
Binary files differ
diff --git a/pa4_ipsec/img/p3.png b/pa4_ipsec/img/p3.png
new file mode 100644
index 0000000..d7d2f3a
--- /dev/null
+++ b/pa4_ipsec/img/p3.png
Binary files differ
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
+```
diff --git a/readme.md b/readme.md
index 727ba4d..16290fd 100644
--- a/readme.md
+++ b/readme.md
@@ -2,3 +2,5 @@
### Labs
1. File Transfer
2. Web Server
+3. Caching Proxy
+4. eBPF IPsec