Skip to content
Draft
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
54 changes: 47 additions & 7 deletions scone_demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ CSP2_IP1=10.0.21.2
CSP2_IP2=10.0.31.1
CAP_IP=10.0.31.2

# IPv6 addresses
USER_IPV6=2001:db8:11::1
CSP1_IPV6_1=2001:db8:11::2
CSP1_IPV6_2=2001:db8:21::1
CSP2_IPV6_1=2001:db8:21::2
CSP2_IPV6_2=2001:db8:31::1
CAP_IPV6=2001:db8:31::2

PLAIN_UDP_PORT=10000
NON_SCONE_PORT=20000
SCONE_PORT=60000
Expand All @@ -63,33 +71,51 @@ ip netns add $SCONE_USER_NS
ip netns add $SCONE_CSP1_NS
ip netns add $SCONE_CSP2_NS
ip netns add $SCONE_CAP_NS
echo " Enabling IPv4 and IPv6 forwarding."
ip netns exec $SCONE_USER_NS sysctl -q -w net.ipv4.ip_forward=1
ip netns exec $SCONE_CSP1_NS sysctl -q -w net.ipv4.ip_forward=1
ip netns exec $SCONE_CSP2_NS sysctl -q -w net.ipv4.ip_forward=1
ip netns exec $SCONE_CAP_NS sysctl -q -w net.ipv4.ip_forward=1
ip netns exec $SCONE_USER_NS sysctl -q -w net.ipv6.conf.all.forwarding=1
ip netns exec $SCONE_CSP1_NS sysctl -q -w net.ipv6.conf.all.forwarding=1
ip netns exec $SCONE_CSP2_NS sysctl -q -w net.ipv6.conf.all.forwarding=1
ip netns exec $SCONE_CAP_NS sysctl -q -w net.ipv6.conf.all.forwarding=1
echo " Creating virtual links."
ip -n $SCONE_USER_NS link add name $USER_CSP_LINK type veth peer netns $SCONE_CSP1_NS name $CSP_USER_LINK
ip -n $SCONE_CSP1_NS link add name $CSP_CSP_1_LINK type veth peer netns $SCONE_CSP2_NS name $CSP_CSP_2_LINK
ip -n $SCONE_CSP2_NS link add name $CSP_CAP_LINK type veth peer netns $SCONE_CAP_NS name $CAP_CSP_LINK
echo " Assigning IP addresses and bringing up interfaces."
echo " Assigning IPv4 and IPv6 addresses and bringing up interfaces."
ip -n $SCONE_USER_NS address add $USER_IP/30 dev $USER_CSP_LINK
ip -n $SCONE_CSP1_NS address add $CSP1_IP1/30 dev $CSP_USER_LINK
ip -n $SCONE_CSP1_NS address add $CSP1_IP2/30 dev $CSP_CSP_1_LINK
ip -n $SCONE_CSP2_NS address add $CSP2_IP1/30 dev $CSP_CSP_2_LINK
ip -n $SCONE_CSP2_NS address add $CSP2_IP2/30 dev $CSP_CAP_LINK
ip -n $SCONE_CAP_NS address add $CAP_IP/30 dev $CAP_CSP_LINK
ip -n $SCONE_USER_NS address add $USER_IPV6/64 dev $USER_CSP_LINK
ip -n $SCONE_CSP1_NS address add $CSP1_IPV6_1/64 dev $CSP_USER_LINK
ip -n $SCONE_CSP1_NS address add $CSP1_IPV6_2/64 dev $CSP_CSP_1_LINK
ip -n $SCONE_CSP2_NS address add $CSP2_IPV6_1/64 dev $CSP_CSP_2_LINK
ip -n $SCONE_CSP2_NS address add $CSP2_IPV6_2/64 dev $CSP_CAP_LINK
ip -n $SCONE_CAP_NS address add $CAP_IPV6/64 dev $CAP_CSP_LINK
ip -n $SCONE_USER_NS link set $USER_CSP_LINK up
ip -n $SCONE_CSP1_NS link set $CSP_USER_LINK up
ip -n $SCONE_CSP1_NS link set $CSP_CSP_1_LINK up
ip -n $SCONE_CSP2_NS link set $CSP_CSP_2_LINK up
ip -n $SCONE_CSP2_NS link set $CSP_CAP_LINK up
ip -n $SCONE_CAP_NS link set $CAP_CSP_LINK up
echo " Setting routes."
echo " Setting IPv4 and IPv6 routes."
ip -n $SCONE_USER_NS route add $CAP_IP/32 via $CSP1_IP1
ip -n $SCONE_CSP1_NS route add $CAP_IP/32 via $CSP2_IP1
ip -n $SCONE_CSP2_NS route add $USER_IP/32 via $CSP1_IP2
ip -n $SCONE_CAP_NS route add $USER_IP/32 via $CSP2_IP2
ip -n $SCONE_USER_NS -6 route add $CAP_IPV6/128 via $CSP1_IPV6_1
ip -n $SCONE_CSP1_NS -6 route add $CAP_IPV6/128 via $CSP2_IPV6_1
ip -n $SCONE_CSP2_NS -6 route add $USER_IPV6/128 via $CSP1_IPV6_2
ip -n $SCONE_CAP_NS -6 route add $USER_IPV6/128 via $CSP2_IPV6_2
echo " Testing IPv4 connectivity."
ip netns exec $SCONE_USER_NS ping -c 3 $CAP_IP
echo " Testing IPv6 connectivity."
ip netns exec $SCONE_USER_NS ping6 -c 3 $CAP_IPV6
#ip -n $SCONE_USER_NS stats set dev $USER_CSP_LINK l3_stats on


