diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index c5c7e60..4ba05ad 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index 107781c..6eac78f 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/freebsd-x64/dvtws b/binaries/freebsd-x64/dvtws index c695fe3..251e2d3 100755 Binary files a/binaries/freebsd-x64/dvtws and b/binaries/freebsd-x64/dvtws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index 7d2b24c..97cfc59 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 9492030..a2a7070 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 a33304a..be326ea 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 c9021fa..675fd7e 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index 2db7c03..8f13fd9 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 b00d4f0..19dbddf 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/docs/readme.eng.md b/docs/readme.eng.md index 19554b0..06dbdac 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -140,6 +140,8 @@ nfqws takes the following parameters: ``` --debug=0|1 ; 1=print debug info --qnum= + --bind-fix4 ; apply outgoing interface selection fix for generated ipv4 packets + --bind-fix6 ; apply outgoing interface selection fix for generated ipv6 packets --wsize=[:] ; change window size in SYN,ACK packets. default is not to change scale factor (OBSOLETE !) --wssize=[:] ; change window size in outgoing packets. default scale factor is 0. (see CONNTRACK) --wssize-cutoff=[n|d|s]N ; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N diff --git a/docs/readme.txt b/docs/readme.txt index d514358..3e56033 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -197,6 +197,8 @@ nfqws --user= ; менять uid процесса --uid=uid[:gid] ; менять uid процесса --qnum=N ; номер очереди N + --bind-fix4 ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv4 пакетов + --bind-fix6 ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv6 пакетов --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 diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index ccc68a1..03fa01f 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -851,6 +851,7 @@ void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_fac static int rawsend_sock4=-1, rawsend_sock6=-1; +static bool b_bind_fix4=false, b_bind_fix6=false; static void rawsend_clean_sock(int *sock) { if (sock && *sock!=-1) @@ -934,7 +935,28 @@ static int rawsend_socket_raw(int domain, int proto) return fd; } -static int rawsend_socket(sa_family_t family,uint32_t fwmark) +static bool set_socket_fwmark(int sock, uint32_t fwmark) +{ +#ifdef BSD +#ifdef SO_USER_COOKIE + if (setsockopt(sock, SOL_SOCKET, SO_USER_COOKIE, &fwmark, sizeof(fwmark)) == -1) + { + perror("rawsend: setsockopt(SO_USER_COOKIE)"); + return false; + } +#endif +#elif defined(__linux__) + if (setsockopt(sock, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark)) == -1) + { + perror("rawsend: setsockopt(SO_MARK)"); + return false; + } + +#endif + return true; +} + +static int rawsend_socket(sa_family_t family) { int yes=1; int *sock = rawsend_family_sock(family); @@ -971,20 +993,8 @@ static int rawsend_socket(sa_family_t family,uint32_t fwmark) goto exiterr; } #endif -#ifdef SO_USER_COOKIE - if (setsockopt(*sock, SOL_SOCKET, SO_USER_COOKIE, &fwmark, sizeof(fwmark)) == -1) - { - perror("rawsend: setsockopt(SO_MARK)"); - goto exiterr; - } -#endif #endif #ifdef __linux__ - if (setsockopt(*sock, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark)) == -1) - { - perror("rawsend: setsockopt(SO_MARK)"); - goto exiterr; - } if (setsockopt(*sock, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri)) == -1) { perror("rawsend: setsockopt(SO_PRIORITY)"); @@ -995,6 +1005,16 @@ static int rawsend_socket(sa_family_t family,uint32_t fwmark) perror("rawsend: setsockopt(IP_NODEFRAG)"); goto exiterr; } + if (family==AF_INET && setsockopt(*sock, IPPROTO_IP, IP_FREEBIND, &yes, sizeof(yes)) == -1) + { + perror("rawsend: setsockopt(IP_FREEBIND)"); + goto exiterr; + } + if (family==AF_INET6 && setsockopt(*sock, SOL_IPV6, IPV6_FREEBIND, &yes, sizeof(yes)) == -1) + { + perror("rawsend: setsockopt(IPV6_FREEBIND)"); + // dont error because it's supported only from kernel 4.15 + } #endif } return *sock; @@ -1002,14 +1022,17 @@ exiterr: rawsend_clean_sock(sock); return -1; } -bool rawsend_preinit(uint32_t fwmark) +bool rawsend_preinit(bool bind_fix4, bool bind_fix6) { - return rawsend_socket(AF_INET,fwmark)!=-1 && rawsend_socket(AF_INET6,fwmark)!=-1; + b_bind_fix4 = bind_fix4; + b_bind_fix6 = bind_fix6; + return rawsend_socket(AF_INET)!=-1 && rawsend_socket(AF_INET6)!=-1; } -bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len) +bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len) { - int sock=rawsend_socket(dst->sa_family,fwmark); + int sock=rawsend_socket(dst->sa_family); if (sock==-1) return false; + if (!set_socket_fwmark(sock,fwmark)) return false; int salen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); struct sockaddr_storage dst2; @@ -1028,7 +1051,7 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t 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)"); -[5~ // the only way to control source address is bind. make it equal to ip6_hdr + // the only way to control source address is bind. make it equal to ip6_hdr if (bind(sock, (struct sockaddr*)&sa_src, salen) < 0) perror("rawsend bind: "); //printf("BSD v6 RAWSEND "); print_sockaddr((struct sockaddr*)&sa_src); printf(" -> "); print_sockaddr((struct sockaddr*)&dst2); printf("\n"); @@ -1059,6 +1082,44 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t ((struct ip*)data)->ip_off = htons(((struct ip*)data)->ip_off); } #endif + +#if defined(__linux__) + struct sockaddr_storage sa_src; + switch(dst->sa_family) + { + case AF_INET: + if (!b_bind_fix4) goto nofix; + extract_endpoints(data,NULL,NULL,NULL, &sa_src, NULL); + break; + case AF_INET6: + if (!b_bind_fix6) goto nofix; + extract_endpoints(NULL,data,NULL,NULL, &sa_src, NULL); + break; + default: + return false; // should not happen + } + //printf("family %u dev %s bind : ", dst->sa_family, ifout); print_sockaddr((struct sockaddr *)&sa_src); printf("\n"); + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifout, ifout ? strlen(ifout)+1 : 0) == -1) + { + perror("rawsend: setsockopt(SO_BINDTODEVICE)"); + return false; + } + if (bind(sock, (const struct sockaddr*)&sa_src, dst->sa_family==AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) + { + perror("rawsend: bind (ignoring)"); + // do not fail. this can happen regardless of IP_FREEBIND + // rebind to any address + memset(&sa_src,0,sizeof(sa_src)); + sa_src.ss_family = dst->sa_family; + if (bind(sock, (const struct sockaddr*)&sa_src, dst->sa_family==AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) + { + perror("rawsend: bind to any"); + return false; + } + } +nofix: +#endif + // normal raw socket sendto ssize_t bytes = sendto(sock, data, len, 0, (struct sockaddr*)&dst2, salen); #if defined(__FreeBSD) && __FreeBSD__<=10 diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index 1f37cfd..90f60d0 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -118,9 +118,9 @@ uint32_t *tcp_find_timestamps(struct tcphdr *tcp); uint8_t tcp_find_scale_factor(const struct tcphdr *tcp); // auto creates internal socket and uses it for subsequent calls -bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len); +bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len); // should pre-do it if dropping privileges. otherwise its not necessary -bool rawsend_preinit(uint32_t fwmark); +bool rawsend_preinit(bool bind_fix4, bool bind_fix6); // cleans up socket autocreated by rawsend void rawsend_cleanup(); diff --git a/nfq/desync.c b/nfq/desync.c index d0e84c4..5d1a533 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -122,10 +122,10 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) // auto creates internal socket and uses it for subsequent calls -static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t len) +static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len) { for (int i=0;ipacket_id) : 0; uint32_t mark = nfq_get_nfmark(nfa); len = nfq_get_payload(nfa, &data); - DLOG("packet: id=%d len=%d\n", id, len) + + *ifout=0; + if (params.bind_fix4 || params.bind_fix6) + { + ifidx = nfq_get_outdev(nfa); + if (ifidx) if_indextoname(ifidx,ifout); + + DLOG("packet: id=%d len=%d ifout=%s(%u)\n", id, len, ifout, ifidx) + } + else + // save some syscalls + DLOG("packet: id=%d len=%d\n", id, len) if (len >= 0) { - switch (processPacketData(data, len, &mark)) + switch (processPacketData(&mark, ifout, data, len)) { case modify: DLOG("packet: id=%d pass modified\n", id); @@ -266,6 +274,10 @@ static int nfq_main() // dot not fail. not supported on old linuxes <3.6 } + printf("initializing raw sockets bind-fix4=%u bind-fix6=%u\n",params.bind_fix4,params.bind_fix6); + if (!rawsend_preinit(params.bind_fix4,params.bind_fix6)) + goto exiterr; + if (params.droproot && !droproot(params.uid, params.gid)) goto exiterr; print_id(); @@ -378,8 +390,8 @@ static int dvt_main() #endif fdmax = (fd[0]>fd[1] ? fd[0] : fd[1]) + 1; - printf("initializing raw sockets with sockarg 0x%08X (%u)\n", params.desync_fwmark, params.desync_fwmark); - if (!rawsend_preinit(params.desync_fwmark)) + printf("initializing raw sockets\n"); + if (!rawsend_preinit(false,false)) goto exiterr; if (params.droproot && !droproot(params.uid, params.gid)) @@ -420,8 +432,9 @@ static int dvt_main() } else if (rd>0) { + uint32_t mark=0; DLOG("packet: id=%u len=%zd\n", id, rd) - ppr = processPacketData(buf, rd); + ppr = processPacketData(&mark, NULL, buf, rd); switch (ppr) { case pass: @@ -497,6 +510,10 @@ static void exithelp() " --pidfile=\t\t\t; write pid to file\n" " --user=\t\t\t; drop root privs\n" " --uid=uid[:gid]\t\t\t; drop root privs\n" +#ifdef __linux__ + " --bind-fix4\t\t\t\t; apply outgoing interface selection fix for generated ipv4 packets\n" + " --bind-fix6\t\t\t\t; apply outgoing interface selection fix for generated ipv6 packets\n" +#endif " --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" @@ -686,6 +703,10 @@ int main(int argc, char **argv) {"dpi-desync-udplen-increment",required_argument,0,0},// optidx=33 {"dpi-desync-cutoff",required_argument,0,0},// optidx=34 {"hostlist",required_argument,0,0}, // optidx=35 +#ifdef __linux__ + {"bind-fix4",no_argument,0,0}, // optidx=36 + {"bind-fix6",no_argument,0,0}, // optidx=37 +#endif {NULL,0,NULL,0} }; if (argc < 2) exithelp(); @@ -986,6 +1007,14 @@ int main(int argc, char **argv) strncpy(params.hostfile,optarg,sizeof(params.hostfile)); params.hostfile[sizeof(params.hostfile)-1]='\0'; break; +#ifdef __linux__ + case 36: /* bind-fix4 */ + params.bind_fix4 = true; + break; + case 37: /* bind-fix6 */ + params.bind_fix6 = true; + break; +#endif } } // not specified - use desync_ttl value instead diff --git a/nfq/params.h b/nfq/params.h index 4882844..f577662 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -37,6 +38,7 @@ struct params_s #elif defined(BSD) uint16_t port; // divert port #endif + char bind_fix4,bind_fix6; bool hostcase, hostnospace, domcase; char hostspell[4]; enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2;