From 5ddc0aa01bb19992dc1aa9179df309d838bb25be Mon Sep 17 00:00:00 2001
From: bol-van <k@vodka.home.kg>
Date: Fri, 19 Mar 2021 15:39:32 +0300
Subject: [PATCH] nfqws: extend conntrack

---
 nfq/conntrack.c | 14 ++++++++++++--
 nfq/conntrack.h |  2 ++
 nfq/darkmagic.c | 37 +++++++++++++++++++++++++++----------
 nfq/darkmagic.h |  8 +++++++-
 nfq/desync.c    | 17 +++++++++--------
 5 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/nfq/conntrack.c b/nfq/conntrack.c
index d348d8f..622f7da 100644
--- a/nfq/conntrack.c
+++ b/nfq/conntrack.c
@@ -98,6 +98,7 @@ t_conntrack6 *ConntrackPoolSearch6(t_conntrack6 *p, const t_conn6 *c)
 static void ConntrackInitTrack(t_ctrack *t)
 {
 	memset(t,0,sizeof(*t));
+	t->scale_orig = t->scale_reply = SCALE_NONE;
 	time(&t->t_start);
 }
 
@@ -122,6 +123,7 @@ static t_conntrack6 *ConntrackNew6(t_conntrack6 **pp, const t_conn6 *c)
 
 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==FIN) ConntrackInitTrack(t); // erase current entry
@@ -148,17 +150,23 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
 			if (!bReverse && !t->ack0) t->ack0 = htonl(tcphdr->th_ack)-1;
 		}
 	}
+	scale = tcp_find_scale_factor(tcphdr);
 	if (bReverse)
 	{
 		t->seq_last = htonl(tcphdr->th_ack);
 		t->ack_last = htonl(tcphdr->th_seq) + len_payload;
 		t->pcounter_reply++;
+		t->winsize_reply = htons(tcphdr->th_win);
+		if (scale!=SCALE_NONE) t->scale_reply = scale;
+		
 	}
 	else
 	{
 		t->seq_last = htonl(tcphdr->th_seq) + len_payload;
 		t->ack_last = htonl(tcphdr->th_ack);
 		t->pcounter_orig++;
+		t->winsize_orig = htons(tcphdr->th_win);
+		if (scale!=SCALE_NONE) t->scale_orig = scale;
 	}
 	time(&t->t_last);
 }
@@ -271,13 +279,15 @@ void ConntrackPoolPurge(t_conntrack *p)
 	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=%lld last=t0+%lld now=last+%lld cutoff=%u packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u\n", \
+		printf("[%s]:%u => [%s]:%u : %s : t0=%lld last=t0+%lld now=last+%lld cutoff=%u packets_orig=%llu packets_reply=%llu seq0=%u rseq=%u ack0=%u rack=%u wsize_orig=%u:%d wsize_reply=%u:%d\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), \
 			t->track.b_cutoff, \
 			(unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pcounter_reply, \
-			t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - t->track.ack0); \
+			t->track.seq0, t->track.seq_last - t->track.seq0, t->track.ack0, t->track.ack_last - 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 ); \
 	};
 void ConntrackPoolDump4(t_conntrack4 *p)
 {
diff --git a/nfq/conntrack.h b/nfq/conntrack.h
index 19e4e36..efeb9b8 100644
--- a/nfq/conntrack.h
+++ b/nfq/conntrack.h
@@ -51,6 +51,8 @@ typedef struct
 	uint32_t seq0, ack0;			// starting seq and ack
 	uint32_t seq_last, ack_last;		// current seq and ack
 	uint64_t pcounter_orig, pcounter_reply;	// packet counter
+	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
 } t_ctrack;
diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c
index 00fe480..3bfe572 100644
--- a/nfq/darkmagic.c
+++ b/nfq/darkmagic.c
@@ -46,8 +46,14 @@ uint32_t *tcp_find_timestamps(struct tcphdr *tcp)
 	uint8_t *t = tcp_find_option(tcp,8);
 	return (t && t[1]==10) ? (uint32_t*)(t+2) : NULL;
 }
+uint8_t tcp_find_scale_factor(const struct tcphdr *tcp)
+{
+	uint8_t *scale = tcp_find_option((struct tcphdr*)tcp,3); // tcp option 3 - scale factor
+	if (scale && scale[1]==3) return scale[2];
+	return SCALE_NONE;
+}
 