Expand Down Expand Up @@ -117,10 +143,15 @@ sleep 10

# Run servers.
echo "=== Starting servers."
echo " Starting IPv4 servers."
ip netns exec $SCONE_CAP_NS nc -u -l $PLAIN_UDP_PORT & nc_pid=$!
ip netns exec $SCONE_CAP_NS ./hq --mode=server --host=$CAP_IP --port=$NON_SCONE_PORT --logtostderr=false & hqs1_pid=$!
ip netns exec $SCONE_CAP_NS ./hq --mode=server --host=$CAP_IP --port=$SCONE_PORT --logtostderr=false --h2port=7776 & hqs2_pid=$!
# Note: h2port is set on the 2nd hq server above, so that it doesn't collide with the first.
echo " Starting IPv6 servers."
ip netns exec $SCONE_CAP_NS nc -u -l -6 $((PLAIN_UDP_PORT + 1)) & nc6_pid=$!
ip netns exec $SCONE_CAP_NS ./hq --mode=server --host=$CAP_IPV6 --port=$((NON_SCONE_PORT + 1)) --logtostderr=false & hqs1_ipv6_pid=$!
ip netns exec $SCONE_CAP_NS ./hq --mode=server --host=$CAP_IPV6 --port=$((SCONE_PORT + 1)) --logtostderr=false --h2port=7777 & hqs2_ipv6_pid=$!
# Note: h2port is set differently for IPv6 servers to avoid collisions.
echo " Waiting 5 seconds."
sleep 5

