Skip to content

Commit

Permalink
Send packet data before handshake to userspace
Browse files Browse the repository at this point in the history
Current implementation simply throws away these packets. This commit is the first step of implementing re-sending them after handshake.
  • Loading branch information
hack3ric committed Mar 31, 2024
1 parent fbf73eb commit f40d9b2
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 8 deletions.
55 changes: 52 additions & 3 deletions src/bpf/egress.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,56 @@ static inline void update_tcp_header(struct tcphdr* tcp, __u16 udp_len, __u32 se
tcp->urg_ptr = 0;
}

#define SEG_LEN 256

static inline int store_packet(struct __sk_buff* skb, __u32 pkt_off, struct conn_tuple* key) {

This comment has been minimized.

Copy link
@Vigilans

Vigilans Mar 31, 2024

Contributor

Just FYI: Did you consider using bpf_sk_assign and bpf_redirect like what's done in daeuniverse/dae#375, daeuniverse/dae#458?

Though I think this might not be suitable for mimic since a socket with listening port might be required (dae uses a tproxy port)? Don't know whether bpf_sk_assign could be used on raw socket.

This comment has been minimized.

Copy link
@hack3ric

hack3ric Mar 31, 2024

Author Owner

Initially I did think about redirecting to a TUN interface (after several trials and errors fighting against eBPF verifier), but it turned out to be quite heavy, needing an additional interface just for one or two packets.

bpf_sk_assign works on TC ingress path but not on egress, though.

int retcode;
__u32 data_len = skb->len - pkt_off;
if (!key || data_len > MAX_PACKET_SIZE) return TC_ACT_SHOT;

bool has_remainder = data_len % SEGMENT_SIZE;
__u32 segments = data_len / SEGMENT_SIZE + has_remainder;
__u32 alloc_size = sizeof(struct rb_item) + segments * SEGMENT_SIZE;
struct bpf_dynptr ptr = {};
if (bpf_ringbuf_reserve_dynptr(&mimic_rb, alloc_size, 0, &ptr) < 0) {
cleanup(TC_ACT_SHOT);
}

struct rb_item* item = bpf_dynptr_data(&ptr, 0, sizeof(*item));
if (!item) cleanup(TC_ACT_SHOT);
item->type = RB_ITEM_STORE_PACKET;
item->store_packet.conn_key = *key;
item->store_packet.len = data_len;
item->store_packet.l4_csum_partial = mimic_inspect_skb(skb)->ip_summed == CHECKSUM_PARTIAL;

char* packet = NULL;
__u32 offset = 0, i = 0;
for (; i < segments - has_remainder; i++) {
if (i > MAX_PACKET_SIZE / SEGMENT_SIZE + 1) break;
offset = i * SEGMENT_SIZE;
packet = bpf_dynptr_data(&ptr, sizeof(*item) + offset, SEGMENT_SIZE);
if (!packet) cleanup(TC_ACT_SHOT);
if (bpf_skb_load_bytes(skb, pkt_off + offset, packet, SEGMENT_SIZE) < 0) cleanup(TC_ACT_SHOT);
}
if (has_remainder) {
offset = i * SEGMENT_SIZE;
__u32 copy_len = data_len - offset;
if (copy_len > 0 && copy_len < SEGMENT_SIZE) {
if (copy_len < 2) copy_len = 1;
if (copy_len < 3) copy_len = 2;

packet = bpf_dynptr_data(&ptr, sizeof(*item) + offset, SEGMENT_SIZE);
if (!packet) cleanup(TC_ACT_SHOT);
if (bpf_skb_load_bytes(skb, pkt_off + offset, packet, copy_len) < 0) cleanup(TC_ACT_SHOT);
}
}
bpf_ringbuf_submit_dynptr(&ptr, 0);
return TC_ACT_STOLEN;
cleanup:
bpf_ringbuf_discard_dynptr(&ptr, 0);
return retcode;
}

SEC("tc")
int egress_handler(struct __sk_buff* skb) {
decl_ok(struct ethhdr, eth, 0, skb);
Expand Down Expand Up @@ -98,8 +148,7 @@ int egress_handler(struct __sk_buff* skb) {
}
bpf_spin_unlock(&conn->lock);
send_ctrl_packet(&conn_key, SYN, seq, ack_seq);
// TODO: store packet in userspace buffer and send them after establishing
return TC_ACT_STOLEN;
return store_packet(skb, ip_end, &conn_key);
}
conn_state = conn->state;
conn_seq = conn->seq;
Expand All @@ -122,7 +171,7 @@ int egress_handler(struct __sk_buff* skb) {
ipv6->nexthdr = IPPROTO_TCP;
}