-static void fill_tcphdr(struct tcphdr *tcp, uint8_t tcp_flags, uint32_t seq, uint32_t ack_seq, uint8_t fooling, uint16_t nsport, uint16_t ndport, uint16_t nwsize, uint32_t *timestamps)
+static void fill_tcphdr(struct tcphdr *tcp, uint8_t tcp_flags, uint32_t seq, uint32_t ack_seq, uint8_t fooling, uint16_t nsport, uint16_t ndport, uint16_t nwsize, uint8_t scale_factor, uint32_t *timestamps)
 {
 	char *tcpopt = (char*)(tcp+1);
 	uint8_t t=0;
@@ -87,14 +93,21 @@ static void fill_tcphdr(struct tcphdr *tcp, uint8_t tcp_flags, uint32_t seq, uin
 		*(uint32_t*)(tcpopt+t+6) = (timestamps && !(fooling & TCP_FOOL_TS)) ? timestamps[1] : -1;
 		t+=10;
 	}
+	if (scale_factor!=SCALE_NONE)
+	{
+		tcpopt[t++]=3;
+		tcpopt[t++]=3;
+		tcpopt[t++]=scale_factor;
+	}
 	while (t&3) tcpopt[t++]=1; // noop
 	tcp->th_off += t>>2;
 }
-static uint16_t tcpopt_len(uint8_t fooling, uint32_t *timestamps)
+static uint16_t tcpopt_len(uint8_t fooling, 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 (scale_factor!=SCALE_NONE) t+=3;
 	return (t+3)&~3;
 }
 
@@ -253,6 +266,7 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const void *data,size_t
 {
 	int sock=rawsend_socket(dst->sa_family,fwmark);
 	if (sock==-1) return false;
+
 	int salen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
 	struct sockaddr_storage dst2;
 	memcpy(&dst2,dst,salen);
@@ -323,13 +337,14 @@ bool prepare_tcp_segment4(
 	uint8_t tcp_flags,
 	uint32_t seq, uint32_t ack_seq,
 	uint16_t wsize,
+	uint8_t scale_factor,
 	uint32_t *timestamps,
 	uint8_t ttl,
 	uint8_t fooling,
 	const void *data, uint16_t len,
 	uint8_t *buf, size_t *buflen)
 {
-	uint16_t tcpoptlen = tcpopt_len(fooling,timestamps);
+	uint16_t tcpoptlen = tcpopt_len(fooling,timestamps, scale_factor);
 	uint16_t pktlen = sizeof(struct ip) + sizeof(struct tcphdr) + tcpoptlen  + len;
 	if (pktlen>*buflen)
 	{
@@ -350,7 +365,7 @@ bool prepare_tcp_segment4(
 	ip->ip_src = src->sin_addr;
 	ip->ip_dst = dst->sin_addr;
 
-	fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin_port,dst->sin_port,wsize,timestamps);
+	fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin_port,dst->sin_port,wsize,scale_factor,timestamps);
 
 	memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
 	tcp4_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip->ip_src,&ip->ip_dst);
@@ -366,13 +381,14 @@ bool prepare_tcp_segment6(
 	uint8_t tcp_flags,
 	uint32_t seq, uint32_t ack_seq,
 	uint16_t wsize,
+	uint8_t scale_factor,
 	uint32_t *timestamps,
 	uint8_t ttl,
 	uint8_t fooling,
 	const void *data, uint16_t len,
 	uint8_t *buf, size_t *buflen)
 {
-	uint16_t tcpoptlen = tcpopt_len(fooling,timestamps);
+	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)
@@ -391,7 +407,7 @@ bool prepare_tcp_segment6(
 	ip6->ip6_src = src->sin6_addr;
 	ip6->ip6_dst = dst->sin6_addr;
 
-	fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin6_port,dst->sin6_port,wsize,timestamps);
+	fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin6_port,dst->sin6_port,wsize,scale_factor,timestamps);
 
 	memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
 	tcp6_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,&ip6->ip6_src,&ip6->ip6_dst);
@@ -406,6 +422,7 @@ bool prepare_tcp_segment(
 	uint8_t tcp_flags,
 	uint32_t seq, uint32_t ack_seq,
 	uint16_t wsize,
+	uint8_t scale_factor,
 	uint32_t *timestamps,
 	uint8_t ttl,
 	uint8_t fooling,
@@ -413,9 +430,9 @@ bool prepare_tcp_segment(
 	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,seq,ack_seq,wsize,timestamps,ttl,fooling,data,len,buf,buflen) :
+		prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,seq,ack_seq,wsize,scale_factor,timestamps,ttl,fooling,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,seq,ack_seq,wsize,timestamps,ttl,fooling,data,len,buf,buflen) :
+		prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,seq,ack_seq,wsize,scale_factor,timestamps,ttl,fooling,data,len,buf,buflen) :
 		false;
 }
 
