Merge branch 'main' into kmod

This commit is contained in:
Vadim Vetrov 2024-09-14 20:17:25 +03:00
commit d93763ac44
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
6 changed files with 167 additions and 32 deletions

View File

@ -44,7 +44,7 @@ jobs:
strategy:
matrix:
# arch: [x86_64, x86, aarch64, armhf, armv7, ppc64le, s390x]
arch: [x86_64, x86, aarch64, armhf]
arch: [x86_64, x86, aarch64, armhf, armv7]
branch: [latest-stable]
steps:
- name: Checkout
@ -77,7 +77,7 @@ jobs:
shell: alpine.sh {0}
run: |
case $ARCH in
x86_64) PLATFORM=x86_64 ;;
x86_64) PLATFORM=x86-64 ;;
x86) PLATFORM=x86 ;;
aarch64) PLATFORM=arm64 ;;
armhf) PLATFORM=arm ;;
@ -85,12 +85,8 @@ jobs:
esac
make -j$(nproc) CC="ccache gcc -static-libgcc -static" || exit 1
strip -s build/youtubeUnblock
rm -rf youtubeUnblock || true
mkdir youtubeUnblock
cp build/youtubeUnblock youtubeUnblock
cp youtubeUnblock.service youtubeUnblock
cp README.md youtubeUnblock
tar -czvf youtubeUnblock-$VERSION-$SHA-$PLATFORM-static.tar.gz youtubeUnblock
cp -va build/youtubeUnblock .
tar -czvf youtubeUnblock-$VERSION-$SHA-$PLATFORM-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
ccache --show-stats
- name: Upload artifacts
@ -100,6 +96,66 @@ jobs:
name: youtubeUnblock-static-${{ matrix.arch }}
path: ./**/youtubeUnblock*.tar.gz
build-static-cross:
needs: prepare
name: build ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- arch: mips64el
tool: mips64el-unknown-linux-musl
- arch: mips64
tool: mips64-unknown-linux-musl
- arch: mipsel
tool: mipsel-unknown-linux-musl
- arch: mipselsf
tool: mipsel-unknown-linux-muslsf
- arch: mips
tool: mips-unknown-linux-musl
- arch: mipssf
tool: mips-unknown-linux-muslsf
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up build tools
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: 'musl-cross/musl-cross'
TOOL: ${{ matrix.tool }}
run: |
mkdir -p $HOME/tools
gh api repos/$REPO/releases/latest --jq '.tag_name' |\
xargs -I{} wget -qO- https://github.com/$REPO/releases/download/{}/$TOOL.tgz | tar -C $HOME/tools -xz || exit 1
[ -d "$HOME/tools/$TOOL/bin" ] && echo "$HOME/tools/$TOOL/bin" >> $GITHUB_PATH
- name: Build
id: build
env:
ARCH: ${{ matrix.arch }}
TOOL: ${{ matrix.tool }}
VERSION: ${{ needs.prepare.outputs.version }}
SHA: ${{ needs.prepare.outputs.sha }}
run: |
make -j$(nproc) \
CC="$TOOL-gcc -static-libgcc -static" \
LD=$TOOL-ld \
AR=$TOOL-ar \
NM=$TOOL-nm \
STRIP=$TOOL-strip \
CROSS_COMPILE_PLATFORM=$TOOL || exit 1
$TOOL-strip -s build/youtubeUnblock
cp -va build/youtubeUnblock .
tar -czvf youtubeUnblock-$VERSION-$SHA-$ARCH-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
- name: Upload artifacts
if: steps.build.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: static-${{ matrix.arch }}
path: ./**/youtubeUnblock*.tar.gz
build-openwrt:
needs: prepare
runs-on: ubuntu-latest
@ -267,7 +323,7 @@ jobs:
pre-release:
if: github.event_name != 'pull_request' && github.ref_name == 'main'
needs: [build-static, build-openwrt, build-entware]
needs: [build-static, build-static-cross, build-openwrt, build-entware]
permissions:
contents: write
runs-on: ubuntu-latest