Expand All @@ -129,14 +160,20 @@ sleep 5
echo "=== Running clients."
NC_DATA=/tmp/random_nc_data
openssl rand -hex 48 >$NC_DATA
#cat $NC_DATA | ip netns exec $SCONE_USER_NS nc -u $CAP_IP $PLAIN_UDP_PORT
#ip netns exec $SCONE_USER_NS nc -u $CAP_IP $PLAIN_UDP_PORT
echo " Doing non-SCONE-enabled fetch."
echo " IPv4 client tests:"
echo " Doing non-SCONE-enabled fetch (IPv4)."
ip netns exec $SCONE_USER_NS ./hq --mode=client --host=$CAP_IP --port=$NON_SCONE_PORT --path=/
sleep 2
echo " Doing SCONE-enabled fetch."
echo " Doing SCONE-enabled fetch (IPv4)."
ip netns exec $SCONE_USER_NS ./hq --mode=client --host=$CAP_IP --port=$SCONE_PORT --path=/
sleep 2
echo " IPv6 client tests:"
echo " Doing non-SCONE-enabled fetch (IPv6)."
ip netns exec $SCONE_USER_NS ./hq --mode=client --host=$CAP_IPV6 --port=$((NON_SCONE_PORT + 1)) --path=/
sleep 2
echo " Doing SCONE-enabled fetch (IPv6)."
ip netns exec $SCONE_USER_NS ./hq --mode=client --host=$CAP_IPV6 --port=$((SCONE_PORT + 1)) --path=/
sleep 2

# Stop and cleanup.
echo "=== Cleaning up."
Expand All @@ -147,9 +184,12 @@ kill -9 $dump_pid4
echo " User network interface stats:"
#ip netns exec $SCONE_USER_NS ip stats show dev $USER_CSP_LINK
kill -9 $nc_pid
kill -9 $nc6_pid
kill -9 $hqs1_pid
kill -9 $hqs1_ipv6_pid
sleep 2
kill -9 $hqs2_pid
kill -9 $hqs2_ipv6_pid
echo " Stopping User<-CSP1 eBPF program."
kill -2 $scone_pid1
sleep 2
Expand Down
182 changes: 147 additions & 35 deletions scone_ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ const u32 quic_versions[] = { 0x00000000, 0x00000001,
0xfaceb001, 0xfaceb002 };
const int NUM_QUIC_VERSIONS = 9;

// This is a hard-coded destination UDP port number to add SCONE packets for.
const unsigned short SCONE_PORT = 60000;
// These are hard-coded destination UDP port numbers to add SCONE packets for.
const unsigned short SCONE_PORT_IPV4 = 60000;
const unsigned short SCONE_PORT_IPV6 = 60001;
const unsigned short NON_SCONE_PORT_IPV4 = 20000;
const unsigned short NON_SCONE_PORT_IPV6 = 20001;

