diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index 255a6cf..b90a6f2 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index a5b6689..38a4d2b 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index 23faad9..915a873 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index 0034203..920cabc 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index f1a5ccf..7d04126 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index e37512b..7b64639 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index 8069433..59a010c 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index 36efcd7..31ddab1 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/docs/changes.txt b/docs/changes.txt index 202f983..a2b0d50 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -206,3 +206,7 @@ install_easy : openrc support v42 blockcheck.sh + +v43 + +nfqws: UDP desync with conntrack support (any-protocol only for now) diff --git a/docs/readme.eng.md b/docs/readme.eng.md index 65c623a..7c955e3 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -1,7 +1,7 @@ ## What is it for A stand-alone (without 3rd party servers) DPI circumvention tool. -May allow to bypass http(s) website blocking or speed shaping, resist signature tcp protocol discovery. +May allow to bypass http(s) website blocking or speed shaping, resist signature tcp/udp protocol discovery. The project is mainly aimed at the Russian audience to fight russian regulator named "Roskomnadzor". Some features of the project are russian reality specific (such as getting list of sites @@ -154,6 +154,7 @@ nfqws takes the following parameters: --dpi-desync-fake-http= ; file containing fake http request. replacement for built-in --dpi-desync-fake-tls= ; file containing fake TLS ClientHello (for https). replacement for built-in --dpi-desync-fake-unknown= ; file containing unknown protocol fake payload. default is 256 zeroes + --dpi-desync-fake-unknown-udp= ; file containing unknown udp protocol fake payload --dpi-desync-cutoff=[n|d|s]N ; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N --hostlist= ; apply fooling only to the listed hosts (one host per line, subdomains auto apply) ``` @@ -380,6 +381,17 @@ If the connection falls out of the conntrack and --dpi-desync-cutoff is set, dpi Set conntrack timeouts appropriately. +### UDP support + +UDP attacks are limited. Its not possible to fragment UDP on transport level, only on network (ip) level. +IP fragmentation is not implemented now. +No protocol recognition is implemented yet so only - `-dpi-desync-any-protocol` will work. +Conntrack supports udp. `--dpi-desync-cutoff` will work. UDP conntrack timeout can be set in the 4th +parameter of `--ctrack-timeouts`. +Fake attack is useful only for stateful DPI and useless for stateless dealing with each packet independently. +By default fake payload is 64 zeroes. Can be overriden using `--dpi-desync-fake-unknown-udp`. + + ## tpws tpws is transparent proxy. diff --git a/docs/readme.txt b/docs/readme.txt index 5dc59ce..43647d6 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -1,4 +1,4 @@ -zapret v.42 +zapret v.43 English ------- @@ -9,7 +9,7 @@ For english version refer to docs/readme.eng.txt ----------------- Автономное, без задействования сторонних серверов, средство противодействия DPI. -Может помочь обойти блокировки или замедление сайтов http(s), сигнатурный анализ tcp протоколов, +Может помочь обойти блокировки или замедление сайтов http(s), сигнатурный анализ tcp и udp протоколов, например с целью блокировки VPN. Проект нацелен прежде всего на маломощные embedded устройства - роутеры, работающие под openwrt. @@ -182,7 +182,7 @@ nfqws --wsize=[:] ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !) --wssize=[:] ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !) --wssize-cutoff=[n|d|s]N ; изменять server window size в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N - --ctrack-timeouts=S:E:F ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN. по умолчанию 60:300:60 + --ctrack-timeouts=S:E:F[:U] ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN, таймаут udp. по умолчанию 60:300:60:60 --hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:". --hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета --hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase @@ -202,6 +202,7 @@ nfqws --dpi-desync-fake-http= ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному w3.org --dpi-desync-fake-tls= ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному w3.org --dpi-desync-fake-unknown= ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт + --dpi-desync-fake-unknown-udp= ; файл, содержащий фейковый пейлоад неизвестного udp протокола для dpi-desync=fake, на замену стандартным нулям 64 байт --dpi-desync-cutoff=[n|d|s]N ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N --hostlist= ; применять дурение только к хостам из листа @@ -415,6 +416,14 @@ window size итоговый размер окна стал максимальн На склонных к бездействию соединениях следует изменить таймауты conntrack. Если соединение выпало из conntrack и задана опция --dpi-desync-cutoff, dpi desync применяться не будет. +ПОДДЕРЖКА UDP +Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip. +ip фрагментация на данный момент не реализована. +Пока что реализован только метод десинхронизации fake в режиме --dpi-desync-any-protocol. +Реализован conntrack для udp. Можно пользоваться --dpi-desync-cutoff. Таймаут conntrack для udp +можно изменить 4-м параметром в --ctrack-timeouts. +Атака fake полезна только для stateful DPI, она бесполезна для анализа на уровне отдельных пакетов. +По умолчанию fake наполнение - 64 нуля. Можно указать файл в --dpi-desync-fake-unknown-udp. tpws ----- diff --git a/nfq/checksum.c b/nfq/checksum.c index 09be36b..dcc3657 100644 --- a/nfq/checksum.c +++ b/nfq/checksum.c @@ -11,6 +11,9 @@ static uint16_t from64to16(uint64_t x) return (uint16_t)u + (uint16_t)(u>>16); } +// this function preserves data alignment requirements (otherwise it will be damn slow on mips arch) +// and uses 64-bit arithmetics to improve speed +// taken from linux source code static uint16_t do_csum(const uint8_t * buff, size_t len) { uint8_t odd; @@ -122,12 +125,12 @@ uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8 void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr) { tcp->th_sum = 0; - tcp->th_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_TCP,csum_partial(tcp, len)); + tcp->th_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_TCP,csum_partial(tcp,len)); } void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr) { tcp->th_sum = 0; - tcp->th_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp, len)); + tcp->th_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp,len)); } void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr) { @@ -136,3 +139,21 @@ void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const st else if (ip6hdr) tcp6_fix_checksum(tcp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst); } + +void udp4_fix_checksum(struct udphdr *udp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr) +{ + udp->uh_sum = 0; + udp->uh_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_UDP,csum_partial(udp,len)); +} +void udp6_fix_checksum(struct udphdr *udp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr) +{ + udp->uh_sum = 0; + udp->uh_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_UDP,csum_partial(udp,len)); +} +void udp_fix_checksum(struct udphdr *udp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr) +{ + if (ip) + udp4_fix_checksum(udp, len, &ip->ip_src, &ip->ip_dst); + else if (ip6hdr) + udp6_fix_checksum(udp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst); +} diff --git a/nfq/checksum.h b/nfq/checksum.h index 6776739..a10c5df 100644 --- a/nfq/checksum.h +++ b/nfq/checksum.h @@ -7,13 +7,19 @@ #include #include #include +#include uint16_t csum_partial(const void *buff, size_t len); uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum); uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum); + uint16_t ip4_compute_csum(const void *buff, size_t len); void ip4_fix_checksum(struct ip *ip); void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr); void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr); void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr); + +void udp4_fix_checksum(struct udphdr *udp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr); +void udp6_fix_checksum(struct udphdr *udp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr); +void udp_fix_checksum(struct udphdr *udp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr); diff --git a/nfq/conntrack.c b/nfq/conntrack.c index d9ed424..bd7e7f9 100644 --- a/nfq/conntrack.c +++ b/nfq/conntrack.c @@ -12,88 +12,68 @@ static void ut_oom_recover(void *elem) oom = true; } - static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"}; -#define _connswap \ - memset(c2,0,sizeof(*c2)); \ - c2->e1 = c->e2; \ - c2->e2 = c->e1; - -static void connswap4(const t_conn4 *c, t_conn4 *c2) +static void connswap(const t_conn *c, t_conn *c2) { - _connswap -} -static void connswap6(const t_conn6 *c, t_conn6 *c2) -{ - _connswap + memset(c2,0,sizeof(*c2)); + c2->l3proto = c->l3proto; + c2->l4proto = c->l4proto; + c2->src = c->dst; + c2->dst = c->src; + c2->sport = c->dport; + c2->dport = c->sport; } -#define _ConntrackPoolDestroy(v) \ - t_conntrack##v *elem, *tmp; \ +static void ConntrackPoolDestroyPool(t_conntrack_pool **pp) +{ + t_conntrack_pool *elem, *tmp; HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); free(elem); } -static void ConntrackPoolDestroy4(t_conntrack4 **pp) -{ - _ConntrackPoolDestroy(4) -} -static void ConntrackPoolDestroy6(t_conntrack6 **pp) -{ - _ConntrackPoolDestroy(6) } void ConntrackPoolDestroy(t_conntrack *p) { - ConntrackPoolDestroy4(&p->pool4); - ConntrackPoolDestroy6(&p->pool6); + ConntrackPoolDestroyPool(&p->pool); } -void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin) +void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp) { p->timeout_syn = timeout_syn; p->timeout_established = timeout_established; p->timeout_fin = timeout_fin; + p->timeout_udp= timeout_udp; p->t_purge_interval = purge_interval; time(&p->t_last_purge); - p->pool4 = NULL; - p->pool6 = NULL; + p->pool = NULL; } - -#define _ConntrackExtractConn(v) \ - memset(c,0,sizeof(*c)); \ - if (bReverse) { \ - c->e1.adr = ip->ip##v##_dst; \ - c->e2.adr = ip->ip##v##_src; \ - c->e1.port = htons(tcphdr->th_dport); \ - c->e2.port = htons(tcphdr->th_sport); \ - } else { \ - c->e1.adr = ip->ip##v##_src; \ - c->e2.adr = ip->ip##v##_dst; \ - c->e1.port = htons(tcphdr->th_sport); \ - c->e2.port = htons(tcphdr->th_dport); \ +void ConntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr) +{ + memset(c,0,sizeof(*c)); + if (ip) + { + c->l3proto = IPPROTO_IP; + c->dst.ip = bReverse ? ip->ip_src : ip->ip_dst; + c->src.ip = bReverse ? ip->ip_dst : ip->ip_src; } -void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr) -{ - _ConntrackExtractConn() -} -void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr) -{ - _ConntrackExtractConn(6) + else if (ip6) + { + c->l3proto = IPPROTO_IPV6; + c->dst.ip6 = bReverse ? ip6->ip6_src : ip6->ip6_dst; + c->src.ip6 = bReverse ? ip6->ip6_dst : ip6->ip6_src; + } + else + c->l3proto = -1; + extract_ports(tcphdr, udphdr, &c->l4proto, bReverse ? &c->dport : &c->sport, bReverse ? &c->sport : &c->dport); } -#define _ConntrackPoolSearch(v) \ - t_conntrack##v *t; \ - HASH_FIND(hh, p, c, sizeof(*c), t); \ + +static t_conntrack_pool *ConntrackPoolSearch(t_conntrack_pool *p, const t_conn *c) +{ + t_conntrack_pool *t; + HASH_FIND(hh, p, c, sizeof(*c), t); return t; -t_conntrack4 *ConntrackPoolSearch4(t_conntrack4 *p, const t_conn4 *c) -{ - _ConntrackPoolSearch(4) } -t_conntrack6 *ConntrackPoolSearch6(t_conntrack6 *p, const t_conn6 *c) -{ - _ConntrackPoolSearch(6) -} - static void ConntrackInitTrack(t_ctrack *t) { @@ -102,206 +82,218 @@ static void ConntrackInitTrack(t_ctrack *t) time(&t->t_start); } -#define _ConntrackNew(v) \ - t_conntrack##v *new; \ - if (!(new = calloc(1,sizeof(*new)))) return NULL; \ - new->conn = *c; \ - oom = false; \ - HASH_ADD(hh, *pp, conn, sizeof(*c), new); \ - if (oom) { free(new); return NULL; } \ - ConntrackInitTrack(&new->track); \ + +static t_conntrack_pool *ConntrackNew(t_conntrack_pool **pp, const t_conn *c) +{ + t_conntrack_pool *new; + if (!(new = malloc(sizeof(*new)))) return NULL; + new->conn = *c; + oom = false; + HASH_ADD(hh, *pp, conn, sizeof(*c), new); + if (oom) { free(new); return NULL; } + ConntrackInitTrack(&new->track); return new; -static t_conntrack4 *ConntrackNew4(t_conntrack4 **pp, const t_conn4 *c) -{ - _ConntrackNew(4) -} -static t_conntrack6 *ConntrackNew6(t_conntrack6 **pp, const t_conn6 *c) -{ - _ConntrackNew(6) } - +// non-tcp packets are passed with tcphdr=NULL but len_payload filled static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload) { uint8_t scale; - if (tcp_syn_segment(tcphdr)) - { - if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry - t->seq0 = htonl(tcphdr->th_seq); - } - else if (tcp_synack_segment(tcphdr)) - { - if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry - if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1; - t->ack0 = htonl(tcphdr->th_seq); - } - else if (tcphdr->th_flags & (TH_FIN|TH_RST)) - { - t->state = FIN; - } - else - { - if (t->state==SYN) - { - t->state=ESTABLISHED; - if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1; - } - } - scale = tcp_find_scale_factor(tcphdr); + if (bReverse) { - t->pos_orig = t->seq_last = htonl(tcphdr->th_ack); - t->ack_last = htonl(tcphdr->th_seq); - t->pos_reply = t->ack_last + len_payload; t->pcounter_reply++; t->pdcounter_reply+=!!len_payload; - t->winsize_reply = htons(tcphdr->th_win); - if (scale!=SCALE_NONE) t->scale_reply = scale; } else { - t->seq_last = htonl(tcphdr->th_seq); - t->pos_orig = t->seq_last + len_payload; - t->pos_reply = t->ack_last = htonl(tcphdr->th_ack); t->pcounter_orig++; t->pdcounter_orig+=!!len_payload; - t->winsize_orig = htons(tcphdr->th_win); - if (scale!=SCALE_NONE) t->scale_orig = scale; } + + if (tcphdr) + { + if (tcp_syn_segment(tcphdr)) + { + if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry + t->seq0 = htonl(tcphdr->th_seq); + } + else if (tcp_synack_segment(tcphdr)) + { + if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry + if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1; + t->ack0 = htonl(tcphdr->th_seq); + } + else if (tcphdr->th_flags & (TH_FIN|TH_RST)) + { + t->state = FIN; + } + else + { + if (t->state==SYN) + { + t->state=ESTABLISHED; + if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1; + } + } + scale = tcp_find_scale_factor(tcphdr); + if (bReverse) + { + t->pos_orig = t->seq_last = htonl(tcphdr->th_ack); + t->ack_last = htonl(tcphdr->th_seq); + t->pos_reply = t->ack_last + len_payload; + t->winsize_reply = htons(tcphdr->th_win); + if (scale!=SCALE_NONE) t->scale_reply = scale; + + } + else + { + t->seq_last = htonl(tcphdr->th_seq); + t->pos_orig = t->seq_last + len_payload; + t->pos_reply = t->ack_last = htonl(tcphdr->th_ack); + t->winsize_orig = htons(tcphdr->th_win); + if (scale!=SCALE_NONE) t->scale_orig = scale; + } + } + else + { + if (bReverse) + { + t->ack_last=t->pos_reply; + t->pos_reply+=len_payload; + } + else + { + t->seq_last=t->pos_orig; + t->pos_orig+=len_payload; + } + } + time(&t->t_last); } -#define _ConntrackPoolFeed(v) \ - t_conn##v conn, connswap; \ - t_conntrack##v *ctr; \ - bool b_rev; \ - ConntrackExtractConn##v(&conn,false,ip,tcphdr); \ - if ((ctr=ConntrackPoolSearch##v(*pp,&conn))) \ - { \ - ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); \ - goto ok; \ - } \ - else \ - { \ - connswap##v(&conn,&connswap); \ - if ((ctr=ConntrackPoolSearch##v(*pp,&connswap))) \ - { \ - ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); \ - goto ok; \ - } \ - } \ - b_rev = tcp_synack_segment(tcphdr); \ - if (tcp_syn_segment(tcphdr) || b_rev) \ - { \ - if ((ctr=ConntrackNew##v(pp, b_rev ? &connswap : &conn))) \ - { \ - ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); \ - goto ok; \ - } \ - } \ - return false; \ -ok: \ - if (ctrack) *ctrack = &ctr->track; \ - if (bReverse) *bReverse = b_rev; \ - return true; -static bool ConntrackPoolFeed4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse) +static bool ConntrackPoolFeedPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse) { - _ConntrackPoolFeed(4) -} -static bool ConntrackPoolFeed6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr, uint32_t len_payload, t_ctrack **ctrack, bool *bReverse) -{ - _ConntrackPoolFeed(6) -} -bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse) -{ - return ip ? ConntrackPoolFeed4(&p->pool4,ip,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : ip6 ? ConntrackPoolFeed6(&p->pool6,ip6,tcphdr,(uint32_t)len_payload,ctrack,bReverse) : false; -} + t_conn conn, connswp; + t_conntrack_pool *ctr; + bool b_rev; - -#define _ConntrackPoolDrop(v) \ - t_conn##v conn, connswap; \ - t_conntrack##v *t; \ - ConntrackExtractConn##v(&conn,false,ip,tcphdr); \ - if (!(t=ConntrackPoolSearch##v(*pp,&conn))) \ - { \ - connswap##v(&conn,&connswap); \ - t=ConntrackPoolSearch##v(*pp,&connswap); \ - } \ - if (!t) return false; \ - HASH_DEL(*pp, t); free(t); \ - return true; -static bool ConntrackPoolDrop4(t_conntrack4 **pp, const struct ip *ip, const struct tcphdr *tcphdr) -{ - _ConntrackPoolDrop(4) -} -static bool ConntrackPoolDrop6(t_conntrack6 **pp, const struct ip6_hdr *ip, const struct tcphdr *tcphdr) -{ - _ConntrackPoolDrop(6) -} -bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr) -{ - return ip ? ConntrackPoolDrop4(&p->pool4,ip,tcphdr) : ip6 ? ConntrackPoolDrop6(&p->pool6,ip6,tcphdr) : false; -} - -#define _ConntrackPoolPurge(v, pp) \ - { \ - t_conntrack##v *t, *tmp; \ - time_t tidle; \ - HASH_ITER(hh, *pp , t, tmp) { \ - tidle = tnow - t->track.t_last; \ - if ( t->track.b_cutoff || \ - t->track.state==SYN && tidle>=p->timeout_syn || \ - t->track.state==ESTABLISHED && tidle>=p->timeout_established || \ - t->track.state==FIN && tidle>=p->timeout_fin) \ - { \ - HASH_DEL(*pp, t); free(t); \ - } \ - } \ + ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr); + if ((ctr=ConntrackPoolSearch(*pp,&conn))) + { + ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); + goto ok; } + else + { + connswap(&conn,&connswp); + if ((ctr=ConntrackPoolSearch(*pp,&connswp))) + { + ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); + goto ok; + } + } + b_rev = tcphdr && tcp_synack_segment(tcphdr); + if (tcphdr && tcp_syn_segment(tcphdr) || b_rev || udphdr) + { + if ((ctr=ConntrackNew(pp, b_rev ? &connswp : &conn))) + { + ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); + goto ok; + } + } + return false; +ok: + if (ctrack) *ctrack = &ctr->track; + if (bReverse) *bReverse = b_rev; + return true; +} +bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse) +{ + return ConntrackPoolFeedPool(&p->pool,ip,ip6,tcphdr,udphdr,len_payload,ctrack,bReverse); +} + +static bool ConntrackPoolDropPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr) +{ + t_conn conn, connswp; + t_conntrack_pool *t; + ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr); + if (!(t=ConntrackPoolSearch(*pp,&conn))) + { + connswap(&conn,&connswp); + t=ConntrackPoolSearch(*pp,&connswp); + } + if (!t) return false; + HASH_DEL(*pp, t); free(t); + return true; +} +bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr) +{ + return ConntrackPoolDropPool(&p->pool,ip,ip6,tcphdr,udphdr); +} void ConntrackPoolPurge(t_conntrack *p) { - time_t tnow = time(NULL); \ + time_t tidle, tnow = time(NULL); + t_conntrack_pool *t, *tmp; + if ((tnow - p->t_last_purge)>=p->t_purge_interval) { - _ConntrackPoolPurge(4, &p->pool4); - _ConntrackPoolPurge(6, &p->pool6); + HASH_ITER(hh, p->pool , t, tmp) { + tidle = tnow - t->track.t_last; + if ( t->track.b_cutoff || + t->conn.l4proto==IPPROTO_TCP && ( + t->track.state==SYN && tidle>=p->timeout_syn || + t->track.state==ESTABLISHED && tidle>=p->timeout_established || + t->track.state==FIN && tidle>=p->timeout_fin) || + t->conn.l4proto==IPPROTO_UDP && + tidle>=p->timeout_udp) + { + HASH_DEL(p->pool, t); free(t); + } + } p->t_last_purge = tnow; } } +static void taddr2str(uint8_t l3proto, const t_addr *a, char *buf, size_t bufsize) +{ + if (!inet_ntop(family_from_proto(l3proto), a, buf, bufsize) && bufsize) *buf=0; +} -#define _ConntrackPoolDump(v,f) \ - t_conntrack##v *t, *tmp; \ - char sa1[40],sa2[40]; \ - time_t tnow = time(NULL); \ - HASH_ITER(hh, p, t, tmp) { \ - *sa1=0; inet_ntop(AF_INET##f, &t->conn.e1.adr, sa1, sizeof(sa1)); \ - *sa2=0; inet_ntop(AF_INET##f, &t->conn.e2.adr, sa2, sizeof(sa2)); \ - printf("[%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d cutoff=%u wss_cutoff=%u d_cutoff=%u\n", \ - sa1, t->conn.e1.port, sa2, t->conn.e2.port, \ - connstate_s[t->track.state], \ - (unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), \ - (unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, \ - (unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply,\ - t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0, \ - t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0, \ - t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, \ - t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply, \ - t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff); \ +void ConntrackPoolDump(const t_conntrack *p) +{ + t_conntrack_pool *t, *tmp; + char sa1[40],sa2[40]; + time_t tnow = time(NULL); + HASH_ITER(hh, p->pool, t, tmp) { + taddr2str(t->conn.l3proto, &t->conn.src, sa1, sizeof(sa1)); + taddr2str(t->conn.l3proto, &t->conn.dst, sa2, sizeof(sa2)); + if (t->conn.l4proto==IPPROTO_TCP) + printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d", + proto_name(t->conn.l4proto), + sa1, t->conn.sport, sa2, t->conn.dport, + t->conn.l4proto==IPPROTO_TCP ? connstate_s[t->track.state] : "-", + (unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), + (unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, + (unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply, + t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0, + t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0, + t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, + t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply); + else + printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu rseq=%u pos_orig=%u rack=%u pos_reply=%u", + proto_name(t->conn.l4proto), + sa1, t->conn.sport, sa2, t->conn.dport, + t->conn.l4proto==IPPROTO_TCP ? connstate_s[t->track.state] : "-", + (unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), + (unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, + (unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply, + t->track.seq_last, t->track.pos_orig, + t->track.ack_last, t->track.pos_reply); + printf(" cutoff=%u wss_cutoff=%u d_cutoff=%u\n", + t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff); }; -void ConntrackPoolDump4(t_conntrack4 *p) -{ - _ConntrackPoolDump(4,) -} -void ConntrackPoolDump6(t_conntrack6 *p) -{ - _ConntrackPoolDump(6,6) -} -void ConntrackPoolDump(t_conntrack *p) -{ - ConntrackPoolDump4(p->pool4); - ConntrackPoolDump6(p->pool6); } diff --git a/nfq/conntrack.h b/nfq/conntrack.h index 9f63af1..a9b220d 100644 --- a/nfq/conntrack.h +++ b/nfq/conntrack.h @@ -12,6 +12,7 @@ #include #include #include +#include //#define HASH_BLOOM 20 #define HASH_NONFATAL_OOM 1 @@ -19,26 +20,18 @@ #define HASH_FUNCTION HASH_BER #include "uthash.h" + +typedef union { + struct in_addr ip; + struct in6_addr ip6; +} t_addr; typedef struct { - struct in_addr adr; - uint16_t port; -} t_endpoint4; -typedef struct -{ - struct in6_addr adr; - uint16_t port; -} t_endpoint6; -// e1 - initiator. the one who have sent SYN -// e2 - acceptor -typedef struct -{ - t_endpoint4 e1,e2; -} t_conn4; -typedef struct -{ - t_endpoint6 e1,e2; -} t_conn6; + t_addr src, dst; + uint16_t sport,dport; + uint8_t l3proto; // IPPROTO_IP, IPPROTO_IPV6 + uint8_t l4proto; // IPPROTO_TCP, IPPROTO_UDP +} t_conn; // SYN - SYN or SYN/ACK received // ESTABLISHED - any except SYN or SYN/ACK received @@ -46,53 +39,41 @@ typedef struct typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate; typedef struct { - t_connstate state; + // common state time_t t_start, t_last; - uint32_t seq0, ack0; // starting seq and ack - uint32_t seq_last, ack_last; // last seen seq and ack - uint32_t pos_orig, pos_reply; // seq_last+payload, ack_last+payload uint64_t pcounter_orig, pcounter_reply; // packet counter uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload) + uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current + uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current + + // tcp only state, not used in udp + t_connstate state; + uint32_t seq0, ack0; // starting seq and ack uint16_t winsize_orig, winsize_reply; // last seen window size uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none bool b_cutoff; // mark for deletion - bool b_wssize_cutoff, b_desync_cutoff; } t_ctrack; -// use separate pools for ipv4 and ipv6 to save RAM. otherwise could use union key typedef struct { t_ctrack track; UT_hash_handle hh; // makes this structure hashable - t_conn4 conn; // key -} t_conntrack4; -typedef struct -{ - t_ctrack track; - UT_hash_handle hh; // makes this structure hashable - t_conn6 conn; // key -} t_conntrack6; + t_conn conn; // key +} t_conntrack_pool; typedef struct { // inactivity time to purge an entry in each connection state - uint32_t timeout_syn,timeout_established,timeout_fin; + uint32_t timeout_syn,timeout_established,timeout_fin,timeout_udp; time_t t_purge_interval, t_last_purge; - t_conntrack4 *pool4; - t_conntrack6 *pool6; + t_conntrack_pool *pool; } t_conntrack; -void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin); +void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp); void ConntrackPoolDestroy(t_conntrack *p); -bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse); -bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr); - -void ConntrackExtractConn4(t_conn4 *c, bool bReverse, const struct ip *ip, const struct tcphdr *tcphdr); -void ConntrackExtractConn6(t_conn6 *c, bool bReverse, const struct ip6_hdr *ip, const struct tcphdr *tcphdr); - -void ConntrackPoolDump4(t_conntrack4 *p); -void ConntrackPoolDump6(t_conntrack6 *p); -void ConntrackPoolDump(t_conntrack *p); - +bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse); +bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr); +void CaonntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr); +void ConntrackPoolDump(const t_conntrack *p); void ConntrackPoolPurge(t_conntrack *p); diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index 500ee5d..b8c0671 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -67,9 +67,9 @@ static void fill_tcphdr( uint8_t t=0; memset(tcp,0,sizeof(*tcp)); - tcp->th_sport = nsport; - tcp->th_dport = ndport; - if (fooling & TCP_FOOL_BADSEQ) + tcp->th_sport = nsport; + tcp->th_dport = ndport; + if (fooling & FOOL_BADSEQ) { tcp->th_seq = net32_add(nseq,badseq_increment); tcp->th_ack = net32_add(nack_seq,badseq_ack_increment); @@ -82,7 +82,7 @@ static void fill_tcphdr( tcp->th_off = 5; *((uint8_t*)tcp+13)= tcp_flags; tcp->th_win = nwsize; - if (fooling & TCP_FOOL_MD5SIG) + if (fooling & FOOL_MD5SIG) { tcpopt[0] = 19; // kind tcpopt[1] = 18; // len @@ -92,13 +92,13 @@ static void fill_tcphdr( *(uint32_t*)(tcpopt+14)=random(); t=18; } - if (timestamps || (fooling & TCP_FOOL_TS)) + if (timestamps || (fooling & FOOL_TS)) { tcpopt[t] = 8; // kind tcpopt[t+1] = 10; // len // forge only TSecr if orig timestamp is present *(uint32_t*)(tcpopt+t+2) = timestamps ? timestamps[0] : -1; - *(uint32_t*)(tcpopt+t+6) = (timestamps && !(fooling & TCP_FOOL_TS)) ? timestamps[1] : -1; + *(uint32_t*)(tcpopt+t+6) = (timestamps && !(fooling & FOOL_TS)) ? timestamps[1] : -1; t+=10; } if (scale_factor!=SCALE_NONE) @@ -109,16 +109,518 @@ static void fill_tcphdr( } while (t&3) tcpopt[t++]=1; // noop tcp->th_off += t>>2; + tcp->th_sum = 0; } static uint16_t tcpopt_len(uint8_t fooling, const uint32_t *timestamps, uint8_t scale_factor) { uint16_t t=0; - if (fooling & TCP_FOOL_MD5SIG) t=18; - if ((fooling & TCP_FOOL_TS) || timestamps) t+=10; + if (fooling & FOOL_MD5SIG) t=18; + if ((fooling & FOOL_TS) || timestamps) t+=10; if (scale_factor!=SCALE_NONE) t+=3; return (t+3)&~3; } +// n prefix (nsport, nwsize) means network byte order +static void fill_udphdr(struct udphdr *udp, uint16_t nsport, uint16_t ndport, uint16_t len_payload) +{ + udp->uh_sport = nsport; + udp->uh_dport = ndport; + udp->uh_ulen = htons(len_payload+sizeof(struct udphdr)); + udp->uh_sum = 0; +} + +static void fill_iphdr(struct ip *ip, const struct in_addr *src, const struct in_addr *dst, uint16_t pktlen, uint8_t proto, uint8_t ttl) +{ + ip->ip_off = 0; + ip->ip_v = 4; + ip->ip_hl = 5; + ip->ip_len = htons(pktlen); + ip->ip_id = 0; + ip->ip_ttl = ttl; + ip->ip_p = proto; + ip->ip_src = *src; + ip->ip_dst = *dst; +} +static void fill_ip6hdr(struct ip6_hdr *ip6, const struct in6_addr *src, const struct in6_addr *dst, uint16_t payloadlen, uint8_t proto, uint8_t ttl) +{ + ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000); + ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(payloadlen); + ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = proto; + ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl; + ip6->ip6_src = *src; + ip6->ip6_dst = *dst; +} + +bool prepare_tcp_segment4( + const struct sockaddr_in *src, const struct sockaddr_in *dst, + uint8_t tcp_flags, + uint32_t nseq, uint32_t nack_seq, + uint16_t nwsize, + uint8_t scale_factor, + uint32_t *timestamps, + uint8_t ttl, + uint8_t fooling, + uint32_t badseq_increment, + uint32_t badseq_ack_increment, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen) +{ + uint16_t tcpoptlen = tcpopt_len(fooling,timestamps,scale_factor); + uint16_t ip_payload_len = sizeof(struct tcphdr) + tcpoptlen + len; + uint16_t pktlen = sizeof(struct ip) + ip_payload_len; + if (pktlen>*buflen) return false; + + struct ip *ip = (struct ip*)buf; + struct tcphdr *tcp = (struct tcphdr*)(ip+1); + uint8_t *payload = (uint8_t*)(tcp+1)+tcpoptlen; + + fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_TCP, ttl); + fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin_port,dst->sin_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment); + + memcpy(payload,data,len); + tcp4_fix_checksum(tcp,ip_payload_len,&ip->ip_src,&ip->ip_dst); + if (fooling & FOOL_BADSUM) tcp->th_sum^=htons(0xBEAF); + + *buflen = pktlen; + return true; +} + +bool prepare_tcp_segment6( + const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, + uint8_t tcp_flags, + uint32_t nseq, uint32_t nack_seq, + uint16_t nwsize, + uint8_t scale_factor, + uint32_t *timestamps, + uint8_t ttl, + uint8_t fooling, + uint32_t badseq_increment, + uint32_t badseq_ack_increment, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen) +{ + uint16_t tcpoptlen = tcpopt_len(fooling,timestamps,scale_factor); + uint16_t ip_payload_len = sizeof(struct tcphdr) + tcpoptlen + len; + uint16_t pktlen = sizeof(struct ip6_hdr) + ip_payload_len; + if (pktlen>*buflen) return false; + + struct ip6_hdr *ip6 = (struct ip6_hdr*)buf; + struct tcphdr *tcp = (struct tcphdr*)(ip6+1); + uint8_t *payload = (uint8_t*)(tcp+1)+tcpoptlen; + + fill_ip6hdr(ip6, &src->sin6_addr, &dst->sin6_addr, ip_payload_len, IPPROTO_TCP, ttl); + fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin6_port,dst->sin6_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment); + + memcpy(payload,data,len); + tcp6_fix_checksum(tcp,ip_payload_len,&ip6->ip6_src,&ip6->ip6_dst); + if (fooling & FOOL_BADSUM) tcp->th_sum^=htons(0xBEAF); + + *buflen = pktlen; + return true; +} + +bool prepare_tcp_segment( + const struct sockaddr *src, const struct sockaddr *dst, + uint8_t tcp_flags, + uint32_t nseq, uint32_t nack_seq, + uint16_t nwsize, + uint8_t scale_factor, + uint32_t *timestamps, + uint8_t ttl, + uint8_t fooling, + uint32_t badseq_increment, + uint32_t badseq_ack_increment, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen) +{ + return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ? + prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) : + (src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ? + prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) : + false; +} + + +bool prepare_udp_segment4( + const struct sockaddr_in *src, const struct sockaddr_in *dst, + uint8_t ttl, + uint8_t fooling, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen) +{ + uint16_t ip_payload_len = sizeof(struct udphdr) + len; + uint16_t pktlen = sizeof(struct ip) + ip_payload_len; + if (pktlen>*buflen) return false; + + struct ip *ip = (struct ip*)buf; + struct udphdr *udp = (struct udphdr*)(ip+1); + uint8_t *payload = (uint8_t*)(udp+1); + + fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_UDP, ttl); + fill_udphdr(udp, src->sin_port, dst->sin_port, len); + + memcpy(payload,data,len); + udp4_fix_checksum(udp,ip_payload_len,&ip->ip_src,&ip->ip_dst); + if (fooling & FOOL_BADSUM) udp->uh_sum^=htons(0xBEAF); + + *buflen = pktlen; + return true; +} +bool prepare_udp_segment6( + const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, + uint8_t ttl, + uint8_t fooling, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen) +{ + uint16_t ip_payload_len = sizeof(struct udphdr) + len; + uint16_t pktlen = sizeof(struct ip6_hdr) + ip_payload_len; + if (pktlen>*buflen) return false; + + struct ip6_hdr *ip6 = (struct ip6_hdr*)buf; + struct udphdr *udp = (struct udphdr*)(ip6+1); + uint8_t *payload = (uint8_t*)(udp+1); + + fill_ip6hdr(ip6, &src->sin6_addr, &dst->sin6_addr, ip_payload_len, IPPROTO_UDP, ttl); + fill_udphdr(udp, src->sin6_port, dst->sin6_port, len); + + memcpy(payload,data,len); + udp6_fix_checksum(udp,ip_payload_len,&ip6->ip6_src,&ip6->ip6_dst); + if (fooling & FOOL_BADSUM) udp->uh_sum^=htons(0xBEAF); + + *buflen = pktlen; + return true; +} +bool prepare_udp_segment( + const struct sockaddr *src, const struct sockaddr *dst, + uint8_t ttl, + uint8_t fooling, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen) +{ + return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ? + prepare_udp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,ttl,fooling,data,len,buf,buflen) : + (src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ? + prepare_udp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,ttl,fooling,data,len,buf,buflen) : + false; +} + + + +void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport) +{ + if (sport) *sport = htons(tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0); + if (dport) *dport = htons(tcphdr ? tcphdr->th_dport : udphdr ? udphdr->uh_dport : 0); + if (proto) *proto = tcphdr ? IPPROTO_TCP : udphdr ? IPPROTO_UDP : -1; +} + +void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst) +{ + if (ip) + { + struct sockaddr_in *si; + + if (dst) + { + si = (struct sockaddr_in*)dst; + si->sin_family = AF_INET; + si->sin_port = tcphdr ? tcphdr->th_dport : udphdr ? udphdr->uh_dport : 0; + si->sin_addr = ip->ip_dst; + } + + if (src) + { + si = (struct sockaddr_in*)src; + si->sin_family = AF_INET; + si->sin_port = tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0; + si->sin_addr = ip->ip_src; + } + } + else if (ip6hdr) + { + struct sockaddr_in6 *si; + + if (dst) + { + si = (struct sockaddr_in6*)dst; + si->sin6_family = AF_INET6; + si->sin6_port = tcphdr ? tcphdr->th_dport : udphdr ? udphdr->uh_dport : 0; + si->sin6_addr = ip6hdr->ip6_dst; + si->sin6_flowinfo = 0; + si->sin6_scope_id = 0; + } + + if (src) + { + si = (struct sockaddr_in6*)src; + si->sin6_family = AF_INET6; + si->sin6_port = tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0; + si->sin6_addr = ip6hdr->ip6_src; + si->sin6_flowinfo = 0; + si->sin6_scope_id = 0; + } + } +} + +const char *proto_name(uint8_t proto) +{ + switch(proto) + { + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_UDP: + return "udp"; + case IPPROTO_ICMP: + return "icmp"; + case IPPROTO_IGMP: + return "igmp"; + case IPPROTO_ESP: + return "esp"; + case IPPROTO_AH: + return "ah"; + case IPPROTO_IPV6: + return "6in4"; + case IPPROTO_IPIP: + return "4in4"; + case IPPROTO_GRE: + return "gre"; +#ifdef IPPROTO_SCTP + case IPPROTO_SCTP: + return "sctp"; +#endif + default: + return NULL; + } +} +static void str_proto_name(char *s, size_t s_len, uint8_t proto) +{ + const char *name = proto_name(proto); + if (name) + snprintf(s,s_len,"%s",name); + else + snprintf(s,s_len,"%u",proto); +} +uint16_t family_from_proto(uint8_t l3proto) +{ + switch(l3proto) + { + case IPPROTO_IP: return AF_INET; + case IPPROTO_IPV6: return AF_INET6; + default: return -1; + } +} + +static void str_srcdst_ip(char *s, size_t s_len, const void *saddr,const void *daddr) +{ + char s_ip[16],d_ip[16]; + *s_ip=*d_ip=0; + inet_ntop(AF_INET, saddr, s_ip, sizeof(s_ip)); + inet_ntop(AF_INET, daddr, d_ip, sizeof(d_ip)); + snprintf(s,s_len,"%s => %s",s_ip,d_ip); +} +static void str_ip(char *s, size_t s_len, const struct ip *ip) +{ + char ss[35],s_proto[16]; + str_srcdst_ip(ss,sizeof(ss),&ip->ip_src,&ip->ip_dst); + str_proto_name(s_proto,sizeof(s_proto),ip->ip_p); + snprintf(s,s_len,"%s proto=%s",ss,s_proto); +} +void print_ip(const struct ip *ip) +{ + char s[64]; + str_ip(s,sizeof(s),ip); + printf("%s",s); +} +static void str_srcdst_ip6(char *s, size_t s_len, const void *saddr,const void *daddr) +{ + char s_ip[40],d_ip[40]; + *s_ip=*d_ip=0; + inet_ntop(AF_INET6, saddr, s_ip, sizeof(s_ip)); + inet_ntop(AF_INET6, daddr, d_ip, sizeof(d_ip)); + snprintf(s,s_len,"%s => %s",s_ip,d_ip); +} +static void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto) +{ + char ss[83],s_proto[16]; + str_srcdst_ip6(ss,sizeof(ss),&ip6hdr->ip6_src,&ip6hdr->ip6_dst); + str_proto_name(s_proto,sizeof(s_proto),proto); + snprintf(s,s_len,"%s proto=%s",ss,s_proto); +} +void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto) +{ + char s[128]; + str_ip6hdr(s,sizeof(s),ip6hdr,proto); + printf("%s",s); +} +static void str_tcphdr(char *s, size_t s_len, const struct tcphdr *tcphdr) +{ + char flags[7],*f=flags; + if (tcphdr->th_flags & TH_SYN) *f++='S'; + if (tcphdr->th_flags & TH_ACK) *f++='A'; + if (tcphdr->th_flags & TH_RST) *f++='R'; + if (tcphdr->th_flags & TH_FIN) *f++='F'; + if (tcphdr->th_flags & TH_PUSH) *f++='P'; + if (tcphdr->th_flags & TH_URG) *f++='U'; + *f=0; + snprintf(s,s_len,"sport=%u dport=%u flags=%s seq=%u ack_seq=%u",htons(tcphdr->th_sport),htons(tcphdr->th_dport),flags,htonl(tcphdr->th_seq),htonl(tcphdr->th_ack)); +} +void print_tcphdr(const struct tcphdr *tcphdr) +{ + char s[80]; + str_tcphdr(s,sizeof(s),tcphdr); + printf("%s",s); +} +static void str_udphdr(char *s, size_t s_len, const struct udphdr *udphdr) +{ + snprintf(s,s_len,"sport=%u dport=%u",htons(udphdr->uh_sport),htons(udphdr->uh_dport)); +} +void print_udphdr(const struct udphdr *udphdr) +{ + char s[30]; + str_udphdr(s,sizeof(s),udphdr); + printf("%s",s); +} + + + + +bool proto_check_ipv4(const uint8_t *data, size_t len) +{ + return len >= 20 && (data[0] & 0xF0) == 0x40 && + len >= ((data[0] & 0x0F) << 2); +} +// move to transport protocol +void proto_skip_ipv4(uint8_t **data, size_t *len) +{ + size_t l; + + l = (**data & 0x0F) << 2; + *data += l; + *len -= l; +} +bool proto_check_tcp(const uint8_t *data, size_t len) +{ + return len >= 20 && len >= ((data[12] & 0xF0) >> 2); +} +void proto_skip_tcp(uint8_t **data, size_t *len) +{ + size_t l; + l = ((*data)[12] & 0xF0) >> 2; + *data += l; + *len -= l; +} +bool proto_check_udp(const uint8_t *data, size_t len) +{ + return len >= 8 && len>=(data[4]<<8 | data[5]); +} +void proto_skip_udp(uint8_t **data, size_t *len) +{ + *data += 8; + *len -= 8; +} + +bool proto_check_ipv6(const uint8_t *data, size_t len) +{ + return len >= 40 && (data[0] & 0xF0) == 0x60 && + (len - 40) >= htons(*(uint16_t*)(data + 4)); // payload length +} +// move to transport protocol +// proto_type = 0 => error +void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type) +{ + size_t hdrlen; + uint8_t HeaderType; + + if (proto_type) *proto_type = 0; // put error in advance + + HeaderType = (*data)[6]; // NextHeader field + *data += 40; *len -= 40; // skip ipv6 base header + while (*len > 0) // need at least one byte for NextHeader field + { + switch (HeaderType) + { + case 0: // Hop-by-Hop Options + case 43: // routing + case 51: // authentication + case 60: // Destination Options + case 135: // mobility + case 139: // Host Identity Protocol Version v2 + case 140: // Shim6 + if (*len < 2) return; // error + hdrlen = 8 + ((*data)[1] << 3); + break; + case 44: // fragment. length fixed to 8, hdrlen field defined as reserved + hdrlen = 8; + break; + case 59: // no next header + return; // error + default: + // we found some meaningful payload. it can be tcp, udp, icmp or some another exotic shit + if (proto_type) *proto_type = HeaderType; + return; + } + if (*len < hdrlen) return; // error + HeaderType = **data; + // advance to the next header location + *len -= hdrlen; + *data += hdrlen; + } + // we have garbage +} + +bool tcp_synack_segment(const struct tcphdr *tcphdr) +{ + /* check for set bits in TCP hdr */ + return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN)); +} +bool tcp_syn_segment(const struct tcphdr *tcphdr) +{ + /* check for set bits in TCP hdr */ + return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_SYN); +} +bool tcp_ack_segment(const struct tcphdr *tcphdr) +{ + /* check for set bits in TCP hdr */ + return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_ACK); +} + +void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor) +{ + uint8_t *scale,scale_factor_old; + + if (scale_factor!=SCALE_NONE) + { + scale = tcp_find_option(tcp,3); // tcp option 3 - scale factor + if (scale && scale[1]==3) // length should be 3 + { + scale_factor_old=scale[2]; + // do not allow increasing scale factor + if (scale_factor>=scale_factor_old) + DLOG("Scale factor %u unchanged\n", scale_factor_old) + else + { + scale[2]=scale_factor; + DLOG("Scale factor change %u => %u\n", scale_factor_old, scale_factor) + } + } + } +} +// scale_factor=SCALE_NONE - do not change +void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor) +{ + uint16_t winsize_old; + + winsize_old = htons(tcp->th_win); // << scale_factor; + tcp->th_win = htons(winsize); + DLOG("Window size change %u => %u\n", winsize_old, winsize) + + tcp_rewrite_wscale(tcp, scale_factor); +} + + + + + static int rawsend_sock4=-1, rawsend_sock6=-1; static void rawsend_clean_sock(int *sock) { @@ -288,7 +790,7 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t // BSD ipv6 raw socks are limited. cannot pass the whole packet with ip6 header. struct sockaddr_storage sa_src; int v; - extract_endpoints(NULL,(struct ip6_hdr *)data,NULL, &sa_src, NULL); + extract_endpoints(NULL,(struct ip6_hdr *)data,NULL,NULL, &sa_src, NULL); v = ((struct ip6_hdr *)data)->ip6_ctlun.ip6_un1.ip6_un1_hlim; if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &v, sizeof(v)) == -1) perror("rawsend: setsockopt(IPV6_HOPLIMIT)"); @@ -316,7 +818,7 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t #endif #if defined(__FreeBSD__) && __FreeBSD__<=10 - // old FreeBSD requires some fields in the host byte order + // old FreeBSD requires some fields in host byte order if (dst->sa_family==AF_INET && len>=sizeof(struct ip)) { ((struct ip*)data)->ip_len = htons(((struct ip*)data)->ip_len); @@ -340,385 +842,3 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t } return true; } -bool prepare_tcp_segment4( - const struct sockaddr_in *src, const struct sockaddr_in *dst, - uint8_t tcp_flags, - uint32_t nseq, uint32_t nack_seq, - uint16_t nwsize, - uint8_t scale_factor, - uint32_t *timestamps, - uint8_t ttl, - uint8_t fooling, - uint32_t badseq_increment, - uint32_t badseq_ack_increment, - const void *data, uint16_t len, - uint8_t *buf, size_t *buflen) -{ - uint16_t tcpoptlen = tcpopt_len(fooling,timestamps, scale_factor); - uint16_t pktlen = sizeof(struct ip) + sizeof(struct tcphdr) + tcpoptlen + len; - if (pktlen>*buflen) - { - fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen); - return false; - } - - struct ip *ip = (struct ip*) buf; - struct tcphdr *tcp = (struct tcphdr*) (ip+1); - - ip->ip_off = 0; - ip->ip_v = 4; - ip->ip_hl = 5; - ip->ip_len = htons(pktlen); - ip->ip_id = 0; - ip->ip_ttl = ttl; - ip->ip_p = IPPROTO_TCP; - ip->ip_src = src->sin_addr; - ip->ip_dst = dst->sin_addr; - - fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin_port,dst->sin_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment); - - memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len); - tcp4_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip->ip_src,&ip->ip_dst); - if (fooling & TCP_FOOL_BADSUM) tcp->th_sum^=0xBEAF; - - *buflen = pktlen; - return true; -} - - -bool prepare_tcp_segment6( - const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, - uint8_t tcp_flags, - uint32_t nseq, uint32_t nack_seq, - uint16_t nwsize, - uint8_t scale_factor, - uint32_t *timestamps, - uint8_t ttl, - uint8_t fooling, - uint32_t badseq_increment, - uint32_t badseq_ack_increment, - const void *data, uint16_t len, - uint8_t *buf, size_t *buflen) -{ - uint16_t tcpoptlen = tcpopt_len(fooling,timestamps, scale_factor); - uint16_t payloadlen = sizeof(struct tcphdr) + tcpoptlen + len; - uint16_t pktlen = sizeof(struct ip6_hdr) + payloadlen; - if (pktlen>*buflen) - { - fprintf(stderr,"prepare_tcp_segment : packet len cannot exceed %zu\n",*buflen); - return false; - } - - struct ip6_hdr *ip6 = (struct ip6_hdr*) buf; - struct tcphdr *tcp = (struct tcphdr*) (ip6+1); - - ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000); - ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(payloadlen); - ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_TCP; - ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl; - ip6->ip6_src = src->sin6_addr; - ip6->ip6_dst = dst->sin6_addr; - - fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin6_port,dst->sin6_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment); - - memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len); - tcp6_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip6->ip6_src,&ip6->ip6_dst); - if (fooling & TCP_FOOL_BADSUM) tcp->th_sum^=0xBEAF; - - *buflen = pktlen; - return true; -} - -bool prepare_tcp_segment( - const struct sockaddr *src, const struct sockaddr *dst, - uint8_t tcp_flags, - uint32_t nseq, uint32_t nack_seq, - uint16_t nwsize, - uint8_t scale_factor, - uint32_t *timestamps, - uint8_t ttl, - uint8_t fooling, - uint32_t badseq_increment, - uint32_t badseq_ack_increment, - const void *data, uint16_t len, - uint8_t *buf, size_t *buflen) -{ - return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ? - prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) : - (src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ? - prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) : - false; -} - - -void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst) -{ - if (ip) - { - struct sockaddr_in *si; - - if (dst) - { - si = (struct sockaddr_in*)dst; - si->sin_family = AF_INET; - si->sin_port = tcphdr ? tcphdr->th_dport : 0; - si->sin_addr = ip->ip_dst; - } - - if (src) - { - si = (struct sockaddr_in*)src; - si->sin_family = AF_INET; - si->sin_port = tcphdr ? tcphdr->th_sport : 0; - si->sin_addr = ip->ip_src; - } - } - else if (ip6hdr) - { - struct sockaddr_in6 *si; - - if (dst) - { - si = (struct sockaddr_in6*)dst; - si->sin6_family = AF_INET6; - si->sin6_port = tcphdr ? tcphdr->th_dport : 0; - si->sin6_addr = ip6hdr->ip6_dst; - si->sin6_flowinfo = 0; - si->sin6_scope_id = 0; - } - - if (src) - { - si = (struct sockaddr_in6*)src; - si->sin6_family = AF_INET6; - si->sin6_port = tcphdr ? tcphdr->th_sport : 0; - si->sin6_addr = ip6hdr->ip6_src; - si->sin6_flowinfo = 0; - si->sin6_scope_id = 0; - } - } -} - -static const char *proto_name(uint8_t proto) -{ - switch(proto) - { - case IPPROTO_TCP: - return "tcp"; - case IPPROTO_UDP: - return "udp"; - case IPPROTO_ICMP: - return "icmp"; - case IPPROTO_IGMP: - return "igmp"; - case IPPROTO_ESP: - return "esp"; - case IPPROTO_AH: - return "ah"; - case IPPROTO_IPV6: - return "6in4"; -#ifdef IPPROTO_SCTP - case IPPROTO_SCTP: - return "sctp"; -#endif - default: - return NULL; - } -} -static void str_proto_name(char *s, size_t s_len, uint8_t proto) -{ - const char *name = proto_name(proto); - if (name) - snprintf(s,s_len,"%s",name); - else - snprintf(s,s_len,"%u",proto); -} - -static void str_srcdst_ip(char *s, size_t s_len, const void *saddr,const void *daddr) -{ - char s_ip[16],d_ip[16]; - *s_ip=*d_ip=0; - inet_ntop(AF_INET, saddr, s_ip, sizeof(s_ip)); - inet_ntop(AF_INET, daddr, d_ip, sizeof(d_ip)); - snprintf(s,s_len,"%s => %s",s_ip,d_ip); -} -static void str_ip(char *s, size_t s_len, const struct ip *ip) -{ - char ss[35],s_proto[16]; - str_srcdst_ip(ss,sizeof(ss),&ip->ip_src,&ip->ip_dst); - str_proto_name(s_proto,sizeof(s_proto),ip->ip_p); - snprintf(s,s_len,"%s proto=%s",ss,s_proto); -} -void print_ip(const struct ip *ip) -{ - char s[64]; - str_ip(s,sizeof(s),ip); - printf("%s",s); -} -static void str_srcdst_ip6(char *s, size_t s_len, const void *saddr,const void *daddr) -{ - char s_ip[40],d_ip[40]; - *s_ip=*d_ip=0; - inet_ntop(AF_INET6, saddr, s_ip, sizeof(s_ip)); - inet_ntop(AF_INET6, daddr, d_ip, sizeof(d_ip)); - snprintf(s,s_len,"%s => %s",s_ip,d_ip); -} -static void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto) -{ - char ss[83],s_proto[16]; - str_srcdst_ip6(ss,sizeof(ss),&ip6hdr->ip6_src,&ip6hdr->ip6_dst); - str_proto_name(s_proto,sizeof(s_proto),proto); - snprintf(s,s_len,"%s proto=%s",ss,s_proto); -} -void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto) -{ - char s[128]; - str_ip6hdr(s,sizeof(s),ip6hdr,proto); - printf("%s",s); -} - -static void str_tcphdr(char *s, size_t s_len, const struct tcphdr *tcphdr) -{ - char flags[7],*f=flags; - if (tcphdr->th_flags & TH_SYN) *f++='S'; - if (tcphdr->th_flags & TH_ACK) *f++='A'; - if (tcphdr->th_flags & TH_RST) *f++='R'; - if (tcphdr->th_flags & TH_FIN) *f++='F'; - if (tcphdr->th_flags & TH_PUSH) *f++='P'; - if (tcphdr->th_flags & TH_URG) *f++='U'; - *f=0; - snprintf(s,s_len,"sport=%u dport=%u flags=%s seq=%u ack_seq=%u",htons(tcphdr->th_sport),htons(tcphdr->th_dport),flags,htonl(tcphdr->th_seq),htonl(tcphdr->th_ack)); -} -void print_tcphdr(const struct tcphdr *tcphdr) -{ - char s[80]; - str_tcphdr(s,sizeof(s),tcphdr); - printf("%s",s); -} - - - - -bool proto_check_ipv4(const uint8_t *data, size_t len) -{ - return len >= 20 && (data[0] & 0xF0) == 0x40 && - len >= ((data[0] & 0x0F) << 2); -} -// move to transport protocol -void proto_skip_ipv4(uint8_t **data, size_t *len) -{ - size_t l; - - l = (**data & 0x0F) << 2; - *data += l; - *len -= l; -} -bool proto_check_tcp(const uint8_t *data, size_t len) -{ - return len >= 20 && len >= ((data[12] & 0xF0) >> 2); -} -void proto_skip_tcp(uint8_t **data, size_t *len) -{ - size_t l; - l = ((*data)[12] & 0xF0) >> 2; - *data += l; - *len -= l; -} - -bool proto_check_ipv6(const uint8_t *data, size_t len) -{ - return len >= 40 && (data[0] & 0xF0) == 0x60 && - (len - 40) >= htons(*(uint16_t*)(data + 4)); // payload length -} -// move to transport protocol -// proto_type = 0 => error -void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type) -{ - size_t hdrlen; - uint8_t HeaderType; - - if (proto_type) *proto_type = 0; // put error in advance - - HeaderType = (*data)[6]; // NextHeader field - *data += 40; *len -= 40; // skip ipv6 base header - while (*len > 0) // need at least one byte for NextHeader field - { - switch (HeaderType) - { - case 0: // Hop-by-Hop Options - case 43: // routing - case 51: // authentication - case 60: // Destination Options - case 135: // mobility - case 139: // Host Identity Protocol Version v2 - case 140: // Shim6 - if (*len < 2) return; // error - hdrlen = 8 + ((*data)[1] << 3); - break; - case 44: // fragment. length fixed to 8, hdrlen field defined as reserved - hdrlen = 8; - break; - case 59: // no next header - return; // error - default: - // we found some meaningful payload. it can be tcp, udp, icmp or some another exotic shit - if (proto_type) *proto_type = HeaderType; - return; - } - if (*len < hdrlen) return; // error - HeaderType = **data; - // advance to the next header location - *len -= hdrlen; - *data += hdrlen; - } - // we have garbage -} - -bool tcp_synack_segment(const struct tcphdr *tcphdr) -{ - /* check for set bits in TCP hdr */ - return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN)); -} -bool tcp_syn_segment(const struct tcphdr *tcphdr) -{ - /* check for set bits in TCP hdr */ - return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_SYN); -} -bool tcp_ack_segment(const struct tcphdr *tcphdr) -{ - /* check for set bits in TCP hdr */ - return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_ACK); -} - -void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor) -{ - uint8_t *scale,scale_factor_old; - - if (scale_factor!=SCALE_NONE) - { - scale = tcp_find_option(tcp,3); // tcp option 3 - scale factor - if (scale && scale[1]==3) // length should be 3 - { - scale_factor_old=scale[2]; - // do not allow increasing scale factor - if (scale_factor>=scale_factor_old) - DLOG("Scale factor %u unchanged\n", scale_factor_old) - else - { - scale[2]=scale_factor; - DLOG("Scale factor change %u => %u\n", scale_factor_old, scale_factor) - } - } - } -} -// scale_factor=SCALE_NONE - do not change -void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor) -{ - uint16_t winsize_old; - - winsize_old = htons(tcp->th_win); // << scale_factor; - tcp->th_win = htons(winsize); - DLOG("Window size change %u => %u\n", winsize_old, winsize) - - tcp_rewrite_wscale(tcp, scale_factor); -} diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index dfe194a..40f60d4 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -7,17 +7,18 @@ #include #include #include +#include #include #include // returns netorder value uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment); -#define TCP_FOOL_NONE 0 -#define TCP_FOOL_MD5SIG 1 -#define TCP_FOOL_BADSUM 2 -#define TCP_FOOL_TS 4 -#define TCP_FOOL_BADSEQ 8 +#define FOOL_NONE 0 +#define FOOL_MD5SIG 1 +#define FOOL_BADSUM 2 +#define FOOL_TS 4 +#define FOOL_BADSEQ 8 #define SCALE_NONE ((uint8_t)-1) @@ -62,7 +63,29 @@ bool prepare_tcp_segment( const void *data, uint16_t len, uint8_t *buf, size_t *buflen); -void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst); + +bool prepare_udp_segment4( + const struct sockaddr_in *src, const struct sockaddr_in *dst, + uint8_t ttl, + uint8_t fooling, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen); +bool prepare_udp_segment6( + const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, + uint8_t ttl, + uint8_t fooling, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen); +bool prepare_udp_segment( + const struct sockaddr *src, const struct sockaddr *dst, + uint8_t ttl, + uint8_t fooling, + const void *data, uint16_t len, + uint8_t *buf, size_t *buflen); + + +void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport); +void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst); uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind); uint32_t *tcp_find_timestamps(struct tcphdr *tcp); uint8_t tcp_find_scale_factor(const struct tcphdr *tcp); @@ -74,17 +97,21 @@ bool rawsend_preinit(uint32_t fwmark); // cleans up socket autocreated by rawsend void rawsend_cleanup(); +const char *proto_name(uint8_t proto); +uint16_t family_from_proto(uint8_t l3proto); void print_ip(const struct ip *ip); void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto); void print_tcphdr(const struct tcphdr *tcphdr); - +void print_udphdr(const struct udphdr *udphdr); bool proto_check_ipv4(const uint8_t *data, size_t len); void proto_skip_ipv4(uint8_t **data, size_t *len); -bool proto_check_tcp(const uint8_t *data, size_t len); -void proto_skip_tcp(uint8_t **data, size_t *len); bool proto_check_ipv6(const uint8_t *data, size_t len); void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type); +bool proto_check_tcp(const uint8_t *data, size_t len); +void proto_skip_tcp(uint8_t **data, size_t *len); +bool proto_check_udp(const uint8_t *data, size_t len); +void proto_skip_udp(uint8_t **data, size_t *len); bool tcp_synack_segment(const struct tcphdr *tcphdr); bool tcp_syn_segment(const struct tcphdr *tcphdr); diff --git a/nfq/desync.c b/nfq/desync.c index a2b5e5e..ba70bfc 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -120,15 +120,18 @@ static bool cutoff_test(t_ctrack *ctrack, uint64_t cutoff, char mode) { return cutoff && cutoff_get_limit(ctrack, mode)>=cutoff; } -static void maybe_cutoff(t_ctrack *ctrack) +static void maybe_cutoff(t_ctrack *ctrack, uint8_t proto) { if (ctrack) { - ctrack->b_wssize_cutoff |= cutoff_test(ctrack, params.wssize_cutoff, params.wssize_cutoff_mode); + if (proto==IPPROTO_TCP) + ctrack->b_wssize_cutoff |= cutoff_test(ctrack, params.wssize_cutoff, params.wssize_cutoff_mode); ctrack->b_desync_cutoff |= cutoff_test(ctrack, params.desync_cutoff, params.desync_cutoff_mode); - // we do not need conntrack entry anymore if all cutoff conditions are either not defined or reached - ctrack->b_cutoff |= (!params.wssize || ctrack->b_wssize_cutoff) && (!params.desync_cutoff || ctrack->b_desync_cutoff); + if (proto==IPPROTO_TCP) + // we do not need conntrack entry anymore if all cutoff conditions are either not defined or reached + // do not drop udp entry because it will be recreated when next packet arrives + ctrack->b_cutoff |= (!params.wssize || ctrack->b_wssize_cutoff) && (!params.desync_cutoff || ctrack->b_desync_cutoff); } } static void wssize_cutoff(t_ctrack *ctrack) @@ -136,12 +139,12 @@ static void wssize_cutoff(t_ctrack *ctrack) if (ctrack) { ctrack->b_wssize_cutoff = true; - maybe_cutoff(ctrack); + maybe_cutoff(ctrack, IPPROTO_TCP); } } #define CONNTRACK_REQUIRED (params.wssize || params.desync_cutoff) // result : true - drop original packet, false = dont drop -packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload) +packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload) { packet_process_result res=pass; t_ctrack *ctrack=NULL; @@ -158,8 +161,8 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc if (CONNTRACK_REQUIRED) { ConntrackPoolPurge(¶ms.conntrack); - if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, tcphdr, len_payload, &ctrack, &bReverse)) - maybe_cutoff(ctrack); + if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, tcphdr, NULL, len_payload, &ctrack, &bReverse)) + maybe_cutoff(ctrack, IPPROTO_TCP); } //ConntrackPoolDump(¶ms.conntrack); @@ -202,14 +205,14 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc scale_factor = tcp_find_scale_factor(tcphdr); timestamps = tcp_find_timestamps(tcphdr); - extract_endpoints(ip, ip6hdr, tcphdr, &src, &dst); + extract_endpoints(ip, ip6hdr, tcphdr, NULL, &src, &dst); } if (params.desync_mode0==DESYNC_SYNACK && tcp_syn_segment(tcphdr)) { newlen = sizeof(newdata); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_SYN|TH_ACK, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_tcp_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, NULL, 0, newdata, &newlen)) { return res; @@ -352,7 +355,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc { case DESYNC_FAKE: if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_tcp_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, fake, fake_size, newdata, &newlen)) { return res; @@ -364,7 +367,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc case DESYNC_RST: case DESYNC_RSTACK: if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_tcp_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, NULL, 0, newdata, &newlen)) { return res; @@ -416,7 +419,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc if (split_posth_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,TCP_FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload+split_pos, len_payload-split_pos, newdata, &newlen)) return res; DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos) @@ -430,7 +433,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc { fakeseg_len = sizeof(fakeseg); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_tcp_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, zeropkt, split_pos, fakeseg, &fakeseg_len)) return res; DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) @@ -442,7 +445,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc newlen = sizeof(newdata); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,TCP_FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload, split_pos, newdata, &newlen)) return res; DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) @@ -472,7 +475,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc { fakeseg_len = sizeof(fakeseg); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_tcp_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, zeropkt, split_pos, fakeseg, &fakeseg_len)) return res; DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) @@ -483,7 +486,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc newlen = sizeof(newdata); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,TCP_FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload, split_pos, newdata, &newlen)) return res; DLOG("sending 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) @@ -503,7 +506,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc { newlen = sizeof(newdata); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,TCP_FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload+split_pos, len_payload-split_pos, newdata, &newlen)) return res; DLOG("sending 2nd tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos) @@ -516,8 +519,113 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc } break; } - - return res; + } + + return res; +} + + + +packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct udphdr *udphdr, uint8_t *data_payload, size_t len_payload) +{ + packet_process_result res=pass; + t_ctrack *ctrack=NULL; + bool bReverse=false; + + struct sockaddr_storage src, dst; + uint8_t newdata[DPI_DESYNC_MAX_FAKE_LEN+100]; + size_t newlen; + uint8_t ttl_orig,ttl_fake; + + if (!!ip == !!ip6hdr) return res; // one and only one must be present + + if (CONNTRACK_REQUIRED) + { + ConntrackPoolPurge(¶ms.conntrack); + if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, NULL, udphdr, len_payload, &ctrack, &bReverse)) + maybe_cutoff(ctrack, IPPROTO_UDP); + } + + //ConntrackPoolDump(¶ms.conntrack); + + if (bReverse) return res; // nothing to do. do not waste cpu + + if (params.desync_cutoff) + { + if (ctrack) + { + if (ctrack->b_desync_cutoff) + { + DLOG("not desyncing. desync-cutoff reached (mode %c): %llu/%u\n", params.desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_cutoff_mode), params.desync_cutoff); + return res; + } + DLOG("desync-cutoff not reached (mode %c): %llu/%u\n", params.desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_cutoff_mode), params.desync_cutoff); + } + else + { + DLOG("not desyncing. desync-cutoff is set but conntrack entry is missing\n"); + return res; + } + } + + if (params.desync_mode==DESYNC_NONE) return res; + + ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; + if (ip6hdr) ttl_fake = params.desync_ttl6 ? params.desync_ttl6 : ttl_orig; + else ttl_fake = params.desync_ttl ? params.desync_ttl : ttl_orig; + extract_endpoints(ip, ip6hdr, NULL, udphdr, &src, &dst); + + if (len_payload) + { + const uint8_t *fake; + size_t fake_size; + bool b; + + if (!params.desync_any_proto) return res; + DLOG("applying tampering to unknown protocol\n") + + fake = params.fake_unknown_udp; + fake_size = params.fake_unknown_udp_size; + + if (params.debug) + { + printf("dpi desync src="); + print_sockaddr((struct sockaddr *)&src); + printf(" dst="); + print_sockaddr((struct sockaddr *)&dst); + printf("\n"); + } + + newlen = sizeof(newdata); + b = false; + switch(params.desync_mode) + { + case DESYNC_FAKE: + if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, params.desync_fooling_mode, fake, fake_size, newdata, &newlen)) + return res; + DLOG("sending fake request : "); + hexdump_limited_dlog(fake,fake_size,PKT_MAXDUMP); DLOG("\n") + b = true; + break; + } + + if (b) + { + if (!rawsend_rep((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen)) + return res; + DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload) + #ifdef __FreeBSD__ + // FreeBSD tend to pass ipv6 frames with wrong checksum + if (res==modify || ip6hdr) + #else + // if original packet was tampered earlier it needs checksum fixed + if (res==modify) + #endif + udp_fix_checksum(udphdr,sizeof(struct udphdr)+len_payload,ip,ip6hdr); + if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt)) + return res; + return drop; + } } return res; diff --git a/nfq/desync.h b/nfq/desync.h index fa556a1..74402d2 100644 --- a/nfq/desync.h +++ b/nfq/desync.h @@ -40,4 +40,5 @@ bool desync_valid_first_stage(enum dpi_desync_mode mode); bool desync_valid_second_stage(enum dpi_desync_mode mode); void desync_init(); -packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload); +packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload); +packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct udphdr *udphdr, uint8_t *data_payload, size_t len_payload); diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 699632b..cbb59a3 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -39,6 +39,7 @@ #define CTRACK_T_SYN 60 #define CTRACK_T_FIN 60 #define CTRACK_T_EST 300 +#define CTRACK_T_UDP 60 struct params_s params; @@ -86,7 +87,8 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt struct ip *ip = NULL; struct ip6_hdr *ip6hdr = NULL; struct tcphdr *tcphdr = NULL; - size_t len = len_pkt, len_tcp; + struct udphdr *udphdr = NULL; + size_t len = len_pkt, len_with_th; uint8_t *data = data_pkt; packet_process_result res = pass; uint8_t proto; @@ -129,7 +131,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt if (proto==IPPROTO_TCP && proto_check_tcp(data, len)) { tcphdr = (struct tcphdr *) data; - len_tcp = len; + len_with_th = len; proto_skip_tcp(&data, &len); if (params.debug) @@ -141,7 +143,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt if (len) { DLOG("TCP: ") hexdump_limited_dlog(data, len, 32); DLOG("\n") } - res = dpi_desync_packet(data_pkt, len_pkt, ip, ip6hdr, tcphdr, len_tcp, data, len); + res = dpi_desync_tcp_packet(data_pkt, len_pkt, ip, ip6hdr, tcphdr, len_with_th, data, len); // in my FreeBSD divert tests only ipv4 packets were reinjected with correct checksum // ipv6 packets were with incorrect checksum #ifdef __FreeBSD__ @@ -150,7 +152,30 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt #else if (res==modify) #endif - tcp_fix_checksum(tcphdr,len_tcp,ip,ip6hdr); + tcp_fix_checksum(tcphdr,len_with_th,ip,ip6hdr); + } + else if (proto==IPPROTO_UDP && proto_check_udp(data, len)) + { + udphdr = (struct udphdr *) data; + len_with_th = len; + proto_skip_udp(&data, &len); + + if (params.debug) + { + printf(" "); + print_udphdr(udphdr); + printf("\n"); + } + if (len) { DLOG("UDP: ") hexdump_limited_dlog(data, len, 32); DLOG("\n") } + + res = dpi_desync_udp_packet(data_pkt, len_pkt, ip, ip6hdr, udphdr, data, len); +#ifdef __FreeBSD__ + // FreeBSD tend to pass ipv6 frames with wrong checksum + if (res==modify || ip6hdr) +#else + if (res==modify) +#endif + udp_fix_checksum(udphdr,len_with_th,ip,ip6hdr); } else { @@ -474,7 +499,7 @@ static void exithelp() " --wsize=[:]\t; set window size. 0 = do not modify. OBSOLETE !\n" " --wssize=[:]; set window size for server. 0 = do not modify. default scale_factor = 0.\n" " --wssize-cutoff=[n|d|s]N\t\t; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n" - " --ctrack-timeouts=S:E:F\t\t; internal conntrack timeouts for SYN, ESTABLISHED and FIN stage. default %u:%u:%u\n" + " --ctrack-timeouts=S:E:F[:U]\t\t; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default %u:%u:%u:%u\n" " --hostcase\t\t\t\t; change Host: => host:\n" " --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" " --hostnospace\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" @@ -500,9 +525,10 @@ static void exithelp() " --dpi-desync-fake-http=\t; file containing fake http request\n" " --dpi-desync-fake-tls=\t; file containing fake TLS ClientHello (for https)\n" " --dpi-desync-fake-unknown=\t; file containing unknown protocol fake payload\n" + " --dpi-desync-fake-unknown-udp= ; file containing unknown udp protocol fake payload\n" " --dpi-desync-cutoff=[n|d|s]N\t\t; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n" " --hostlist=\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply)\n", - CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, + CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, CTRACK_T_UDP, #if defined(__linux__) || defined(SO_USER_COOKIE) DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT, #endif @@ -549,6 +575,17 @@ static bool parse_badseq_increment(const char *opt, uint32_t *value) return sscanf(opt, "%d", (int32_t*)value)>0; } } +static void load_file_or_exit(const char *filename, void *buf, size_t *size) +{ + if (!load_file_nonempty(filename,buf,size)) + { + fprintf(stderr, "could not read %s\n",filename); + exit_clean(1); + } + DLOG("read %zu bytes from %s\n",*size,filename) +} + + int main(int argc, char **argv) { int result, v; @@ -571,10 +608,12 @@ int main(int argc, char **argv) params.fake_http_size = strlen(fake_http_request_default); memcpy(params.fake_http,fake_http_request_default,params.fake_http_size); params.fake_unknown_size = 256; + params.fake_unknown_udp_size = 64; params.wscale=-1; // default - dont change scale factor (client) params.ctrack_t_syn = CTRACK_T_SYN; params.ctrack_t_est = CTRACK_T_EST; params.ctrack_t_fin = CTRACK_T_FIN; + params.ctrack_t_udp = CTRACK_T_UDP; params.desync_ttl6 = 0xFF; // unused params.desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT; params.desync_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT; @@ -628,8 +667,9 @@ int main(int argc, char **argv) {"dpi-desync-fake-http",required_argument,0,0},// optidx=26 {"dpi-desync-fake-tls",required_argument,0,0},// optidx=27 {"dpi-desync-fake-unknown",required_argument,0,0},// optidx=28 - {"dpi-desync-cutoff",required_argument,0,0},// optidx=29 - {"hostlist",required_argument,0,0}, // optidx=30 + {"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=29 + {"dpi-desync-cutoff",required_argument,0,0},// optidx=30 + {"hostlist",required_argument,0,0}, // optidx=31 {NULL,0,NULL,0} }; if (argc < 2) exithelp(); @@ -706,7 +746,7 @@ int main(int argc, char **argv) } break; case 9: /* ctrack-timeouts */ - if (sscanf(optarg, "%u:%u:%u", ¶ms.ctrack_t_syn, ¶ms.ctrack_t_est, ¶ms.ctrack_t_fin)!=3) + if (sscanf(optarg, "%u:%u:%u:%u", ¶ms.ctrack_t_syn, ¶ms.ctrack_t_est, ¶ms.ctrack_t_fin, ¶ms.ctrack_t_udp)<3) { fprintf(stderr, "invalid ctrack-timeouts value\n"); exit_clean(1); @@ -796,18 +836,18 @@ int main(int argc, char **argv) e = strchr(p,','); if (e) *e++=0; if (!strcmp(p,"md5sig")) - params.desync_tcp_fooling_mode |= TCP_FOOL_MD5SIG; + params.desync_fooling_mode |= FOOL_MD5SIG; else if (!strcmp(p,"ts")) - params.desync_tcp_fooling_mode |= TCP_FOOL_TS; + params.desync_fooling_mode |= FOOL_TS; else if (!strcmp(p,"badsum")) { #ifdef __OpenBSD__ - printf("\nWARNING !!! OpenBSD may forcibly recompute tcp checksums !!! In this case badsum fooling will not work.\nYou should check tcp checksum correctness in tcpdump manually before using badsum.\n\n"); + printf("\nWARNING !!! OpenBSD may forcibly recompute tcp/udp checksums !!! In this case badsum fooling will not work.\nYou should check tcp checksum correctness in tcpdump manually before using badsum.\n\n"); #endif - params.desync_tcp_fooling_mode |= TCP_FOOL_BADSUM; + params.desync_fooling_mode |= FOOL_BADSUM; } else if (!strcmp(p,"badseq")) - params.desync_tcp_fooling_mode |= TCP_FOOL_BADSEQ; + params.desync_fooling_mode |= FOOL_BADSEQ; else if (strcmp(p,"none")) { fprintf(stderr, "dpi-desync-fooling allowed values : none,md5sig,ts,badseq,badsum\n"); @@ -850,7 +890,6 @@ int main(int argc, char **argv) fprintf(stderr, "dpi-desync-badseq-increment should be signed decimal or signed 0xHEX\n"); exit_clean(1); } -printf("FFF %08X\n", params.desync_badseq_increment); break; case 24: /* dpi-desync-badack-increment */ if (!parse_badseq_increment(optarg,¶ms.desync_badseq_ack_increment)) @@ -864,36 +903,28 @@ printf("FFF %08X\n", params.desync_badseq_increment); break; case 26: /* dpi-desync-fake-http */ params.fake_http_size = sizeof(params.fake_http); - if (!load_file_nonempty(optarg,params.fake_http,¶ms.fake_http_size)) - { - fprintf(stderr, "could not read %s\n",optarg); - exit_clean(1); - } + load_file_or_exit(optarg,params.fake_http,¶ms.fake_http_size); break; case 27: /* dpi-desync-fake-tls */ params.fake_tls_size = sizeof(params.fake_tls); - if (!load_file_nonempty(optarg,params.fake_tls,¶ms.fake_tls_size)) - { - fprintf(stderr, "could not read %s\n",optarg); - exit_clean(1); - } + load_file_or_exit(optarg,params.fake_tls,¶ms.fake_tls_size); break; case 28: /* dpi-desync-fake-unknown */ params.fake_unknown_size = sizeof(params.fake_unknown); - if (!load_file_nonempty(optarg,params.fake_unknown,¶ms.fake_unknown_size)) - { - fprintf(stderr, "could not read %s\n",optarg); - exit_clean(1); - } + load_file_or_exit(optarg,params.fake_unknown,¶ms.fake_unknown_size); break; - case 29: /* desync-cutoff */ + case 29: /* dpi-desync-fake-unknown-udp */ + params.fake_unknown_udp_size = sizeof(params.fake_unknown_udp); + load_file_or_exit(optarg,params.fake_unknown_udp,¶ms.fake_unknown_udp_size); + break; + case 30: /* desync-cutoff */ if (!parse_cutoff(optarg, ¶ms.desync_cutoff, ¶ms.desync_cutoff_mode)) { fprintf(stderr, "invalid desync-cutoff value\n"); exit_clean(1); } break; - case 30: /* hostlist */ + case 31: /* hostlist */ if (!LoadHostList(¶ms.hostlist, optarg)) exit_clean(1); strncpy(params.hostfile,optarg,sizeof(params.hostfile)); @@ -919,8 +950,8 @@ printf("FFF %08X\n", params.desync_badseq_increment); goto exiterr; } - DLOG("initializing conntrack with timeouts %u:%u:%u\n", params.ctrack_t_syn, params.ctrack_t_est, params.ctrack_t_fin) - ConntrackPoolInit(¶ms.conntrack, 10, params.ctrack_t_syn, params.ctrack_t_est, params.ctrack_t_fin); + DLOG("initializing conntrack with timeouts tcp=%u:%u:%u udp=%u\n", params.ctrack_t_syn, params.ctrack_t_est, params.ctrack_t_fin, params.ctrack_t_udp) + ConntrackPoolInit(¶ms.conntrack, 10, params.ctrack_t_syn, params.ctrack_t_est, params.ctrack_t_fin, params.ctrack_t_udp); #ifdef __linux__ result = nfq_main(); diff --git a/nfq/params.h b/nfq/params.h index f957478..3c10520 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -40,18 +40,18 @@ struct params_s char desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence unsigned int desync_cutoff; uint8_t desync_ttl, desync_ttl6; - uint8_t desync_tcp_fooling_mode; + uint8_t desync_fooling_mode; uint32_t desync_fwmark; // unused in BSD uint32_t desync_badseq_increment, desync_badseq_ack_increment; char hostfile[256]; strpool *hostlist; - uint8_t fake_http[1432],fake_tls[1432],fake_unknown[1432]; - size_t fake_http_size,fake_tls_size,fake_unknown_size; + uint8_t fake_http[1432],fake_tls[1432],fake_unknown[1432],fake_unknown_udp[1472]; + size_t fake_http_size,fake_tls_size,fake_unknown_size,fake_unknown_udp_size; bool droproot; uid_t uid; gid_t gid; - unsigned int ctrack_t_syn, ctrack_t_est, ctrack_t_fin; + unsigned int ctrack_t_syn, ctrack_t_est, ctrack_t_fin, ctrack_t_udp; t_conntrack conntrack; };