diff --git a/src/bpf/egress.c b/src/bpf/egress.c index 08c3ce7..c876aa4 100644 --- a/src/bpf/egress.c +++ b/src/bpf/egress.c @@ -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) { + 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); @@ -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; @@ -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); diff --git a/src/bpf/mimic.h b/src/bpf/mimic.h index c11bbe3..1b267d6 100644 --- a/src/bpf/mimic.h +++ b/src/bpf/mimic.h @@ -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; diff --git a/src/run.c b/src/run.c index ec7a567..f5dc6b0 100644 --- a/src/run.c +++ b/src/run.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -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"); @@ -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; diff --git a/src/shared/checksum.h b/src/shared/checksum.h index 420269f..082ee32 100644 --- a/src/shared/checksum.h +++ b/src/shared/checksum.h @@ -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]); \ } \ diff --git a/src/shared/misc.h b/src/shared/misc.h index 87f61a0..0842f52 100644 --- a/src/shared/misc.h +++ b/src/shared/misc.h @@ -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; }; diff --git a/src/shared/try.h b/src/shared/try.h index f2c0a94..29a14f6 100644 --- a/src/shared/try.h +++ b/src/shared/try.h @@ -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) \ ({ \ @@ -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 diff --git a/src/shared/util.h b/src/shared/util.h index 5358c3e..27c7bda 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -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 @@ -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