// TODO: Redefine these to the actual SCONE values.
#define SCONE_V1 0x53434f4E
Expand Down Expand Up @@ -108,12 +111,30 @@ static __always_inline u64 check_quic(void *data, void *data_end) {
struct ethhdr *eth = data;
u16 ipproto = bpf_ntohs(eth->h_proto);
struct iphdr *ip = (void *)eth + sizeof(*eth);
struct ipv6hdr *ip6 = (void *)ip;
struct udphdr *udp = (void *)ip + sizeof(*ip);
if (ipproto == ETH_P_IPV6) udp = (void *)ip6 + sizeof(*ip);
char *quic = (char *)udp + sizeof(*udp);
struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
struct udphdr *udp;
char *quic;
u16 port;


// Bounds check for Ethernet header
if ((void *)eth + sizeof(*eth) > data_end)
return TOO_SMALL_COUNTER;

// Set UDP pointer based on IP version with proper bounds checking
if (ipproto == ETH_P_IPV6) {
// IPv6: Ethernet + IPv6 header + UDP header
if ((void *)ip6 + sizeof(*ip6) + sizeof(struct udphdr) > data_end)
return TOO_SMALL_COUNTER;
udp = (void *)ip6 + sizeof(*ip6);
} else {
// IPv4: Ethernet + IPv4 header + UDP header
if ((void *)ip + sizeof(*ip) + sizeof(struct udphdr) > data_end)
return TOO_SMALL_COUNTER;
udp = (void *)ip + sizeof(*ip);
}

quic = (char *)udp + sizeof(*udp);

// Pass through if it's too small to be a QUIC packet.
if ((void*)quic >= data_end)
return TOO_SMALL_COUNTER;
Expand All @@ -129,8 +150,9 @@ static __always_inline u64 check_quic(void *data, void *data_end) {
port = bpf_ntohs(udp->source);
ports.increment(port);

// Pass through if the UDP destination port isn't configured for SCONE.
if (port != SCONE_PORT)
// Pass through if the UDP source port isn't configured for SCONE or non-SCONE testing.
if (port != SCONE_PORT_IPV4 && port != SCONE_PORT_IPV6 &&
port != NON_SCONE_PORT_IPV4 && port != NON_SCONE_PORT_IPV6)
return NOT_SCONE_PORT_COUNTER;

// TODO: Remove later, this is just for seeing what is received.
Expand Down Expand Up @@ -162,7 +184,7 @@ static __always_inline u64 check_quic(void *data, void *data_end) {
}

// If the checks made it all the way here, it's QUIC.
return QUIC_IPV4_LONG_COUNTER;
return (ipproto == ETH_P_IP) ? QUIC_IPV4_LONG_COUNTER : QUIC_IPV6_LONG_COUNTER;
}

// This is hooked to receive all packets, so first it needs to check whether
Expand All @@ -174,14 +196,45 @@ int add_scone_ebpf(struct xdp_md *ctx) {

struct ethhdr *eth = data;
struct iphdr *ip = (void *)eth + sizeof(*eth);
struct udphdr *udp = (void *)ip + sizeof(*ip);

size_t lower_hdrlen = sizeof(*eth) + sizeof(*ip) + sizeof(*udp);
u8 *quic = data + lower_hdrlen;
struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
struct udphdr *udp;
size_t lower_hdrlen;
u8 *quic;
u8 src_conn_id_len, dst_conn_id_len;

u64 result = check_quic(data, data_end);
if (result != QUIC_IPV4_LONG_COUNTER) {
if (result != QUIC_IPV4_LONG_COUNTER && result != QUIC_IPV6_LONG_COUNTER) {
counters.increment(result);
return XDP_PASS;
}

// Set header pointers and lengths based on IP version with bounds checking
if (result == QUIC_IPV6_LONG_COUNTER) {
// IPv6 packet - need to re-verify bounds for IPv6 header access
if ((void *)ip6 + sizeof(*ip6) + sizeof(struct udphdr) > data_end)
return XDP_PASS;
udp = (void*)ip6 + sizeof(*ip6);
lower_hdrlen = sizeof(*eth) + sizeof(*ip6) + sizeof(*udp);
quic = data + lower_hdrlen;
// Verify QUIC payload is accessible
if (quic >= (u8*)data_end)
return XDP_PASS;
} else {
// IPv4 packet
if ((void *)ip + sizeof(*ip) + sizeof(struct udphdr) > data_end)
return XDP_PASS;
udp = (void *)ip + sizeof(*ip);
lower_hdrlen = sizeof(*eth) + sizeof(*ip) + sizeof(*udp);
quic = data + lower_hdrlen;
// Verify QUIC payload is accessible
if (quic >= (u8*)data_end)
return XDP_PASS;
}

// Only add SCONE to SCONE-enabled ports, pass through non-SCONE test traffic
u16 src_port = bpf_ntohs(udp->source);
if (src_port == NON_SCONE_PORT_IPV4 || src_port == NON_SCONE_PORT_IPV6) {
// Pass through non-SCONE test traffic without modification
counters.increment(result);
return XDP_PASS;
}
Expand Down Expand Up @@ -233,7 +286,14 @@ int add_scone_ebpf(struct xdp_md *ctx) {
u8 *new_data_end = (u8 *)(long)ctx->data_end;
if (new_data + lower_hdrlen > new_data_end) return XDP_ABORTED;
if (new_data + delta + lower_hdrlen > new_data_end) return XDP_ABORTED;
__builtin_memmove(new_data, new_data+delta, lower_hdrlen);
// Manual byte-by-byte copy to avoid memmove (supports both IPv4 and IPv6)
// IPv4 headers: 14+20+8=42, IPv6 headers: 14+40+8=62
int max_hdr_len = (result == QUIC_IPV6_LONG_COUNTER) ? 62 : 42;
if (lower_hdrlen <= max_hdr_len && new_data + lower_hdrlen < new_data_end && new_data + delta + lower_hdrlen < new_data_end) {
for (int i = 0; i < max_hdr_len && i < lower_hdrlen; i++) {
new_data[i] = new_data[delta + i];
}
}

// Fill in SCONE packet.
eth = (void*)(long)ctx->data;
Expand All @@ -251,14 +311,32 @@ int add_scone_ebpf(struct xdp_md *ctx) {
if ((u8*)&(scone->dst_conn_id) + QUIC_CONN_ID_LEN >= new_data_end) return XDP_ABORTED;
u8 *dst_conn_id = new_data + lower_hdrlen + sizeof(*scone) + 7;
if ((u8*)dst_conn_id + QUIC_CONN_ID_LEN >= new_data_end) return XDP_ABORTED;
__builtin_memmove(&scone->dst_conn_id, dst_conn_id, QUIC_CONN_ID_LEN);
// Manual copy to avoid memmove issues (QUIC_CONN_ID_LEN = 8)
if ((u8*)&(scone->dst_conn_id[0]) + 8 < new_data_end && dst_conn_id + 8 < new_data_end) {
scone->dst_conn_id[0] = dst_conn_id[0];
scone->dst_conn_id[1] = dst_conn_id[1];
scone->dst_conn_id[2] = dst_conn_id[2];
scone->dst_conn_id[3] = dst_conn_id[3];
scone->dst_conn_id[4] = dst_conn_id[4];
scone->dst_conn_id[5] = dst_conn_id[5];
scone->dst_conn_id[6] = dst_conn_id[6];
scone->dst_conn_id[7] = dst_conn_id[7];
}

// Fix up IP header to reflect new length.
if ((u8*)ip + sizeof(*ip) >= new_data_end) return XDP_ABORTED;
ip->tot_len = bpf_htons(bpf_ntohs(ip->tot_len) + sizeof(*scone));
__u64 cs = ip->check = 0;
ipv4_csum(ip, sizeof(*ip), &cs);
ip->check = cs;
// Fix up IP header to reflect new length (different for IPv4 vs IPv6).
if (result == QUIC_IPV4_LONG_COUNTER) {
if ((u8*)ip + sizeof(*ip) >= new_data_end) return XDP_ABORTED;
ip->tot_len = bpf_htons(bpf_ntohs(ip->tot_len) + sizeof(*scone));
__u64 cs = ip->check = 0;
ipv4_csum(ip, sizeof(*ip), &cs);
ip->check = cs;
} else {
// IPv6 case
struct ipv6hdr *ip6 = (void*)eth + sizeof(*eth);
if ((u8*)ip6 + sizeof(*ip6) >= new_data_end) return XDP_ABORTED;
ip6->payload_len = bpf_htons(bpf_ntohs(ip6->payload_len) + sizeof(*scone));
// IPv6 has no header checksum
}

// Fix up UDP header to reflect new length.
if ((u8*)udp + sizeof(*udp) >= new_data_end) return XDP_ABORTED;
Expand Down Expand Up @@ -320,23 +398,48 @@ int remove_scone_ebpf(struct xdp_md *ctx) {
u64 result = check_quic(data, data_end);

// If it doesn't look like SCONE, ignore it.
if (result != SCONE_IPV4_COUNTER) {
if (result != SCONE_IPV4_COUNTER && result != SCONE_IPV6_COUNTER) {
counters.increment(result);
return XDP_PASS;
}

struct ethhdr *eth = data;
struct iphdr *ip = (void *)eth + sizeof(*eth);
struct udphdr *udp = (void *)ip + sizeof(*ip);
struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
struct udphdr *udp;
int lower_hdrs_len;

// Set header pointers and lengths based on IP version with bounds checking
if (result == SCONE_IPV6_COUNTER) {
// IPv6 packet - need to re-verify bounds for IPv6 header access
if ((void *)ip6 + sizeof(*ip6) + sizeof(struct udphdr) > data_end)
return XDP_PASS;
udp = (void *)ip6 + sizeof(*ip6);
lower_hdrs_len = sizeof(*eth) + sizeof(*ip6) + sizeof(*udp);
} else {
// IPv4 packet
if ((void *)ip + sizeof(*ip) + sizeof(struct udphdr) > data_end)
return XDP_PASS;
udp = (void *)ip + sizeof(*ip);
lower_hdrs_len = sizeof(*eth) + sizeof(*ip) + sizeof(*udp);
}

u8 *quic = (u8*)udp + sizeof(*udp);
u8 src_conn_id_len, dst_conn_id_len;

// Copy the lower headers back, overwriting the SCONE packet.
const int lower_hdrs_len = sizeof(*eth) + sizeof(*ip) + sizeof(*udp);
if (data+sizeof(struct sconepkt)+lower_hdrs_len >= data_end)
return XDP_ABORTED;
__builtin_memmove(data+sizeof(struct sconepkt), data, lower_hdrs_len);
// Manual byte-by-byte copy for header removal (supports both IPv4 and IPv6)
u8 *src = (u8*)data;
u8 *dst = (u8*)data + sizeof(struct sconepkt);
// IPv4 headers: 14+20+8=42, IPv6 headers: 14+40+8=62
int max_hdr_len = (result == SCONE_IPV6_COUNTER) ? 62 : 42;
if (lower_hdrs_len <= max_hdr_len && src + lower_hdrs_len < (u8*)data_end && dst + lower_hdrs_len < (u8*)data_end) {
for (int i = 0; i < max_hdr_len && i < lower_hdrs_len; i++) {
dst[i] = src[i];
}
}
// Adjust the packet length to shrink it.
if (bpf_xdp_adjust_head(ctx, sizeof(struct sconepkt)) != 0) {
// TODO: Some kind of error.
Expand All @@ -345,18 +448,27 @@ int remove_scone_ebpf(struct xdp_md *ctx) {
return XDP_PASS;
}

// Adjust IP length and checksum values.
// Adjust IP length and checksum values (different for IPv4 vs IPv6).
eth = (void *)(long)ctx->data;
ip = (void *)eth + sizeof(*eth);
data_end = (void *)(long)ctx->data_end;
if ((void*)ip + sizeof(*ip) >= data_end) return XDP_ABORTED;
ip->tot_len = bpf_htons(bpf_ntohs(ip->tot_len) - sizeof(struct sconepkt));
__u64 cs = ip->check = 0;
ipv4_csum(ip, sizeof(*ip), &cs);
ip->check = cs;

if (result == SCONE_IPV4_COUNTER) {
ip = (void *)eth + sizeof(*eth);
if ((void*)ip + sizeof(*ip) >= data_end) return XDP_ABORTED;
ip->tot_len = bpf_htons(bpf_ntohs(ip->tot_len) - sizeof(struct sconepkt));
__u64 cs = ip->check = 0;
ipv4_csum(ip, sizeof(*ip), &cs);
ip->check = cs;
udp = (void *)ip + sizeof(*ip);
} else {
// IPv6 case
struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
if ((void*)ip6 + sizeof(*ip6) >= data_end) return XDP_ABORTED;
ip6->payload_len = bpf_htons(bpf_ntohs(ip6->payload_len) - sizeof(struct sconepkt));
udp = (void *)ip6 + sizeof(*ip6);
}

// Adjust UDP length and checksum values.
udp = (void *)ip + sizeof(*ip);
if ((void*)udp + sizeof(*udp) >= data_end) return XDP_ABORTED;
udp->len = bpf_htons(bpf_ntohs(udp->len) - sizeof(struct sconepkt));
udp->check = 0; // TODO: Use real checksum.
Expand Down