diff --git a/bpf/egress.c b/bpf/egress.c index 230d2f0..da6e00a 100644 --- a/bpf/egress.c +++ b/bpf/egress.c @@ -128,12 +128,15 @@ int egress_handler(struct __sk_buff* skb) { __u16 payload_len = udp_len - sizeof(*udp); __u32 seq = 0, ack_seq = 0, conn_cwnd; __u32 random = bpf_get_prandom_u32(); + __u32 padding; bpf_spin_lock(&conn->lock); if (likely(conn->state == CONN_ESTABLISHED)) { seq = conn->seq; ack_seq = conn->ack_seq; - conn->seq += payload_len + conn->settings.padding; + padding = + conn->settings.padding == PADDING_RANDOM ? (seq + ack_seq) % 11 : conn->settings.padding; + conn->seq += payload_len + padding; } else { if (conn->state == CONN_IDLE) { __u32 cooldown = conn_cooldown(conn); @@ -176,7 +179,7 @@ int egress_handler(struct __sk_buff* skb) { conn_cwnd = conn->cwnd; bpf_spin_unlock(&conn->lock); - size_t reserve_len = TCP_UDP_HEADER_DIFF + conn->settings.padding; + size_t reserve_len = TCP_UDP_HEADER_DIFF + padding; if (ipv4) { __be16 old_len = ipv4->tot_len; __be16 new_len = htons(ntohs(old_len) + reserve_len); @@ -192,7 +195,7 @@ int egress_handler(struct __sk_buff* skb) { } __be32 csum_diff = 0; - try_tc(mangle_data(skb, ip_end + sizeof(*udp), &csum_diff, conn->settings.padding)); + try_tc(mangle_data(skb, ip_end + sizeof(*udp), &csum_diff, padding)); decl_shot(struct tcphdr, tcp, ip_end, skb); update_tcp_header(tcp, payload_len, seq, ack_seq, conn_cwnd); diff --git a/bpf/ingress.c b/bpf/ingress.c index fd2d8b5..5ac1e47 100644 --- a/bpf/ingress.c +++ b/bpf/ingress.c @@ -307,7 +307,10 @@ int ingress_handler(struct xdp_md* xdp) { } if (will_drop) return XDP_DROP; - size_t reserve_len = TCP_UDP_HEADER_DIFF + conn->settings.padding; + __u32 padding = conn->settings.padding == PADDING_RANDOM + ? (ntohl(tcp->seq) + ntohl(tcp->ack_seq)) % 11 + : conn->settings.padding; + size_t reserve_len = TCP_UDP_HEADER_DIFF + padding; if (ipv4) { __be16 old_len = ipv4->tot_len; __be16 new_len = htons(ntohs(old_len) - reserve_len); @@ -328,8 +331,7 @@ int ingress_handler(struct xdp_md* xdp) { __u32 csum = (__u16)~ntohs(tcp->check); __be32 csum_diff = 0; - try_xdp(restore_data(xdp, ip_end + sizeof(*tcp), ip_end + ip_payload_len, &csum_diff, - conn->settings.padding)); + try_xdp(restore_data(xdp, ip_end + sizeof(*tcp), ip_end + ip_payload_len, &csum_diff, padding)); decl_drop(struct udphdr, udp, ip_end, xdp); csum += u32_fold(ntohl(csum_diff)); diff --git a/common/defs.h b/common/defs.h index 705cf48..7d25b9f 100644 --- a/common/defs.h +++ b/common/defs.h @@ -145,6 +145,7 @@ static inline void cleanup_malloc_str(char** ptr) { cleanup_malloc((void*)ptr); #define MAX_PACKET_SIZE 10000 #define MAX_PADDING_LEN 16 +#define PADDING_RANDOM (-127) // Used for reading packet data in bulk #define SEGMENT_SIZE 64 @@ -246,7 +247,7 @@ static const struct filter_settings FALLBACK_SETTINGS = { static inline void filter_settings_apply(struct filter_settings* local, const struct filter_settings* global) { for (int i = 0; i < sizeof_array(local->array); i++) - if (local->array[i] < 0) local->array[i] = global->array[i]; + if (local->array[i] == -1) local->array[i] = global->array[i]; } struct conn_tuple { diff --git a/src/config.c b/src/config.c index 2542719..26b028c 100644 --- a/src/config.c +++ b/src/config.c @@ -131,7 +131,7 @@ int parse_handshake(char* str, struct filter_handshake* h) { int nums[2]; try(parse_int_seq(str, nums, 2)); for (int i = 0; i < 2; i++) - if (nums[i] >= 0) h->array[i] = nums[i]; + if (nums[i] != -1) h->array[i] = nums[i]; return 0; } @@ -140,11 +140,29 @@ int parse_keepalive(char* str, struct filter_keepalive* k) { int nums[4]; try(parse_int_seq(str, nums, 4)); for (int i = 0; i < 4; i++) - if (nums[i] >= 0) k->array[i] = nums[i]; + if (nums[i] != -1) k->array[i] = nums[i]; return 0; } -int parse_padding(const char* str) { return parse_int_non_neg(str, 0, MAX_PADDING_LEN); } +int parse_padding(const char* str, __s16* padding) { + if (strcmp(str, "random") == 0) + *padding = PADDING_RANDOM; + else + *padding = try(parse_int_non_neg(str, 0, MAX_PADDING_LEN)); + return 0; +} + +static int parse_setting(const char* k, char* v, struct filter_settings* settings) { + if (strcmp("handshake", k) == 0) + try(parse_handshake(v, &settings->handshake)); + else if (strcmp("keepalive", k) == 0) + try(parse_keepalive(v, &settings->keepalive)); + else if (strcmp("padding", k) == 0) + try(parse_padding(v, &settings->padding)); + else + return 0; + return 1; +} int parse_filter(char* filter_str, struct filter* filters, struct filter_info* info, int size) { int ret; @@ -201,13 +219,7 @@ int parse_filter(char* filter_str, struct filter* filters, struct filter_info* i next_delim = strchr(delim, ','); if (next_delim) *next_delim = '\0'; try(parse_kv(delim, &k, &v)); - if (strcmp("handshake", k) == 0) - try(parse_handshake(v, &info[0].settings.handshake)); - else if (strcmp("keepalive", k) == 0) - try(parse_keepalive(v, &info[0].settings.keepalive)); - else if (strcmp("padding", k) == 0) - info[0].settings.padding = try(parse_padding(v)); - else + if (!try(parse_setting(k, v, &info[0].settings))) ret(-EINVAL, _("unsupported option type: '%s'"), k); if (!next_delim) break; } @@ -249,12 +261,6 @@ int parse_config_file(FILE* file, struct run_args* args) { } log_verbosity = parsed; - } else if (strcmp(k, "handshake") == 0) { - try(parse_handshake(v, &args->gsettings.handshake)); - } else if (strcmp(k, "keepalive") == 0) { - try(parse_keepalive(v, &args->gsettings.keepalive)); - } else if (strcmp(k, "padding") == 0) { - args->gsettings.padding = try(parse_padding(v)); } else if (strcmp(k, "filter") == 0) { unsigned int fc = args->filter_count; ret = parse_filter(v, &args->filters[fc], &args->info[fc], sizeof_array(args->filters) - fc); @@ -265,7 +271,8 @@ int parse_config_file(FILE* file, struct run_args* args) { return ret; else args->filter_count += ret; - } else { + + } else if (!try(parse_setting(k, v, &args->gsettings))) { ret(-EINVAL, _("unknown key '%s'"), k); } } @@ -303,11 +310,7 @@ int parse_lock_file(FILE* file, struct lock_content* c) { try(parse_int_any(v, &c->whitelist_id)); else if (strcmp(k, "conns_id") == 0) try(parse_int_any(v, &c->conns_id)); - else if (strcmp(k, "handshake") == 0) - try(parse_handshake(v, &c->settings.handshake)); - else if (strcmp(k, "keepalive") == 0) - try(parse_keepalive(v, &c->settings.keepalive)); - else + else if (!try(parse_setting(k, v, &c->settings))) ret(-EINVAL, _("unknown key '%s'"), k); } if (!version_checked) ret(-EINVAL, _("no version found in lock file")); @@ -324,5 +327,6 @@ int write_lock_file(int fd, const struct lock_content* c) { try(dprintf(fd, "handshake=%d:%d\n", c->settings.h.i, c->settings.h.r)); try(dprintf(fd, "keepalive=%d:%d:%d:%d\n", c->settings.k.t, c->settings.k.i, c->settings.k.r, c->settings.k.s)); + try(dprintf(fd, "padding=%d", c->settings.padding)); return 0; } diff --git a/src/main.h b/src/main.h index a28d6ed..681da29 100644 --- a/src/main.h +++ b/src/main.h @@ -51,7 +51,7 @@ struct lock_content { int parse_handshake(char* str, struct filter_handshake* h); int parse_keepalive(char* str, struct filter_keepalive* k); -int parse_padding(const char* str); +int parse_padding(const char* str, __s16* padding); int parse_filter(char* filter_str, struct filter* filters, struct filter_info* info, int size); int parse_config_file(FILE* file, struct run_args* args); int parse_lock_file(FILE* file, struct lock_content* c); diff --git a/src/run.c b/src/run.c index 6161858..1007ac0 100644 --- a/src/run.c +++ b/src/run.c @@ -74,7 +74,7 @@ static inline error_t args_parse_opt(int key, char* arg, struct argp_state* stat try(parse_keepalive(arg, &args->gsettings.keepalive)); break; case 'p': - args->gsettings.padding = try(parse_padding(arg)); + try(parse_padding(arg, &args->gsettings.padding)); break; case 'F': args->file = arg; diff --git a/src/show.c b/src/show.c index a8c929a..197441b 100644 --- a/src/show.c +++ b/src/show.c @@ -58,7 +58,10 @@ int show_overview(int whitelist_fd, struct filter_settings* gs, int log_verbosit if (LOG_ALLOW_INFO) fprintf(out, "%s%s " RESET, log_prefixes[2][0], log_prefixes[2][1]); fprintf(out, _(" %ssettings:%s handshake %d:%d, keepalive %d:%d:%d:%d"), BOLD, RESET, gs->h.i, gs->h.r, gs->k.t, gs->k.i, gs->k.r, gs->k.s); - if (gs->padding) fprintf(out, _(", padding %d"), gs->padding); + if (gs->padding == PADDING_RANDOM) + fprintf(out, _(", padding random")); + else if (gs->padding) + fprintf(out, _(", padding %d"), gs->padding); fprintf(out, "\n"); char buf[FILTER_FMT_MAX_LEN]; @@ -88,7 +91,12 @@ int show_overview(int whitelist_fd, struct filter_settings* gs, int log_verbosit if (i < 3) fprintf(out, ":"); } } - if (a->padding != b->padding) fprintf(out, ",padding=%d", info.settings.padding); + if (a->padding != b->padding) { + if (info.settings.padding == PADDING_RANDOM) + fprintf(out, ",padding=random"); + else + fprintf(out, ",padding=%d", info.settings.padding); + } if (strlen(info.host) != 0) fprintf(out, _(" %s(resolved from %s)"), RESET GRAY, info.host); fprintf(out, RESET "\n"); }