mirror of
https://github.com/ValdikSS/GoodbyeDPI.git
synced 2026-09-14 11:05:37 +05:00
Title: Add ACK splitting and QoS DSCP marking functionality
Key features implemented: - Added src/acksplit.c and src/acksplit.h implementing TCP ACK splitting with plain and SACK modes for DPI circumvention - Added src/qos.c and src/qos.h implementing DSCP packet marking with low priority WinDivert handle - Extended main filter string in goodbyedpi.c to include ACK splitting traffic for ports 80/443 - Added command line options --ack-split, --ack-split-sack, --ack-split-bytes, --ack-keep-wscale, --qos-dscp - Integrated ACK splitting logic into main packet processing loop with connection tracking - Enhanced checksum recalculation condition to prevent unnecessary processing on injected packets This commit adds sophisticated TCP-level DPI circumvention techniques by allowing fine-grained control over ACK packets and outbound packet prioritization through DSCP marking, integrated seamlessly into the existing WinDivert-based architecture.
This commit is contained in:
parent
f593a276f9
commit
a8a200ae21
60
.gitignore
vendored
60
.gitignore
vendored
@ -1,2 +1,60 @@
|
||||
```
|
||||
# Compiled and binary files
|
||||
*.o
|
||||
*.exe
|
||||
*.obj
|
||||
*.out
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
venv/
|
||||
.venv/
|
||||
__pycache__/
|
||||
.mypy_cache/
|
||||
.pytest_cache/
|
||||
dist/
|
||||
build/
|
||||
target/
|
||||
.gradle/
|
||||
|
||||
# Logs and temp files
|
||||
*.log
|
||||
*.tmp
|
||||
*.swp
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
*.env.*
|
||||
|
||||
# Editors
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Compressed files
|
||||
*.zip
|
||||
*.gz
|
||||
*.tar
|
||||
*.tgz
|
||||
*.bz2
|
||||
*.xz
|
||||
*.7z
|
||||
*.rar
|
||||
*.zst
|
||||
*.lz4
|
||||
*.lzh
|
||||
*.cab
|
||||
*.arj
|
||||
*.rpm
|
||||
*.deb
|
||||
*.Z
|
||||
*.lz
|
||||
*.lzo
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
*.tar.xz
|
||||
*.tar.zst
|
||||
```
|
||||
303
src/acksplit.c
Normal file
303
src/acksplit.c
Normal file
@ -0,0 +1,303 @@
|
||||
#include <windows.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "goodbyedpi.h"
|
||||
#include "acksplit.h"
|
||||
#include "dnsredir.h"
|
||||
#include "utils/uthash.h"
|
||||
|
||||
#define ACK_KEY_LEN 37
|
||||
#define ACK_CLEANUP_INTERVAL_SEC 60
|
||||
|
||||
#undef uthash_strlen
|
||||
#define uthash_strlen(s) ACK_KEY_LEN
|
||||
|
||||
ack_mode_t ack_mode = ACK_OFF;
|
||||
unsigned short ack_step_size = 40;
|
||||
unsigned int ack_limit_bytes = 16384;
|
||||
int ack_zero_wscale = 1;
|
||||
int ack_drop_stack_ack = 1;
|
||||
int ack_sack_max = 2; /* < 3, чтобы не ловить fast retransmit */
|
||||
|
||||
typedef struct ack_conn {
|
||||
char key[ACK_KEY_LEN];
|
||||
time_t time;
|
||||
uint32_t last_ack; /* последний ACK, отправленный нами */
|
||||
uint32_t bytes; /* обработано байт полезной нагрузки */
|
||||
uint8_t have_ack;
|
||||
uint8_t done;
|
||||
UT_hash_handle hh;
|
||||
} ack_conn_t;
|
||||
|
||||
static ack_conn_t *conns = NULL;
|
||||
static time_t last_cleanup = 0;
|
||||
|
||||
/* fill_key_data() скопирована из ttltrack.c для автономности модуля */
|
||||
inline static void fill_key_data(char *key, const uint8_t is_ipv6, const uint32_t srcip[4],
|
||||
const uint32_t dstip[4], const uint16_t srcport, const uint16_t dstport)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
|
||||
if (is_ipv6) {
|
||||
*(uint8_t*)(key) = '6';
|
||||
offset += sizeof(uint8_t);
|
||||
ipv6_copy_addr((uint32_t*)(key + offset), srcip);
|
||||
offset += sizeof(uint32_t) * 4;
|
||||
ipv6_copy_addr((uint32_t*)(key + offset), dstip);
|
||||
offset += sizeof(uint32_t) * 4;
|
||||
}
|
||||
else {
|
||||
*(uint8_t*)(key) = '4';
|
||||
offset += sizeof(uint8_t);
|
||||
ipv4_copy_addr((uint32_t*)(key + offset), srcip);
|
||||
offset += sizeof(uint32_t) * 4;
|
||||
ipv4_copy_addr((uint32_t*)(key + offset), dstip);
|
||||
offset += sizeof(uint32_t) * 4;
|
||||
}
|
||||
|
||||
*(uint16_t*)(key + offset) = srcport;
|
||||
offset += sizeof(srcport);
|
||||
*(uint16_t*)(key + offset) = dstport;
|
||||
offset += sizeof(dstport);
|
||||
}
|
||||
|
||||
inline static void construct_key(const uint32_t srcip[4], const uint32_t dstip[4],
|
||||
const uint16_t srcport, const uint16_t dstport,
|
||||
char *key, const uint8_t is_ipv6)
|
||||
{
|
||||
fill_key_data(key, is_ipv6, srcip, dstip, srcport, dstport);
|
||||
}
|
||||
|
||||
static void ack_cleanup(void) {
|
||||
ack_conn_t *c, *tmp;
|
||||
if (!last_cleanup) { last_cleanup = time(NULL); return; }
|
||||
if (difftime(time(NULL), last_cleanup) < ACK_CLEANUP_INTERVAL_SEC) return;
|
||||
last_cleanup = time(NULL);
|
||||
HASH_ITER(hh, conns, c, tmp) {
|
||||
if (difftime(last_cleanup, c->time) >= ACK_CLEANUP_INTERVAL_SEC) {
|
||||
HASH_DEL(conns, c);
|
||||
free(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ack_conn_t *ack_find(const char *key) {
|
||||
ack_conn_t *c = NULL;
|
||||
if (!conns) return NULL;
|
||||
HASH_FIND_STR(conns, key, c);
|
||||
if (c) c->time = time(NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Обнуление Window Scale в нашем SYN — обязательный шаг */
|
||||
static int tcp_zero_wscale(PWINDIVERT_TCPHDR tcp) {
|
||||
uint8_t *o = (uint8_t*)tcp + sizeof(WINDIVERT_TCPHDR);
|
||||
int len = (int)(tcp->HdrLength * 4) - (int)sizeof(WINDIVERT_TCPHDR);
|
||||
int i = 0, changed = 0;
|
||||
|
||||
while (i < len) {
|
||||
uint8_t kind = o[i], olen;
|
||||
if (kind == 0) break; /* EOL */
|
||||
if (kind == 1) { i++; continue; } /* NOP */
|
||||
if (i + 1 >= len) break;
|
||||
olen = o[i + 1];
|
||||
if (olen < 2 || i + olen > len) break;
|
||||
if (kind == 3 && olen == 3 && o[i + 2] != 0) {
|
||||
o[i + 2] = 0; /* shift.cnt = 0 */
|
||||
changed = 1;
|
||||
}
|
||||
i += olen;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Генерация собственного ACK (с опциональными SACK-блоками) */
|
||||
static void send_ack_packet(HANDLE w_filter, const WINDIVERT_ADDRESS *inaddr,
|
||||
PWINDIVERT_IPHDR ip4, PWINDIVERT_IPV6HDR ip6,
|
||||
PWINDIVERT_TCPHDR tcp,
|
||||
uint32_t ack_num, uint16_t window,
|
||||
const uint32_t sack[][2], int sack_blocks)
|
||||
{
|
||||
char pkt[128];
|
||||
WINDIVERT_ADDRESS addr;
|
||||
PWINDIVERT_TCPHDR t;
|
||||
UINT pktlen, tcplen, optlen = 0;
|
||||
int i;
|
||||
|
||||
if (sack_blocks > 3) sack_blocks = 3; /* 4 + 8*3 = 28 <= 40 байт опций */
|
||||
if (sack_blocks > 0) optlen = 4 + 8u * (unsigned)sack_blocks;
|
||||
|
||||
memset(pkt, 0, sizeof(pkt));
|
||||
|
||||
if (!ip6) {
|
||||
PWINDIVERT_IPHDR h = (PWINDIVERT_IPHDR)pkt;
|
||||
t = (PWINDIVERT_TCPHDR)(pkt + sizeof(WINDIVERT_IPHDR));
|
||||
tcplen = sizeof(WINDIVERT_TCPHDR) + optlen;
|
||||
pktlen = sizeof(WINDIVERT_IPHDR) + tcplen;
|
||||
|
||||
h->Version = 4;
|
||||
h->HdrLength = sizeof(WINDIVERT_IPHDR) / 4;
|
||||
h->TOS = ip4->TOS;
|
||||
h->Length = htons((u_short)pktlen);
|
||||
h->TTL = 128; /* мимикрия под стек Windows */
|
||||
h->Protocol = IPPROTO_TCP;
|
||||
h->SrcAddr = ip4->DstAddr;
|
||||
h->DstAddr = ip4->SrcAddr;
|
||||
}
|
||||
else {
|
||||
PWINDIVERT_IPV6HDR h = (PWINDIVERT_IPV6HDR)pkt;
|
||||
t = (PWINDIVERT_TCPHDR)(pkt + sizeof(WINDIVERT_IPV6HDR));
|
||||
tcplen = sizeof(WINDIVERT_TCPHDR) + optlen;
|
||||
pktlen = sizeof(WINDIVERT_IPV6HDR) + tcplen;
|
||||
|
||||
h->Version = 6;
|
||||
h->Length = htons((u_short)tcplen);
|
||||
h->NextHdr = IPPROTO_TCP;
|
||||
h->HopLimit = 128;
|
||||
ipv6_copy_addr(h->SrcAddr, ip6->DstAddr);
|
||||
ipv6_copy_addr(h->DstAddr, ip6->SrcAddr);
|
||||
}
|
||||
|
||||
t->SrcPort = tcp->DstPort;
|
||||
t->DstPort = tcp->SrcPort;
|
||||
t->SeqNum = tcp->AckNum;
|
||||
t->AckNum = htonl(ack_num);
|
||||
t->HdrLength = tcplen / 4;
|
||||
t->Ack = 1;
|
||||
t->Window = htons(window);
|
||||
|
||||
if (sack_blocks > 0) {
|
||||
uint8_t *opt = (uint8_t*)t + sizeof(WINDIVERT_TCPHDR);
|
||||
*opt++ = 1; *opt++ = 1; /* NOP, NOP — выравнивание */
|
||||
*opt++ = 5; /* kind = SACK */
|
||||
*opt++ = (uint8_t)(2 + 8 * sack_blocks);
|
||||
for (i = 0; i < sack_blocks; i++) {
|
||||
uint32_t l = htonl(sack[i][0]), r = htonl(sack[i][1]);
|
||||
memcpy(opt, &l, 4); opt += 4;
|
||||
memcpy(opt, &r, 4); opt += 4;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&addr, inaddr, sizeof(addr));
|
||||
addr.Outbound = 1;
|
||||
addr.Impostor = 0;
|
||||
addr.IPChecksum = 0;
|
||||
addr.TCPChecksum = 0;
|
||||
|
||||
WinDivertHelperCalcChecksums(pkt, pktlen, &addr, 0);
|
||||
WinDivertSend(w_filter, pkt, pktlen, NULL, &addr);
|
||||
}
|
||||
|
||||
void acksplit_in_data(HANDLE w_filter, const WINDIVERT_ADDRESS *addr,
|
||||
PWINDIVERT_IPHDR ip4, PWINDIVERT_IPV6HDR ip6,
|
||||
PWINDIVERT_TCPHDR tcp, UINT datalen)
|
||||
{
|
||||
char key[ACK_KEY_LEN];
|
||||
ack_conn_t *c;
|
||||
uint32_t seq, end, a;
|
||||
|
||||
if (ack_mode == ACK_OFF || !datalen || !ack_step_size) return;
|
||||
|
||||
ack_cleanup();
|
||||
if (ip6) construct_key(ip6->DstAddr, ip6->SrcAddr, tcp->DstPort, tcp->SrcPort, key, 1);
|
||||
else construct_key((uint32_t*)&ip4->DstAddr, (uint32_t*)&ip4->SrcAddr,
|
||||
tcp->DstPort, tcp->SrcPort, key, 0);
|
||||
|
||||
c = ack_find(key);
|
||||
if (!c || c->done) return;
|
||||
|
||||
seq = ntohl(tcp->SeqNum);
|
||||
end = seq + datalen;
|
||||
|
||||
if (!c->have_ack) { c->last_ack = seq; c->have_ack = 1; }
|
||||
if ((int32_t)(end - c->last_ack) <= 0) return; /* ретрансмит/старьё */
|
||||
|
||||
if (ack_mode == ACK_PLAIN) {
|
||||
/* Каждый шаг ack_step_size подтверждается отдельным ACK */
|
||||
a = c->last_ack;
|
||||
while ((int32_t)(end - a) > 0) {
|
||||
uint32_t step = ack_step_size;
|
||||
if ((uint32_t)(end - a) < step) step = end - a;
|
||||
a += step;
|
||||
send_ack_packet(w_filter, addr, ip4, ip6, tcp, a, ack_step_size, NULL, 0);
|
||||
}
|
||||
c->last_ack = a;
|
||||
}
|
||||
else { /* ACK_SACK */
|
||||
uint32_t left = c->last_ack;
|
||||
uint32_t s;
|
||||
int sent = 0;
|
||||
for (s = seq; (int32_t)(end - s) > 0 && sent < ack_sack_max; s += ack_step_size) {
|
||||
uint32_t e = s + ack_step_size;
|
||||
uint32_t blk[1][2];
|
||||
if ((int32_t)(e - end) > 0) e = end;
|
||||
blk[0][0] = s; blk[0][1] = e;
|
||||
/* кумулятивный ACK придержан на left, шаг подтверждён SACK-блоком */
|
||||
send_ack_packet(w_filter, addr, ip4, ip6, tcp, left, ack_step_size, blk, 1);
|
||||
sent++;
|
||||
}
|
||||
/* закрываем дырку кумулятивно, чтобы сервер не ушёл в fast retransmit */
|
||||
send_ack_packet(w_filter, addr, ip4, ip6, tcp, end, ack_step_size, NULL, 0);
|
||||
c->last_ack = end;
|
||||
}
|
||||
|
||||
c->bytes += datalen;
|
||||
if (ack_limit_bytes && c->bytes >= ack_limit_bytes) c->done = 1;
|
||||
}
|
||||
|
||||
void acksplit_out_syn(PWINDIVERT_TCPHDR tcp, uint32_t srcip[4], uint32_t dstip[4],
|
||||
uint8_t is_ipv6, int *recalc)
|
||||
{
|
||||
ack_conn_t *c;
|
||||
char key[ACK_KEY_LEN];
|
||||
|
||||
if (ack_mode == ACK_OFF) return;
|
||||
ack_cleanup();
|
||||
construct_key(srcip, dstip, tcp->SrcPort, tcp->DstPort, key, is_ipv6);
|
||||
|
||||
c = ack_find(key);
|
||||
if (!c) {
|
||||
c = calloc(1, sizeof(*c));
|
||||
if (!c) return;
|
||||
memcpy(c->key, key, ACK_KEY_LEN);
|
||||
HASH_ADD_STR(conns, key, c);
|
||||
}
|
||||
c->time = time(NULL);
|
||||
c->have_ack = c->done = 0;
|
||||
c->bytes = 0;
|
||||
|
||||
if (ack_zero_wscale && tcp_zero_wscale(tcp)) *recalc = 1;
|
||||
if (ntohs(tcp->Window) > ack_step_size) {
|
||||
tcp->Window = htons(ack_step_size);
|
||||
*recalc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int acksplit_out(PWINDIVERT_TCPHDR tcp, uint32_t srcip[4], uint32_t dstip[4],
|
||||
uint8_t is_ipv6, UINT datalen, int *recalc)
|
||||
{
|
||||
ack_conn_t *c;
|
||||
char key[ACK_KEY_LEN];
|
||||
|
||||
if (ack_mode == ACK_OFF) return 1;
|
||||
construct_key(srcip, dstip, tcp->SrcPort, tcp->DstPort, key, is_ipv6);
|
||||
c = ack_find(key);
|
||||
if (!c) return 1;
|
||||
|
||||
if (tcp->Fin || tcp->Rst) { HASH_DEL(conns, c); free(c); return 1; }
|
||||
if (c->done) return 1;
|
||||
|
||||
if (ack_drop_stack_ack && !datalen && tcp->Ack && !tcp->Syn && c->have_ack &&
|
||||
(int32_t)(ntohl(tcp->AckNum) - c->last_ack) <= 0)
|
||||
{
|
||||
return 0; /* это мы уже подтвердили сами — не реинжектим */
|
||||
}
|
||||
|
||||
if (ntohs(tcp->Window) > ack_step_size) {
|
||||
tcp->Window = htons(ack_step_size);
|
||||
*recalc = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
28
src/acksplit.h
Normal file
28
src/acksplit.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef _ACKSPLIT_H
|
||||
#define _ACKSPLIT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
#include "windivert.h"
|
||||
|
||||
typedef enum {
|
||||
ACK_OFF = 0,
|
||||
ACK_PLAIN, /* --ack-split : кумулятивные ACK шагами */
|
||||
ACK_SACK /* --ack-split-sack : шаги подтверждаются SACK-блоками */
|
||||
} ack_mode_t;
|
||||
|
||||
extern ack_mode_t ack_mode;
|
||||
extern unsigned short ack_step_size; /* размер шага и анонсируемого окна, байт */
|
||||
extern unsigned int ack_limit_bytes; /* обрабатывать первые N байт ответа, 0 = всё */
|
||||
extern int ack_zero_wscale; /* обнулять Window Scale в исходящем SYN */
|
||||
extern int ack_drop_stack_ack; /* давить лишние ACK стека */
|
||||
extern int ack_sack_max; /* макс. SACK-ACK за один входящий сегмент */
|
||||
|
||||
void acksplit_out_syn(PWINDIVERT_TCPHDR tcp, uint32_t srcip[4], uint32_t dstip[4],
|
||||
uint8_t is_ipv6, int *recalc);
|
||||
int acksplit_out(PWINDIVERT_TCPHDR tcp, uint32_t srcip[4], uint32_t dstip[4],
|
||||
uint8_t is_ipv6, UINT datalen, int *recalc);
|
||||
void acksplit_in_data(HANDLE w_filter, const WINDIVERT_ADDRESS *addr,
|
||||
PWINDIVERT_IPHDR ip4, PWINDIVERT_IPV6HDR ip6,
|
||||
PWINDIVERT_TCPHDR tcp, UINT datalen);
|
||||
#endif
|
||||
@ -20,6 +20,8 @@
|
||||
#include "ttltrack.h"
|
||||
#include "blackwhitelist.h"
|
||||
#include "fakepackets.h"
|
||||
#include "acksplit.h"
|
||||
#include "qos.h"
|
||||
|
||||
// My mingw installation does not load inet_pton definition for some reason
|
||||
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);
|
||||
@ -193,6 +195,11 @@ static struct option long_options[] = {
|
||||
{"fake-gen", required_argument, 0, 'j' },
|
||||
{"fake-resend", required_argument, 0, 't' },
|
||||
{"debug-exit", optional_argument, 0, 'x' },
|
||||
{"ack-split", optional_argument, 0, '&' },
|
||||
{"ack-split-sack", optional_argument, 0, '^' },
|
||||
{"ack-split-bytes",required_argument, 0, '~' },
|
||||
{"ack-keep-wscale",no_argument, 0, '=' },
|
||||
{"qos-dscp", optional_argument, 0, '_' },
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@ -617,11 +624,13 @@ int main(int argc, char *argv[]) {
|
||||
do_auto_ttl = 0,
|
||||
do_wrong_chksum = 0,
|
||||
do_wrong_seq = 0,
|
||||
do_native_frag = 0, do_reverse_frag = 0;
|
||||
do_native_frag = 0, do_reverse_frag = 0,
|
||||
do_qos = 0;
|
||||
unsigned int http_fragment_size = 0;
|
||||
unsigned int https_fragment_size = 0;
|
||||
unsigned int current_fragment_size = 0;
|
||||
unsigned short max_payload_size = 0;
|
||||
uint8_t qos_dscp = 46;
|
||||
BYTE should_send_fake = 0;
|
||||
BYTE ttl_of_fake_packet = 0;
|
||||
BYTE ttl_min_nhops = 0;
|
||||
@ -971,6 +980,30 @@ int main(int argc, char *argv[]) {
|
||||
case 'x': // --debug-exit
|
||||
debug_exit = true;
|
||||
break;
|
||||
case '&': /* --ack-split [size] */
|
||||
case '^': /* --ack-split-sack [size] */
|
||||
ack_mode = (opt == '&') ? ACK_PLAIN : ACK_SACK;
|
||||
if (!optarg && argv[optind] && argv[optind][0] != '-')
|
||||
optarg = argv[optind];
|
||||
if (optarg)
|
||||
ack_step_size = atousi(optarg, "ACK split size parameter error!");
|
||||
if (ack_step_size < 8) {
|
||||
puts("WARNING: ack-split size < 8 is impractical, using 8");
|
||||
ack_step_size = 8;
|
||||
}
|
||||
break;
|
||||
case '~':
|
||||
ack_limit_bytes = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case '=':
|
||||
ack_zero_wscale = 0;
|
||||
break;
|
||||
case '_': /* --qos-dscp [0-63] */
|
||||
if (!optarg && argv[optind] && argv[optind][0] != '-')
|
||||
optarg = argv[optind];
|
||||
do_qos = 1;
|
||||
qos_dscp = optarg ? (atoub(optarg, "DSCP parameter error!") & 0x3F) : 46;
|
||||
break;
|
||||
default:
|
||||
puts("Usage: goodbyedpi.exe [OPTION...]\n"
|
||||
" -p block passive DPI\n"
|
||||
|
||||
70
src/qos.c
Normal file
70
src/qos.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include <windows.h>
|
||||
#include "goodbyedpi.h"
|
||||
#include "qos.h"
|
||||
#include "windivert.h"
|
||||
|
||||
#define QOS_FILTER "outbound and !loopback and !impostor and (ip or ipv6)"
|
||||
#define QOS_PRIORITY (-1000)
|
||||
|
||||
static HANDLE qos_handle = NULL, qos_thread = NULL;
|
||||
static volatile LONG qos_run = 0;
|
||||
static uint8_t qos_dscp_value = 46; /* EF */
|
||||
|
||||
static DWORD WINAPI qos_proc(LPVOID unused) {
|
||||
char packet[MAX_PACKET_SIZE];
|
||||
UINT packetLen;
|
||||
WINDIVERT_ADDRESS addr;
|
||||
(void)unused;
|
||||
|
||||
while (qos_run) {
|
||||
if (!WinDivertRecv(qos_handle, packet, sizeof(packet), &packetLen, &addr)) {
|
||||
if (GetLastError() == ERROR_NO_DATA) break;
|
||||
continue;
|
||||
}
|
||||
if (!addr.IPv6 && packetLen >= sizeof(WINDIVERT_IPHDR)) {
|
||||
PWINDIVERT_IPHDR ip = (PWINDIVERT_IPHDR)packet;
|
||||
uint8_t newtos = (uint8_t)((qos_dscp_value << 2) | (ip->TOS & 0x03)); /* ECN сохраняем */
|
||||
if (ip->TOS != newtos) {
|
||||
ip->TOS = newtos;
|
||||
addr.IPChecksum = 0;
|
||||
/* меняется только IP-заголовок: транспортные суммы не трогаем */
|
||||
WinDivertHelperCalcChecksums(packet, packetLen, &addr,
|
||||
WINDIVERT_HELPER_NO_TCP_CHECKSUM |
|
||||
WINDIVERT_HELPER_NO_UDP_CHECKSUM |
|
||||
WINDIVERT_HELPER_NO_ICMP_CHECKSUM |
|
||||
WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM);
|
||||
}
|
||||
}
|
||||
else if (addr.IPv6 && packetLen >= sizeof(WINDIVERT_IPV6HDR)) {
|
||||
uint8_t *p = (uint8_t*)packet;
|
||||
uint8_t tc = (uint8_t)(((p[0] & 0x0F) << 4) | (p[1] >> 4));
|
||||
uint8_t ntc = (uint8_t)((qos_dscp_value << 2) | (tc & 0x03));
|
||||
p[0] = (uint8_t)((p[0] & 0xF0) | (ntc >> 4));
|
||||
p[1] = (uint8_t)((p[1] & 0x0F) | ((ntc & 0x0F) << 4));
|
||||
/* в IPv6 нет контрольной суммы заголовка */
|
||||
}
|
||||
WinDivertSend(qos_handle, packet, packetLen, NULL, &addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qos_start(uint8_t dscp) {
|
||||
if (qos_handle) return TRUE;
|
||||
qos_dscp_value = dscp & 0x3F;
|
||||
qos_handle = WinDivertOpen(QOS_FILTER, WINDIVERT_LAYER_NETWORK, QOS_PRIORITY, 0);
|
||||
if (qos_handle == INVALID_HANDLE_VALUE) { qos_handle = NULL; return FALSE; }
|
||||
WinDivertSetParam(qos_handle, WINDIVERT_PARAM_QUEUE_LENGTH, 16384);
|
||||
WinDivertSetParam(qos_handle, WINDIVERT_PARAM_QUEUE_TIME, 2000);
|
||||
qos_run = 1;
|
||||
qos_thread = CreateThread(NULL, 0, qos_proc, NULL, 0, NULL);
|
||||
return qos_thread != NULL;
|
||||
}
|
||||
|
||||
void qos_stop(void) {
|
||||
if (!qos_handle) return;
|
||||
qos_run = 0;
|
||||
WinDivertShutdown(qos_handle, WINDIVERT_SHUTDOWN_BOTH);
|
||||
if (qos_thread) { WaitForSingleObject(qos_thread, 2000); CloseHandle(qos_thread); }
|
||||
WinDivertClose(qos_handle);
|
||||
qos_handle = NULL; qos_thread = NULL;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user