View File

@ -65,12 +65,23 @@ Next step is to add required firewall rules.
For nftables on OpenWRT rules comes out-of-the-box and stored under `/usr/share/nftables.d/ruleset-post/537-youtubeUnblock.nft`. All you need is install requirements and do `/etc/init.d/firewall reload`. If no, go to [Firewall configuration](#firewall-configuration).
Now we are ready to demonize the application.
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).
If you installed package from Github Actions or built it yourself with OpenWRT SDK, rc scripts are preinstalled. All you need is to do `/etc/init.d/youtubeUnblock start`.
Elsewhere copy `owrt/youtubeUnblock.owrt` to `/etc/init.d/youtubeUnblock` and put the program's binary into /usr/bin/. (Don't forget to `chmod +x` both). Now run `/etc/init.d/youtubeUnblock start`.
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.
You can also run `/etc/init.d/youtubeUnblock enable` to force OpenWRT autostart on boot, but I don't recommend this since if the package has bugs you may lose access to the router (I think you will be able to reset it with reset settings tricks documented for your router).
UCI configuration is available in /etc/config/youtubeUnblock file, in section `youtubeUnblock.youtubeUnblock`. The configuration is done with [flags](#flags). Note, that names of flags are not the same: you should replace `-` with `_`, you shouldn't use leading `--` for flag. Also you will enable toggle flags (without parameters) with `1`.
For example, to enable trace logs you should do
```sh
uci set youtubeUnblock.youtubeUnblock.trace=1
```
You can check the logs in CLI mode with `logread -l 200 | grep youtubeUnblock` command.
For uci, to save the configs you should do `uci commit` and then `reload_config` to restart the youtubeUnblock
In CLI mode you will use youtubeUnblock as a normal init.d service:
for example, you can enable it with `/etc/init.d/youtubeUnblock enable`.
### Entware
@ -89,15 +100,19 @@ Copy `youtubeUnblock.service` to `/usr/lib/systemd/system` (you should change th
On nftables you should put next nftables rules:
```sh
nft add rule inet fw4 mangle_forward tcp dport 443 ct original "packets < 20" counter queue num 537 bypass
nft insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept
nft add chain inet fw4 youtubeUnblock '{ type filter hook postrouting priority mangle - 1; policy accept; }'
nft add rule inet fw4 youtubeUnblock 'meta l4proto { tcp, udp } th dport 443 ct original packets < 20 counter queue num 537 bypass'
nft insert rule inet fw4 output 'mark and 0x8000 == 0x8000 counter accept'
```
#### Iptables rules
On iptables you should put next iptables rules:
```sh
iptables -t mangle -A FORWARD -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
iptables -t mangle -N YOUTUBEUNBLOCK
iptables -t mangle -A YOUTUBEUNBLOCK -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
iptables -t mangle -A YOUTUBEUNBLOCK -p udp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
iptables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK
iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
```
@ -105,12 +120,13 @@ iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
For IPv6 on iptables you need to duplicate rules above for ip6tables:
```sh
ip6tables -t mangle -A FORWARD -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
ip6tables -t mangle -N YOUTUBEUNBLOCK
ip6tables -t mangle -A YOUTUBEUNBLOCK -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
ip6tables -t mangle -A YOUTUBEUNBLOCK -p udp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
ip6tables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK
ip6tables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
```
Note that above rules use *conntrack* to route only first 20 packets from the connection to **youtubeUnblock**.
If you got some troubles with it, for example **youtubeUnblock** doesn't detect YouTube, try to delete *connbytes* from the rules. But it is an unlikely behavior and you should probably check your ruleset.
@ -147,11 +163,12 @@ 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**.
- `--faking-strategy={randseq|ttl|tcp_check|pastseq}` 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.
- `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured.
- `pastseq` is like `randseq` but sequence number is not random but references the packet sent in the past (before current).
- `tcp_check` will invalidate faking packet with invalid checksum. May be handled and dropped by some providers/TSPUs.
- `md5sum` will invalidate faking packet with invalid TCP md5sum. md5sum is a TCP option which is handled by the destination server but may be skipped by TSPU.
- `--faking-ttl=<ttl>` Tunes the time to live (TTL) of fake SNI messages. TTL is specified like that the packet will go through the DPI system and captured by it, but will not reach the destination server. Defaults to **8**.
@ -200,6 +217,8 @@ If you are on Chromium you may have to disable *kyber* (the feature that makes t
If your browser is using QUIC it may not work properly. Disable it in Chrome in `chrome://flags` and in Firefox `network.http.http{2,3}.enable(d)` in `about:config` option.
It seems like some TSPUs started to block wrongseq packets, so you should play around with faking strategies. I personally recommend to start with `md5sum` faking strategy.
### TV
Televisions are the biggest headache.

7
args.c
View File

@ -152,7 +152,7 @@ void print_usage(const char *argv0) {
printf("\t--fake-sni-seq-len=<length>\n");
printf("\t--fake-seq-offset=<offset>\n");
printf("\t--faking-ttl=<ttl>\n");
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq}\n");
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n");
printf("\t--synfake={1|0}\n");
printf("\t--synfake-len=<len>\n");
printf("\t--frag={tcp,ip,none}\n");
@ -282,6 +282,8 @@ int parse_args(int argc, char *argv[]) {
config.faking_strategy = FAKE_STRAT_TCP_CHECK;
} else if (strcmp(optarg, "pastseq") == 0) {
config.faking_strategy = FAKE_STRAT_PAST_SEQ;
} else if (strcmp(optarg, "md5sum") == 0) {
config.faking_strategy = FAKE_STRAT_TCP_MD5SUM;
} else {
goto invalid_opt;
}
@ -450,6 +452,9 @@ void print_welcome() {
case FAKE_STRAT_PAST_SEQ:
printf("Past seq faking strategy will be used\n");
break;
case FAKE_STRAT_TCP_MD5SUM:
printf("md5sum faking strategy will be used\n");
break;
}
if (config.fk_winsize) {

View File

@ -95,6 +95,7 @@ extern struct config_t config;
#define FAKE_STRAT_TTL 2
#define FAKE_STRAT_PAST_SEQ 3
#define FAKE_STRAT_TCP_CHECK 4
#define FAKE_STRAT_TCP_MD5SUM 5
#ifndef FAKING_STRATEGY

View File

@ -296,13 +296,15 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) {
const struct udphdr *udph;
const uint8_t *data;
uint32_t dlen;
int ipver = netproto_version(pkt, pktlen);
lgtrace_start("Got udp packet");
lgtrace_addp("IPv%d", ipver);
int ret = udp_payload_split((uint8_t *)pkt, pktlen,
(void **)&iph, &iph_len,
(struct udphdr **)&udph,
(uint8_t **)&data, &dlen);
lgtrace_start("Got udp packet");
if (ret < 0) {
lgtrace_addp("undefined");
@ -487,6 +489,7 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses
uint32_t f1len = MAX_PACKET_SIZE;
uint32_t f2len = MAX_PACKET_SIZE;
uint32_t fake_pad_len = MAX_PACKET_SIZE;
int ret;
@ -525,6 +528,7 @@ send_frag1:
send_fake:
if (config.frag_sni_faked) {
uint32_t iphfl, tcphfl;
fake_pad_len = f2len;
ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL);
if (ret < 0) {
lgerror("Invalid frag2", ret);
@ -538,12 +542,12 @@ send_fake:
fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs);
lgtrace_addp("%u, ", ntohl(fakethdr->seq));
}
ret = fail_packet(fake_pad, f2len);
ret = fail_packet(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, f2len, NULL, 0, 0);
ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0);
if (ret < 0) {
goto erret_lc;
}
@ -935,7 +939,7 @@ int gen_fake_sni(const void *ipxh, uint32_t iph_len,
const char *data = config.fake_sni_pkt;
size_t data_len = config.fake_sni_pkt_sz;
size_t dlen = iph_len + tcph_len + data_len;
uint32_t dlen = iph_len + tcph_len + data_len;
if (*buflen < dlen)
return -ENOMEM;
@ -953,14 +957,24 @@ int gen_fake_sni(const void *ipxh, uint32_t iph_len,
niph->ip6_plen = htons(dlen - iph_len);
}
fail_packet(buf, *buflen);
fail_packet(buf, &dlen, *buflen);
*buflen = dlen;
return 0;
}
int fail_packet(uint8_t *payload, uint32_t plen) {
#define TCP_MD5SIG_LEN 16
#define TCP_MD5SIG_KIND 19
struct tcp_md5sig_opt {
uint8_t kind;
uint8_t len;
uint8_t sig[TCP_MD5SIG_LEN];
};
#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt))
// Real length of the option, with NOOP fillers
#define TCP_MD5SIG_OPT_RLEN 20
int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) {
void *iph;
uint32_t iph_len;
struct tcphdr *tcph;
@ -969,14 +983,18 @@ int fail_packet(uint8_t *payload, uint32_t plen) {
uint32_t dlen;
int ret;
ret = tcp_payload_split(payload, plen,
ret = tcp_payload_split(payload, *plen,
&iph, &iph_len, &tcph, &tcph_len,
&data, &dlen);
uint32_t ipxv = netproto_version(payload, *plen);
if (ret < 0) {
return ret;
}
int sizedelta = 0;
if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) {
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
@ -1001,7 +1019,6 @@ int fail_packet(uint8_t *payload, uint32_t plen) {
} else if (config.faking_strategy == FAKE_STRAT_TTL) {
lgtrace_addp("set fake ttl to %d", config.faking_ttl);
uint32_t ipxv = netproto_version(payload, plen);
if (ipxv == IP4VERSION) {
((struct iphdr *)iph)->ttl = config.faking_ttl;
} else if (ipxv == IP6VERSION) {
@ -1010,6 +1027,43 @@ int fail_packet(uint8_t *payload, uint32_t plen) {
lgerror("fail_packet: IP version is unsupported", -EINVAL);
return -EINVAL;
}
} else if (config.faking_strategy == FAKE_STRAT_TCP_MD5SUM) {
int optp_len = tcph_len - sizeof(struct tcphdr);
int delta = TCP_MD5SIG_OPT_RLEN - optp_len;
lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta);
if (delta > 0) {
if (avail_buflen - *plen < delta) {
return -1;
}
uint8_t *ndata = data + delta;
memcpy(ndata, data, dlen);
data = ndata;
tcph_len = tcph_len + delta;
tcph->doff = tcph_len >> 2;
if (ipxv == IP4VERSION) {
((struct iphdr *)iph)->tot_len = htons(ntohs(((struct iphdr *)iph)->tot_len) + delta);
} else if (ipxv == IP6VERSION) {
((struct ip6_hdr *)iph)->ip6_plen = htons(ntohs(((struct ip6_hdr *)iph)->ip6_plen) + delta);
} else {
lgerror("fail_packet: IP version is unsupported", -EINVAL);
return -EINVAL;
}
optp_len += delta;
*plen += delta;
}
uint8_t *optplace = (uint8_t *)tcph + sizeof(struct tcphdr);
struct tcp_md5sig_opt *mdopt = (void *)optplace;
mdopt->kind = TCP_MD5SIG_KIND;
mdopt->len = TCP_MD5SIG_OPT_LEN;
optplace += sizeof(struct tcp_md5sig_opt);
optp_len -= sizeof(struct tcp_md5sig_opt);
while (optp_len-- > 0) {
*optplace++ = 0x01;
}
}
set_ip_checksum(iph, iph_len);

View File

@ -30,7 +30,7 @@ int gen_fake_sni(const void *iph, uint32_t iph_len,
* Invalidates the raw packet. The function aims to invalid the packet
* in such way as it will be accepted by DPI, but dropped by target server
*/
int fail_packet(uint8_t *payload, uint32_t plen);
int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
#define PKT_ACCEPT 0
#define PKT_DROP 1