diff --git a/src/Makefile b/src/Makefile index 7c6b0b6..fadbb49 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ MODULES_DIR := /lib/modules/$(shell uname -r) KERNEL_DIR := ${MODULES_DIR}/build obj-m += xt_ts3init.o -xt_ts3init-objs += ts3init_module.o ts3init_match.o ts3init_cookie.o ts3init_target.o siphash24.o +xt_ts3init-objs += ts3init_module.o ts3init_match.o ts3init_cookie.o ts3init_target.o ts3init_cache.o siphash24.o all: $(MAKE) -C ${KERNEL_DIR} M=$$PWD; diff --git a/src/Makefile.xtables b/src/Makefile.xtables index 6c3f58b..2d7d310 100644 --- a/src/Makefile.xtables +++ b/src/Makefile.xtables @@ -1,5 +1,5 @@ CFLAGS = -O2 -Wall -LIBS = libxt_ts3init_get_cookie.so libxt_ts3init_get_puzzle.so libxt_ts3init_reset.so +LIBS = libxt_ts3init_get_cookie.so libxt_ts3init_get_puzzle.so libxt_TS3INIT_RESET.so libxt_TS3INIT_SET_COOKIE.so all: $(LIBS) diff --git a/src/libxt_ts3init_reset.c b/src/libxt_TS3INIT_RESET.c similarity index 96% rename from src/libxt_ts3init_reset.c rename to src/libxt_TS3INIT_RESET.c index 070ad11..11f34aa 100644 --- a/src/libxt_ts3init_reset.c +++ b/src/libxt_TS3INIT_RESET.c @@ -14,10 +14,9 @@ #include #include #include +#include "ts3init_cookie_seed.h" #include "ts3init_target.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) - static void ts3init_reset_help(void) { printf("ts3init_reset takes no options\n\n"); diff --git a/src/libxt_TS3INIT_SET_COOKIE.c b/src/libxt_TS3INIT_SET_COOKIE.c new file mode 100644 index 0000000..21c8f20 --- /dev/null +++ b/src/libxt_TS3INIT_SET_COOKIE.c @@ -0,0 +1,120 @@ +/* + * "ts3init_set_cookie" target extension for iptables + * Niels Werensteijn , 2016-10-03 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License; either version 2 + * or 3 of the License, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ts3init_cookie_seed.h" +#include "ts3init_target.h" + +#define param_act(t, s, f) xtables_param_act((t), "ts3init_set_cookie", (s), (f)) + +static void ts3init_set_cookie_tg_help(void) +{ + printf( + "ts3init_get_cookie match options:\n" + " --zero-random-sequence Always return 0 as random sequence.\n" + " --cookie-seed seed Seed is a 60 byte random number in lowecase\n" + " hex. A source could be /dev/random.\n"); +} + +static const struct option ts3init_set_cookie_tg_opts[] = { + {.name = "zero-random-sequence", .has_arg = false, .val = '1'}, + {.name = "cookie-seed", .has_arg = true, .val = '2'}, + {NULL}, +}; + +static int ts3init_set_cookie_tg_parse(int c, char **argv, + int invert, unsigned int *flags, const void *entry, + struct xt_entry_target **target) +{ + struct xt_ts3init_set_cookie_tginfo *info = (void *)(*target)->data; + switch (c) { + case '1': + param_act(XTF_ONLY_ONCE, "--zero-random-sequence", info->specific_options & TARGET_SET_COOKIE_ZERO_RANDOM_SEQUENCE); + param_act(XTF_NO_INVERT, "--check-time", invert); + info->specific_options |= TARGET_SET_COOKIE_ZERO_RANDOM_SEQUENCE; + return true; + case '2': + param_act(XTF_ONLY_ONCE, "--cookie-seed", info->specific_options & TARGET_SET_COOKIE_SEED); + param_act(XTF_NO_INVERT, "--cookie-seed", invert); + if (strlen(optarg) != (COOKIE_SEED_LEN * 2)) + xtables_error(PARAMETER_PROBLEM, + "TS3INIT_SET_COOKIE: invalid cookie-seed length"); + if (!hex2int_seed(optarg, info->cookie_seed)) + xtables_error(PARAMETER_PROBLEM, + "TS3INIT_SET_COOKIE: invalid cookie-seed. (not lowercase hex)"); + info->specific_options |= TARGET_SET_COOKIE_SEED; + *flags |= TARGET_SET_COOKIE_SEED; + return true; + + default: + return false; + } +} + +static void ts3init_set_cookie_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_ts3init_set_cookie_tginfo *info = (const void *)target->data; + if (info->specific_options & TARGET_SET_COOKIE_ZERO_RANDOM_SEQUENCE) + { + printf("--zero-random-sequence "); + } + if (info->specific_options & TARGET_SET_COOKIE_SEED) + { + printf("--cookie-seed "); + for (int i = 0; i < COOKIE_SEED_LEN; i++) + { + printf("%02X", info->cookie_seed[i]); + } + printf(" "); + } +} + +static void ts3init_set_cookie_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + printf(" -j TS3INIT_SET_COOKIE "); + ts3init_set_cookie_tg_save(ip, target); +} + +static void ts3init_set_cookie_tg_check(unsigned int flags) +{ + if ((flags & TARGET_SET_COOKIE_SEED) == 0) + { + xtables_error(PARAMETER_PROBLEM, + "TS3INIT_SET_COOKIE: --cookie-seed must be specified"); + } +} + +/* register and init */ +static struct xtables_target ts3init_set_cookie_tg_reg = +{ + .name = "TS3INIT_SET_COOKIE", + .revision = 0, + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_ts3init_set_cookie_tginfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_ts3init_set_cookie_tginfo)), + .help = ts3init_set_cookie_tg_help, + .parse = ts3init_set_cookie_tg_parse, + .print = ts3init_set_cookie_tg_print, + .save = ts3init_set_cookie_tg_save, + .final_check = ts3init_set_cookie_tg_check, + .extra_opts = ts3init_set_cookie_tg_opts, +}; + +static __attribute__((constructor)) void ts3init_set_cookie_tg_ldr(void) +{ + xtables_register_target(&ts3init_set_cookie_tg_reg); +} diff --git a/src/libxt_ts3init_get_cookie.c b/src/libxt_ts3init_get_cookie.c index ba72abc..f8d2230 100644 --- a/src/libxt_ts3init_get_cookie.c +++ b/src/libxt_ts3init_get_cookie.c @@ -14,6 +14,7 @@ #include #include #include +#include "ts3init_cookie_seed.h" #include "ts3init_match.h" #define param_act(t, s, f) xtables_param_act((t), "ts3init_get_cookie", (s), (f)) diff --git a/src/libxt_ts3init_get_puzzle.c b/src/libxt_ts3init_get_puzzle.c index 634b191..aec344e 100644 --- a/src/libxt_ts3init_get_puzzle.c +++ b/src/libxt_ts3init_get_puzzle.c @@ -14,30 +14,12 @@ #include #include #include +#include "ts3init_cookie_seed.h" #include "ts3init_match.h" #define param_act(t, s, f) xtables_param_act((t), "ts3init_get_puzzle", (s), (f)) #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) -static bool hex2int_seed(const char *src, __u8* dst) -{ - for (int i = 0; i < 60; ++i) - { - int v = 0; - for (int j = 0; j < 2; ++j) - { - uint8_t byte = *src++; - if (byte >= '0' && byte <= '9') byte = byte - '0'; - else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10; - else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10; - else return false; - v = (v << 4) | byte; - } - *dst++ = v; - } - return true; -} - static void ts3init_get_puzzle_help(void) { printf( @@ -76,7 +58,7 @@ static int ts3init_get_puzzle_parse(int c, char **argv, int invert, unsigned int case '2': param_act(XTF_ONLY_ONCE, "--check-cookie", info->specific_options & CHK_GET_PUZZLE_CHECK_COOKIE); param_act(XTF_NO_INVERT, "--check-cookie", invert); - if (strlen(optarg) != 120) + if (strlen(optarg) != (COOKIE_SEED_LEN * 2)) xtables_error(PARAMETER_PROBLEM, "ts3init_get_puzzle: invalid cookie-seed length"); if (!hex2int_seed(optarg, info->cookie_seed)) @@ -100,7 +82,7 @@ static void ts3init_get_puzzle_save(const void *ip, const struct xt_entry_match if (info->specific_options & CHK_GET_PUZZLE_CHECK_COOKIE) { printf("--check-cookie "); - for (int i = 0; i < 60; i++) + for (int i = 0; i < COOKIE_SEED_LEN; i++) { printf("%02X", info->cookie_seed[i]); } diff --git a/src/ts3init_cache.c b/src/ts3init_cache.c new file mode 100644 index 0000000..3259559 --- /dev/null +++ b/src/ts3init_cache.c @@ -0,0 +1,118 @@ +/* + * "ts3init" extension for Xtables + * + * Description: A module to aid in ts3 spoof protection + * This is the "caching of cookies" related code + * + * Authors: + * Niels Werensteijn , 2016-10-03 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License; either version 2 + * or 3 of the License, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include "ts3init_cookie.h" +#include "ts3init_cache.h" + +struct ts3init_cache_t +{ + unsigned long saved_jiffies; + time_t unix_time; + struct xt_ts3init_cookie_cache cookie_cache; +}; + +DEFINE_PER_CPU(struct ts3init_cache_t, ts3init_cache); + +static inline void update_cache_time(unsigned long jifs, + struct ts3init_cache_t* cache) +{ + if (((long)jifs - (long)cache->saved_jiffies) >= HZ) + { + /* it's been 1 second sinds last time update. + * Get the new unix time and cache it*/ + struct timeval tv; + cache->saved_jiffies = jifs; + do_gettimeofday(&tv); + cache->unix_time = tv.tv_sec; + } +} + +time_t get_cached_unix_time(void) +{ + struct ts3init_cache_t* cache; + unsigned long jifs; + time_t current_unix_time; + + jifs = jiffies; + + cache = &get_cpu_var(ts3init_cache); + + update_cache_time(jifs, cache); + + current_unix_time = cache->unix_time; + + put_cpu_var(ts3init_cache); + + return current_unix_time; +} + +bool get_cookie_for_package_index(u8 packet_index, const u8* seed, u64 (*cookie)[2]) +{ + struct ts3init_cache_t* cache; + u64* result; + unsigned long jifs; + time_t current_unix_time; + + jifs = jiffies; + cache = &get_cpu_var(ts3init_cache); + + update_cache_time(jifs, cache); + + current_unix_time = cache->unix_time; + + result = ts3init_get_cookie_seed(current_unix_time, + packet_index, &cache->cookie_cache, seed); + + if (result) + { + (*cookie)[0] = result[0]; + (*cookie)[1] = result[1]; + } + put_cpu_var(ts3init_cache); + return result; +} + +bool get_current_cookie(const u8* seed, u64 (*cookie)[2], u8 *packet_index) +{ + struct ts3init_cache_t* cache; + u64* result; + unsigned long jifs; + time_t current_unix_time; + + jifs = jiffies; + cache = &get_cpu_var(ts3init_cache); + + update_cache_time(jifs, cache); + + current_unix_time = cache->unix_time; + + *packet_index = current_unix_time % 8; + + result = ts3init_get_cookie_seed(current_unix_time, + *packet_index, &cache->cookie_cache, seed); + + if (result) + { + (*cookie)[0] = result[0]; + (*cookie)[1] = result[1]; + } + put_cpu_var(ts3init_cache); + return result; +} \ No newline at end of file diff --git a/src/ts3init_cache.h b/src/ts3init_cache.h new file mode 100644 index 0000000..f30d8c4 --- /dev/null +++ b/src/ts3init_cache.h @@ -0,0 +1,10 @@ +#ifndef _TS3INIT_CACHE_H +#define _TS3INIT_CACHE_H + +time_t get_cached_unix_time(void); + +bool get_cookie_for_package_index(u8 packet_index, const u8* seed, u64 (*cookie)[2]); + +bool get_current_cookie(const u8* seed, u64 (*cookie)[2], u8 *packet_index); + +#endif /* _TS3INIT_CACHE_H */ diff --git a/src/ts3init_cookie.c b/src/ts3init_cookie.c index 81a3589..44c9333 100644 --- a/src/ts3init_cookie.c +++ b/src/ts3init_cookie.c @@ -19,6 +19,7 @@ #include #include #include "siphash24.h" +#include "ts3init_cookie_seed.h" #include "ts3init_cookie.h" static void check_update_seed_cache(time_t time, __u8 index, @@ -33,10 +34,10 @@ static void check_update_seed_cache(time_t time, __u8 index, if (time == cache->time[index]) return; /* We need to update the cache. */ - /* seed = sha512(cookie_seed[60] + __le32 time) */ + /* seed = sha512(cookie_seed[COOKIE_SEED_LEN] + __le32 time) */ seed_hash_time = cpu_to_le32( (__u32)time); sg_init_table(sg, ARRAY_SIZE(sg)); - sg_set_buf(&sg[0], cookie_seed, 60); + sg_set_buf(&sg[0], cookie_seed, COOKIE_SEED_LEN); sg_set_buf(&sg[1], &seed_hash_time, 4); desc.tfm = crypto_alloc_hash("sha512", 0, 0); @@ -93,7 +94,8 @@ __u64* ts3init_get_cookie_seed(time_t current_time, __u8 packet_index, return cache->seed64 + ((SIP_KEY_SIZE/sizeof(__u64)) * packet_index ); } -int ts3init_calculate_cookie(const struct sk_buff *skb, struct xt_action_param *par, struct udphdr *udp, u64 k0, u64 k1, __u64* out) +int ts3init_calculate_cookie(const struct sk_buff *skb, const struct xt_action_param *par, + struct udphdr *udp, __u64 k0, __u64 k1, __u64* out) { int addr_offset; int addr_len; diff --git a/src/ts3init_cookie.h b/src/ts3init_cookie.h index f528ab0..c1f2072 100644 --- a/src/ts3init_cookie.h +++ b/src/ts3init_cookie.h @@ -21,7 +21,7 @@ __u64* ts3init_get_cookie_seed(time_t current_time, __u8 packet_index, const __u8* cookie_seed); int ts3init_calculate_cookie(const struct sk_buff *skb, - struct xt_action_param *par, struct udphdr *udp, - u64 k0, u64 k1, __u64* out); - + const struct xt_action_param *par, struct udphdr *udp, + __u64 k0, __u64 k1, __u64* out); + #endif /* _TS3INIT_COOKIE_H */ diff --git a/src/ts3init_cookie_seed.h b/src/ts3init_cookie_seed.h new file mode 100644 index 0000000..ec3fe92 --- /dev/null +++ b/src/ts3init_cookie_seed.h @@ -0,0 +1,28 @@ +#ifndef _TS3INIT_COOKIE_SEED_H +#define _TS3INIT_COOKIE_SEED_H + +enum { + COOKIE_SEED_LEN = 60 +}; + +static inline bool hex2int_seed(const char *src, __u8* dst) +{ + int i, j; + for (i = 0; i < 60; ++i) + { + int v = 0; + for ( j = 0; j < 2; ++j) + { + uint8_t byte = *src++; + if (byte >= '0' && byte <= '9') byte = byte - '0'; + else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10; + else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10; + else return false; + v = (v << 4) | byte; + } + *dst++ = v; + } + return true; +} + +#endif /* _TS3INIT_COOKIE_SEED_H */ diff --git a/src/ts3init_match.c b/src/ts3init_match.c index acb2db4..330da7d 100644 --- a/src/ts3init_match.c +++ b/src/ts3init_match.c @@ -16,19 +16,13 @@ #include #include #include -#include #include #include -#include "ts3init_match.h" +#include "ts3init_cookie_seed.h" #include "ts3init_cookie.h" +#include "ts3init_match.h" #include "ts3init_header.h" - -struct ts3init_cache_t -{ - unsigned long saved_jiffies; - time_t unix_time; - struct xt_ts3init_cookie_cache cookie_cache; -}; +#include "ts3init_cache.h" static const struct ts3_init_header_tag ts3init_header_tag_signature = { .tag8 = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'} }; @@ -36,8 +30,6 @@ static const struct ts3_init_header_tag ts3init_header_tag_signature = static const int header_size = 18; static int ts3init_payload_sizes[] = { 16, 20, 20, 244, -1, 1 }; -DEFINE_PER_CPU(struct ts3init_cache_t, ts3init_cache); - static bool check_header(const struct sk_buff *skb, const struct xt_action_param *par, struct ts3_init_checked_header_data* header_data, __u32 min_client_version) { @@ -88,20 +80,6 @@ static bool check_header(const struct sk_buff *skb, const struct xt_action_param return true; } -static inline void update_cache_time(unsigned long jifs, - struct ts3init_cache_t* cache) -{ - if (((long)jifs - (long)cache->saved_jiffies) >= HZ) - { - /* it's been 1 second sinds last time update. - * Get the new unix time and cache it*/ - struct timeval tv; - cache->saved_jiffies = jifs; - do_gettimeofday(&tv); - cache->unix_time = tv.tv_sec; - } -} - static bool ts3init_get_cookie_mt(const struct sk_buff *skb, struct xt_action_param *par) { @@ -115,19 +93,9 @@ ts3init_get_cookie_mt(const struct sk_buff *skb, struct xt_action_param *par) if (info->specific_options & CHK_GET_COOKIE_CHECK_TIMESTAMP) { - struct ts3init_cache_t* cache; - unsigned long jifs; time_t current_unix_time, packet_unix_time; - jifs = jiffies; - - cache = &get_cpu_var(ts3init_cache); - - update_cache_time(jifs, cache); - - current_unix_time = cache->unix_time; - - put_cpu_var(ts3init_cache); + current_unix_time = get_cached_unix_time(); packet_unix_time = header_data.ts3_header->payload[0] << 24 | @@ -179,39 +147,16 @@ ts3init_get_puzzle_mt(const struct sk_buff *skb, struct xt_action_param *par) if (info->specific_options & CHK_GET_PUZZLE_CHECK_COOKIE) { - struct ts3init_cache_t* cache; struct ts3_init_header* ts3_header = header_data.ts3_header; - __u64* cookie_seed, cookie_seed0, cookie_seed1; + __u64 cookie_seed[2]; __u64 cookie, packet_cookie; - unsigned long jifs; - time_t current_unix_time; - - jifs = jiffies; - cache = &get_cpu_var(ts3init_cache); - - update_cache_time(jifs, cache); - - current_unix_time = cache->unix_time; - - cookie_seed = ts3init_get_cookie_seed(current_unix_time, - ts3_header->payload[8], &cache->cookie_cache, - info->cookie_seed); - - if (!cookie_seed) - { - put_cpu_var(ts3init_cache); + if (get_cookie_for_package_index(ts3_header->payload[8], info->cookie_seed, &cookie_seed) == false) return false; - } - - cookie_seed0 = cookie_seed[0]; - cookie_seed1 = cookie_seed[1]; - - put_cpu_var(ts3init_cache); /* use cookie_seed and ipaddress and port to create a hash * (cookie) for this connection */ - if (ts3init_calculate_cookie(skb, par, header_data.udp, cookie_seed0, cookie_seed1, &cookie)) + if (ts3init_calculate_cookie(skb, par, header_data.udp, cookie_seed[0], cookie_seed[1], &cookie)) return false; /*something went wrong*/ /* compare cookie with payload bytes 0-7. if equal, cookie @@ -245,7 +190,7 @@ static int ts3init_get_puzzle_mt_check(const struct xt_mtchk_param *par) if (info->specific_options & ~(CHK_GET_PUZZLE_VALID_MASK)) { - printk(KERN_INFO KBUILD_MODNAME ": invalid (specific) options for get_cookie\n"); + printk(KERN_INFO KBUILD_MODNAME ": invalid (specific) options for get_puzzle\n"); return -EINVAL; } @@ -256,7 +201,7 @@ static int ts3init_get_puzzle_mt_check(const struct xt_mtchk_param *par) static struct xt_match ts3init_mt_reg[] __read_mostly = { { - .name = "ts3init_get_cookie", + .name = "TS3INIT_GET_COOKIE", .revision = 0, .family = NFPROTO_IPV4, .proto = IPPROTO_UDP, @@ -266,7 +211,7 @@ static struct xt_match ts3init_mt_reg[] __read_mostly = .me = THIS_MODULE, }, { - .name = "ts3init_get_cookie", + .name = "TS3INIT_GET_COOKIE", .revision = 0, .family = NFPROTO_IPV6, .proto = IPPROTO_UDP, @@ -276,7 +221,7 @@ static struct xt_match ts3init_mt_reg[] __read_mostly = .me = THIS_MODULE, }, { - .name = "ts3init_get_puzzle", + .name = "TS3INIT_GET_PUZZLE", .revision = 0, .family = NFPROTO_IPV4, .proto = IPPROTO_UDP, @@ -286,7 +231,7 @@ static struct xt_match ts3init_mt_reg[] __read_mostly = .me = THIS_MODULE, }, { - .name = "ts3init_get_puzzle", + .name = "TS3INIT_GET_PUZZLE", .revision = 0, .family = NFPROTO_IPV6, .proto = IPPROTO_UDP, diff --git a/src/ts3init_match.h b/src/ts3init_match.h index 6bbd1b7..9030fd3 100644 --- a/src/ts3init_match.h +++ b/src/ts3init_match.h @@ -32,8 +32,6 @@ enum { CHK_GET_PUZZLE_CHECK_COOKIE = 1 << 0, CHK_GET_PUZZLE_VALID_MASK = (1 << 1) - 1, - - SEED_LEN = 60 }; struct xt_ts3init_get_puzzle_mtinfo @@ -42,7 +40,7 @@ struct xt_ts3init_get_puzzle_mtinfo __u8 specific_options; __u16 reserved1; __u32 min_client_version; - __u8 cookie_seed[SEED_LEN]; + __u8 cookie_seed[COOKIE_SEED_LEN]; }; #endif /* _TS3INIT_MATCH_H */ diff --git a/src/ts3init_target.c b/src/ts3init_target.c index 84ab4ae..0d8120a 100644 --- a/src/ts3init_target.c +++ b/src/ts3init_target.c @@ -26,22 +26,24 @@ #include #include #include "compat_xtables.h" +#include "ts3init_cookie_seed.h" +#include "ts3init_cookie.h" #include "ts3init_target.h" #include "ts3init_header.h" +#include "ts3init_cache.h" -bool -ts3init_send_ipv6(const struct sk_buff *oldskb, const struct xt_action_param *par, u8 command, const void *payload, const size_t payload_size) +static bool +ts3init_prepare_ipv6_reply(const struct sk_buff *oldskb, const struct xt_action_param *par, + const size_t payload_size, struct sk_buff **newskb, struct ipv6hdr **newip, + struct udphdr **newudp, u8 **payload) { const struct udphdr *oldudp; const struct ipv6hdr *oldip; - struct udphdr *newudp, oldudp_buf; - struct ipv6hdr *newip; - struct sk_buff *newskb; - struct flowi6 fl; - struct dst_entry *dst = NULL; - struct net *net = dev_net((par->in != NULL) ? par->in : par->out); - + struct udphdr *udp, oldudp_buf; + struct ipv6hdr *ip; + struct sk_buff *skb; + oldip = ipv6_hdr(oldskb); oldudp = skb_header_pointer(oldskb, par->thoff, sizeof(*oldudp), &oldudp_buf); @@ -50,76 +52,91 @@ ts3init_send_ipv6(const struct sk_buff *oldskb, const struct xt_action_param *pa if (ntohs(oldudp->len) <= sizeof(*oldudp)) return false; - newskb = alloc_skb(LL_MAX_HEADER + sizeof(*newip) + - sizeof(*newudp) + payload_size, GFP_ATOMIC); - if (newskb == NULL) + skb = alloc_skb(LL_MAX_HEADER + sizeof(*ip) + + sizeof(*udp) + payload_size, GFP_ATOMIC); + if (skb == NULL) return false; - skb_reserve(newskb, LL_MAX_HEADER); - newskb->protocol = oldskb->protocol; - - skb_reset_network_header(newskb); - newip = (void *)skb_put(newskb, sizeof(*newip)); - newip->version = oldip->version; - newip->priority = oldip->priority; - memcpy(newip->flow_lbl, oldip->flow_lbl, sizeof(newip->flow_lbl)); - newip->nexthdr = par->target->proto; - newip->saddr = oldip->daddr; - newip->daddr = oldip->saddr; - - skb_reset_transport_header(newskb); - newudp = (void *)skb_put(newskb, sizeof(*newudp)); - newudp->source = oldudp->dest; - newudp->dest = oldudp->source; - newudp->len = htons(sizeof(*newudp) + payload_size); + skb_reserve(skb, LL_MAX_HEADER); + skb->protocol = oldskb->protocol; + + skb_reset_network_header(skb); + ip = (void *)skb_put(skb, sizeof(*ip)); + ip->version = oldip->version; + ip->priority = oldip->priority; + memcpy(ip->flow_lbl, oldip->flow_lbl, sizeof(ip->flow_lbl)); + ip->nexthdr = par->target->proto; + ip->saddr = oldip->daddr; + ip->daddr = oldip->saddr; + + skb_reset_transport_header(skb); + udp = (void *)skb_put(skb, sizeof(*udp)); + udp->source = oldudp->dest; + udp->dest = oldudp->source; + udp->len = htons(sizeof(*udp) + payload_size); + + *payload = skb_put(skb, payload_size); + ip->payload_len = htons(skb->len); + + *newskb = skb; + *newip = ip; + *newudp = udp; + return true; +} - memcpy(skb_put(newskb, payload_size), payload, payload_size); - newip->payload_len = htons(newskb->len); +static bool +ts3init_send_ipv6_reply(struct sk_buff *oldskb, const struct xt_action_param *par, + struct sk_buff *skb, struct ipv6hdr *ip, struct udphdr *udp) +{ + struct flowi6 fl; + struct dst_entry *dst = NULL; + struct net *net = dev_net((par->in != NULL) ? par->in : par->out); - newudp->check = 0; - newudp->check = csum_ipv6_magic(&newip->saddr, &newip->daddr, - ntohs(newudp->len), IPPROTO_UDP, - csum_partial(newudp, ntohs(newudp->len), 0)); + udp->check = 0; + udp->check = csum_ipv6_magic(&ip->saddr, &ip->daddr, + ntohs(udp->len), IPPROTO_UDP, + csum_partial(udp, ntohs(udp->len), 0)); memset(&fl, 0, sizeof(fl)); - fl.flowi6_proto = newip->nexthdr; - memcpy(&fl.saddr, &newip->saddr, sizeof(fl.saddr)); - memcpy(&fl.daddr, &newip->daddr, sizeof(fl.daddr)); - fl.fl6_sport = newudp->source; - fl.fl6_dport = newudp->dest; + fl.flowi6_proto = ip->nexthdr; + memcpy(&fl.saddr, &ip->saddr, sizeof(fl.saddr)); + memcpy(&fl.daddr, &ip->daddr, sizeof(fl.daddr)); + fl.fl6_sport = udp->source; + fl.fl6_dport = udp->dest; security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi(&fl)); dst = ip6_route_output(net, NULL, &fl); - if (dst == NULL || dst->error != 0) - { + if (dst == NULL || dst->error != 0) { dst_release(dst); goto free_nskb; } - skb_dst_set(newskb, dst); - newip->hop_limit = ip6_dst_hoplimit(skb_dst(newskb)); - newskb->ip_summed = CHECKSUM_NONE; + skb_dst_set(skb, dst); + ip->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); + skb->ip_summed = CHECKSUM_NONE; /* "Never happens" (?) */ - if (newskb->len > dst_mtu(skb_dst(newskb))) + if (skb->len > dst_mtu(skb_dst(skb))) goto free_nskb; - nf_ct_attach(newskb, oldskb); - ip6_local_out(par_net(par), newskb->sk, newskb); + nf_ct_attach(skb, oldskb); + ip6_local_out(par_net(par), skb->sk, skb); return true; free_nskb: - kfree_skb(newskb); + kfree_skb(skb); return false; } -bool -ts3init_send_ipv4(const struct sk_buff *oldskb, const struct xt_action_param *par, u8 command, const void *payload, const size_t payload_size) +static bool +ts3init_prepare_ipv4_reply(const struct sk_buff *oldskb, const struct xt_action_param *par, + const size_t payload_size, struct sk_buff **newskb, struct iphdr **newip, + struct udphdr **newudp, u8 **payload) { const struct udphdr *oldudp; const struct iphdr *oldip; - struct udphdr *newudp, oldudp_buf; - struct iphdr *newip; - struct sk_buff *newskb; + struct udphdr *udp, oldudp_buf; + struct iphdr *ip; + struct sk_buff *skb; oldip = ip_hdr(oldskb); oldudp = skb_header_pointer(oldskb, par->thoff, @@ -129,96 +146,261 @@ ts3init_send_ipv4(const struct sk_buff *oldskb, const struct xt_action_param *pa if (ntohs(oldudp->len) <= sizeof(*oldudp)) return false; - newskb = alloc_skb(LL_MAX_HEADER + sizeof(*newip) + - sizeof(*newudp) + payload_size, GFP_ATOMIC); - if (newskb == NULL) + skb = alloc_skb(LL_MAX_HEADER + sizeof(*ip) + + sizeof(*udp) + payload_size, GFP_ATOMIC); + if (skb == NULL) return false; - skb_reserve(newskb, LL_MAX_HEADER); - newskb->protocol = oldskb->protocol; - - skb_reset_network_header(newskb); - newip = (void *)skb_put(newskb, sizeof(*newip)); - newip->version = oldip->version; - newip->ihl = sizeof(*newip) / 4; - newip->tos = oldip->tos; - newip->id = oldip->id; - newip->frag_off = 0; - newip->protocol = oldip->protocol; - newip->check = 0; - newip->saddr = oldip->daddr; - newip->daddr = oldip->saddr; - - skb_reset_transport_header(newskb); - newudp = (void *)skb_put(newskb, sizeof(*newudp)); - newudp->source = oldudp->dest; - newudp->dest = oldudp->source; - newudp->len = htons(sizeof(*newudp) + payload_size); - - memcpy(skb_put(newskb, payload_size), payload, payload_size); - newip->tot_len = htons(newskb->len); - - newudp->check = 0; - newudp->check = csum_tcpudp_magic(newip->saddr, newip->daddr, - ntohs(newudp->len), IPPROTO_UDP, - csum_partial(newudp, ntohs(newudp->len), 0)); + skb_reserve(skb, LL_MAX_HEADER); + skb->protocol = oldskb->protocol; + + skb_reset_network_header(skb); + ip = (void *)skb_put(skb, sizeof(*ip)); + ip->version = oldip->version; + ip->ihl = sizeof(*ip) / 4; + ip->tos = oldip->tos; + ip->id = oldip->id; + ip->frag_off = 0; + ip->protocol = oldip->protocol; + ip->check = 0; + ip->saddr = oldip->daddr; + ip->daddr = oldip->saddr; + + skb_reset_transport_header(skb); + udp = (void *)skb_put(skb, sizeof(*udp)); + udp->source = oldudp->dest; + udp->dest = oldudp->source; + udp->len = htons(sizeof(*udp) + payload_size); + + *payload = skb_put(skb, payload_size); + ip->tot_len = htons(skb->len); + + *newskb = skb; + *newip = ip; + *newudp = udp; + return true; +} + +static bool +ts3init_send_ipv4_reply(struct sk_buff *oldskb, const struct xt_action_param *par, + struct sk_buff *skb, struct iphdr *ip, struct udphdr *udp) +{ + udp->check = 0; + udp->check = csum_tcpudp_magic(ip->saddr, ip->daddr, + ntohs(udp->len), IPPROTO_UDP, + csum_partial(udp, ntohs(udp->len), 0)); /* ip_route_me_harder expects the skb's dst to be set */ - skb_dst_set(newskb, dst_clone(skb_dst(oldskb))); + skb_dst_set(skb, dst_clone(skb_dst(oldskb))); - if (ip_route_me_harder(par_net(par), newskb, RTN_UNSPEC) != 0) + if (ip_route_me_harder(par_net(par), skb, RTN_UNSPEC) != 0) goto free_nskb; - newip->ttl = ip4_dst_hoplimit(skb_dst(newskb)); - newskb->ip_summed = CHECKSUM_NONE; + ip->ttl = ip4_dst_hoplimit(skb_dst(skb)); + skb->ip_summed = CHECKSUM_NONE; /* "Never happens" (?) */ - if (newskb->len > dst_mtu(skb_dst(newskb))) + if (skb->len > dst_mtu(skb_dst(skb))) goto free_nskb; - nf_ct_attach(newskb, oldskb); - ip_local_out(par_net(par), newskb->sk, newskb); + nf_ct_attach(skb, oldskb); + ip_local_out(par_net(par), skb->sk, skb); return true; free_nskb: - kfree_skb(newskb); + kfree_skb(skb); return false; } static const char ts3init_reset_package[] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1', 0x65, 0, 0x88, COMMAND_RESET_PUZZLE, 0 }; static unsigned int -ts3init_reset_tg(struct sk_buff *skb, const struct xt_action_param *par) +ts3init_reset_ipv4_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct sk_buff *newskb; + struct iphdr *newip; + struct udphdr *newudp; + u8 *payload; + + if (ts3init_prepare_ipv4_reply(skb, par, sizeof(ts3init_reset_package), &newskb, &newip, &newudp, &payload)) + { + memcpy(payload, ts3init_reset_package, sizeof(ts3init_reset_package)); + ts3init_send_ipv4_reply(skb, par, newskb, newip, newudp); + } + return NF_DROP; +} + +static unsigned int +ts3init_reset_ipv6_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct sk_buff *newskb; + struct ipv6hdr *newip; + struct udphdr *newudp; + u8 *payload; + + if (ts3init_prepare_ipv6_reply(skb, par, sizeof(ts3init_reset_package), &newskb, &newip, &newudp, &payload)) + { + memcpy(payload, ts3init_reset_package, sizeof(ts3init_reset_package)); + ts3init_send_ipv6_reply(skb, par, newskb, newip, newudp); + } + return NF_DROP; +} + +static const char set_cookie_package_header[12] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1', 0x65, 0, 0x88, COMMAND_SET_COOKIE }; + +static bool +ts3init_fill_set_cookie_payload(const struct sk_buff *skb, const struct xt_action_param *par, + struct sk_buff *newskb, struct udphdr *newudp, u8 *newpayload) +{ + const struct xt_ts3init_set_cookie_tginfo *info = par->targinfo; + u8 *payload, payload_buf[34]; + __u64 cookie[2]; + u64 cookie_hash; + u8 packet_index; + + if (get_current_cookie(info->cookie_seed, &cookie, &packet_index) == false) + return false; + + if (ts3init_calculate_cookie(newskb, par, newudp, cookie[0], cookie[1], &cookie_hash)) + return false; + + memcpy(newpayload, set_cookie_package_header, sizeof(set_cookie_package_header)); + newpayload[12] = (u8)cookie_hash; + newpayload[13] = (u8)(cookie_hash >> 8); + newpayload[14] = (u8)(cookie_hash >> 16); + newpayload[15] = (u8)(cookie_hash >> 24); + newpayload[16] = (u8)(cookie_hash >> 32); + newpayload[17] = (u8)(cookie_hash >> 40); + newpayload[18] = (u8)(cookie_hash >> 48); + newpayload[19] = (u8)(cookie_hash >> 56); + newpayload[20] = packet_index; + if (info->specific_options & TARGET_SET_COOKIE_ZERO_RANDOM_SEQUENCE) + { + memset(&newpayload[21], 0, 11); + } + else + { + memset(&newpayload[21], 0, 7); + payload = skb_header_pointer(skb, par->thoff + sizeof(struct udphdr), + sizeof(payload_buf), payload_buf); + if (payload == NULL) + { + printk(KERN_WARNING KBUILD_MODNAME ": was expecting a ts3init_get_cookie package. Use -m ts3init_get_cookie!\n"); + return false; + } + newpayload[28] = payload[25]; + newpayload[29] = payload[24]; + newpayload[30] = payload[23]; + newpayload[31] = payload[22]; + } + return true; +} + +static unsigned int +ts3init_set_cookie_ipv4_tg(struct sk_buff *skb, const struct xt_action_param *par) { - switch (par->family) + struct sk_buff *newskb; + struct iphdr *newip; + struct udphdr *newudp; + u8 *payload; + const int payload_size = sizeof(set_cookie_package_header) + 20; + if (ts3init_prepare_ipv4_reply(skb, par, payload_size, &newskb, &newip, &newudp, &payload)) { - case NFPROTO_IPV4: - ts3init_send_ipv4(skb, par, COMMAND_RESET_PUZZLE, ts3init_reset_package, sizeof(ts3init_reset_package)); - break; + if (ts3init_fill_set_cookie_payload(skb, par, newskb, newudp, payload)) + { + ts3init_send_ipv4_reply(skb, par, newskb, newip, newudp); + } + else + { + kfree_skb(newskb); + } + } + return NF_DROP; +} - case NFPROTO_IPV6: - ts3init_send_ipv6(skb, par, COMMAND_RESET_PUZZLE, ts3init_reset_package, sizeof(ts3init_reset_package)); - break; +static unsigned int +ts3init_set_cookie_ipv6_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct sk_buff *newskb; + struct ipv6hdr *newip; + struct udphdr *newudp; + u8 *payload; + const int payload_size = sizeof(set_cookie_package_header) + 20; + + if (ts3init_prepare_ipv6_reply(skb, par, payload_size, &newskb, &newip, &newudp, &payload)) + { + if (ts3init_fill_set_cookie_payload(skb, par, newskb, newudp, payload)) + { + ts3init_send_ipv6_reply(skb, par, newskb, newip, newudp); + } + else + { + kfree_skb(newskb); + } } return NF_DROP; } -static struct xt_target ts3init_tg_reg[] __read_mostly = +static int ts3init_set_cookie_tg_check(const struct xt_tgchk_param *par) { + struct xt_ts3init_set_cookie_tginfo *info = par->targinfo; + + if (! (par->family == NFPROTO_IPV4 || par->family == NFPROTO_IPV6)) + { + printk(KERN_INFO KBUILD_MODNAME ": invalid protocol (only ipv4 and ipv6) for TS3INIT_SET_COOKIE\n"); + return -EINVAL; + } + + if (info->common_options & ~(TARGET_COMMON_VALID_MASK)) + { + printk(KERN_INFO KBUILD_MODNAME ": invalid (common) options for TS3INIT_SET_COOKIE\n"); + return -EINVAL; + } + + if (info->specific_options & ~(TARGET_SET_COOKIE_VALID_MASK)) + { + printk(KERN_INFO KBUILD_MODNAME ": invalid (specific) options for TS3INIT_SET_COOKIE\n"); + return -EINVAL; + } + + return 0; +} + +static struct xt_target ts3init_tg_reg[] __read_mostly = { + { + .name = "TS3INIT_RESET", + .revision = 0, + .family = NFPROTO_IPV4, + .proto = IPPROTO_UDP, + .target = ts3init_reset_ipv4_tg, + .me = THIS_MODULE, + }, + { + .name = "TS3INIT_RESET", + .revision = 0, + .family = NFPROTO_IPV6, + .proto = IPPROTO_UDP, + .target = ts3init_reset_ipv6_tg, + .me = THIS_MODULE, + }, { - .name = "ts3init_reset", + .name = "TS3INIT_SET_COOKIE", .revision = 0, .family = NFPROTO_IPV4, .proto = IPPROTO_UDP, - .target = ts3init_reset_tg, + .targetsize = sizeof(struct xt_ts3init_set_cookie_tginfo), + .target = ts3init_set_cookie_ipv4_tg, + .checkentry = ts3init_set_cookie_tg_check, .me = THIS_MODULE, }, { - .name = "ts3init_reset", + .name = "TS3INIT_SET_COOKIE", .revision = 0, .family = NFPROTO_IPV6, .proto = IPPROTO_UDP, - .target = ts3init_reset_tg, + .targetsize = sizeof(struct xt_ts3init_set_cookie_tginfo), + .target = ts3init_set_cookie_ipv6_tg, + .checkentry = ts3init_set_cookie_tg_check, .me = THIS_MODULE, }, }; diff --git a/src/ts3init_target.h b/src/ts3init_target.h index 6d047d5..5b17d0a 100644 --- a/src/ts3init_target.h +++ b/src/ts3init_target.h @@ -1,4 +1,27 @@ #ifndef _TS3INIT_TARGET_H #define _TS3INIT_TARGET_H +/* Common Enums for targets */ +enum +{ + TARGET_COMMON_VALID_MASK = (1 << 0) -1 +}; + +/* Enums and structs for set_cookie */ +enum +{ + TARGET_SET_COOKIE_ZERO_RANDOM_SEQUENCE = 1 << 0, + TARGET_SET_COOKIE_SEED = 1 << 1, + TARGET_SET_COOKIE_VALID_MASK = (1 << 2) - 1 +}; + + +struct xt_ts3init_set_cookie_tginfo +{ + __u8 common_options; + __u8 specific_options; + __u16 reserved1; + __u8 cookie_seed[COOKIE_SEED_LEN]; +}; + #endif /* _TS3INIT_TARGET_H */