Fix #17. Update troubleshooting documentation, update packet marks.

Update for marks may be related to #15. But is primarily used to avoid
random EPERMs.
This commit is contained in:
Vadim Vetrov 2024-08-02 23:00:08 +03:00
parent 709fc4c3be
commit 571692b6bd
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
2 changed files with 29 additions and 7 deletions

View File

@ -34,9 +34,17 @@ Also DNS over HTTPS (DOH) is preferred for additional anonimity.
If you have any troubles with youtubeUnblock, here are some options to tune. If them don't work in your case, please, open an issue. You can pass these options in make CFLAGS (`make CFLAGS=...`) or edit CFLAGS variable in Makefile.
Available flags:
- -DUSE_SEG2_DELAY This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet. You can tune the amount of time in `#define SEG2_DELAY 100` where 100 stands for milliseconds.
- -DNO_FAKE_SNI This flag disables -DFAKE_SNI which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. Use this flag if you got gen_fake_sni error. Track this issue in https://github.com/Waujito/youtubeUnblock/issues/17
- -DNO_FAKE_SNI This flag disables -DFAKE_SNI which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to FAQ for EPERMS.
- -DNOUSE_GSO This flag disables fix for Google Chrome fat ClientHello. The GSO is well tested now, so this flag probably won't fix anything.
### FAQ for EPERMS (Operation not permitted)
EPERM may occur in a lot of places but generally here are two: mnl_cb_run and when sending the packet via rawsocket (raw_frags_send and send fake sni).
- mnl_cb_run Operation not permitted indicates that another instance of youtubeUnblock is running on the specified queue-num.
- rawsocket Operation not permitted indicates that the packet is being dropped by nefilter rules. In fact this is a hint from the kernel that something wrong is going on and we should check the firewall rules. Before dive into the problem let's make it clean how the mangled packets are being sent. Nefilter queue provides us with the ability to mangle the packet on fly but that is not suitable for this program because we need to split the packet to at least two independent packets. So we are using [linux raw sockets](https://man7.org/linux/man-pages/man7/raw.7.html) which allows us to send any ipv4 packet. **The packet goes from the OUTPUT chain even when NFQUEUE is set up on FORWARD (suitable for OpenWRT).** So we need to escape packet rejects here.
* raw_frags_send EPERM: just make sure outgoing traffic is allowed (RELATED,ESTABLISHED should work, if not, go to step 3)
* send fake sni EPERM: Fake SNI is out-of-state thing and will likely corrupt the connection (the behavior is expected). conntrack considers it as an invalid packet. By default OpenWRT set up to drop outgoing packets like this one. You may delete nftables/iptables rule that drops packets with invalid conntrack state, but I don't recommend to do this. The step 3 is better solution.
* Step 3, ultimate solution. Use mark (don't confuse with connmark). The youtubeUnblock uses mark internally to avoid infinity packet loops (when the packet is sent by youtubeUnblock but on next step handled by itself). Currently it uses mark (1 << 15) = 32768. You should put iptables/nftables that ultimately accepts such marks at the very start of the filter OUTPUT chain: `iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT` or `nft insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept`.
## OpenWRT case
The package is also compatible with routers. The router should be running by free opensource linux-based system such as [OpenWRT](https://openwrt.org/). You should cross-compile it under your host machine. Be ready for compilation errors and a lot of googling about it. It is not such a trivial process! You can get crosscompilation toolsuite compatible with your router from OpenWRT repositories. For example, I have ramips/mt76x8 based router so for me the toolsuite is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. You can find out more about your router model on it's openwrt page. When you download the toolsuite, untar it somewhere. Now we are ready for compilation. My cross gcc asked me to create a staging dir for it and pass it as an environment variable. Also you should notice toolsuite packages and replace my make command with yours. ```STAGING_DIR=temp make CC=/usr/bin/mipsel-openwrt-linux-gcc LD=/usr/bin/mipsel-openwrt-linux-ld AR=/usr/bin/mipsel-openwrt-linux-ar OBJDUMP=/usr/bin/mipsel-openwrt-linux-objdump NM=/usr/bin/mipsel-openwrt-linux-nm STRIP=/usr/bin/mipsel-openwrt-linux-strip CROSS_COMPILE_PLATFORM=mipsel-buildroot-linux-gnu```. Take a look at `CROSS_COMPILE_PLATFORM` It is required by autotools but I think it is not necessary. Anyways I put `mipsel-buildroot-linux-gnu` in here. For your model may be an [automake cross-compile manual](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) will be helpful. When compilation is done, the binary file will be in build directory. Copy it to your router. Note that an ssh access is likely to be required to proceed. sshfs don't work on my model so I injected the application to the router via Software Upload Package page. It has given me an error, but also a `/tmp/upload.ipk` file which I copied in root directory, `chmod +x`-ed and run.

View File

@ -31,7 +31,7 @@
#define USE_TCP_SEGMENTATION
#endif
#define RAWSOCKET_MARK 0xfc70
#define RAWSOCKET_MARK (1 << 15)
#ifdef USE_SEG2_DELAY
#define SEG2_DELAY 100
@ -251,18 +251,27 @@ static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset,
struct iphdr *hdr = nfq_ip_get_hdr(pktb);
size_t hdr_len = hdr->ihl * 4;
if (hdr == NULL) {errno = EINVAL; return -1;}
if (hdr == NULL) {
perror("tcp4_frag: nfq_ip_get_hdr is null\n");
errno = EINVAL;
return -1;
}
if (hdr->protocol != IPPROTO_TCP || !(ntohs(hdr->frag_off) & IP_DF)) {
perror("tcp4_frag: proto is tcp or request is fragmented");
errno = EINVAL;
return -1;
}
if (nfq_ip_set_transport_header(pktb, hdr))
if (nfq_ip_set_transport_header(pktb, hdr)) {
perror("tcp4_frag: ip_set_transport_header");
return -1;
}
struct tcphdr *tcph = nfq_tcp_get_hdr(pktb);
size_t tcph_len = tcph->doff * 4;
if (tcph == NULL) {
perror("tcp4_frag: tcph is NULL");
errno = EINVAL;
return -1;
}
@ -270,7 +279,8 @@ static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset,
uint8_t *payload = nfq_tcp_get_payload(tcph, pktb);
size_t plen = nfq_tcp_get_payload_len(tcph, pktb);
if (hdr == NULL || payload == NULL || plen <= payload_offset) {
if (payload == NULL || plen <= payload_offset) {
perror("tcp4_frag: payload is too small or NULL");
errno = EINVAL;
return -1;
}
@ -280,6 +290,10 @@ static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset,
size_t s2_plen = plen - payload_offset;
size_t s2_dlen = s2_plen + hdr_len + tcph_len;
if (s1_dlen > MNL_SOCKET_BUFFER_SIZE || s2_dlen > MNL_SOCKET_BUFFER_SIZE) {
errno = ENOMEM;
return -1;
}
memcpy(buff1, hdr, hdr_len);
memcpy(buff2, hdr, hdr_len);
@ -711,7 +725,7 @@ static int process_packet(const struct packet_data packet) {
ret = send_raw_socket(fake_sni);
if (ret < 0) {
perror("send fake sni\n");
perror("send fake sni");
pktb_free(fake_sni);
goto fallback;
}
@ -859,7 +873,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
if (attr[NFQA_MARK] != NULL) {
// Skip packets sent by rawsocket to escape infinity loop.
if (ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) ==
if ((ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) & RAWSOCKET_MARK) ==
RAWSOCKET_MARK) {
return fallback_accept_packet(packet.id);
}