Update faking strategies

Use random ip4 id for frags, use sequential ip4 id for fakes
This commit is contained in:
Vadim Vetrov 2024-10-12 12:19:33 +03:00
parent 30bc3a8d3f
commit e9b033ccca
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
14 changed files with 397 additions and 270 deletions

View File

@ -85,7 +85,7 @@ For nftables on OpenWRT rules comes out-of-the-box and stored under `/usr/share/
Now we go to the configuration. For OpenWRT here is configuration via [UCI](https://openwrt.org/docs/guide-user/base-system/uci) and [LuCI](https://openwrt.org/docs/guide-user/luci/start) available (CLI and GUI respectively). Now we go to the configuration. For OpenWRT here is configuration via [UCI](https://openwrt.org/docs/guide-user/base-system/uci) and [LuCI](https://openwrt.org/docs/guide-user/luci/start) available (CLI and GUI respectively).
For **LuCI** aka **GUI** aka **web-interface of router** you should install luci-app-youtubeUnblock package like you did it with the normal youtubeUnblock package. Note, that lists of official opkg feeds should be loaded (**Do it with Update lists option**). For **LuCI** aka **GUI** aka **web-interface of router** you should install **luci-app-youtubeUnblock** package like you did it with the normal youtubeUnblock package. Note, that lists of official opkg feeds should be loaded (**Do it with Update lists option**).
LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted. LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted.
@ -183,6 +183,10 @@ Available flags:
- `--fake-sni-seq-len=<length>` This flag specifies **youtubeUnblock** to build a complicated construction of fake client hello packets. length determines how much fakes will be sent. Defaults to **1**. - `--fake-sni-seq-len=<length>` This flag specifies **youtubeUnblock** to build a complicated construction of fake client hello packets. length determines how much fakes will be sent. Defaults to **1**.
- `--fake-sni-type={default|custom|random}` This flag specifies which faking messages should use fake packets. If you pass random, the message of random length and random payload will be sent. For default the default payload is used. And for custom the payload from `--fake-custom-payload` is used. Default to `default`.
- `--fake-custom-payload=<payload>` Usable with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake.
- `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq` - `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq`
- `randseq` specifies that random sequence/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled. - `randseq` specifies that random sequence/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled.
- `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured. - `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured.

18
args.c
View File

@ -8,6 +8,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "types.h" #include "types.h"
#include "args.h"
static char custom_fake_buf[MAX_FAKE_SIZE]; static char custom_fake_buf[MAX_FAKE_SIZE];
@ -20,7 +21,6 @@ struct config_t config = {
.faking_ttl = FAKE_TTL, .faking_ttl = FAKE_TTL,
.fake_sni = 1, .fake_sni = 1,
.fake_sni_seq_len = 1, .fake_sni_seq_len = 1,
.fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT,
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, .fake_sni_type = FAKE_PAYLOAD_DEFAULT,
.frag_middle_sni = 1, .frag_middle_sni = 1,
.frag_sni_pos = 1, .frag_sni_pos = 1,
@ -69,7 +69,6 @@ struct config_t config = {
#define OPT_FAKING_TTL 3 #define OPT_FAKING_TTL 3
#define OPT_FAKING_STRATEGY 10 #define OPT_FAKING_STRATEGY 10
#define OPT_FAKE_SNI_SEQ_LEN 11 #define OPT_FAKE_SNI_SEQ_LEN 11
#define OPT_FAKE_SNI_SEQ_TYPE 26
#define OPT_FAKE_SNI_TYPE 27 #define OPT_FAKE_SNI_TYPE 27
#define OPT_FAKE_CUSTOM_PAYLOAD 28 #define OPT_FAKE_CUSTOM_PAYLOAD 28
#define OPT_FRAG 4 #define OPT_FRAG 4
@ -103,7 +102,6 @@ static struct option long_opt[] = {
{"synfake", 1, 0, OPT_SYNFAKE}, {"synfake", 1, 0, OPT_SYNFAKE},
{"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN},
{"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN},
{"fake-sni-seq-type", 1, 0, OPT_FAKE_SNI_SEQ_TYPE},
{"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE}, {"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE},
{"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD}, {"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD},
{"faking-strategy", 1, 0, OPT_FAKING_STRATEGY}, {"faking-strategy", 1, 0, OPT_FAKING_STRATEGY},
@ -162,7 +160,6 @@ void print_usage(const char *argv0) {
printf("\t--exclude-domains=<comma separated domain list>\n"); printf("\t--exclude-domains=<comma separated domain list>\n");
printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni={1|0}\n");
printf("\t--fake-sni-seq-len=<length>\n"); printf("\t--fake-sni-seq-len=<length>\n");
printf("\t--fake-sni-seq-type={default|random|custom}\n");
printf("\t--fake-sni-type={default|random|custom}\n"); printf("\t--fake-sni-type={default|random|custom}\n");
printf("\t--fake-custom-payload=<hex payload>\n"); printf("\t--fake-custom-payload=<hex payload>\n");
printf("\t--fake-seq-offset=<offset>\n"); printf("\t--fake-seq-offset=<offset>\n");
@ -337,18 +334,6 @@ int parse_args(int argc, char *argv[]) {
} }
config.fake_sni_seq_len = num; config.fake_sni_seq_len = num;
break;
case OPT_FAKE_SNI_SEQ_TYPE:
if (strcmp(optarg, "default") == 0) {
config.fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT;
} else if (strcmp(optarg, "random") == 0) {
config.fake_sni_seq_type = FAKE_PAYLOAD_RANDOM;
} else if (strcmp(optarg, "custom") == 0) {
config.fake_sni_seq_type = FAKE_PAYLOAD_CUSTOM;
} else {
goto invalid_opt;
}
break; break;
case OPT_FAKE_SNI_TYPE: case OPT_FAKE_SNI_TYPE:
if (strcmp(optarg, "default") == 0) { if (strcmp(optarg, "default") == 0) {
@ -553,3 +538,4 @@ void print_welcome() {
} }
} }

4
args.h
View File

@ -1,3 +1,6 @@
#include "utils.h"
#include "tls.h"
#ifndef ARGS_H #ifndef ARGS_H
#define ARGS_H #define ARGS_H
@ -8,4 +11,5 @@ int parse_args(int argc, char *argv[]);
/* Prints starting messages */ /* Prints starting messages */
void print_welcome(); void print_welcome();
#endif /* ARGS_H */ #endif /* ARGS_H */

View File

@ -10,7 +10,7 @@ typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len);
* Sends the packet after delay_ms. The function should schedule send and return immediately * Sends the packet after delay_ms. The function should schedule send and return immediately
* (for example, open daemon thread) * (for example, open daemon thread)
*/ */
typedef void (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms); typedef int (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms);
struct instance_config_t { struct instance_config_t {
raw_send_t send_raw_packet; raw_send_t send_raw_packet;
@ -37,7 +37,6 @@ struct config_t {
#define FAKE_PAYLOAD_CUSTOM 1 #define FAKE_PAYLOAD_CUSTOM 1
// In default mode all other options will be skipped. // In default mode all other options will be skipped.
#define FAKE_PAYLOAD_DEFAULT 2 #define FAKE_PAYLOAD_DEFAULT 2
int fake_sni_seq_type;
int fake_sni_type; int fake_sni_type;
#define VERBOSE_INFO 0 #define VERBOSE_INFO 0

View File

@ -23,7 +23,6 @@ struct config_t config = {
.mark = DEFAULT_RAWSOCKET_MARK, .mark = DEFAULT_RAWSOCKET_MARK,
.synfake = 0, .synfake = 0,
.synfake_len = 0, .synfake_len = 0,
.fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT,
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, .fake_sni_type = FAKE_PAYLOAD_DEFAULT,
.sni_detection = SNI_DETECTION_PARSE, .sni_detection = SNI_DETECTION_PARSE,

View File

@ -217,19 +217,22 @@ erret_lc:
int ipvx = netproto_version(pkt, pktlen); int ipvx = netproto_version(pkt, pktlen);
if (ipvx == IP4VERSION) if (ipvx == IP4VERSION) {
return send_raw_ipv4(pkt, pktlen); return send_raw_ipv4(pkt, pktlen);
} else if (ipvx == IP6VERSION) {
else if (ipvx == IP6VERSION)
return send_raw_ipv6(pkt, pktlen); return send_raw_ipv6(pkt, pktlen);
} else {
printf("proto version %d is unsupported\n", ipvx); printf("proto version %d is unsupported\n", ipvx);
return -EINVAL; return -EINVAL;
}
lgtrace_addp("raw_sock_send: %d", ret);
return ret;
} }
static void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { static int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
pr_info("delay_packet_send won't work on current youtubeUnblock version"); pr_info("delay_packet_send won't work on current youtubeUnblock version");
send_raw_socket(data, data_len); return send_raw_socket(data, data_len);
} }
struct instance_config_t instance_config = { struct instance_config_t instance_config = {

269
mangle.c
View File

@ -175,18 +175,27 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) {
if (config.fk_winsize) { if (config.fk_winsize) {
tcph->window = htons(config.fk_winsize); tcph->window = htons(config.fk_winsize);
set_tcp_checksum(tcph, iph, iph_len);
}
if (0) {
int delta = 2;
ret = seqovl_packet(payload, &payload_len, delta);
int ret = tcp_payload_split(payload, payload_len,
&iph, &iph_len, &tcph, &tcph_len,
&data, &dlen);
if (ret < 0) {
lgerror("seqovl_packet delta %d", ret, delta);
}
} }
set_ip_checksum(iph, iph_len);
set_tcp_checksum(tcph, iph, iph_len);
if (dlen > 1480 && config.verbose) { if (dlen > 1480 && config.verbose) {
lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!"); lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!");
} }
if (config.fake_sni) { if (config.fake_sni) {
post_fake_sni(iph, iph_len, tcph, tcph_len, post_fake_sni(args_default_fake_type(), iph, iph_len, tcph, tcph_len);
config.fake_sni_seq_len);
} }
size_t ipd_offset; size_t ipd_offset;
@ -324,6 +333,8 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) {
printf("], "); printf("], ");
} }
if (config.quic_drop) {
lgtrace_addp("QUIC probe"); lgtrace_addp("QUIC probe");
const struct quic_lhdr *qch; const struct quic_lhdr *qch;
uint32_t qch_len; uint32_t qch_len;
@ -336,15 +347,13 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) {
if (ret < 0) { if (ret < 0) {
lgtrace_addp("undefined type"); lgtrace_addp("undefined type");
goto accept; goto accept_quic;
} }
lgtrace_addp("QUIC detected"); lgtrace_addp("QUIC detected");
uint8_t qtype = qch->type; uint8_t qtype = qch->type;
if (config.quic_drop) {
goto drop; goto drop;
}
if (qch->version == QUIC_V1) if (qch->version == QUIC_V1)
qtype = quic_convtype_v1(qtype); qtype = quic_convtype_v1(qtype);
@ -353,10 +362,46 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) {
if (qtype != QUIC_INITIAL_TYPE) { if (qtype != QUIC_INITIAL_TYPE) {
lgtrace_addp("quic message type: %d", qtype); lgtrace_addp("quic message type: %d", qtype);
goto accept; goto accept_quic;
} }
lgtrace_addp("quic initial message"); lgtrace_addp("quic initial message");
}
accept_quic:
;
/*
if (1) {
lgtrace_addp("Probe udp");
if (ipver == IP4VERSION && ntohs(udph->dest) > 30) {
lgtrace_addp("udp fool");
const uint8_t *payload;
uint32_t payload_len;
uint32_t poses[10];
int cnt = 3;
poses[0] = 8;
for (int i = 1; i < cnt; i++) {
poses[i] = poses[i - 1] + 8;
}
ret = send_ip4_frags(pkt, pktlen, poses, cnt, 0);
if (ret < 0) {
lgerror("ip4 send frags", ret);
goto accept;
}
goto drop;
} else {
printf("WARNING: IP fragmentation is supported only for IPv4\n");
goto accept;
}
}
*/
accept: accept:
lgtrace_addp("accepted"); lgtrace_addp("accepted");
@ -377,11 +422,13 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
return -EINVAL; return -EINVAL;
} }
lgtrace_addp("Sent %d delayed for %d", pktlen, config.seg2_delay);
instance_config.send_delayed_packet( instance_config.send_delayed_packet(
packet, pktlen, config.seg2_delay); packet, pktlen, config.seg2_delay);
return 0; return 0;
} else { } else {
lgtrace_addp("Sent %d bytes", pktlen);
return instance_config.send_raw_packet( return instance_config.send_raw_packet(
packet, pktlen); packet, pktlen);
} }
@ -399,8 +446,17 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
return -ENOMEM; return -ENOMEM;
} }
NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE);
if (!NETBUF_CHECK(fake_pad)) {
lgerror("Allocation error", -ENOMEM);
NETBUF_FREE(frag1);
NETBUF_FREE(frag2);
return -ENOMEM;
}
uint32_t f1len = MAX_PACKET_SIZE; uint32_t f1len = MAX_PACKET_SIZE;
uint32_t f2len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE;
uint32_t fake_pad_len = MAX_PACKET_SIZE;
int ret; int ret;
@ -410,7 +466,10 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
goto erret_lc; goto erret_lc;
} }
ret = ip4_frag(packet, pktlen, poses[0] - dvs, uint32_t frag_pos = poses[0] - dvs;
frag_pos += 8 - frag_pos % 8;
ret = ip4_frag(packet, pktlen, frag_pos,
frag1, &f1len, frag2, &f2len); frag1, &f1len, frag2, &f2len);
if (ret < 0) { if (ret < 0) {
@ -418,6 +477,8 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
goto erret_lc; goto erret_lc;
} }
dvs += frag_pos;
if (config.frag_sni_reverse) if (config.frag_sni_reverse)
goto send_frag2; goto send_frag2;
send_frag1: send_frag1:
@ -429,23 +490,54 @@ send_frag1:
if (config.frag_sni_reverse) if (config.frag_sni_reverse)
goto out_lc; goto out_lc;
send_fake:
/*
if (config.frag_sni_faked) {
ITER_FAKE_STRAT(config.faking_strategy, strategy) {
uint32_t iphfl;
fake_pad_len = f2len;
ret = ip4_payload_split(frag2, f2len, NULL, &iphfl, NULL, NULL);
if (ret < 0) {
lgerror("Invalid frag2", ret);
goto erret_lc;
}
memcpy(fake_pad, frag2, iphfl + sizeof(struct udphdr));
memset(fake_pad + iphfl + sizeof(struct udphdr), 0, f2len - iphfl - sizeof(struct udphdr));
((struct iphdr *)fake_pad)->tot_len = htons(fake_pad_len);
((struct iphdr *)fake_pad)->id = 1;
((struct iphdr *)fake_pad)->ttl = 8;
((struct iphdr *)fake_pad)->frag_off = 0;
ip4_set_checksum((struct iphdr*)fake_pad);
// *(struct udphdr *)(fake_pad + iphfl) = *(struct udphdr *)(frag2 + iphfl);
ret = send_ip4_frags(fake_pad, fake_pad_len, NULL, 0, 0);
if (ret < 0) {
goto erret_lc;
}
}
}
*/
if (config.frag_sni_reverse)
goto send_frag1;
send_frag2: send_frag2:
dvs += poses[0];
ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs);
if (ret < 0) { if (ret < 0) {
goto erret_lc; goto erret_lc;
} }
if (config.frag_sni_reverse) if (config.frag_sni_reverse)
goto send_frag1; goto send_fake;
out_lc: out_lc:
NETBUF_FREE(frag1); NETBUF_FREE(frag1);
NETBUF_FREE(frag2); NETBUF_FREE(frag2);
NETBUF_FREE(fake_pad);
goto out; goto out;
erret_lc: erret_lc:
NETBUF_FREE(frag1); NETBUF_FREE(frag1);
NETBUF_FREE(frag2); NETBUF_FREE(frag2);
NETBUF_FREE(fake_pad);
return ret; return ret;
} }
@ -483,18 +575,8 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
return -ENOMEM; return -ENOMEM;
} }
NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE);
if (!NETBUF_CHECK(fake_pad)) {
lgerror("Allocation error", -ENOMEM);
NETBUF_FREE(frag1);
NETBUF_FREE(frag2);
return -ENOMEM;
}
uint32_t f1len = MAX_PACKET_SIZE; uint32_t f1len = MAX_PACKET_SIZE;
uint32_t f2len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE;
uint32_t fake_pad_len = MAX_PACKET_SIZE;
int ret; int ret;
@ -508,6 +590,7 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
ret = tcp_frag(packet, pktlen, poses[0] - dvs, ret = tcp_frag(packet, pktlen, poses[0] - dvs,
frag1, &f1len, frag2, &f2len); frag1, &f1len, frag2, &f2len);
lgtrace_addp("Packet split in %d bytes position of payload start, dvs: %d to two packets of %d and %d lengths", poses[0], dvs, f1len, f2len); lgtrace_addp("Packet split in %d bytes position of payload start, dvs: %d to two packets of %d and %d lengths", poses[0], dvs, f1len, f2len);
if (ret < 0) { if (ret < 0) {
@ -516,6 +599,8 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
} }
dvs += poses[0];
if (config.frag_sni_reverse) if (config.frag_sni_reverse)
goto send_frag2; goto send_frag2;
@ -532,33 +617,20 @@ send_frag1:
send_fake: send_fake:
if (config.frag_sni_faked) { if (config.frag_sni_faked) {
ITER_FAKE_STRAT(config.faking_strategy, strategy) {
uint32_t iphfl, tcphfl; uint32_t iphfl, tcphfl;
fake_pad_len = f2len; void *iph;
ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); struct tcphdr *tcph;
if (ret < 0) { ret = tcp_payload_split(frag2, f2len, &iph, &iphfl, &tcph, &tcphfl, NULL, NULL);
lgerror("Invalid frag2", ret); struct fake_type f_type = args_default_fake_type();
goto erret_lc; if ((f_type.strategy.strategy & FAKE_STRAT_PAST_SEQ) == FAKE_STRAT_PAST_SEQ) {
} f_type.strategy.strategy ^= FAKE_STRAT_PAST_SEQ;
memcpy(fake_pad, frag2, iphfl + tcphfl); f_type.strategy.strategy |= FAKE_STRAT_RAND_SEQ;
memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); f_type.strategy.randseq_offset = dvs;
struct tcphdr *fakethdr = (void *)(fake_pad + iphfl);
if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) {
lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq));
fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs);
lgtrace_addp("%u, ", ntohl(fakethdr->seq));
}
ret = fail_packet(strategy,
fake_pad, &fake_pad_len, MAX_PACKET_SIZE);
if (ret < 0) {
lgerror("Failed to fail packet", ret);
goto erret_lc;
}
ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0);
if (ret < 0) {
goto erret_lc;
}
} }
f_type.seg2delay = config.seg2_delay;
post_fake_sni(f_type, iph, iphfl, tcph, tcphfl);
} }
if (config.frag_sni_reverse) if (config.frag_sni_reverse)
@ -566,7 +638,6 @@ send_fake:
send_frag2: send_frag2:
{ {
dvs += poses[0];
ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs);
if (ret < 0) { if (ret < 0) {
goto erret_lc; goto erret_lc;
@ -578,52 +649,38 @@ send_frag2:
out_lc: out_lc:
NETBUF_FREE(frag1); NETBUF_FREE(frag1);
NETBUF_FREE(frag2); NETBUF_FREE(frag2);
NETBUF_FREE(fake_pad);
goto out; goto out;
erret_lc: erret_lc:
NETBUF_FREE(frag1); NETBUF_FREE(frag1);
NETBUF_FREE(frag2); NETBUF_FREE(frag2);
NETBUF_FREE(fake_pad);
return ret; return ret;
} }
out: out:
return 0; return 0;
} }
int post_fake_sni(const void *iph, unsigned int iph_len, int post_fake_sni(struct fake_type f_type,
const struct tcphdr *tcph, unsigned int tcph_len, const void *iph, unsigned int iph_len,
unsigned char sequence_len) { const struct tcphdr *tcph, unsigned int tcph_len) {
uint8_t rfsiph[128]; uint8_t rfsiph[128];
uint8_t rfstcph[60]; uint8_t rfstcph[60];
int ret; int ret;
int ipxv = netproto_version(iph, iph_len);
memcpy(rfsiph, iph, iph_len); memcpy(rfsiph, iph, iph_len);
memcpy(rfstcph, tcph, tcph_len); memcpy(rfstcph, tcph, tcph_len);
void *fsiph = (void *)rfsiph; void *fsiph = (void *)rfsiph;
struct tcphdr *fstcph = (void *)rfstcph; struct tcphdr *fstcph = (void *)rfstcph;
ITER_FAKE_STRAT(config.faking_strategy, strategy) { ITER_FAKE_STRAT(f_type.strategy.strategy, strategy) {
struct fake_type fake_seq_type = { struct fake_type fake_seq_type = f_type;
.type = FAKE_PAYLOAD_DEFAULT, fake_seq_type.strategy.strategy = strategy;
.strategy = strategy,
};
switch (config.fake_sni_seq_type) {
case FAKE_PAYLOAD_RANDOM:
fake_seq_type.type = FAKE_PAYLOAD_RANDOM;
break;
case FAKE_PAYLOAD_CUSTOM:
fake_seq_type.type = FAKE_PAYLOAD_CUSTOM;
fake_seq_type.fake_data = config.fake_custom_pkt;
fake_seq_type.fake_len = config.fake_custom_pkt_sz;
break;
default:
fake_seq_type.type = FAKE_PAYLOAD_DEFAULT;
}
// one goes for default fake // one goes for default fake
for (int i = 1; i < sequence_len; i++) { for (int i = 0; i < fake_seq_type.sequence_len; i++) {
NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE);
if (!NETBUF_CHECK(fake_sni)) { if (!NETBUF_CHECK(fake_sni)) {
lgerror("Allocation error", -ENOMEM); lgerror("Allocation error", -ENOMEM);
@ -641,16 +698,16 @@ int post_fake_sni(const void *iph, unsigned int iph_len,
} }
lgtrace_addp("post fake sni #%d", i + 1); lgtrace_addp("post fake sni #%d", i + 1);
lgtrace_addp("post with %d bytes", fsn_len);
if (f_type.seg2delay) {
ret = instance_config.send_delayed_packet(fake_sni, fsn_len, f_type.seg2delay);
} else {
ret = instance_config.send_raw_packet(fake_sni, fsn_len); ret = instance_config.send_raw_packet(fake_sni, fsn_len);
}
if (ret < 0) { if (ret < 0) {
lgerror("send fake sni", ret); lgerror("send fake sni", ret);
goto erret_lc; goto erret_lc;
} }
if (!(config.faking_strategy == FAKE_STRAT_PAST_SEQ ||
config.faking_strategy == FAKE_STRAT_RAND_SEQ)) {
uint32_t iph_len; uint32_t iph_len;
uint32_t tcph_len; uint32_t tcph_len;
uint32_t plen; uint32_t plen;
@ -665,12 +722,21 @@ int post_fake_sni(const void *iph, unsigned int iph_len,
goto erret_lc; goto erret_lc;
} }
if (!(strategy == FAKE_STRAT_PAST_SEQ ||
strategy == FAKE_STRAT_RAND_SEQ)) {
fstcph->seq = htonl(ntohl(fstcph->seq) + plen); fstcph->seq = htonl(ntohl(fstcph->seq) + plen);
}
if (ipxv == IP4VERSION) {
((struct iphdr *)fsiph)->id = htons(ntohs(((struct iphdr *)fsiph)->id) + 1);
}
memcpy(rfsiph, fsiph, iph_len); memcpy(rfsiph, fsiph, iph_len);
memcpy(rfstcph, fstcph, tcph_len); memcpy(rfstcph, fstcph, tcph_len);
fsiph = (void *)rfsiph; fsiph = (void *)rfsiph;
fstcph = (void *)rfstcph; fstcph = (void *)rfstcph;
}
NETBUF_FREE(fake_sni); NETBUF_FREE(fake_sni);
continue; continue;
@ -678,55 +744,6 @@ erret_lc:
NETBUF_FREE(fake_sni); NETBUF_FREE(fake_sni);
return ret; return ret;
} }
struct fake_type ftype = {
.type = FAKE_PAYLOAD_DEFAULT,
.strategy = strategy
};
switch (config.fake_sni_type) {
case FAKE_PAYLOAD_RANDOM:
ftype.type = FAKE_PAYLOAD_RANDOM;
break;
case FAKE_PAYLOAD_CUSTOM:
ftype.type = FAKE_PAYLOAD_CUSTOM;
ftype.fake_data = config.fake_custom_pkt;
ftype.fake_len = config.fake_custom_pkt_sz;
break;
default:
ftype.type = FAKE_PAYLOAD_DEFAULT;
}
NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE);
if (!NETBUF_CHECK(fake_sni)) {
lgerror("Allocation error", -ENOMEM);
return -ENOMEM;
}
uint32_t fsn_len = MAX_PACKET_SIZE;
ret = gen_fake_sni(
ftype,
iph, iph_len, tcph, tcph_len,
fake_sni, &fsn_len);
if (ret < 0) {
lgerror("gen_fake_sni", ret);
goto erret_lc_cst;
}
lgtrace_addp("post normal fake sni");
lgtrace_addp("post with %d bytes", fsn_len);
ret = instance_config.send_raw_packet(fake_sni, fsn_len);
if (ret < 0) {
lgerror("send fake sni", ret);
goto erret_lc_cst;
}
goto after_cus2;
erret_lc_cst:
NETBUF_FREE(fake_sni);
return ret;
after_cus2:
;
} }
return 0; return 0;

View File

@ -2,6 +2,7 @@
#define YU_MANGLE_H #define YU_MANGLE_H
#include "types.h" #include "types.h"
#include "tls.h"
#define PKT_ACCEPT 0 #define PKT_ACCEPT 0
#define PKT_DROP 1 #define PKT_DROP 1
@ -26,12 +27,14 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len);
*/ */
int process_udp_packet(const uint8_t *pkt, uint32_t pktlen); int process_udp_packet(const uint8_t *pkt, uint32_t pktlen);
/** /**
* Sends fake client hello. * Sends fake client hello.
*/ */
int post_fake_sni(const void *iph, unsigned int iph_len, int post_fake_sni(struct fake_type f_type,
const struct tcphdr *tcph, unsigned int tcph_len, const void *iph, unsigned int iph_len,
unsigned char sequence_len); const struct tcphdr *tcph, unsigned int tcph_len);
/** /**
* Splits packet by poses and posts. * Splits packet by poses and posts.

14
tls.c
View File

@ -273,18 +273,9 @@ int gen_fake_sni(struct fake_type type,
const void *ipxh, uint32_t iph_len, const void *ipxh, uint32_t iph_len,
const struct tcphdr *tcph, uint32_t tcph_len, const struct tcphdr *tcph, uint32_t tcph_len,
uint8_t *buf, uint32_t *buflen) { uint8_t *buf, uint32_t *buflen) {
uint32_t data_len = type.fake_len; uint32_t data_len = type.fake_len;
if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) {
#ifdef KERNEL_SPACE data_len = (uint32_t)randint() % 1200;
get_random_bytes(&data_len, sizeof(data_len));
data_len = data_len % 1200;
#else
data_len = random() % 1200;
#endif
} else if (type.type == FAKE_PAYLOAD_DEFAULT) {
data_len = config.fake_sni_pkt_sz;
} }
if (!ipxh || !tcph || !buf || !buflen) if (!ipxh || !tcph || !buf || !buflen)
@ -320,9 +311,6 @@ int gen_fake_sni(struct fake_type type,
uint8_t *bfdptr = buf + iph_len + tcph_len; uint8_t *bfdptr = buf + iph_len + tcph_len;
switch (type.type) { switch (type.type) {
case FAKE_PAYLOAD_DEFAULT:
memcpy(bfdptr, config.fake_sni_pkt, data_len);
break;
case FAKE_PAYLOAD_DATA: case FAKE_PAYLOAD_DATA:
memcpy(bfdptr, type.fake_data, data_len); memcpy(bfdptr, type.fake_data, data_len);
break; break;

23
tls.h
View File

@ -2,6 +2,7 @@
#define TLS_H #define TLS_H
#include "types.h" #include "types.h"
#include "utils.h"
/** /**
@ -22,28 +23,6 @@ struct tls_verdict {
struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen);
struct fake_type {
#define FAKE_PAYLOAD_RANDOM 0
#define FAKE_PAYLOAD_DATA 1
// In default mode all other options will be skipped.
#define FAKE_PAYLOAD_DEFAULT 2
int type;
// Length of the final fake message.
// Pass 0 in RANDOM mode to make it random
uint16_t fake_len;
// Payload of the fake message of fake_len length.
// Will be omitted in RANDOM mode.
const char *fake_data;
// faking strategy of the fake packet.
// Does not support bitmask, pass standalone strategy.
// Pass 0 if you don't want any faking procedures.
unsigned int strategy;
};
/** /**
* Generates the fake client hello message * Generates the fake client hello message
*/ */

14
types.h
View File

@ -13,6 +13,8 @@
#include <errno.h> // IWYU pragma: export #include <errno.h> // IWYU pragma: export
#include <stdint.h> // IWYU pragma: export #include <stdint.h> // IWYU pragma: export
#include <string.h> // IWYU pragma: export #include <string.h> // IWYU pragma: export
#include <stdlib.h> // IWYU pragma: export
#include <sys/random.h> // IWYU pragma: export
#endif /* SPACES */ #endif /* SPACES */
@ -99,4 +101,16 @@
#define NETBUF_FREE(buf) ; #define NETBUF_FREE(buf) ;
#endif #endif
static inline int randint(void) {
int rnd;
#ifdef KERNEL_SPACE
get_random_bytes(&rnd, sizeof(rnd));
#else
rnd = random();
#endif
return rnd;
}
#endif /* TYPES_H */ #endif /* TYPES_H */

103
utils.c
View File

@ -418,7 +418,6 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
uint8_t *seg1, uint32_t *s1len, uint8_t *seg1, uint32_t *s1len,
uint8_t *seg2, uint32_t *s2len) { uint8_t *seg2, uint32_t *s2len) {
// struct ip6_hdr *hdr6;
void *hdr; void *hdr;
uint32_t hdr_len; uint32_t hdr_len;
struct tcphdr *tcph; struct tcphdr *tcph;
@ -485,6 +484,11 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
struct iphdr *s2_hdr = (void *)seg2; struct iphdr *s2_hdr = (void *)seg2;
s1_hdr->tot_len = htons(s1_dlen); s1_hdr->tot_len = htons(s1_dlen);
s2_hdr->tot_len = htons(s2_dlen); s2_hdr->tot_len = htons(s2_dlen);
s1_hdr->id = randint();
s2_hdr->id = randint();
set_ip_checksum(s1_hdr, sizeof(struct iphdr));
set_ip_checksum(s2_hdr, sizeof(struct iphdr));
} else { } else {
struct ip6_hdr *s1_hdr = (void *)seg1; struct ip6_hdr *s1_hdr = (void *)seg1;
struct ip6_hdr *s2_hdr = (void *)seg2; struct ip6_hdr *s2_hdr = (void *)seg2;
@ -523,6 +527,19 @@ void z_function(const char *str, int *zbuf, size_t len) {
} }
} }
void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta) {
uint8_t *ndptr = data + delta + dlen;
uint8_t *dptr = data + dlen;
uint8_t *ndlptr = data;
for (size_t i = dlen + 1; i > 0; i--) {
*ndptr = *dptr;
--ndptr, --dptr;
}
for (size_t i = 0; i < delta; i++) {
*ndlptr++ = 0;
}
}
#define TCP_MD5SIG_LEN 16 #define TCP_MD5SIG_LEN 16
#define TCP_MD5SIG_KIND 19 #define TCP_MD5SIG_KIND 19
struct tcp_md5sig_opt { struct tcp_md5sig_opt {
@ -534,7 +551,7 @@ struct tcp_md5sig_opt {
// Real length of the option, with NOOP fillers // Real length of the option, with NOOP fillers
#define TCP_MD5SIG_OPT_RLEN 20 #define TCP_MD5SIG_OPT_RLEN 20
int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) {
void *iph; void *iph;
uint32_t iph_len; uint32_t iph_len;
struct tcphdr *tcph; struct tcphdr *tcph;
@ -554,38 +571,29 @@ int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_
} }
if (strategy == FAKE_STRAT_RAND_SEQ) { if (strategy.strategy == FAKE_STRAT_RAND_SEQ) {
lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
if (config.fakeseq_offset) { tcph->seq = htonl(ntohl(tcph->seq) - (strategy.randseq_offset + dlen));
tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset);
} else {
#ifdef KERNEL_SPACE
tcph->seq = 124;
#else
tcph->seq = random();
#endif
}
lgtrace_addp("%u", ntohl(tcph->seq)); lgtrace_addp("%u", ntohl(tcph->seq));
} else if (strategy == FAKE_STRAT_PAST_SEQ) { } else if (strategy.strategy == FAKE_STRAT_PAST_SEQ) {
lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
tcph->seq = htonl(ntohl(tcph->seq) - dlen); tcph->seq = htonl(ntohl(tcph->seq) - dlen);
lgtrace_addp("%u", ntohl(tcph->seq)); lgtrace_addp("%u", ntohl(tcph->seq));
} else if (strategy == FAKE_STRAT_TTL) { } else if (strategy.strategy == FAKE_STRAT_TTL) {
lgtrace_addp("set fake ttl to %d", config.faking_ttl); lgtrace_addp("set fake ttl to %d", strategy.faking_ttl);
if (ipxv == IP4VERSION) { if (ipxv == IP4VERSION) {
((struct iphdr *)iph)->ttl = config.faking_ttl; ((struct iphdr *)iph)->ttl = strategy.faking_ttl;
} else if (ipxv == IP6VERSION) { } else if (ipxv == IP6VERSION) {
((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; ((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl;
} else { } else {
lgerror("fail_packet: IP version is unsupported", -EINVAL); lgerror("fail_packet: IP version is unsupported", -EINVAL);
return -EINVAL; return -EINVAL;
} }
} else if (strategy == FAKE_STRAT_TCP_MD5SUM) { } else if (strategy.strategy == FAKE_STRAT_TCP_MD5SUM) {
int optp_len = tcph_len - sizeof(struct tcphdr); int optp_len = tcph_len - sizeof(struct tcphdr);
int delta = TCP_MD5SIG_OPT_RLEN - optp_len; int delta = TCP_MD5SIG_OPT_RLEN - optp_len;
lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta); lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta);
@ -594,14 +602,9 @@ int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_
if (avail_buflen - *plen < delta) { if (avail_buflen - *plen < delta) {
return -1; return -1;
} }
uint8_t *ndata = data + delta;
uint8_t *ndptr = ndata + dlen; shift_data(data, dlen, delta);
uint8_t *dptr = data + dlen; data += delta;
for (size_t i = dlen + 1; i > 0; i--) {
*ndptr = *dptr;
--ndptr, --dptr;
}
data = ndata;
tcph_len = tcph_len + delta; tcph_len = tcph_len + delta;
tcph->doff = tcph_len >> 2; tcph->doff = tcph_len >> 2;
if (ipxv == IP4VERSION) { if (ipxv == IP4VERSION) {
@ -629,13 +632,57 @@ int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_
} }
} }
if (ipxv == IP4VERSION) {
((struct iphdr *)iph)->frag_off = 0;
}
set_ip_checksum(iph, iph_len); set_ip_checksum(iph, iph_len);
set_tcp_checksum(tcph, iph, iph_len); set_tcp_checksum(tcph, iph, iph_len);
if (strategy == FAKE_STRAT_TCP_CHECK) { if (strategy.strategy == FAKE_STRAT_TCP_CHECK) {
lgtrace_addp("break fake tcp checksum"); lgtrace_addp("break fake tcp checksum");
tcph->check += 1; tcph->check += 1;
} }
return 0; return 0;
} }
int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta) {
int ipxv = netproto_version(payload, *plen);
void *iph;
uint32_t iph_len;
struct tcphdr *tcph;
uint32_t tcph_len;
uint8_t *data;
uint32_t dlen;
int ret = tcp_payload_split(payload, *plen,
&iph, &iph_len, &tcph, &tcph_len,
&data, &dlen);
if (ret < 0) {
return -1;
}
if (ipxv == IP4VERSION) {
struct iphdr *ip4h = iph;
ip4h->tot_len = htons(ntohs(ip4h->tot_len) + seq_delta);
} else if (ipxv == IP6VERSION) {
struct ip6_hdr *ip6h = iph;
ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + seq_delta);
} else {
return -1;
}
tcph->seq = htons(ntohs(tcph->seq) - seq_delta);
shift_data(data, dlen, seq_delta);
*plen += seq_delta;
set_ip_checksum(iph, iph_len);
set_tcp_checksum(tcph, iph, iph_len);
return 0;
}

81
utils.h
View File

@ -2,6 +2,7 @@
#define UTILS_H #define UTILS_H
#include "types.h" #include "types.h"
#include "config.h"
#define IP4VERSION 4 #define IP4VERSION 4
#define IP6VERSION 6 #define IP6VERSION 6
@ -101,6 +102,45 @@ int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len);
void z_function(const char *str, int *zbuf, size_t len); void z_function(const char *str, int *zbuf, size_t len);
/**
* Shifts data left delta bytes. Fills delta buffer with zeroes.
*/
void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta);
struct failing_strategy {
unsigned int strategy;
uint8_t faking_ttl;
uint32_t randseq_offset;
};
struct fake_type {
#define FAKE_PAYLOAD_RANDOM 0
#define FAKE_PAYLOAD_DATA 1
// In default mode all other options will be skipped.
#define FAKE_PAYLOAD_DEFAULT 2
int type;
// Length of the final fake message.
// Pass 0 in RANDOM mode to make it random
uint16_t fake_len;
// Payload of the fake message of fake_len length.
// Will be omitted in RANDOM mode.
const char *fake_data;
unsigned int sequence_len;
// If non-0 the packet send will be delayed for n milliseconds
unsigned int seg2delay;
// faking strategy of the fake packet.
// Does not support bitmask, pass standalone strategy.
// Pass 0 if you don't want any faking procedures.
struct failing_strategy strategy;
};
/** /**
* Invalidates the raw packet. The function aims to invalid the packet * Invalidates the raw packet. The function aims to invalid the packet
@ -108,7 +148,46 @@ void z_function(const char *str, int *zbuf, size_t len);
* *
* Does not support bitmask, pass standalone strategy. * Does not support bitmask, pass standalone strategy.
*/ */
int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
/**
* Shifts the payload right and pushes zeroes before it. Useful for TCP TLS faking.
*/
int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta);
static inline struct failing_strategy args_default_failing_strategy(void) {
struct failing_strategy fl_strat = {
.strategy = (unsigned int)config.faking_strategy,
.faking_ttl = config.faking_ttl,
.randseq_offset = (uint32_t)config.fakeseq_offset
};
return fl_strat;
}
static inline struct fake_type args_default_fake_type(void) {
struct fake_type f_type = {
.sequence_len = config.fake_sni_seq_len,
.strategy = args_default_failing_strategy(),
};
switch (config.fake_sni_type) {
case FAKE_PAYLOAD_RANDOM:
f_type.type = FAKE_PAYLOAD_RANDOM;
break;
case FAKE_PAYLOAD_CUSTOM:
f_type.type = FAKE_PAYLOAD_CUSTOM;
f_type.fake_data = config.fake_custom_pkt;
f_type.fake_len = config.fake_custom_pkt_sz;
break;
default:
f_type.type = FAKE_PAYLOAD_CUSTOM;
f_type.fake_data = config.fake_sni_pkt;
f_type.fake_len = config.fake_sni_pkt_sz;
}
return f_type;
}
#endif /* UTILS_H */ #endif /* UTILS_H */

View File

@ -306,17 +306,19 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
int ipvx = netproto_version(pkt, pktlen); int ipvx = netproto_version(pkt, pktlen);
if (ipvx == IP4VERSION) if (ipvx == IP4VERSION) {
return send_raw_ipv4(pkt, pktlen); ret = send_raw_ipv4(pkt, pktlen);
else if (ipvx == IP6VERSION) } else if (ipvx == IP6VERSION) {
return send_raw_ipv6(pkt, pktlen); ret = send_raw_ipv6(pkt, pktlen);
} else {
printf("proto version %d is unsupported\n", ipvx); printf("proto version %d is unsupported\n", ipvx);
return -EINVAL; return -EINVAL;
}
lgtrace_addp("raw_sock_send: %d", ret);
return ret;
} }
struct packet_data { struct packet_data {
uint32_t id; uint32_t id;
uint16_t hw_proto; uint16_t hw_proto;
@ -375,7 +377,7 @@ void *delay_packet_send_fn(void *data) {
return NULL; return NULL;
} }
void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
struct dps_t *dpdt = malloc(sizeof(struct dps_t)); struct dps_t *dpdt = malloc(sizeof(struct dps_t));
dpdt->pkt = malloc(data_len); dpdt->pkt = malloc(data_len);
memcpy(dpdt->pkt, data, data_len); memcpy(dpdt->pkt, data, data_len);
@ -384,6 +386,9 @@ void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigne
pthread_t thr; pthread_t thr;
pthread_create(&thr, NULL, delay_packet_send_fn, dpdt); pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
pthread_detach(thr); pthread_detach(thr);
lgtrace_addp("Scheduled packet send after %d ms", delay_ms);
return 0;
} }
static int queue_cb(const struct nlmsghdr *nlh, void *data) { static int queue_cb(const struct nlmsghdr *nlh, void *data) {