try(mangle_data(skb, ip_end + sizeof(*udp)));
try_tc(mangle_data(skb, ip_end + sizeof(*udp)));
decl_shot(struct tcphdr, tcp, ip_end, skb);
update_tcp_header(tcp, udp_len, seq, ack_seq);

Expand Down
2 changes: 1 addition & 1 deletion src/bpf/mimic.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ int mimic_change_csum_offset(struct __sk_buff* skb, __u16 protocol) __ksym;

bool matches_whitelist(QUARTET_DEF, bool ingress);

static inline struct conn_tuple gen_conn_key(QUARTET_DEF, bool ingress) {
static __always_inline struct conn_tuple gen_conn_key(QUARTET_DEF, bool ingress) {
struct conn_tuple key = {};
if (udp) {
key.local_port = udp->source;
Expand Down
14 changes: 13 additions & 1 deletion src/run.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <linux/in6.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/udp.h>
#include <net/if.h>
#include <netinet/in.h>
#include <signal.h>
Expand Down Expand Up @@ -224,7 +225,7 @@ static inline int send_ctrl_packet(struct send_options* s) {
static int handle_rb_event(void* ctx, void* data, size_t data_sz) {
struct rb_item* item = data;
const char* name;
int ret;
int ret = 0;
switch (item->type) {
case RB_ITEM_LOG_EVENT:
name = N_("logging event");
Expand All @@ -234,6 +235,17 @@ static int handle_rb_event(void* ctx, void* data, size_t data_sz) {
name = N_("sending control packets");
ret = send_ctrl_packet(&item->send_options);
break;
case RB_ITEM_STORE_PACKET:
name = N_("storing packet");
log_warn(_("userspace received packet with UDP length %d, checksum partial %d"), item->store_packet.len,
item->store_packet.l4_csum_partial);
if (item->store_packet.len > data_sz - sizeof(*item)) break;
// TODO: handle packet store
break;
default:
name = N_("handling unknown ring buffer item");
log_warn(_("unknown ring buffer item type %d, size %d"), item->type, data_sz);
break;
}
if (ret < 0) log_error(_("error %s: %s"), gettext(name), strerror(-ret));
return 0;
Expand Down
2 changes: 1 addition & 1 deletion src/shared/checksum.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static inline void update_csum_ul_neg(__u32* csum, __u32 new) {
({ \
__u16* data = (void*)(__u64)_x->data + off; \
int i = 0; \
for (; i < ETH_DATA_LEN / sizeof(__u16); i++) { \
for (; i < MAX_PACKET_SIZE / sizeof(__u16); i++) { \
if ((__u64)(data + i + 1) > (__u64)_x->data_end) break; \
*csum += ntohs(data[i]); \
} \
Expand Down
1 change: 1 addition & 0 deletions src/shared/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ struct rb_item {
struct send_options send_options;
struct {
struct conn_tuple conn_key;
__u16 len;
bool l4_csum_partial;
} store_packet;
};
Expand Down
10 changes: 8 additions & 2 deletions src/shared/try.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@
_ret; \
})

// Same as `try` with one arguments, but runs XDP subroutine
#define try_tc(expr) \
({ \
int _ret = (expr); \
if (_ret != TC_ACT_OK) return _ret; \
_ret; \
})

// Same as `try` with one arguments, but runs XDP subroutine
#define try_xdp(expr) \
({ \
Expand Down Expand Up @@ -157,6 +165,4 @@
#define try_p_pass(x) try_p_ret(x, XDP_PASS)
#define try_p_drop(x) try_p_ret(x, XDP_DROP)



#endif // _MIMIC_SHARED_TRY_H
11 changes: 11 additions & 0 deletions src/shared/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
// defined in linux/tcp.h
#define tcp_flag_word(tp) (((union tcp_word_hdr*)(tp))->words[3])

#define CHECKSUM_NONE 0
#define CHECKSUM_UNNECESSARY 1
#define CHECKSUM_COMPLETE 2
#define CHECKSUM_PARTIAL 3

#else // _MIMIC_BPF

// Cleanup utilities
Expand Down Expand Up @@ -73,4 +78,10 @@ static inline __attribute__((__format_arg__(1))) const char* _(const char* text)
#endif
#define N_(text) text

// Mainly used for limiting loop counts
#define MAX_PACKET_SIZE 9000

// Used for reading packet data in bulk
#define SEGMENT_SIZE 256

#endif // _MIMIC_SHARED_UTIL_H

0 comments on commit f40d9b2

Please sign in to comment.