@@ -663,7 +680,7 @@ void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor)
 {
 	uint8_t *scale,scale_factor_old;
 
-	if (scale_factor!=(uint8_t)-1)
+	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
@@ -680,7 +697,7 @@ void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor)
 		}
 	}
 }
-// scale_factor=-1 - do not change
+// 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;
diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h
index 1f0338e..e10797f 100644
--- a/nfq/darkmagic.h
+++ b/nfq/darkmagic.h
@@ -19,12 +19,15 @@ uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment);
 #define TCP_FOOL_TS	4
 #define TCP_FOOL_BADSEQ	8
 
+#define SCALE_NONE ((uint8_t)-1)
+
 // seq and wsize have network byte order
 bool prepare_tcp_segment4(
 	const struct sockaddr_in *src, const struct sockaddr_in *dst,
 	uint8_t tcp_flags,
 	uint32_t seq, uint32_t ack_seq,
 	uint16_t wsize,
+	uint8_t scale_factor,
 	uint32_t *timestamps,
 	uint8_t ttl,
 	uint8_t fooling,
@@ -35,6 +38,7 @@ bool prepare_tcp_segment6(
 	uint8_t tcp_flags,
 	uint32_t seq, uint32_t ack_seq,
 	uint16_t wsize,
+	uint8_t scale_factor,
 	uint32_t *timestamps,
 	uint8_t ttl,
 	uint8_t fooling,
@@ -45,6 +49,7 @@ bool prepare_tcp_segment(
 	uint8_t tcp_flags,
 	uint32_t seq, uint32_t ack_seq,
 	uint16_t wsize,
+	uint8_t scale_factor,
 	uint32_t *timestamps,
 	uint8_t ttl,
 	uint8_t fooling,
@@ -54,6 +59,7 @@ bool prepare_tcp_segment(
 void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr, 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);
 
 // 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);
@@ -77,6 +83,6 @@ void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type);
 bool tcp_synack_segment(const struct tcphdr *tcphdr);
 bool tcp_syn_segment(const struct tcphdr *tcphdr);
 bool tcp_ack_segment(const struct tcphdr *tcphdr);
-// scale_factor=-1 - do not change
+// scale_factor=SCALE_NONE - do not change
 void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor);
 void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor);
diff --git a/nfq/desync.c b/nfq/desync.c
index 20ce0bc..a22b982 100644
--- a/nfq/desync.c
+++ b/nfq/desync.c
@@ -240,6 +240,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 		uint8_t flags_orig = *((uint8_t*)tcphdr+13);
 		uint32_t *timestamps = tcp_find_timestamps(tcphdr);
 		enum dpi_desync_mode desync_mode = params.desync_mode;
+		uint8_t scale_factor = tcp_find_scale_factor(tcphdr);
 		bool b;
 
 		newlen = sizeof(newdata);
@@ -247,7 +248,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 		switch(desync_mode)
 		{
 			case DESYNC_FAKE:
-				if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, timestamps,
+				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,
 					fake, fake_size, newdata, &newlen))
 				{
@@ -259,7 +260,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 				break;
 			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, timestamps,
+				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,
 					NULL, 0, newdata, &newlen))
 				{
@@ -311,7 +312,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 
 					if (split_pos<len_payload)
 					{
-						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, timestamps,
+						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,
 								data_payload+split_pos, len_payload-split_pos, newdata, &newlen))
 							return res;
@@ -325,7 +326,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 					if (desync_mode==DESYNC_DISORDER)
 					{
 						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, timestamps,
+						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,
 								zeropkt, split_pos, fakeseg, &fakeseg_len))
 							return res;
@@ -337,7 +338,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, timestamps,
+					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,
 							data_payload, split_pos, newdata, &newlen))
 						return res;
@@ -367,7 +368,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 					if (desync_mode==DESYNC_SPLIT)
 					{
 						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, timestamps,
+						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,
 								zeropkt, split_pos, fakeseg, &fakeseg_len))
 							return res;
@@ -378,7 +379,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, timestamps,
+					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,
 							data_payload, split_pos, newdata, &newlen))
 						return res;
@@ -398,7 +399,7 @@ packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struc
 					if (split_pos<len_payload)
 					{
 						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, timestamps,
+						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,
 								data_payload+split_pos, len_payload-split_pos, newdata, &newlen))
 							return res;