mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2026-01-27 20:50:35 +03:00
Compare commits
230 Commits
v0.2
...
v1.0.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc345ed6d4 | ||
|
|
123c87a24d | ||
|
|
e28a288ea0 | ||
|
|
d1dab6928b | ||
|
|
fd980e5d98 | ||
|
|
9a3b139fb5 | ||
|
|
6568aa37f2 | ||
|
|
3a1deb054a | ||
|
|
ed5a669393 | ||
|
|
5fcb4a8012 | ||
|
|
8718fc5f6c | ||
|
|
062200d9ea | ||
|
|
25199288e8 | ||
|
|
1822983b34 | ||
|
|
ba0f87d195 | ||
|
|
e8bdd05839 | ||
|
|
f4e5cdb328 | ||
|
|
8b602a9f44 | ||
|
|
42e6d574a0 | ||
|
|
457a7a7f04 | ||
|
|
9b5c8a729d | ||
|
|
b452ed2d55 | ||
|
|
f9a51944dd | ||
|
|
7480cd31b8 | ||
|
|
b2ac2e0d03 | ||
|
|
5ee77d517b | ||
|
|
f5a6c5718f | ||
|
|
aefabe7e0a | ||
|
|
fd1ae1e574 | ||
|
|
7303abdaf2 | ||
|
|
cb138b902a | ||
|
|
3a5dbff8ac | ||
|
|
62551a9f82 | ||
|
|
ef120ca100 | ||
|
|
a4975dcdcd | ||
|
|
7ec29bd47b | ||
|
|
20ce07821b | ||
|
|
eb544c11ce | ||
|
|
3376860c0f | ||
|
|
438a3c17d8 | ||
|
|
046ecc4c97 | ||
|
|
bb8992578f | ||
|
|
cb46c89423 | ||
|
|
d87d3c9efb | ||
|
|
562f17e932 | ||
|
|
56b6126f52 | ||
|
|
235bf823db | ||
|
|
c9537bae95 | ||
|
|
0ee6d667e4 | ||
|
|
aa96769559 | ||
|
|
ae9592c71f | ||
|
|
93972f8404 | ||
|
|
7070ddfc74 | ||
|
|
0e9b3ff018 | ||
|
|
f344e525d2 | ||
|
|
f1ab2ac1a9 | ||
|
|
55d359d3e7 | ||
|
|
37a517eb8a | ||
|
|
ee56b67d20 | ||
|
|
ed08feaf20 | ||
|
|
96cf0365ee | ||
|
|
4c7b63fa7f | ||
|
|
58f4802f64 | ||
|
|
05cc0054d8 | ||
|
|
e9b033ccca | ||
|
|
30bc3a8d3f | ||
|
|
c617f238af | ||
|
|
43ca3fe07e | ||
|
|
9dc40bbdf1 | ||
|
|
666b3575fc | ||
|
|
263a04bb95 | ||
|
|
6b21e9b3b3 | ||
|
|
98a3fd5acd | ||
|
|
3ee979f7d1 | ||
|
|
82c49119d3 | ||
|
|
0fe45ec33f | ||
|
|
43d0118767 | ||
|
|
79df04cb07 | ||
|
|
2d1b58bc6d | ||
|
|
cdb26833ba | ||
|
|
c786a44dd5 | ||
|
|
2fd3107401 | ||
|
|
5415bc37ec | ||
|
|
3187b3ca61 | ||
|
|
1cacac7adc | ||
|
|
6eaa0a67c8 | ||
|
|
edbfe120c5 | ||
|
|
7e71c5e9b8 | ||
|
|
1ade21aa22 | ||
|
|
49a48c33cf | ||
|
|
5e28fe83c2 | ||
|
|
d93763ac44 | ||
|
|
85d3843273 | ||
|
|
f0826606e3 | ||
|
|
c2158a7450 | ||
|
|
740df8979f | ||
|
|
7e73fa2613 | ||
|
|
8c405b81df | ||
|
|
2ff83c6030 | ||
|
|
9f5f194a37 | ||
|
|
e38e0e7bd9 | ||
|
|
aef2b5b469 | ||
|
|
bbd9f29a67 | ||
|
|
ec9f5bb20c | ||
|
|
7919f82f4b | ||
|
|
ed6979cbcd | ||
|
|
b3668f07ba | ||
|
|
31aa309198 | ||
|
|
7d01d0974d | ||
|
|
5f2e423dfa | ||
|
|
f96ac2252b | ||
|
|
1e6a9496f6 | ||
|
|
c0dc5d2652 | ||
|
|
8bb5368b85 | ||
|
|
b249903ead | ||
|
|
5870df44df | ||
|
|
b20f15086e | ||
|
|
5eeff9bc0d | ||
|
|
5e327497bb | ||
|
|
731da0dd50 | ||
|
|
9c839a5094 | ||
|
|
27629ba0cc | ||
|
|
3d50c00e4f | ||
|
|
0a679ea41c | ||
|
|
cad262f201 | ||
|
|
8b23ab762d | ||
|
|
3d9481d72d | ||
|
|
0f71d5f3c4 | ||
|
|
33b0ca421b | ||
|
|
bc398cbd02 | ||
|
|
491d485260 | ||
|
|
f273d9cc7a | ||
|
|
c101adcd07 | ||
|
|
725dc1a6d2 | ||
|
|
3b5276c834 | ||
|
|
d16805871f | ||
|
|
5a30ac427b | ||
|
|
a3a497bc82 | ||
|
|
d530dd26d1 | ||
|
|
564820ce38 | ||
|
|
de9b42ae46 | ||
|
|
c10393983a | ||
|
|
e62d76e1d6 | ||
|
|
a859472ef3 | ||
|
|
71a6711b40 | ||
|
|
78ed6a1d72 | ||
|
|
e8d86b9df6 | ||
|
|
c5e941a53b | ||
|
|
551fb5d38d | ||
|
|
b434ef4b7f | ||
|
|
6cf2ec5504 | ||
|
|
a546e783c6 | ||
|
|
1c5d4e68d9 | ||
|
|
fa0552ba66 | ||
|
|
51c21a89fd | ||
|
|
af6e993c07 | ||
|
|
044801efb9 | ||
|
|
7f340fb033 | ||
|
|
727e909db1 | ||
|
|
460f392a91 | ||
|
|
b76cc5fcee | ||
|
|
b68052efa2 | ||
|
|
9af85abc84 | ||
|
|
59d19646a6 | ||
|
|
bcdf1810c4 | ||
|
|
0aef6a991b | ||
|
|
f3db464b97 | ||
|
|
e8519b4973 | ||
|
|
dfc2277efc | ||
|
|
0160c26618 | ||
|
|
4a8f0d18a9 | ||
|
|
d5db8c18e5 | ||
|
|
e6367c72bd | ||
|
|
aafa1a728a | ||
|
|
219062aae2 | ||
|
|
24826f851d | ||
|
|
918ccc822d | ||
|
|
e649ef5567 | ||
|
|
b2cff9699b | ||
|
|
569cfcf049 | ||
|
|
86bada6ea7 | ||
|
|
e39bc9b059 | ||
|
|
7d571e6860 | ||
|
|
05648cc7c2 | ||
|
|
8ca048d9fd | ||
|
|
fa631accb7 | ||
|
|
b4607d69f6 | ||
|
|
9c2d31f51d | ||
|
|
74a9ae3eb1 | ||
|
|
6951c0319e | ||
|
|
6df3b53d7a | ||
|
|
e719c1319b | ||
|
|
2e96aa150e | ||
|
|
d29177d783 | ||
|
|
0126e403fd | ||
|
|
80811a41e6 | ||
|
|
c39e7d81c9 | ||
|
|
b6a8a45502 | ||
|
|
21be10caed | ||
|
|
d83e54701e | ||
|
|
75b7fe3011 | ||
|
|
81fcdf41c0 | ||
|
|
ab402c573b | ||
|
|
ff5bfc9037 | ||
|
|
98e1c2f5d6 | ||
|
|
da10542edc | ||
|
|
dc03bee64b | ||
|
|
34345b127b | ||
|
|
48d8f08957 | ||
|
|
cee1b371fd | ||
|
|
8e592d8957 | ||
|
|
6bbeae3876 | ||
|
|
7b9e7b773b | ||
|
|
8d4fb1f7ad | ||
|
|
4963258c0b | ||
|
|
46e4231f7e | ||
|
|
916d575920 | ||
|
|
7d60fd8854 | ||
|
|
0d43ce60f5 | ||
|
|
8e3fa48510 | ||
|
|
4f9ab69b37 | ||
|
|
50933ee0d6 | ||
|
|
bb66ab13a8 | ||
|
|
0c17702e9d | ||
|
|
b786d78d19 | ||
|
|
d42ecb2b82 | ||
|
|
15619afcdf | ||
|
|
fb47d80543 | ||
|
|
13bafac036 | ||
|
|
22573b7d12 |
@@ -4,3 +4,7 @@ root = true
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
tab_width = 8
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Bug report
|
||||
description: Use this issue template when you are pretty sure here is a bug. Make sure the trouble you are experiencing haven't been already reported earlier and haven't been talked about in discussions.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Use this issue template when you are pretty sure here is a bug.
|
||||
Make sure the trouble you are experiencing haven't been already reported earlier and haven't been talked about in [discussions](https://github.com/Waujito/youtubeUnblock/discussions).
|
||||
|
||||
Note, that *bugs* are internal program errors: crashes, freezes and other types of undefined behavior. You may also report problems with routers infrastructure here, but **NOT** questions like *My router doesn't work help please*. Your questions and other stuff with custom routers setup should be discussed [here](https://github.com/Waujito/youtubeUnblock/discussions/172)
|
||||
|
||||
Discuss problems like *youtube doesn't unblock* [here](https://github.com/Waujito/youtubeUnblock/discussions/173)
|
||||
- type: textarea
|
||||
id: bug-report
|
||||
attributes:
|
||||
label: Bug description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: linux-distro
|
||||
attributes:
|
||||
label: Linux distribution
|
||||
description: Pass here your linux distro e.g. OpenWRT, Entware, Merlin, Padavan, Ubuntu, Debian
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: architecture
|
||||
attributes:
|
||||
label: Device architecture
|
||||
description: Pass here the architecture of your device. (On openwrt do `cat /etc/openwrt_release`, on other systems `lscpu` or `cat /proc/cpuinfo`)
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: configs
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: Pass here the configuration of youtubeUnblock being used. You may pass output of `cat /etc/config/youtubeUnblock` on OpenWRT or raw flags of youtubeUnblock.
|
||||
validations:
|
||||
required: false
|
||||
10
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
1
.github/builder_containers/README.md
vendored
Normal file
1
.github/builder_containers/README.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This directory contains dockerfiles for large docker containers. This allows not to rebuild binaries every build and not to utilize cache.
|
||||
6
.github/builder_containers/entware-aarch64-3.10.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-aarch64-3.10.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/aarch64-3.10.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-armv7-2.6.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-armv7-2.6.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git -b k2.6
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/armv7-2.6.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-armv7-3.2.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-armv7-3.2.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/armv7-3.2.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-mips-3.4.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-mips-3.4.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/mips-3.4.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-mipsel-3.4.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-mipsel-3.4.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/mipsel-3.4.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-x64-3.2.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-x64-3.2.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/x64-3.2.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-x86-2.6.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-x86-2.6.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git -b k2.6
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/x86-2.6.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
1
.github/builder_containers/entware_docker
vendored
Submodule
1
.github/builder_containers/entware_docker
vendored
Submodule
Submodule .github/builder_containers/entware_docker added at 90eb6bc0a4
12
.github/builder_containers/kernel-3.0.101.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-3.0.101.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.0.101.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-3.10.108.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-3.10.108.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.108.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-4.19.322.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-4.19.322.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.322.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-4.4.302.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-4.4.302.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.302.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-5.15.167.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-5.15.167.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.167.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-5.4.284.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-5.4.284.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.284.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-6.6.52.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-6.6.52.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.52.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
378
.github/workflows/build-ci.yml
vendored
Normal file
378
.github/workflows/build-ci.yml
vendored
Normal file
@@ -0,0 +1,378 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.editorconfig'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
- 'README.md'
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.gh.outputs.version }}
|
||||
release: ${{ steps.gh.outputs.release }}
|
||||
sha: ${{ steps.gh.outputs.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: GH
|
||||
id: gh
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(cat Makefile | grep "PKG_VERSION :=" | sed 's/PKG_VERSION := //')" >> $GITHUB_OUTPUT
|
||||
echo "release=$(cat Makefile | grep "PKG_RELEASE :=" | sed 's/PKG_RELEASE := //')" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
|
||||
echo "sha=$(echo ${GITHUB_SHA::7})" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "sha=$(gh api repos/$REPO/commits/main --jq '.sha[:7]')" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build-static:
|
||||
needs: prepare
|
||||
name: build ${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, x86, aarch64, armhf, armv7]
|
||||
branch: [latest-stable]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up ccache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ccache-${{ matrix.arch }}-${{ github.run_id }}
|
||||
restore-keys: ccache-${{ matrix.arch }}-
|
||||
|
||||
- name: Set up Alpine Linux for ${{ matrix.arch }}
|
||||
uses: jirutka/setup-alpine@v1
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
branch: ${{ matrix.branch }}
|
||||
packages: >
|
||||
bash build-base ccache coreutils findutils gawk git grep tar wget xz
|
||||
autoconf automake libtool pkgconf linux-headers
|
||||
shell-name: alpine.sh
|
||||
|
||||
- name: Build inside chroot
|
||||
id: build
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
shell: alpine.sh {0}
|
||||
run: |
|
||||
case $ARCH in
|
||||
x86_64) PLATFORM=x86-64 ;;
|
||||
x86) PLATFORM=x86 ;;
|
||||
aarch64) PLATFORM=arm64 ;;
|
||||
armhf) PLATFORM=arm ;;
|
||||
*) PLATFORM=$ARCH ;;
|
||||
esac
|
||||
make -j$(nproc) CC="ccache gcc -static-libgcc -static" || exit 1
|
||||
strip -s build/youtubeUnblock
|
||||
cp -va build/youtubeUnblock .
|
||||
tar -czvf youtubeUnblock-$VERSION-$RELEASE-$SHA-$PLATFORM-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
ccache --show-stats
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
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
|
||||
- arch: armv7sf
|
||||
tool: armv7-unknown-linux-musleabi
|
||||
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.tar.xz | tar -C $HOME/tools -xJ || 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 }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
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-$RELEASE-$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: youtubeUnblock-static-${{ matrix.arch }}
|
||||
path: ./**/youtubeUnblock*.tar.gz
|
||||
|
||||
build-openwrt:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch:
|
||||
- openwrt-23.05
|
||||
arch:
|
||||
- aarch64_cortex-a53
|
||||
- aarch64_cortex-a72
|
||||
- aarch64_generic
|
||||
- arm_arm1176jzf-s_vfp
|
||||
- arm_arm926ej-s
|
||||
- arm_cortex-a15_neon-vfpv4
|
||||
- arm_cortex-a5_vfpv4
|
||||
- arm_cortex-a7
|
||||
- arm_cortex-a7_neon-vfpv4
|
||||
- arm_cortex-a7_vfpv4
|
||||
- arm_cortex-a8_vfpv3
|
||||
- arm_cortex-a9
|
||||
- arm_cortex-a9_neon
|
||||
- arm_cortex-a9_vfpv3-d16
|
||||
- arm_fa526
|
||||
- arm_mpcore
|
||||
- arm_xscale
|
||||
- mips64_octeonplus
|
||||
- mips_24kc
|
||||
- mips_4kec
|
||||
- mips_mips32
|
||||
- mipsel_24kc
|
||||
- mipsel_24kc_24kf
|
||||
- mipsel_74kc
|
||||
- mipsel_mips32
|
||||
- x86_64
|
||||
container:
|
||||
image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }}
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$RELEASE-$SHA/" youtubeUnblock/Makefile
|
||||
|
||||
- name: Initilalize SDK
|
||||
id: init_sdk
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
HOME=/builder ./setup.sh
|
||||
|
||||
- name: Build packages
|
||||
id: build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf
|
||||
cat feeds.conf
|
||||
./scripts/feeds update youtubeUnblock
|
||||
./scripts/feeds install -a -p youtubeUnblock
|
||||
make defconfig
|
||||
make package/youtubeUnblock/compile V=s
|
||||
mv $(find ./bin -type f -name 'youtubeUnblock*.ipk') ./youtubeUnblock-$VERSION-$RELEASE-$SHA-${{ matrix.arch }}-${{ matrix.branch }}.ipk
|
||||
|
||||
- name: Upload packages
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: youtubeUnblock-${{ matrix.branch }}-${{ matrix.arch }}
|
||||
path: /builder/youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
build-openwrt-luci:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: openwrt/sdk:x86_64-openwrt-23.05
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$RELEASE-$SHA/" youtubeUnblock/Makefile
|
||||
|
||||
- name: Initilalize SDK
|
||||
id: init_sdk
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
HOME=/builder ./setup.sh
|
||||
|
||||
- name: Build packages
|
||||
id: build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf
|
||||
cat feeds.conf
|
||||
./scripts/feeds update youtubeUnblock
|
||||
./scripts/feeds install -a -p youtubeUnblock
|
||||
make defconfig
|
||||
make package/luci-app-youtubeUnblock/compile V=s
|
||||
mv $(find ./bin -type f -name 'luci-app-youtubeUnblock*.ipk') ./luci-app-youtubeUnblock-$VERSION-$RELEASE-$SHA.ipk
|
||||
|
||||
- name: Upload packages
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: luci-app-youtubeUnblock
|
||||
path: /builder/luci-app-youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
build-entware:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- aarch64-3.10
|
||||
- armv7-3.2
|
||||
- mips-3.4
|
||||
- mipsel-3.4
|
||||
- x64-3.2
|
||||
- x86-2.6
|
||||
- armv7-2.6
|
||||
container:
|
||||
image: waujito/entware_builder:${{ matrix.arch }}
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$RELEASE-$SHA/" youtubeUnblock/Makefile
|
||||
|
||||
- name: Build packages
|
||||
id: build
|
||||
working-directory: /home/me/Entware
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf
|
||||
cat feeds.conf
|
||||
./scripts/feeds update youtubeUnblock
|
||||
./scripts/feeds install -a -p youtubeUnblock
|
||||
echo "CONFIG_PACKAGE_youtubeUnblockEntware=m" | tee -a .config
|
||||
make package/youtubeUnblockEntware/compile V=s
|
||||
|
||||
mv $(find ./bin -type f -name 'youtubeUnblockEntware*.ipk') ./youtubeUnblock-$VERSION-$RELEASE-$SHA-entware-${{ matrix.arch }}.ipk
|
||||
|
||||
- name: Upload packages
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: youtubeUnblock-entware-${{ matrix.arch }}
|
||||
path: /home/me/Entware/youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
pre-release:
|
||||
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||
needs: [build-static, build-static-cross, build-openwrt, build-entware, build-openwrt-luci]
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Upload assets
|
||||
uses: slord399/action-automatic-releases@v1.0.1
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
automatic_release_tag: 'continuous'
|
||||
prerelease: true
|
||||
draft: true
|
||||
title: 'Development build'
|
||||
files: |
|
||||
./**/youtubeUnblock*.ipk
|
||||
./**/youtubeUnblock*.tar.gz
|
||||
./**/luci-app-youtubeUnblock*.ipk
|
||||
107
.github/workflows/test.yml
vendored
Normal file
107
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
name: "youtubeUnblock build test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.editorconfig'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
- 'README.md'
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.gh.outputs.version }}
|
||||
sha: ${{ steps.gh.outputs.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: GH
|
||||
id: gh
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(cat youtubeUnblock/Makefile | grep PKG_VERSION | sed 's/PKG_VERSION:=//')" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
|
||||
echo "sha=$(echo ${GITHUB_SHA::7})" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "sha=$(gh api repos/$REPO/commits/main --jq '.sha[:7]')" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build-static:
|
||||
needs: prepare
|
||||
name: build-static ${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64]
|
||||
branch: [latest-stable]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
shell: bash
|
||||
run: |
|
||||
make -j$(nproc)
|
||||
strip -s build/youtubeUnblock
|
||||
cp -va build/youtubeUnblock .
|
||||
tar -czvf static-youtubeUnblock-$VERSION-$SHA-$PLATFORM.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-youtubeUnblock-${{ matrix.arch }}
|
||||
path: ./**/static-youtubeUnblock*.tar.gz
|
||||
|
||||
build-kmod:
|
||||
needs: prepare
|
||||
name: build-kmod ${{ matrix.kernel_version }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
kernel_version:
|
||||
- 6.6.52
|
||||
- 5.15.167
|
||||
- 5.4.284
|
||||
- 4.19.322
|
||||
- 4.4.302
|
||||
- 3.10.108
|
||||
- 3.0.101
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build kernel module
|
||||
id: build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
shell: bash
|
||||
run: |
|
||||
docker run --rm -v ./:/youtubeUnblock -w /youtubeUnblock waujito/kernel-bins:${{ matrix.kernel_version }} make kmake KERNEL_BUILDER_MAKEDIR:=/linux
|
||||
tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-linux-${{ matrix.kernel_version }}.tar.gz kyoutubeUnblock.ko
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kmod-youtubeUnblock-linux-${{ matrix.kernel_version }}
|
||||
path: ./**/kmod-youtubeUnblock*.tar.gz
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,3 +13,5 @@ modules.order
|
||||
Module.symvers
|
||||
*.so
|
||||
*.ko
|
||||
|
||||
!/.github
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule ".github/builder_containers/entware_docker"]
|
||||
path = .github/builder_containers/entware_docker
|
||||
url = https://github.com/Entware/docker.git
|
||||
6
Kbuild
6
Kbuild
@@ -1,3 +1,3 @@
|
||||
obj-m := ipt_YTUNBLOCK.o
|
||||
ipt_YTUNBLOCK-objs := iptk_YTUNBLOCK.o mangle.o
|
||||
ccflags-y := -std=gnu11 -Wno-unused-variable -DKERNEL_SPACE -DDEBUG
|
||||
obj-m := kyoutubeUnblock.o
|
||||
kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o getopt.o args.o
|
||||
ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement
|
||||
|
||||
16
Makefile
16
Makefile
@@ -1,6 +1,13 @@
|
||||
USPACE_TARGETS := default all install uninstall dev run_dev
|
||||
KMAKE_TARGETS := kmake kload kunload kreload xmod xtclean
|
||||
|
||||
PKG_VERSION := 1.0.0
|
||||
PKG_RELEASE := 5
|
||||
|
||||
PKG_FULLVERSION := $(PKG_VERSION)-$(PKG_RELEASE)
|
||||
|
||||
export PKG_VERSION PKG_RELEASE PKG_FULLVERSION
|
||||
|
||||
.PHONY: $(USPACE_TARGETS) $(KMAKE_TARGETS) clean
|
||||
$(USPACE_TARGETS):
|
||||
@$(MAKE) -f uspace.mk $@
|
||||
@@ -9,6 +16,11 @@ $(KMAKE_TARGETS):
|
||||
@$(MAKE) -f kmake.mk $@
|
||||
|
||||
clean:
|
||||
-@$(MAKE) -f kmake.mk kclean
|
||||
@$(MAKE) -f uspace.mk clean
|
||||
-@$(MAKE) -f uspace.mk clean
|
||||
|
||||
distclean: clean
|
||||
-@$(MAKE) -f uspace.mk distclean
|
||||
|
||||
|
||||
kclean:
|
||||
-@$(MAKE) -f kmake.mk kclean
|
||||
|
||||
30
Padavan.md
Normal file
30
Padavan.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## Padavan
|
||||
На падаване есть раздел, доступный для записи (/etc/storage), и, докинув нужные модули, можно запустить youtubeUblock на уже установленной прошивке без USB. Установка самого youtubeUblock мало будет отличаться от классичкской установки. Наибольшая сложность заключается в получении модулей ядра специально для вашего роутера.
|
||||
|
||||
**Версия youtubeUblock должна быть не меньше v1.0.0-rc4.**
|
||||
|
||||
### Сборка прошивки с модулями
|
||||
|
||||
Необходимо собрать ядро с модулями nfqueue. Собирать можно у себя локально, а можно и в github actions (https://github.com/shvchk/padavan-builder-workflow)
|
||||
|
||||
Добавить строки ниже в `padavan-ng/trunk/configs/boards/TPLINK/TL_C5-V4/kernel-3.4.x.config` (вместо TPLINK/TL_C5-V4 нужно выбрать свою модель):
|
||||
|
||||
```sh
|
||||
CONFIG_NETFILTER_NETLINK=m
|
||||
CONFIG_NETFILTER_NETLINK_QUEUE=m
|
||||
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
|
||||
CONFIG_IP_NF_QUEUE=m
|
||||
CONFIG_IP6_NF_QUEUE=m
|
||||
```
|
||||
|
||||
Сборка
|
||||
```sh
|
||||
cd padavan-ng/trunk
|
||||
cp configs/templates/tplink/tl_c5-v4.config .config
|
||||
./build_firmware.sh
|
||||
```
|
||||
Если финальный размер превышает максимум, то можно отключить что-нибудь в .config, например FTP.
|
||||
|
||||
После сборки необходимо установить прошивку на роутер. Подробнее в гитлабе падавана: https://gitlab.com/hadzhioglu/padavan-ng. Как устанавливать: https://4pda.to/forum/index.php?showtopic=975687&st=12980#Spoil-115912586-5
|
||||
|
||||
Далее скачать youtubeUnblock, закинуть его на роутер, добавить правила фаервола и запустить. Можно скачивать static бинарник и запускать вручную, а можно загрузить entware на usb или в память, и поставить соответствующую версию youtubeUblock.
|
||||
443
README.md
443
README.md
@@ -1,87 +1,432 @@
|
||||
- [youtubeUnblock](#youtubeunblock)
|
||||
- [Configuration](#configuration)
|
||||
- [OpenWRT pre configuration](#openwrt-pre-configuration)
|
||||
- [Entware](#entware)
|
||||
- [PC configuration](#pc-configuration)
|
||||
- [Firewall configuration](#firewall-configuration)
|
||||
- [nftables rules](#nftables-rules)
|
||||
- [Iptables rules](#iptables-rules)
|
||||
- [IPv6](#ipv6)
|
||||
- [Check it](#check-it)
|
||||
- [Flags](#flags)
|
||||
- [UDP](#udp)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [TV](#tv)
|
||||
- [Troubleshooting EPERMS (Operation not permitted)](#troubleshooting-eperms-operation-not-permitted)
|
||||
- [Compilation](#compilation)
|
||||
- [OpenWRT case](#openwrt-case)
|
||||
- [Building OpenWRT .ipk package](#building-openwrt-ipk-package)
|
||||
- [Building with toolchain](#building-with-toolchain)
|
||||
- [Kernel module](#kernel-module)
|
||||
- [Building kernel module](#building-kernel-module)
|
||||
- [Building on host system](#building-on-host-system)
|
||||
- [Building on any kernel](#building-on-any-kernel)
|
||||
- [Building with openwrt SDK](#building-with-openwrt-sdk)
|
||||
- [Padavan](#padavan)
|
||||
|
||||
|
||||
# youtubeUnblock
|
||||
Bypasses Googlevideo detection systems that relies on SNI. The package is for Linux only. The package is fully compatible with routers running OpenWRT. To learn how to build the package on OpenWRT, consult [this chapter](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#openwrt-case).
|
||||
|
||||
For Windows use [GoodbyeDPI from ValdikSS](https://github.com/ValdikSS/GoodbyeDPI) (you can find how to use it for YouTube [here](https://github.com/ValdikSS/GoodbyeDPI/issues/378)) The same behavior is also implemented in [zapret package for linux](https://github.com/bol-van/zapret).
|
||||
Bypasses Deep Packet Inspection (DPI) systems that relies on SNI. The package is for Linux only. It is also fully compatible with routers running [OpenWRT](https://github.com/openwrt).
|
||||
|
||||
## How it works:
|
||||
Lets look from the DPIses side of view: All they have is ip and tcp information, higher-level data is encrypted. So from the IP header only IP address might be helpful for them. In tcp here is basically nothing. So they may handle IP addresses and process it. What's wrong? Google servers are on the way: It is very hard to handle all that infrastracture. One server may host multiple websites and it is very bad if them block, say Google Search trying to block googlevideo. But even if googlevideo servers have their own ip for only googlevideo purposes, here is a problem about how large is Google infrastracture and how much servers are here. The DPIs can't even parse normally all the servers, because each video may live on it's cache server. So what's else? Let's take a look at a TLS level. All information here is encrypted. All... Except hello messages! They are used to initialize handshake connections and hold tons of helpful information. If we talk about TLS v1.3, it is optimized to transfer as less information as possible unencrypted. But here is only one thing that may point us which domain the user wants to connect, the SNI extension. It transfers all domain names unencrypted. Exactly what we need! And DPIs may use this thing to detect google video connections and slow down them (In fact they are corrupting a tcp connection with bad packets).
|
||||
The program was primarily developed to bypass YouTube Outage in Russia.
|
||||
|
||||
So we aims to somehow hide the SNI from them. How?
|
||||
- We can alter the SNI name in the tls packet to something else. But what's wrong with this? The server also uses SNI name for certificates. And if we change it, the server will return an invalid certificate which browser can't normally process, which may turn out to the MITM problem.
|
||||
- We can encrypt it. Here are a lot of investigations about SNI, but the server should support the technique. Also ISPs may block encrypted SNI. [Check this Wikipedia page](https://en.wikipedia.org/wiki/Server_Name_Indication)
|
||||
- So what else can we do with the SNI info? If we can't hide it, let's rely on DPIs weak spots. The DPI is an extremly high loaded machine that analyzes every single packet sent to the Internet. And every performance-impacted feature should be avoided for them. One of this features is IP packet fragmentation. We can split the packet in the middle of SNI message and post it. For DPI fragmentation involves too much overhead: they should store a very big mapping table which maps IP id, Source ip and Destination ip. Also note that some packets may be lost and DPI should support auto-clean of that table. So just imagine how much memory and CPU time will this cost for DPI. But fragments are ok for clients and hosts. And that's the base idea behind this package. I have to mention here that the idea isn't mine, I get in here after some research for this side. Here already was a solution for Windows, GoodbyeDPI. I just made an alternative for Linux.
|
||||
```
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
```
|
||||
|
||||
You may read further in an [yt-dlp issue page](https://github.com/yt-dlp/yt-dlp/issues/10443) and in [ntc party forum](https://ntc.party/t/%D0%BE%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%BC%D0%B5%D0%B4%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-youtube-%D0%B2-%D1%80%D0%BE%D1%81%D1%81%D0%B8%D0%B8/8074).
|
||||
The program is distributed in two version:
|
||||
- A userspace application works on top of nfnetlink queue which requires nfnetlink modules in the kernel and firewall rules. This approach is default and normally should be used but it has some limitations on embedded devices which may have no nfnetlink support. Also this solution may break down the internet speed and CPU load on your device because of jumps between userspace and kernelspace for each packet (this behavior may be fixed with connbytes but it also requires conntrack kernel module).
|
||||
- A kernel module which integrates deeply within the netfilter stack and does not interact with the userspace firewall. The module requires only netfilter kernel support but it definetly present on every device connected to the Internet. The only difficulity is how to build it. I cannot provide modules within Github Actions for each single one kernel, even if we talk only about OpenWRT versions. If you want to learn more about the module, jump on [its section in the README](#kernel-module). Whats the benefits of the kernel module? The benefits come for some specific cases: the kernel module is the fastest thing that allows us to process every single packet sent to the linux network stack, while the normal youtubeUnblock requires connbytes to keep the internet speed. Speaking about connbytes, it also requires conntrack to operate, which may be a limitation on some transit-traffic machines. Also userspace youtubeUnblock requires modules for netlink queue, userspace firewall application and modules for it. The kernel module is much simpler and requires only the linux kernel with netfilter built in.
|
||||
|
||||
## How it processes packets
|
||||
When the packet is joining the queue, the application checks sni payload to be googlevideo (right how the DPIs do), segmentates/fragmentates (both TCP and IP fragmentation techniques are supported) and posts the packet. Note that it is impossible to post two fragmented packets from one netfilter queue verdict. Instead, the application drops an original packet and makes another linux raw socket to post the packets in the network. To escape infinity loops the socket marks outgoing packets and the application automatically accepts it.
|
||||
The program is compatible with routers based on OpenWRT, Entware(Keenetic/ASUS) and host machines. The program offers binaries via Github Actions. The binaries are also available via [github releases](https://github.com/Waujito/youtubeUnblock/releases). Use the latest pre-release for the most up to date build. Check out [Github Actions](https://github.com/Waujito/youtubeUnblock/actions/workflows/build-ci.yml) if you want to see all the binaries compiled ever. You should know the arcitecture of your hardware to use binaries. On OpenWRT you can check it with command `grep ARCH /etc/openwrt_release`.
|
||||
|
||||
## Usage:
|
||||
Before compilation make sure `gcc`, `make`, `autoconf`, `automake`, `pkg-config` and `libtool` is installed. For Fedora `glibc-static` should be installed as well.
|
||||
Compile with `make`. Install with `make install`. The package include libnetfilter_queue, libnfnetlink and libmnl as static dependencies. The package requires linux-headers and kernel built with netfilter nfqueue support.
|
||||
On both OpenWRT and Entware install the program with opkg. If you got read-only filesystem error you may unpack the binary manually or specify opkg path `opkg -o <destdir>`.
|
||||
|
||||
You should also configure iptables for this to start working:
|
||||
```iptables -A OUTPUT -p tcp --dport 443 -j NFQUEUE --queue-num 537 --queue-bypass``` (or do ```nft add rule ip mangle OUTPUT tcp dport 443 counter queue num 537 bypass``` for nftables.)
|
||||
Here iptables serves every tcp packet, destinating port 443 for this userspace packet analyzer (via netfilter kernel module) queue-num may be any number from 0 to 65565. --queue-bypass allows traffic to pass if the application is down.
|
||||
For Windows use [GoodbyeDPI by ValdikSS](https://github.com/ValdikSS/GoodbyeDPI) (you can find how to use it for YouTube [here](https://github.com/ValdikSS/GoodbyeDPI/issues/378)) The same behavior is also implemented in [zapret package for linux](https://github.com/bol-van/zapret).
|
||||
|
||||
Also tips to explicitly accept all important outgoing raw packets from youtubeUnblock from [Troubleshooting EPERMS](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#troubleshooting-eperms-operation-not-permitted) may be useful to avoid issues.
|
||||
## Configuration
|
||||
|
||||
Run an application with `youtubeUnblock 537` where `537` stands for the queue-num (must be the same as in the iptables rule).
|
||||
### OpenWRT pre configuration
|
||||
|
||||
Systemd daemon is also available. Do `systemctl enable --now youtubeUnblock.service` after installation (uses queue-num `537`). Please, note that systemd will configure iptables automatically. If you have troubles with it, delete ExecStartPre and ExecStop from youtubeUnblock.service and configure iptables manually (may be a useful case for nftables).
|
||||
When you got the release package, you should install it. Go to your router interface, to *System->Software*, do *Update lists* and install youtubeUnblock via *install_package* button. Then, you should go to *System-Startup* menu and reload the firewall (You may also do it within *Services->youtubeUnblock* menu).
|
||||
|
||||
If you don't want youtubeUnblock to log on each googlevideo request pass -DSILENT as CFLAGS.
|
||||
To make it work you should register an iptables rule and install required kernel modules. The list of modules depends on the version of OpenWRT and which firewall do you use (iptables or nftables). For most modern versions of OpenWRT (v23.x, v22.x) you should use nftables rules, for older ones it depends, but typically iptables.
|
||||
|
||||
Also DNS over HTTPS (DOH) is preferred for additional anonimity.
|
||||
The common dependency is
|
||||
```text
|
||||
kmod-nfnetlink-queue
|
||||
```
|
||||
but it is provided as dependency for another firewall packages.
|
||||
|
||||
So, if you are on **iptables** you should install:
|
||||
```text
|
||||
kmod-ipt-nfqueue
|
||||
iptables-mod-nfqueue
|
||||
kmod-ipt-conntrack-extra
|
||||
iptables-mod-conntrack-extra
|
||||
```
|
||||
and of course, iptables user-space app should be available.
|
||||
|
||||
On **nftables** the dependencies are:
|
||||
```text
|
||||
kmod-nft-queue
|
||||
kmod-nf-conntrack
|
||||
```
|
||||
|
||||
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 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**).
|
||||
|
||||
If you got ` * pkg_hash_check_unresolved: cannot find dependency luci-lua-runtime for luci-app-youtubeUnblock` error, you are using old openwrt. Install [this dummy package](https://github.com/xiaorouji/openwrt-passwall/files/12605732/luci-lua-runtime_all_fake.zip). [Check this comment](https://github.com/Waujito/youtubeUnblock/issues/168#issuecomment-2449227547) for more details.
|
||||
|
||||
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.
|
||||
|
||||
UCI configuration is available in /etc/config/youtubeUnblock file, in section `youtubeUnblock.youtubeUnblock`. You may pass any args as a string to parameter `args`, but before it disable interactive flags (You can configurate with it but it is a way harder and I recommend to use it only with `luci-app-youtubeUnblock`):
|
||||
|
||||
```sh
|
||||
uci set youtubeUnblock.youtubeUnblock.conf_strat="args"
|
||||
uci set youtubeUnblock.youtubeUnblock.args="--queue-num=537 --threads=1"
|
||||
```
|
||||
|
||||
To save the configs you should do `uci commit` and then `reload_config` to restart youtubeUnblock
|
||||
|
||||
|
||||
You can check the logs in CLI mode with `logread -l 200 | grep youtubeUnblock` command.
|
||||
|
||||
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
|
||||
|
||||
For Entware on Keenetic here is an [installation guide (russian)](https://help.keenetic.com/hc/ru/articles/360021214160-%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B-%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F-Entware-%D0%BD%D0%B0-USB-%D0%BD%D0%B0%D0%BA%D0%BE%D0%BF%D0%B8%D1%82%D0%B5%D0%BB%D1%8C).
|
||||
|
||||
Install the binary with `opkg install youtubeUnblock-*.ipk`. After installation, the binary in /opt/bin and the init script in /opt/etc/init.d/S51youtubeUnblock will be available. To run the youtubeUnblock, simply run `/opt/etc/init.d/S51youtubeUnblock start`
|
||||
|
||||
### NFNETLINK_QUEUE kernel modules
|
||||
|
||||
Note, that you should feed the target kernel with nfnetlink_queue kernel module. The module may be disabled or even not present. Entware S51youtubeUnblock will try to insert kmods any way but if they are not provided by software, you should install them manually. AFAIK on keenetics here is a repository with modules compiled by customer. You can find them somewhere in the web interface of your device. On other routers you may want to do deeper research in that case and find your kmods. If you can't find anything, you may ask the customer for GPL codes of linux kernel (and may be even OpenWRT) and compile kmods manually.
|
||||
|
||||
You should insert the module with (this step may be omitted on Entware and OpenWRT):
|
||||
```sh
|
||||
modprobe nfnetlink_queue
|
||||
```
|
||||
|
||||
### PC configuration
|
||||
On local host make sure to change **FORWARD** to **OUTPUT** chain in the following Firewall rulesets.
|
||||
|
||||
Copy `youtubeUnblock.service` to `/usr/lib/systemd/system` (you should change the path inside the file to the program position, for example `/usr/bin/youtubeUnblock`, also you may want to delete default iptables rule addition in systemd file to controll it manually). And run `systemctl start youtubeUnblock`.
|
||||
|
||||
### Firewall configuration
|
||||
|
||||
#### nftables rules
|
||||
|
||||
On nftables you should put next nftables rules:
|
||||
```sh
|
||||
nft add chain inet fw4 youtubeUnblock '{ type filter hook postrouting priority mangle - 1; policy accept; }'
|
||||
nft add rule inet fw4 youtubeUnblock 'tcp dport 443 ct original packets < 20 counter queue num 537 bypass'
|
||||
nft add rule inet fw4 youtubeUnblock 'meta l4proto udp ct original packets < 9 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 -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 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:8 -j NFQUEUE --queue-num 537 --queue-bypass
|
||||
iptables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK
|
||||
iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
|
||||
```
|
||||
|
||||
#### IPv6
|
||||
|
||||
For IPv6 on iptables you need to duplicate rules above for ip6tables:
|
||||
```sh
|
||||
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 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:8 -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.
|
||||
|
||||
You can use `--queue-balance` with multiple instances of **youtubeUnblock** for performance. This behavior is supported via multithreading. Just pass `--threads=n` where n stands for an number of threads you want to be enabled. The n defaults to **1**. The maximum threads defaults to **16** but may be altered programmatically. Note, that if you are about to increase it, here is 100% chance that you are on the wrong way.
|
||||
|
||||
Also [DNS over HTTPS](https://github.com/curl/curl/wiki/DNS-over-HTTPS) is preferred for additional anonymity.
|
||||
|
||||
## Check it
|
||||
|
||||
Here is the command to test whether it working or not:
|
||||
```sh
|
||||
curl -o/dev/null -k --connect-to ::google.com -k -L -H Host:\ mirror.gcr.io https://test.googlevideo.com/v2/cimg/android/blobs/sha256:6fd8bdac3da660bde7bd0b6f2b6a46e1b686afb74b9a4614def32532b73f5eaa
|
||||
```
|
||||
|
||||
It should return low speed without **youtubeUnblock** and faster with it. With **youtubeUnblock** the speed should be the same as fast with the next command:
|
||||
|
||||
```sh
|
||||
curl -o/dev/null -k --connect-to ::google.com -k -L -H Host:\ mirror.gcr.io https://mirror.gcr.io/v2/cimg/android/blobs/sha256:6fd8bdac3da660bde7bd0b6f2b6a46e1b686afb74b9a4614def32532b73f5eaa
|
||||
```
|
||||
|
||||
## Flags
|
||||
Available flags:
|
||||
- `--seg2delay=<delay>` - This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet.
|
||||
- `--fake-sni={ack,ttl, none}` This flag enables fake-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. Note, that this flag is set to `ack` by default. You may disable fake sni by setting it to `none`. Note, that your ISP may have conntrack drop on invalid state enabled, so this flag won't work. Use `ttl` to escape that.
|
||||
- `--fake-sni-ttl=<ttl>` Tunes the time to live of fake sni messages. TTL is specified like that the packet will go through the TSPU and captured by it, but will not reach the destination server. Defaults to 8.
|
||||
- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything.
|
||||
- `--threads=<threads number>` Specifies the amount of threads you want to be running for your program. This defaults to 1 and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance)
|
||||
- `--silent` - Disables Google video detected debug logs.
|
||||
- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by TSPU. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies.
|
||||
|
||||
If you are on Chromium you may have to disable kyber (the feature that makes the TLS ClientHello very fat). I've got the problem with it on router, so to escape possibly errors it is better to just disable it: in chrome://flags search for kyber and switch it to disabled state.
|
||||
Put flags to the **BINARY**, not an init script. If you are on OpenWRT you should put the flags inside the script: open `/etc/init.d/youtubeUnblock` with any text editor, like vi or nano and put your flags after `procd_set_param command /usr/bin/youtubeUnblock` line.
|
||||
|
||||
Available flags:
|
||||
#### General flags
|
||||
Flags that do not scoped to a specific section, used over all the youtubeUnblock
|
||||
|
||||
- `--queue-num=<number of netfilter queue>` The number of netfilter queue **youtubeUnblock** will be linked to. Defaults to **537**.
|
||||
|
||||
- `--silent` Disables verbose mode.
|
||||
|
||||
- `--trace` Maximum verbosity for debugging purposes.
|
||||
|
||||
- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything.
|
||||
|
||||
- `--no-ipv6` Disables support for ipv6. May be useful if you don't want for ipv6 socket to be opened.
|
||||
|
||||
- `--threads=<threads number>` Specifies the amount of threads you want to be running for your program. This defaults to **1** and shouldn't be edited for normal use. But if you really want multiple queue instances of youtubeUnblock, note that you should change --queue-num to --queue balance. For example, with 4 threads, use `--queue-balance 537:540` on iptables and `queue num 537-540` on nftables.
|
||||
|
||||
- `--connbytes-limit=<pkts>` **Kernel module only!** Specify how much packets of connection should be processed by kyoutubeUnblock. Pass 0 if you want for each packet to be processed. This flag may be useful for UDP traffic since unlimited youtubeUnblock may lead to traffic flood and unexpected bans. Defaults to 5. In most cases you don't want to change it.
|
||||
|
||||
- `--daemonize` Daemonizes the youtubeUnblock (forks and detaches it from the shell). Terminate the program with `killall youtubeUnblock`. If you want to track the logs of youtubeUnblock in logread or journalctl, use **--syslog** flag.
|
||||
|
||||
- `--syslog` Redirects logs to the system log. You can read it with `journalctl` or `logread`.
|
||||
|
||||
- `--noclose` Usable only with `--daemonize`. Will not redirect io streams to /dev/null.
|
||||
|
||||
- `--packet-mark=<mark>` Use this option if youtubeUnblock conflicts with other systems rely on packet mark. Note that you may want to change accept rule for iptables to follow the mark.
|
||||
|
||||
#### Section scoped flags
|
||||
|
||||
- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fbegin --sni-domains=l.google.com --faking-strategy=pastseq`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you.
|
||||
|
||||
- `--tls={enabled|disabled}` Set it if you want not to process TLS traffic in current section. May be used if you want to set only UDP-based section. (Here section is a unit between `--fbegin` and `--fend` flags).
|
||||
|
||||
- `--fake-sni={0|1}` This flag enables fake-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 before open an issue refer to [Troubleshooting for EPERMS](#troubleshooting-eperms-operation-not-permitted). 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 message type should be used for fake packets. For `random`, the message of random length and with random payload will be sent. For `default` the default payload (sni=www.google.com) is used. And for the `custom` option, the payload from `--fake-custom-payload` section utilized. Defaults to `default`.
|
||||
- `--fake-custom-payload=<payload>` Useful 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`
|
||||
- `randseq` specifies that random sequence/acknowledgment 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**.
|
||||
|
||||
- `--fake-seq-offset` Tunes the offset from original sequence number for fake packets. Used by randseq faking strategy. Defaults to 10000. If 0, random sequence number will be set.
|
||||
|
||||
- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by DPI system. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies.
|
||||
|
||||
- `--frag-sni-reverse={0|1}` Specifies **youtubeUnblock** to send *ClientHello* fragments in the reverse order. Defaults to **1**.
|
||||
|
||||
- `--frag-sni-faked={0|1}` Specifies **youtubeUnblock** to send fake packets near *ClientHello* (fills payload with zeroes). Defaults to **0**.
|
||||
|
||||
- `--frag-middle-sni={0|1}` With this options **youtubeUnblock** will split the packet in the middle of SNI data. Defaults to 1.
|
||||
|
||||
- `--frag-sni-pos=<pos>` With this option **youtubeUnblock** will split the packet at the position pos. Defaults to 1.
|
||||
|
||||
- `--fk-winsize=<winsize>` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization.
|
||||
|
||||
- `--synfake={1|0}` If 1, syn payload will be sent before each request. The idea is taken from syndata from zapret project. Syn payload will normally be discarded by endpoint but may be handled by TSPU. This option sends normal fake in that payload. Please note, that the option works for all the sites, so --sni-domains won't change anything.
|
||||
|
||||
- `--synfake-len=<len>` The fake packet sent in synfake may be too large. If you experience issues, lower up synfake-len. where len stands for how much bytes should be sent as syndata. Pass 0 if you want to send an entire fake packet. Defaults to 0
|
||||
|
||||
- `--sni-detection={parse|brute}` Specifies how to detect SNI. Parse will normally detect it by parsing the Client Hello message. Brute will go through the entire message and check possibility of SNI occurrence. Please note, that when `--sni-domains` option is not all brute will be O(nm) time complexity where n stands for length of the message and m is number of domains. Defaults to parse.
|
||||
|
||||
- `--seg2delay=<delay>` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet.
|
||||
|
||||
- `--sni-domains=<comma separated domain list>|all` List of domains you want to be handled by SNI. Use this string if you want to change default domain list. Defaults to `googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com`. You can pass **all** if you want for every *ClientHello* to be handled. You can exclude some domains with `--exclude-domains` flag.
|
||||
|
||||
- `--exclude-domains=<comma separated domain list>` List of domains to be excluded from targeting.
|
||||
|
||||
- `--udp-mode={drop|fake}` This flag specifies udp handling strategy. If drop udp packets will be dropped (useful for quic when browser can fallback to tcp), if fake udp will be faked. Defaults to fake.
|
||||
|
||||
- `--udp-fake-seq-len=<amount of faking packets sent>` Specifies how much faking packets will be sent over the network. Defaults to 6.
|
||||
|
||||
- `--udp-fake-len=<size of udp fake>` Size of udp fake payload (typically payload is zeroes). Defaults to 64.
|
||||
|
||||
- `--udp-dport-filter=<5,6,200-500>` Filter the UDP destination ports. Defaults to no ports. Specifie the ports you want to be handled by youtubeUnblock.
|
||||
|
||||
- `--udp-filter-quic={disabled|all}` Enables QUIC filtering for UDP handler. If disabled, quic won't be processed, if all, all quic initial packets will be handled. Defaults to disabled.
|
||||
|
||||
- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Just an alias for `--udp-filter-quic=all --udp-mode=drop`.
|
||||
|
||||
## UDP
|
||||
|
||||
UDP is another communication protocol. Well-known technologies that use it are DNS, QUIC, voice chats. UDP does not provide reliable connection and its header is much simpler than TCP thus fragmentation is limited. The support provided primarily by faking. For QUIC faking may not work well, so use `--quic-drop` if you want to drop all quic traffic. For other technologies I recommend to configure UDP support in the separate section from TCP, like `--fbegin --udp-dport-filter=50000-50099 --tls=disabled`. See more in flags related to udp and [issues tagged with udp label](https://github.com/Waujito/youtubeUnblock/issues?q=label%3Audp+).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Check up [this issue](https://github.com/Waujito/youtubeUnblock/issues/148) for useful configs.
|
||||
|
||||
If you got troubles with some sites and you sure that they are blocked by SNI (youtube for example), use may play around with [flags](#flags) and their combinations. At first it is recommended to try `--faking-strategy` flag and `--frag-sni-faked=1`.
|
||||
If you have troubles with some sites being proxied, you can play with flags values. For example, for someone `--faking-strategy=ttl` works. You should specify proper `--fake-sni-ttl=<ttl value>` where ttl is the amount of hops between you and DPI.
|
||||
|
||||
If you are on Chromium you may have to disable *kyber* (the feature that makes the TLS *ClientHello* very big). I've got the problem with it on router, so to escape possible errors, so it is better to disable it: in `chrome://flags` search for kyber and switch it to disabled state. Alternatively you may set `--sni-detection=brute` and probably adjust `--sni-domains` flag.
|
||||
|
||||
*Kyber* on firefox disables with `security.tls.enable_kyber` in `about:config`.
|
||||
|
||||
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.
|
||||
|
||||
In [this issue](https://github.com/Waujito/youtubeUnblock/issues/59) the problem has been resolved. And now youtubeUnblock should work with default flags. If not, play around with faking strategies and other flags. Also you might be have to disable QUIC. To do it you may use `--quic-drop` [flag](#flags) with proper firewall configuration (check description of the flag). Note, that this flag won't disable gQUIC and some TVs may relay on it. To disable gQUIC you will need to block the entire 443 port for udp in firewall configuration:
|
||||
|
||||
For **nftables** do
|
||||
```
|
||||
nft insert rule inet fw4 forward ip saddr 192.168.. udp dport 443 counter drop
|
||||
```
|
||||
|
||||
For **iptables**
|
||||
```
|
||||
iptables -I OUTPUT --src 192.168.. -p udp --dport 443 -j DROP
|
||||
```
|
||||
|
||||
Where you have to replace 192.168.. with ip of your television.
|
||||
|
||||
### Troubleshooting 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.
|
||||
|
||||
*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`.
|
||||
|
||||
## Performance
|
||||
If you have bad performance you can queue to youtubeUnblock only first, say, 20 packets from the connection. To do so, use nftables conntrack packets counter: `nft add rule inet fw4 mangle_forward tcp dport 443 ct original "packets < 20" counter queue num 537 bypass`. For my 1 CPU core device it worked pretty well. This works because we do care about only first packets with ClientHello. We don't need to process others.
|
||||
## Compilation
|
||||
|
||||
The same behavior is also possible in iptables: `iptables -t mangle -A FORWARD -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass`. (The package iptables-mod-conntrack-extra is required for connbytes on OpenWRT)
|
||||
For hosts change FORWARD to OUTPUT.
|
||||
Before compilation make sure `gcc`, `make`, `autoconf`, `automake`, `pkg-config` and `libtool` is installed. For Fedora `glibc-static` should be installed as well.
|
||||
|
||||
You can use `--queue-balance` with multiple instances of youtubeUnblock. This behavior is supported via multithreading. Just pass -DTHREADS_NUM=n where n stands for an amount of threads you want to be enabled. The n defaults to 1. The maximum threads defaults to 16 but may be altered programatically. Note, that if you are about to increase it, here is 100% chance that you are on the wrong way.
|
||||
Compile with `make`. Install with `make install`. The package include `libnetfilter_queue`, `libnfnetlink` and `libmnl` as static dependencies. The package requires `linux-headers` and kernel built with netfilter nfqueue support.
|
||||
|
||||
## OpenWRT case
|
||||
|
||||
The package is also compatible with routers. The router should be running by linux-based system such as [OpenWRT](https://openwrt.org/).
|
||||
You can build under openwrt with two options: first - through the SDK, which is preferred way and second is cross-compile manually with openwrt toolchain.
|
||||
|
||||
You can build under OpenWRT with two options: first - through the SDK, which is preferred way and second is cross-compile manually with OpenWRT toolchain.
|
||||
|
||||
### Building OpenWRT .ipk package
|
||||
|
||||
OpenWRT provides a high-level SDK for the package builds.
|
||||
First step is to download or compile OpenWRT SDK for your specific platform. The SDK can be compiled according to [this tutorial](https://openwrt.org/docs/guide-developer/toolchain/using_the_sdk). Beside of raw source code of SDK, OpenWRT also offers precompiled SDKs for your router. You can find it on the router page. For example, I have ramips/mt76x8 based router so for me the sdk is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-sdk-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64`. You will need to [install sdk requirements on your system](https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem) If you have any problems, use docker ubuntu:24.04 image. Make sure to be a non-root user since some makesystem fails with it. Next, untar the SDK and cd into it. Do `echo "src-git youtubeUnblock https://github.com/Waujito/youtubeUnblock.git;openwrt" >> feeds.conf`, `./scripts/feeds update youtubeUnblock`, `./scripts/feeds install -a -p youtubeUnblock`, `make package/youtubeUnblock/compile`. Now the packet is built and you can import it to the router. Find it in `bin/packages/<target>/youtubeUnblock/youtubeUnblock-<version>.ipk`. Go to your router interface and put it in via System-Software-install_package. Now the package is on the router. Goto System-Startup, restart firewall and start youtubeUnblock. You are done!
|
||||
|
||||
First step is to download or compile OpenWRT SDK for your specific platform. The SDK can be compiled according to [this tutorial](https://openwrt.org/docs/guide-developer/toolchain/using_the_sdk).
|
||||
|
||||
Beside of raw source code of SDK, OpenWRT also offers precompiled SDKs for your router. You can find it on the router page. For example, I have ramips/mt76x8 based router so for me the sdk is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-sdk-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64`.
|
||||
|
||||
You will need to [install sdk requirements on your system](https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem) If you have any problems, use docker ubuntu:24.04 image. Make sure to be a non-root user since some makesystem fails with it. Next, untar the SDK and cd into it.
|
||||
|
||||
Do
|
||||
```sh
|
||||
echo "src-git youtubeUnblock https://github.com/Waujito/youtubeUnblock.git;openwrt" >> feeds.conf
|
||||
./scripts/feeds update youtubeUnblock
|
||||
./scripts/feeds install -a -p youtubeUnblock
|
||||
make package/youtubeUnblock/compile
|
||||
```
|
||||
|
||||
Now the packet is built and you can import it to the router. Find it in `bin/packages/<target>/youtubeUnblock/youtubeUnblock-<version>.ipk`.
|
||||
|
||||
### Building with toolchain
|
||||
The precompiled toolchain located near the SDK. For me it is called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. When you download the toolchain, 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.
|
||||
|
||||
### Configuration
|
||||
If you compiled the package via the SDK everything is preinstalled. But if you got any issues (suitable for routers with iptables instead of nftables), ssh into the router and check up everything manually.
|
||||
The precompiled toolchain located near the SDK. For example it is called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. When you download the toolchain, 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.
|
||||
|
||||
For iptables: install a normal iptables user-space app: `xtables-legacy iptables-zz-legacy` and kernel/iptables nfqueue extensions: `iptables-mod-nfqueue kmod-ipt-nfqueue` and add `iptables -t mangle -A FORWARD -p tcp -m tcp --dport 443 -j NFQUEUE --queue-num 537 --queue-bypass` rule.
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
If you prefer nftables, this should work: `nft add rule inet fw4 mangle_forward tcp dport 443 counter queue num 537 bypass`. Note that `kmod-nft-queue` should be installed.
|
||||
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 router model name maybe an [automake cross-compile manual](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) will be helpful.
|
||||
|
||||
Also you can copy `owrt/537-youtubeUnblock.nft` to `/usr/share/nftables.d/ruleset-post/537-youtubeUnblock.nft` and run `/etc/init.d/firewall reload`. This will reload the nftables ruleset and automatically link 537-youtubeUnblock.nft with it.
|
||||
When compilation is done, the binary file will be in build directory. Copy it to your router. Note that a 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` it and run.
|
||||
|
||||
## Kernel module
|
||||
|
||||
This section describes the kernel module version of youtubeUnblock. The kernel module operates as a normal module inside the kernel and integrates within the netfilter stack to statelessly mangle the packets sent over the Internet.
|
||||
|
||||
You can configure the module with its flags:
|
||||
|
||||
```sh
|
||||
insmod kyoutubeUnblock.ko
|
||||
echo "--fake_sni=1 --exclude_domains=.ru --quic_drop" | sudo tee /sys/module/kyoutubeUnblock/parameters/parameters
|
||||
```
|
||||
|
||||
You can also do
|
||||
|
||||
```sh
|
||||
cat /sys/module/kyoutubeUnblock/parameters/parameters
|
||||
```
|
||||
|
||||
and check all the parameters configured.
|
||||
|
||||
### Building kernel module
|
||||
|
||||
#### Building on host system
|
||||
|
||||
To build the kernel module on your host system you should install `linux-headers` which will provide build essential tools and `gcc` compiler suite. On host system you may build the module with
|
||||
```sh
|
||||
make kmake
|
||||
```
|
||||
|
||||
#### Building on any kernel
|
||||
|
||||
To build the module for external kernel you should build that kernel locally and point make to it. Use `KERNEL_BUILDER_MAKEDIR=~/linux` flag for make, for example:
|
||||
```
|
||||
make kmake KERNEL_BUILDER_MAKEDIR=~/linux
|
||||
```
|
||||
Note, that the kernel should be already configured and built. See linux kernel building manuals for more information about your specific case.
|
||||
|
||||
#### Building with openwrt SDK
|
||||
|
||||
Building with openwrt SDK is not such a hard thing. The only thing you should do is to obtain the sdk. You can find it by looking to your architecture and version of the openwrt currently used. You should use the exactly your version of openwrt since kernels there change often. You can find the sdk in two ways: by downloading it from their site or by using the openwrt sdk docker container (recommended).
|
||||
|
||||
If you decide to download the tar archive, follow next steps:
|
||||
For me the archive lives in https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-sdk-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64`. You will need to [install sdk requirements on your system](https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem) If you have any problems, use docker ubuntu:24.04 image. Make sure to be a non-root user since some makesystem fails with it. Next, untar the SDK and cd into it.
|
||||
|
||||
Or you can obtain the docker image with sdk built-in: [https://hub.docker.com/u/openwrt/sdk](https://hub.docker.com/u/openwrt/sdk). In my case the image has tag `ramips-mt76x8-23.05.3`. A good thing here is that you don't need to install any dependencies inside the docker container. Also docker hub has a perfect search around tags if you don't sure which one corresponds to your device.
|
||||
|
||||
When you unpacked/installed the sdk, you is ready to start with building the kernel module.
|
||||
|
||||
Do
|
||||
```sh
|
||||
echo "src-git youtubeUnblock https://github.com/Waujito/youtubeUnblock.git;openwrt" >> feeds.conf
|
||||
./scripts/feeds update youtubeUnblock
|
||||
./scripts/feeds install -a -p youtubeUnblock
|
||||
make defconfig
|
||||
make package/kyoutubeUnblock/compile V=s
|
||||
```
|
||||
|
||||
When the commands finish, the module is ready. Find it with `find bin -name "kmod-youtubeUnblock*.ipk"`, copy to your host and install to the router via gui software interface. The module should start immediately. If not, do `modprobe kyoutubeUnblock`.
|
||||
|
||||
|
||||
## Padavan
|
||||
YoutubeUnblock may also run on Padavan. [Check the manual here\[rus\]](Padavan.md)
|
||||
|
||||
|
||||
>If you have any questions/suggestions/problems feel free to open an [issue](https://github.com/Waujito/youtubeUnblock/issues).
|
||||
|
||||
Next step is to daemonize the application in openwrt. Copy `owrt/youtubeUnblock.owrt` to `/etc/init.d/youtubeUnblock` and put the program into /usr/bin/. (Don't forget to `chmod +x` both). Now run `/etc/init.d/youtubeUnblock start`. You can alo run `/etc/init.d/youtubeUnblock enable` to force OpenWRT autostart the program on boot, but I don't recommend this since if the packet has bug you may lose access to the router (I think you will be able to reset it with reset settings tricks documented for your router).
|
||||
|
||||
## If you have any questions/suggestions/problems feel free to open an issue.
|
||||
|
||||
23
args.h
Normal file
23
args.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef ARGS_H
|
||||
#define ARGS_H
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
void print_version(void);
|
||||
void print_usage(const char *argv0);
|
||||
int yparse_args(int argc, char *argv[]);
|
||||
size_t print_config(char *buffer, size_t buffer_size);
|
||||
|
||||
// Initializes configuration storage.
|
||||
int init_config(struct config_t *config);
|
||||
// Allocates and initializes configuration section.
|
||||
int init_section_config(struct section_config_t **section, struct section_config_t *prev);
|
||||
// Frees configuration section
|
||||
void free_config_section(struct section_config_t *config);
|
||||
// Frees sections under config
|
||||
void free_config(struct config_t config);
|
||||
|
||||
/* Prints starting messages */
|
||||
void print_welcome(void);
|
||||
|
||||
#endif /* ARGS_H */
|
||||
236
config.h
236
config.h
@@ -1,17 +1,123 @@
|
||||
#ifndef YTB_CONFIG_H
|
||||
#define YTB_CONFIG_H
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
#define USER_SPACE
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
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
|
||||
* (for example, open daemon thread)
|
||||
*/
|
||||
typedef int (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms);
|
||||
|
||||
struct instance_config_t {
|
||||
raw_send_t send_raw_packet;
|
||||
delayed_send_t send_delayed_packet;
|
||||
};
|
||||
extern struct instance_config_t instance_config;
|
||||
|
||||
struct udp_dport_range {
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
};
|
||||
|
||||
struct domains_list {
|
||||
char *domain_name;
|
||||
uint16_t domain_len;
|
||||
|
||||
struct domains_list *next;
|
||||
};
|
||||
|
||||
struct section_config_t {
|
||||
int id;
|
||||
struct section_config_t *next;
|
||||
struct section_config_t *prev;
|
||||
|
||||
struct domains_list *sni_domains;
|
||||
struct domains_list *exclude_sni_domains;
|
||||
unsigned int all_domains;
|
||||
|
||||
int tls_enabled;
|
||||
|
||||
int fragmentation_strategy;
|
||||
int frag_sni_reverse;
|
||||
int frag_sni_faked;
|
||||
int faking_strategy;
|
||||
int frag_middle_sni;
|
||||
int frag_sni_pos;
|
||||
unsigned char faking_ttl;
|
||||
int fake_sni;
|
||||
unsigned int fake_sni_seq_len;
|
||||
|
||||
#define FAKE_PAYLOAD_RANDOM 0
|
||||
#define FAKE_PAYLOAD_CUSTOM 1
|
||||
// In default mode all other options will be skipped.
|
||||
#define FAKE_PAYLOAD_DEFAULT 2
|
||||
int fake_sni_type;
|
||||
|
||||
/* In milliseconds */
|
||||
unsigned int seg2_delay;
|
||||
int synfake;
|
||||
unsigned int synfake_len;
|
||||
|
||||
const char *fake_sni_pkt;
|
||||
unsigned int fake_sni_pkt_sz;
|
||||
|
||||
char *fake_custom_pkt;
|
||||
unsigned int fake_custom_pkt_sz;
|
||||
|
||||
unsigned int fk_winsize;
|
||||
int fakeseq_offset;
|
||||
|
||||
#define SNI_DETECTION_PARSE 0
|
||||
#define SNI_DETECTION_BRUTE 1
|
||||
int sni_detection;
|
||||
|
||||
int udp_mode;
|
||||
unsigned int udp_fake_seq_len;
|
||||
unsigned int udp_fake_len;
|
||||
int udp_faking_strategy;
|
||||
|
||||
struct udp_dport_range *udp_dport_range;
|
||||
int udp_dport_range_len;
|
||||
int udp_filter_quic;
|
||||
};
|
||||
|
||||
#define MAX_CONFIGLIST_LEN 64
|
||||
|
||||
struct config_t {
|
||||
unsigned int queue_start_num;
|
||||
int rawsocket;
|
||||
int threads;
|
||||
int use_gso;
|
||||
int fragmentation_strategy;
|
||||
unsigned char fake_sni_ttl;
|
||||
int fake_sni_strategy;
|
||||
int use_ipv6;
|
||||
unsigned int mark;
|
||||
int daemonize;
|
||||
// Same as daemon() noclose
|
||||
int noclose;
|
||||
int syslog;
|
||||
|
||||
int connbytes_limit;
|
||||
|
||||
#define VERBOSE_INFO 0
|
||||
#define VERBOSE_DEBUG 1
|
||||
#define VERBOSE_TRACE 2
|
||||
int verbose;
|
||||
unsigned int seg2_delay;
|
||||
|
||||
struct section_config_t *first_section;
|
||||
struct section_config_t *last_section;
|
||||
};
|
||||
|
||||
extern struct config_t config;
|
||||
|
||||
#define ITER_CONFIG_SECTIONS(config, section) \
|
||||
for (struct section_config_t *section = (config)->last_section; section != NULL; section = section->prev)
|
||||
|
||||
#define CONFIG_SECTION_NUMBER(section) ((section)->id)
|
||||
|
||||
#define MAX_THREADS 16
|
||||
|
||||
#ifndef THREADS_NUM
|
||||
@@ -34,30 +140,39 @@ extern struct config_t config;
|
||||
#define FRAGMENTATION_STRATEGY FRAG_STRAT_TCP
|
||||
#endif
|
||||
|
||||
#define RAWSOCKET_MARK (1 << 15)
|
||||
#define DEFAULT_RAWSOCKET_MARK (1 << 15)
|
||||
|
||||
#ifdef USE_SEG2_DELAY
|
||||
#define SEG2_DELAY 100
|
||||
#endif
|
||||
|
||||
#define FAKE_SNI_TTL 8
|
||||
#define FAKE_TTL 8
|
||||
|
||||
// No fake SNI
|
||||
#define FKSN_STRAT_NONE 0
|
||||
// Will invalidate fake client hello by out-of-ack_seq out-of-seq request
|
||||
#define FKSN_STRAT_ACK_SEQ 1
|
||||
// Will assume that GGC server is located further than FAKE_SNI_TTL
|
||||
// Thus, Fake Client Hello will be eliminated automatically.
|
||||
#define FKSN_STRAT_TTL 2
|
||||
// Will invalidate fake packets by out-of-ack_seq out-of-seq request
|
||||
#define FAKE_STRAT_RAND_SEQ (1 << 0)
|
||||
// Will assume that GGC server is located further than FAKE_TTL
|
||||
// Thus, Fake packet will be eliminated automatically.
|
||||
#define FAKE_STRAT_TTL (1 << 1)
|
||||
#define FAKE_STRAT_PAST_SEQ (1 << 2)
|
||||
#define FAKE_STRAT_TCP_CHECK (1 << 3)
|
||||
#define FAKE_STRAT_TCP_MD5SUM (1 << 4)
|
||||
#define FAKE_STRAT_UDP_CHECK (1 << 5)
|
||||
|
||||
#define FAKE_STRAT_COUNT 6
|
||||
|
||||
#ifdef NO_FAKE_SNI
|
||||
#define FAKE_SNI_STRATEGY FKSN_STRAT_NONE
|
||||
/**
|
||||
* This macros iterates through all faking strategies and executes code under it.
|
||||
* destination strategy will be available under name of `strategy` variable.
|
||||
*/
|
||||
#define ITER_FAKE_STRAT(fake_bitmask, strategy) \
|
||||
for (int strategy = 1; strategy <= (1 << FAKE_STRAT_COUNT); strategy <<= 1) \
|
||||
if ((fake_bitmask) & strategy)
|
||||
|
||||
#ifndef FAKING_STRATEGY
|
||||
#define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ
|
||||
#endif
|
||||
|
||||
#ifndef FAKE_SNI_STRATEGY
|
||||
#define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ
|
||||
#endif
|
||||
#define MAX_FAKE_SIZE 1300
|
||||
|
||||
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
||||
#define DEBUG
|
||||
@@ -65,4 +180,85 @@ extern struct config_t config;
|
||||
|
||||
// The Maximum Transmission Unit size for rawsocket
|
||||
// Larger packets will be fragmented. Applicable for Chrome's kyber.
|
||||
#define AVAILABLE_MTU 1384
|
||||
#define AVAILABLE_MTU 1400
|
||||
|
||||
#define DEFAULT_QUEUE_NUM 537
|
||||
|
||||
#define MAX_PACKET_SIZE 8192
|
||||
|
||||
#define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com"
|
||||
|
||||
static const char default_snistr[] = DEFAULT_SNISTR;
|
||||
|
||||
enum {
|
||||
UDP_MODE_DROP,
|
||||
UDP_MODE_FAKE,
|
||||
};
|
||||
|
||||
enum {
|
||||
UDP_FILTER_QUIC_DISABLED,
|
||||
UDP_FILTER_QUIC_ALL,
|
||||
};
|
||||
|
||||
#define default_section_config { \
|
||||
.sni_domains = NULL, \
|
||||
.exclude_sni_domains = NULL, \
|
||||
.all_domains = 0, \
|
||||
.tls_enabled = 1, \
|
||||
.frag_sni_reverse = 1, \
|
||||
.frag_sni_faked = 0, \
|
||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY, \
|
||||
.faking_strategy = FAKING_STRATEGY, \
|
||||
.faking_ttl = FAKE_TTL, \
|
||||
.fake_sni = 1, \
|
||||
.fake_sni_seq_len = 1, \
|
||||
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, \
|
||||
.fake_custom_pkt = NULL, \
|
||||
.fake_custom_pkt_sz = 0, \
|
||||
.frag_middle_sni = 1, \
|
||||
.frag_sni_pos = 1, \
|
||||
.fakeseq_offset = 10000, \
|
||||
.synfake = 0, \
|
||||
.synfake_len = 0, \
|
||||
\
|
||||
.seg2_delay = 0, \
|
||||
\
|
||||
.sni_detection = SNI_DETECTION_PARSE, \
|
||||
\
|
||||
.udp_mode = UDP_MODE_FAKE, \
|
||||
.udp_fake_seq_len = 6, \
|
||||
.udp_fake_len = 64, \
|
||||
.udp_faking_strategy = FAKE_STRAT_UDP_CHECK, \
|
||||
.udp_dport_range = NULL, \
|
||||
.udp_dport_range_len = 0, \
|
||||
.udp_filter_quic = UDP_FILTER_QUIC_DISABLED, \
|
||||
\
|
||||
.prev = NULL, \
|
||||
.next = NULL, \
|
||||
.id = 0, \
|
||||
}
|
||||
|
||||
#define default_config_set { \
|
||||
.threads = THREADS_NUM, \
|
||||
.queue_start_num = DEFAULT_QUEUE_NUM, \
|
||||
.mark = DEFAULT_RAWSOCKET_MARK, \
|
||||
.use_ipv6 = 1, \
|
||||
.connbytes_limit = 8, \
|
||||
\
|
||||
.verbose = VERBOSE_DEBUG, \
|
||||
.use_gso = 1, \
|
||||
\
|
||||
.first_section = NULL, \
|
||||
.last_section = NULL, \
|
||||
\
|
||||
.daemonize = 0, \
|
||||
.noclose = 0, \
|
||||
.syslog = 0, \
|
||||
}
|
||||
|
||||
#define CONFIG_SET(config) \
|
||||
struct config_t config = default_config_set; \
|
||||
config->last_section = &(config.default_config) \
|
||||
|
||||
|
||||
#endif /* YTB_CONFIG_H */
|
||||
|
||||
204
getopt.c
Normal file
204
getopt.c
Normal file
@@ -0,0 +1,204 @@
|
||||
#include "types.h"
|
||||
#include "logging.h"
|
||||
#include "getopt.h"
|
||||
|
||||
char *optarg;
|
||||
int optind=1, opterr=1, optopt, __optpos, optreset=0;
|
||||
|
||||
#define optpos __optpos
|
||||
|
||||
static void __getopt_msg(const char *b, const char *c, size_t l)
|
||||
{
|
||||
lgerr("%s %.*s\n", b, (int)l, c);
|
||||
}
|
||||
|
||||
int getopt(int argc, char * const argv[], const char *optstring)
|
||||
{
|
||||
int i, c, d;
|
||||
int k, l;
|
||||
char *optchar;
|
||||
|
||||
if (!optind || optreset) {
|
||||
optreset = 0;
|
||||
__optpos = 0;
|
||||
optind = 1;
|
||||
}
|
||||
|
||||
if (optind >= argc || !argv[optind])
|
||||
return -1;
|
||||
|
||||
if (argv[optind][0] != '-') {
|
||||
if (optstring[0] == '-') {
|
||||
optarg = argv[optind++];
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!argv[optind][1])
|
||||
return -1;
|
||||
|
||||
if (argv[optind][1] == '-' && !argv[optind][2])
|
||||
return optind++, -1;
|
||||
|
||||
if (!optpos) optpos++;
|
||||
c = argv[optind][optpos], k = 1;
|
||||
optchar = argv[optind]+optpos;
|
||||
optopt = c;
|
||||
optpos += k;
|
||||
|
||||
if (!argv[optind][optpos]) {
|
||||
optind++;
|
||||
optpos = 0;
|
||||
}
|
||||
|
||||
if (optstring[0] == '-' || optstring[0] == '+')
|
||||
optstring++;
|
||||
|
||||
i = 0;
|
||||
d = 0;
|
||||
do {
|
||||
d = optstring[i], l = 1;
|
||||
if (l>0) i+=l; else i++;
|
||||
} while (l && d != c);
|
||||
|
||||
if (d != c) {
|
||||
if (optstring[0] != ':' && opterr)
|
||||
__getopt_msg("Unrecognized option: ", optchar, k);
|
||||
return '?';
|
||||
}
|
||||
if (optstring[i] == ':') {
|
||||
if (optstring[i+1] == ':') optarg = 0;
|
||||
else if (optind >= argc) {
|
||||
if (optstring[0] == ':') return ':';
|
||||
if (opterr) __getopt_msg("Option requires an argument: ",
|
||||
optchar, k);
|
||||
return '?';
|
||||
}
|
||||
if (optstring[i+1] != ':' || optpos) {
|
||||
optarg = argv[optind++] + optpos;
|
||||
optpos = 0;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void permute(char *const *argv, int dest, int src)
|
||||
{
|
||||
char **av = (char **)argv;
|
||||
char *tmp = av[src];
|
||||
int i;
|
||||
for (i=src; i>dest; i--)
|
||||
av[i] = av[i-1];
|
||||
av[dest] = tmp;
|
||||
}
|
||||
|
||||
static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
||||
{
|
||||
optarg = 0;
|
||||
if (longopts && argv[optind][0] == '-' &&
|
||||
((longonly && argv[optind][1] && argv[optind][1] != '-') ||
|
||||
(argv[optind][1] == '-' && argv[optind][2])))
|
||||
{
|
||||
int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
|
||||
int i, cnt, match = -1;
|
||||
char *opt;
|
||||
for (cnt=i=0; longopts[i].name; i++) {
|
||||
const char *name = longopts[i].name;
|
||||
opt = argv[optind]+1;
|
||||
if (*opt == '-') opt++;
|
||||
for (; *name && *name == *opt; name++, opt++);
|
||||
if (*opt && *opt != '=') continue;
|
||||
match = i;
|
||||
if (!*name) {
|
||||
cnt = 1;
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
if (cnt==1) {
|
||||
i = match;
|
||||
optind++;
|
||||
optopt = longopts[i].val;
|
||||
if (*opt == '=') {
|
||||
if (!longopts[i].has_arg) {
|
||||
if (colon || !opterr)
|
||||
return '?';
|
||||
__getopt_msg(
|
||||
"Option does not take an argument: ",
|
||||
longopts[i].name,
|
||||
strlen(longopts[i].name));
|
||||
return '?';
|
||||
}
|
||||
optarg = opt+1;
|
||||
} else if (longopts[i].has_arg == required_argument) {
|
||||
if (!(optarg = argv[optind])) {
|
||||
if (colon) return ':';
|
||||
if (!opterr) return '?';
|
||||
__getopt_msg(
|
||||
"Option requires an argument: ",
|
||||
longopts[i].name,
|
||||
strlen(longopts[i].name));
|
||||
return '?';
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
if (idx) *idx = i;
|
||||
if (longopts[i].flag) {
|
||||
*longopts[i].flag = longopts[i].val;
|
||||
return 0;
|
||||
}
|
||||
return longopts[i].val;
|
||||
}
|
||||
if (argv[optind][1] == '-') {
|
||||
if (!colon && opterr)
|
||||
__getopt_msg(cnt ?
|
||||
"Option is ambiguous: " :
|
||||
"Unrecognized option: ",
|
||||
argv[optind]+2,
|
||||
strlen(argv[optind]+2));
|
||||
optind++;
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
return getopt(argc, argv, optstring);
|
||||
}
|
||||
|
||||
static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
||||
{
|
||||
int ret, skipped, resumed;
|
||||
if (!optind || optreset) {
|
||||
optreset = 0;
|
||||
__optpos = 0;
|
||||
optind = 1;
|
||||
}
|
||||
if (optind >= argc || !argv[optind]) return -1;
|
||||
skipped = optind;
|
||||
if (optstring[0] != '+' && optstring[0] != '-') {
|
||||
int i;
|
||||
for (i=optind; ; i++) {
|
||||
if (i >= argc || !argv[i]) return -1;
|
||||
if (argv[i][0] == '-' && argv[i][1]) break;
|
||||
}
|
||||
optind = i;
|
||||
}
|
||||
resumed = optind;
|
||||
ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
|
||||
if (resumed > skipped) {
|
||||
int i, cnt = optind-resumed;
|
||||
for (i=0; i<cnt; i++)
|
||||
permute(argv, skipped, optind-1);
|
||||
optind = skipped + cnt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
|
||||
{
|
||||
return __getopt_long(argc, argv, optstring, longopts, idx, 0);
|
||||
}
|
||||
|
||||
int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
|
||||
{
|
||||
return __getopt_long(argc, argv, optstring, longopts, idx, 1);
|
||||
}
|
||||
45
getopt.h
Normal file
45
getopt.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2005-2014 Rich Felker, et al.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _GETOPT_H
|
||||
#define _GETOPT_H
|
||||
|
||||
int getopt(int, char * const [], const char *);
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt, optreset;
|
||||
|
||||
struct option {
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
int getopt_long(int, char *const *, const char *, const struct option *, int *);
|
||||
int getopt_long_only(int, char *const *, const char *, const struct option *, int *);
|
||||
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef IPT_YTUNBLOCK_H
|
||||
#define IPT_YTUNBLOCK_H
|
||||
|
||||
struct xt_ytunblock_tginfo {};
|
||||
|
||||
#endif /* IPT_YTUNBLOCK_H */
|
||||
341
iptk_YTUNBLOCK.c
341
iptk_YTUNBLOCK.c
@@ -1,341 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
// Kernel module for youtubeUnblock.
|
||||
// Make with make kmake && sudo iptables -t mangle -D OUTPUT 1 && sudo make kreload && sudo iptables -t mangle -I OUTPUT -p tcp -j YTUNBLOCK
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include "ipt_YTUNBLOCK.h"
|
||||
|
||||
#include "mangle.h"
|
||||
#include "config.h"
|
||||
#include "raw_replacements.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1");
|
||||
MODULE_AUTHOR("Vadim Vetrov <vetrovvd@gmail.com>");
|
||||
MODULE_DESCRIPTION("Linux kernel module for youtube unblock");
|
||||
|
||||
static int rsfd;
|
||||
static struct socket *rawsocket;
|
||||
DEFINE_MUTEX(rslock);
|
||||
|
||||
static int open_raw_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_alert("Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
sockptr_t optval = {
|
||||
.kernel = NULL,
|
||||
.is_kernel = 1
|
||||
};
|
||||
|
||||
int mark = RAWSOCKET_MARK;
|
||||
optval.kernel = &mark;
|
||||
ret = sock_setsockopt(rawsocket, SOL_SOCKET, SO_MARK, optval, sizeof(mark));
|
||||
if (ret < 0)
|
||||
{
|
||||
pr_alert("setsockopt(SO_MARK, %d) failed\n", mark);
|
||||
goto sr_err;
|
||||
}
|
||||
int one = 1;
|
||||
optval.kernel = &one;
|
||||
|
||||
return 0;
|
||||
sr_err:
|
||||
sock_release(rawsocket);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void close_raw_socket(void) {
|
||||
sock_release(rawsocket);
|
||||
}
|
||||
|
||||
#define AVAILABLE_MTU 1384
|
||||
|
||||
static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
|
||||
if (pktlen > AVAILABLE_MTU) {
|
||||
pr_warn("The packet is too big and may cause issues!");
|
||||
|
||||
__u32 buff1_size = pktlen;
|
||||
__u32 buff2_size = pktlen;
|
||||
__u8 *buff1 = kmalloc(pktlen, GFP_ATOMIC);
|
||||
if (buff1 == NULL) return -1;
|
||||
__u8 *buff2 = kmalloc(pktlen, GFP_ATOMIC);
|
||||
if (buff2 == NULL) {
|
||||
kfree(buff1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret;
|
||||
|
||||
#if defined(USE_TCP_SEGMENTATION) || defined(RAWSOCK_TCP_FSTRAT)
|
||||
if ((ret = tcp4_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0)
|
||||
return ret;
|
||||
#elif defined(USE_IP_FRAGMENTATION) || defined(RAWSOCK_IP_FSTRAT)
|
||||
if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0)
|
||||
return ret;
|
||||
#else
|
||||
pr_warn("send_raw_socket: Packet is too big but fragmentation is disabled! "
|
||||
"Pass -DRAWSOCK_TCP_FSTRAT or -DRAWSOCK_IP_FSTRAT as CFLAGS "
|
||||
"To enable it only for raw socket\n");
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
int sent = 0;
|
||||
ret = send_raw_socket(buff1, buff1_size);
|
||||
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
kfree(buff1);
|
||||
kfree(buff2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(buff1);
|
||||
|
||||
ret = send_raw_socket(buff2, buff2_size);
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
kfree(buff2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(buff2);
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
struct iphdr *iph;
|
||||
|
||||
int ret;
|
||||
if ((ret = ip4_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in daddr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
.sin_addr = {
|
||||
.s_addr = iph->daddr
|
||||
}
|
||||
};
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, 1);
|
||||
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
mutex_lock(&rslock);
|
||||
ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen);
|
||||
mutex_unlock(&rslock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static unsigned int ykb_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
{
|
||||
if ((skb->mark & RAWSOCKET_MARK) == RAWSOCKET_MARK)
|
||||
return XT_CONTINUE;
|
||||
|
||||
if (skb->head == NULL) return XT_CONTINUE;
|
||||
|
||||
// TODO: Mallocs are bad!
|
||||
uint32_t buflen = skb->len;
|
||||
__u8 *buf = kmalloc(skb->len, GFP_ATOMIC);
|
||||
if (buf == NULL) {
|
||||
pr_err("Cannot alloc enough buffer space");
|
||||
goto accept;
|
||||
}
|
||||
if (skb_copy_bits(skb, 0, buf, skb->len) < 0) {
|
||||
pr_err("Unable copy bits\n");
|
||||
goto ac_fkb;
|
||||
}
|
||||
struct iphdr *iph;
|
||||
uint32_t iph_len;
|
||||
struct tcphdr *tcph;
|
||||
uint32_t tcph_len;
|
||||
__u8 *payload;
|
||||
uint32_t plen;
|
||||
|
||||
int ret = tcp4_payload_split(buf, buflen, &iph, &iph_len,
|
||||
&tcph, &tcph_len, &payload, &plen);
|
||||
|
||||
if (ret < 0)
|
||||
goto ac_fkb;
|
||||
|
||||
struct verdict vrd = analyze_tls_data(payload, plen);
|
||||
|
||||
if (vrd.gvideo_hello) {
|
||||
int ret;
|
||||
pr_info("Googlevideo detected\n");
|
||||
|
||||
ip4_set_checksum(iph);
|
||||
tcp4_set_checksum(tcph, iph);
|
||||
|
||||
uint32_t f1len = skb->len;
|
||||
uint32_t f2len = skb->len;
|
||||
__u8 *frag1 = kmalloc(f1len, GFP_ATOMIC);
|
||||
if (!frag1) {
|
||||
pr_err("Cannot alloc enough gv frag1 buffer space");
|
||||
goto ac_fkb;
|
||||
}
|
||||
__u8 *frag2 = kmalloc(f2len, GFP_ATOMIC);
|
||||
if (!frag2) {
|
||||
pr_err("Cannot alloc enough gv frag1 buffer space");
|
||||
kfree(frag1);
|
||||
goto ac_fkb;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FAKE_SNI
|
||||
uint32_t fksn_len = FAKE_SNI_MAXLEN;
|
||||
__u8 *fksn_buf = kmalloc(fksn_len, GFP_ATOMIC);
|
||||
if (!fksn_buf) {
|
||||
pr_err("Cannot alloc enough gksn buffer space");
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
ret = gen_fake_sni(iph, tcph, fksn_buf, &fksn_len);
|
||||
if (ret < 0) {
|
||||
pr_err("Cannot alloc enough gksn buffer space");
|
||||
goto fksn_fb;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_TCP_SEGMENTATION)
|
||||
size_t ipd_offset = vrd.sni_offset;
|
||||
size_t mid_offset = ipd_offset + vrd.sni_len / 2;
|
||||
|
||||
|
||||
if ((ret = tcp4_frag(buf, skb->len,
|
||||
mid_offset, frag1, &f1len, frag2, &f2len)) < 0) {
|
||||
pr_err("tcp4_frag: %d", ret);
|
||||
goto fksn_fb;
|
||||
}
|
||||
#elif defined(USE_IP_FRAGMENTATION)
|
||||
size_t ipd_offset = tcph_len + vrd.sni_offset;
|
||||
size_t mid_offset = ipd_offset + vrd.sni_len / 2;
|
||||
mid_offset += 8 - mid_offset % 8;
|
||||
|
||||
if ((ret = ip4_frag(buf, skb->len,
|
||||
mid_offset, frag1, &f1len, frag2, &f2len)) < 0) {
|
||||
pr_err("ip4_frag: %d", ret);
|
||||
goto fksn_fb;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FAKE_SNI
|
||||
ret = send_raw_socket(fksn_buf, fksn_len);
|
||||
if (ret < 0) {
|
||||
pr_err("fksn_send: %d", ret);
|
||||
goto fksn_fb;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_NO_FRAGMENTATION)
|
||||
#ifdef SEG2_DELAY
|
||||
#error "SEG2_DELAY is incompatible with NO FRAGMENTATION"
|
||||
#endif
|
||||
ret = send_raw_socket(buf, buflen);
|
||||
if (ret < 0) {
|
||||
pr_err("nofrag_send: %d", ret);
|
||||
}
|
||||
goto fksn_fb;
|
||||
#endif
|
||||
|
||||
ret = send_raw_socket(frag2, f2len);
|
||||
if (ret < 0) {
|
||||
pr_err("raw frag2 send: %d", ret);
|
||||
goto fksn_fb;
|
||||
}
|
||||
|
||||
#ifdef SEG2_DELAY
|
||||
#error "Seg2 delay is unsupported yet for kmod"
|
||||
#else
|
||||
ret = send_raw_socket(frag1, f1len);
|
||||
if (ret < 0) {
|
||||
pr_err("raw frag1 send: %d", ret);
|
||||
goto fksn_fb;
|
||||
}
|
||||
#endif
|
||||
|
||||
fksn_fb:
|
||||
#ifdef FAKE_SNI
|
||||
kfree(fksn_buf);
|
||||
#endif
|
||||
fallback:
|
||||
#ifndef SEG2_DELAY
|
||||
kfree(frag1);
|
||||
#endif
|
||||
kfree(frag2);
|
||||
kfree(buf);
|
||||
kfree_skb(skb);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
ac_fkb:
|
||||
kfree(buf);
|
||||
accept:
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static int ykb_chk(const struct xt_tgchk_param *par) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct xt_target ykb_tg_reg __read_mostly = {
|
||||
.name = "YTUNBLOCK",
|
||||
.target = ykb_tg,
|
||||
.table = "mangle",
|
||||
.hooks = (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD),
|
||||
.targetsize = sizeof(struct xt_ytunblock_tginfo),
|
||||
.proto = IPPROTO_TCP,
|
||||
.family = NFPROTO_IPV4,
|
||||
.checkentry = ykb_chk,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init ykb_init(void) {
|
||||
int ret = 0;
|
||||
|
||||
ret = open_raw_socket();
|
||||
if (ret < 0) goto err;
|
||||
|
||||
ret = xt_register_target(&ykb_tg_reg);
|
||||
if (ret < 0) goto close_rawsocket;
|
||||
|
||||
pr_info("youtubeUnblock kernel module started.\n");
|
||||
return 0;
|
||||
close_rawsocket:
|
||||
close_raw_socket();
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ykb_destroy(void) {
|
||||
xt_unregister_target(&ykb_tg_reg);
|
||||
close_raw_socket();
|
||||
pr_info("youtubeUnblock kernel module destroyed.\n");
|
||||
}
|
||||
|
||||
module_init(ykb_init);
|
||||
module_exit(ykb_destroy);
|
||||
565
kargs.c
Normal file
565
kargs.c
Normal file
@@ -0,0 +1,565 @@
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include <linux/moduleparam.h>
|
||||
#include "types.h"
|
||||
#include "args.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define MAX_ARGC 1024
|
||||
static char *argv[MAX_ARGC];
|
||||
|
||||
static int params_set(const char *cval, const struct kernel_param *kp) {
|
||||
int ret = 0;
|
||||
|
||||
int cv_len = strlen(cval);
|
||||
if (cv_len >= 1 && cval[cv_len - 1] == '\n') {
|
||||
cv_len--;
|
||||
}
|
||||
|
||||
const char *ytb_prefix = "youtubeUnblock ";
|
||||
int ytbp_len = strlen(ytb_prefix);
|
||||
int len = cv_len + ytbp_len;
|
||||
|
||||
char *val = kmalloc(len + 1, GFP_KERNEL); // 1 for null-terminator
|
||||
strncpy(val, ytb_prefix, ytbp_len);
|
||||
strncpy(val + ytbp_len, cval, cv_len);
|
||||
val[len] = '\0';
|
||||
|
||||
int argc = 0;
|
||||
argv[argc++] = val;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (val[i] == ' ') {
|
||||
val[i] = '\0';
|
||||
|
||||
// safe because of null-terminator
|
||||
if (val[i + 1] != ' ' && val[i + 1] != '\0') {
|
||||
argv[argc++] = val + i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = yparse_args(argc, argv);
|
||||
kfree(val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int params_get(char *buffer, const struct kernel_param *kp) {
|
||||
size_t len = print_config(buffer, 4000);
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops params_ops = {
|
||||
.set = params_set,
|
||||
.get = params_get,
|
||||
};
|
||||
|
||||
module_param_cb(parameters, ¶ms_ops, NULL, 0664);
|
||||
|
||||
|
||||
//
|
||||
// static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||
//
|
||||
// struct config_t config = {
|
||||
// .threads = THREADS_NUM,
|
||||
// .queue_start_num = DEFAULT_QUEUE_NUM,
|
||||
// .mark = DEFAULT_RAWSOCKET_MARK,
|
||||
// .use_ipv6 = 1,
|
||||
//
|
||||
// .verbose = VERBOSE_DEBUG,
|
||||
// .use_gso = 1,
|
||||
//
|
||||
// .default_config = default_section_config,
|
||||
// .custom_configs_len = 0
|
||||
// };
|
||||
//
|
||||
// #define def_section (&config.default_config)
|
||||
//
|
||||
// static int unumeric_set(const char *val, const struct kernel_param *kp) {
|
||||
// int n = 0, ret;
|
||||
// ret = kstrtoint(val, 10, &n);
|
||||
// if (ret != 0 || n < 0)
|
||||
// return -EINVAL;
|
||||
//
|
||||
//
|
||||
// return param_set_int(val, kp);
|
||||
// }
|
||||
//
|
||||
// static int boolean_set(const char *val, const struct kernel_param *kp) {
|
||||
// int n = 0, ret;
|
||||
// ret = kstrtoint(val, 10, &n);
|
||||
// if (ret != 0 || (n != 0 && n != 1))
|
||||
// return -EINVAL;
|
||||
//
|
||||
// return param_set_int(val, kp);
|
||||
// }
|
||||
//
|
||||
// static int inverse_boolean_set(const char *val, const struct kernel_param *kp) {
|
||||
// int n = 0, ret;
|
||||
// ret = kstrtoint(val, 10, &n);
|
||||
// if (ret != 0 || (n != 0 && n != 1))
|
||||
// return -EINVAL;
|
||||
//
|
||||
// n = !n;
|
||||
// if (kp->arg == NULL)
|
||||
// return -EINVAL;
|
||||
//
|
||||
// *(int *)kp->arg = n;
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) {
|
||||
// if (*(int *)kp->arg == 0) {
|
||||
// buffer[0] = '1';
|
||||
// } else {
|
||||
// buffer[0] = '0';
|
||||
// }
|
||||
// buffer[1] = '\0';
|
||||
// return strlen(buffer);
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops unumeric_parameter_ops = {
|
||||
// .set = unumeric_set,
|
||||
// .get = param_get_int
|
||||
// };
|
||||
//
|
||||
// static const struct kernel_param_ops boolean_parameter_ops = {
|
||||
// .set = boolean_set,
|
||||
// .get = param_get_int
|
||||
// };
|
||||
//
|
||||
// static const struct kernel_param_ops inverse_boolean_ops = {
|
||||
// .set = inverse_boolean_set,
|
||||
// .get = inverse_boolean_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664);
|
||||
// module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664);
|
||||
// module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664);
|
||||
// module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664);
|
||||
// module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664);
|
||||
// module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664);
|
||||
// module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664);
|
||||
// module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664);
|
||||
// module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664);
|
||||
// module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664);
|
||||
// module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664);
|
||||
// module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664);
|
||||
// // module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664);
|
||||
//
|
||||
// static int sni_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
// int ret;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// ret = param_set_charp(val, kp);
|
||||
//
|
||||
// if (ret < 0) {
|
||||
// def_section->domains_strlen = 0;
|
||||
// } else {
|
||||
// def_section->domains_strlen = len;
|
||||
// if (len == 3 && !strncmp(val, "all", len)) {
|
||||
// def_section->all_domains = 1;
|
||||
// } else {
|
||||
// def_section->all_domains = 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops sni_domains_ops = {
|
||||
// .set = sni_domains_set,
|
||||
// .get = param_get_charp,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664);
|
||||
//
|
||||
// static int exclude_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
// int ret;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// ret = param_set_charp(val, kp);
|
||||
//
|
||||
// if (ret < 0) {
|
||||
// def_section->exclude_domains_strlen = 0;
|
||||
// } else {
|
||||
// def_section->exclude_domains_strlen = len;
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops exclude_domains_ops = {
|
||||
// .set = exclude_domains_set,
|
||||
// .get = param_get_charp,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664);
|
||||
//
|
||||
// module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664);
|
||||
//
|
||||
// static int quic_drop_set(const char *val, const struct kernel_param *kp) {
|
||||
// int n = 0, ret;
|
||||
// ret = kstrtoint(val, 10, &n);
|
||||
// if (ret != 0 || (n != 0 && n != 1))
|
||||
// return -EINVAL;
|
||||
//
|
||||
// if (n) {
|
||||
// def_section->udp_mode = UDP_MODE_DROP;
|
||||
// def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL;
|
||||
// } else {
|
||||
// def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int quic_drop_get(char *buffer, const struct kernel_param *kp) {
|
||||
// if (def_section->udp_mode == UDP_MODE_DROP &&
|
||||
// def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) {
|
||||
// return sprintf(buffer, "%d\n", 1);
|
||||
// } else {
|
||||
// return sprintf(buffer, "%d\n", 0);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops quic_drop_ops = {
|
||||
// .set = quic_drop_set,
|
||||
// .get = quic_drop_get
|
||||
// };
|
||||
//
|
||||
// module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664);
|
||||
//
|
||||
// static int verbosity_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// if (strncmp(val, "trace", len) == 0) {
|
||||
// *(int *)kp->arg = VERBOSE_TRACE;
|
||||
// } else if (strncmp(val, "debug", len) == 0) {
|
||||
// *(int *)kp->arg = VERBOSE_DEBUG;
|
||||
// } else if (strncmp(val, "silent", len) == 0) {
|
||||
// *(int *)kp->arg = VERBOSE_INFO;
|
||||
// } else {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// static int verbosity_get(char *buffer, const struct kernel_param *kp) {
|
||||
// switch (*(int *)kp->arg) {
|
||||
// case VERBOSE_TRACE:
|
||||
// strcpy(buffer, "trace\n");
|
||||
// break;
|
||||
// case VERBOSE_DEBUG:
|
||||
// strcpy(buffer, "debug\n");
|
||||
// break;
|
||||
// case VERBOSE_INFO:
|
||||
// strcpy(buffer, "silent\n");
|
||||
// break;
|
||||
// default:
|
||||
// strcpy(buffer, "unknown\n");
|
||||
// }
|
||||
//
|
||||
// return strlen(buffer);
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops verbosity_ops = {
|
||||
// .set = verbosity_set,
|
||||
// .get = verbosity_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664);
|
||||
//
|
||||
// static int frag_strat_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// if (strncmp(val, "tcp", len) == 0) {
|
||||
// *(int *)kp->arg = FRAG_STRAT_TCP;
|
||||
// } else if (strncmp(val, "ip", len) == 0) {
|
||||
// *(int *)kp->arg = FRAG_STRAT_IP;
|
||||
// } else if (strncmp(val, "none", len) == 0) {
|
||||
// *(int *)kp->arg = FRAG_STRAT_NONE;
|
||||
// } else {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int frag_strat_get(char *buffer, const struct kernel_param *kp) {
|
||||
// switch (*(int *)kp->arg) {
|
||||
// case FRAG_STRAT_TCP:
|
||||
// strcpy(buffer, "tcp\n");
|
||||
// break;
|
||||
// case FRAG_STRAT_IP:
|
||||
// strcpy(buffer, "ip\n");
|
||||
// break;
|
||||
// case FRAG_STRAT_NONE:
|
||||
// strcpy(buffer, "none\n");
|
||||
// break;
|
||||
// default:
|
||||
// strcpy(buffer, "unknown\n");
|
||||
// }
|
||||
//
|
||||
// return strlen(buffer);
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops frag_strat_ops = {
|
||||
// .set = frag_strat_set,
|
||||
// .get = frag_strat_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664);
|
||||
//
|
||||
// static int fake_strat_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// if (strncmp(val, "randseq", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_STRAT_RAND_SEQ;
|
||||
// } else if (strncmp(val, "ttl", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_STRAT_TTL;
|
||||
// } else if (strncmp(val, "tcp_check", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_STRAT_TCP_CHECK;
|
||||
// } else if (strncmp(val, "pastseq", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_STRAT_PAST_SEQ;
|
||||
// } else if (strncmp(val, "md5sum", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM;
|
||||
// } else {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int fake_strat_get(char *buffer, const struct kernel_param *kp) {
|
||||
// switch (*(int *)kp->arg) {
|
||||
// case FAKE_STRAT_RAND_SEQ:
|
||||
// strcpy(buffer, "randseq\n");
|
||||
// break;
|
||||
// case FAKE_STRAT_TTL:
|
||||
// strcpy(buffer, "ttl\n");
|
||||
// break;
|
||||
// case FAKE_STRAT_TCP_CHECK:
|
||||
// strcpy(buffer, "tcp_check\n");
|
||||
// break;
|
||||
// case FAKE_STRAT_PAST_SEQ:
|
||||
// strcpy(buffer, "pastseq\n");
|
||||
// break;
|
||||
// case FAKE_STRAT_TCP_MD5SUM:
|
||||
// strcpy(buffer, "md5sum\n");
|
||||
// break;
|
||||
// default:
|
||||
// strcpy(buffer, "unknown\n");
|
||||
// }
|
||||
//
|
||||
// return strlen(buffer);
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops fake_strat_ops = {
|
||||
// .set = fake_strat_set,
|
||||
// .get = fake_strat_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664);
|
||||
//
|
||||
// static int sni_detection_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// if (strncmp(val, "parse", len) == 0) {
|
||||
// *(int *)kp->arg = SNI_DETECTION_PARSE;
|
||||
// } else if (strncmp(val, "brute", len) == 0) {
|
||||
// *(int *)kp->arg = SNI_DETECTION_BRUTE;
|
||||
// } else {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int sni_detection_get(char *buffer, const struct kernel_param *kp) {
|
||||
// switch (*(int *)kp->arg) {
|
||||
// case SNI_DETECTION_PARSE:
|
||||
// strcpy(buffer, "parse\n");
|
||||
// break;
|
||||
// case SNI_DETECTION_BRUTE:
|
||||
// strcpy(buffer, "brute\n");
|
||||
// break;
|
||||
// default:
|
||||
// strcpy(buffer, "unknown\n");
|
||||
// }
|
||||
//
|
||||
// return strlen(buffer);
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops sni_detection_ops = {
|
||||
// .set = sni_detection_set,
|
||||
// .get = sni_detection_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664);
|
||||
//
|
||||
// static int fake_type_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// if (strncmp(val, "default", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_PAYLOAD_DEFAULT;
|
||||
// } else if (strncmp(val, "custom", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_PAYLOAD_CUSTOM;
|
||||
// } else if (strncmp(val, "random", len) == 0) {
|
||||
// *(int *)kp->arg = FAKE_PAYLOAD_RANDOM;
|
||||
// } else {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int fake_type_get(char *buffer, const struct kernel_param *kp) {
|
||||
// switch (*(int *)kp->arg) {
|
||||
// case FAKE_PAYLOAD_DEFAULT:
|
||||
// strcpy(buffer, "default\n");
|
||||
// break;
|
||||
// case FAKE_PAYLOAD_RANDOM:
|
||||
// strcpy(buffer, "random\n");
|
||||
// break;
|
||||
// case FAKE_PAYLOAD_CUSTOM:
|
||||
// strcpy(buffer, "custom\n");
|
||||
// break;
|
||||
// default:
|
||||
// strcpy(buffer, "unknown\n");
|
||||
// }
|
||||
//
|
||||
// return strlen(buffer);
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops fake_type_ops = {
|
||||
// .set = fake_type_set,
|
||||
// .get = fake_type_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664);
|
||||
//
|
||||
// static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) {
|
||||
// size_t len;
|
||||
//
|
||||
// len = strnlen(val, STR_MAXLEN + 1);
|
||||
// if (len == STR_MAXLEN + 1) {
|
||||
// pr_err("%s: string parameter too long\n", kp->name);
|
||||
// return -ENOSPC;
|
||||
// }
|
||||
//
|
||||
// if (len >= 1 && val[len - 1] == '\n') {
|
||||
// len--;
|
||||
// }
|
||||
//
|
||||
// uint8_t *const custom_buf = (uint8_t *)custom_fake_buf;
|
||||
// const char *custom_hex_fake = val;
|
||||
// size_t custom_hlen = len;
|
||||
//
|
||||
// if ((custom_hlen & 1) == 1) {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// size_t custom_len = custom_hlen >> 1;
|
||||
// if (custom_len > MAX_FAKE_SIZE) {
|
||||
// return -EINVAL;
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < custom_len; i++) {
|
||||
// sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
// }
|
||||
//
|
||||
// def_section->fake_custom_pkt_sz = custom_len;
|
||||
// def_section->fake_custom_pkt = (char *)custom_buf;
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) {
|
||||
// int cflen = def_section->fake_custom_pkt_sz;
|
||||
// const uint8_t *cbf_data = def_section->fake_custom_pkt;
|
||||
// int bflen = def_section->fake_custom_pkt_sz << 1;
|
||||
//
|
||||
// for (int i = 0; i < cflen; i++) {
|
||||
// sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i));
|
||||
// }
|
||||
//
|
||||
// return bflen;
|
||||
// }
|
||||
//
|
||||
// static const struct kernel_param_ops fake_custom_pl_ops = {
|
||||
// .set = fake_custom_pl_set,
|
||||
// .get = fake_custom_pl_get,
|
||||
// };
|
||||
//
|
||||
// module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664);
|
||||
27
kmake.mk
27
kmake.mk
@@ -7,36 +7,23 @@ LD := ld
|
||||
CFLAGS :=
|
||||
LDFLAGS :=
|
||||
|
||||
IPT_CFLAGS := -Wall -Wpedantic -O2
|
||||
KERNEL_BUILDER_MAKEDIR:=/lib/modules/$(shell uname -r)/build
|
||||
|
||||
.PHONY: kmake kload kunload kreload kclean kmclean xclean
|
||||
kmake: kmod xmod
|
||||
kmake: kmod
|
||||
|
||||
kmod:
|
||||
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
||||
xmod: libipt_YTUNBLOCK.so
|
||||
|
||||
libipt_YTUNBLOCK.so: libipt_YTUNBLOCK.o
|
||||
$(CCLD) -shared -fPIC ${IPT_CFLAGS} -o $@ $^;
|
||||
|
||||
libipt_YTUNBLOCK.o: libipt_YTUNBLOCK.c
|
||||
$(CC) ${IPT_CFLAGS} -D_INIT=lib$*_init -fPIC -c -o $@ $<;
|
||||
$(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) EXTRA_CFLAGS='-DPKG_VERSION=\"$(PKG_FULLVERSION)\"' modules
|
||||
|
||||
kload:
|
||||
insmod ipt_YTUNBLOCK.ko
|
||||
cp ./libipt_YTUNBLOCK.so /usr/lib/xtables/
|
||||
insmod kyoutubeUnblock.ko
|
||||
|
||||
kunload:
|
||||
-rmmod ipt_YTUNBLOCK
|
||||
-/bin/rm /usr/lib/xtables/libipt_YTUNBLOCK.so
|
||||
-rmmod kyoutubeUnblock
|
||||
|
||||
kreload: kunload kload
|
||||
|
||||
kclean: xtclean kmclean
|
||||
kclean: kmclean
|
||||
|
||||
kmclean:
|
||||
-$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
|
||||
xtclean:
|
||||
-/bin/rm -f libipt_YTUNBLOCK.so libipt_YTUNBLOCK.o
|
||||
-$(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) clean
|
||||
|
||||
464
kytunblock.c
Normal file
464
kytunblock.c
Normal file
@@ -0,0 +1,464 @@
|
||||
#ifndef KERNEL_SPACE
|
||||
#error "You are trying to compile the kernel module not in the kernel space"
|
||||
#endif
|
||||
|
||||
// Kernel module for youtubeUnblock.
|
||||
// Build with make kmake
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_acct.h>
|
||||
|
||||
#include "mangle.h"
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
#include "args.h"
|
||||
|
||||
#if defined(PKG_VERSION)
|
||||
MODULE_VERSION(PKG_VERSION);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vadim Vetrov <vetrovvd@gmail.com>");
|
||||
MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock");
|
||||
|
||||
static struct socket *rawsocket;
|
||||
|
||||
static struct socket *raw6socket;
|
||||
|
||||
static int open_raw_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket);
|
||||
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// That's funny, but this is how it is done in the kernel
|
||||
// https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916
|
||||
rawsocket->sk->sk_mark=config.mark;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void close_raw_socket(void) {
|
||||
sock_release(rawsocket);
|
||||
}
|
||||
|
||||
static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret = 0;
|
||||
if (pktlen > AVAILABLE_MTU) return -ENOMEM;
|
||||
|
||||
struct iphdr *iph;
|
||||
|
||||
if ((ret = ip4_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in daddr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
.sin_addr = {
|
||||
.s_addr = iph->daddr
|
||||
}
|
||||
};
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
|
||||
msg.msg_flags = MSG_DONTWAIT;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
|
||||
ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int open_raw6_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket);
|
||||
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// That's funny, but this is how it is done in the kernel
|
||||
// https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916
|
||||
raw6socket->sk->sk_mark=config.mark;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void close_raw6_socket(void) {
|
||||
sock_release(raw6socket);
|
||||
}
|
||||
|
||||
static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret = 0;
|
||||
if (pktlen > AVAILABLE_MTU) return -ENOMEM;
|
||||
|
||||
struct ip6_hdr *iph;
|
||||
|
||||
if ((ret = ip6_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 daddr = {
|
||||
.sin6_family = AF_INET6,
|
||||
/* Always 0 for raw socket */
|
||||
.sin6_port = 0,
|
||||
.sin6_addr = iph->ip6_dst
|
||||
};
|
||||
|
||||
struct kvec iov;
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
|
||||
msg.msg_flags = MSG_DONTWAIT;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret;
|
||||
|
||||
if (pktlen > AVAILABLE_MTU) {
|
||||
lgdebug("The packet is too big and may cause issues!");
|
||||
|
||||
NETBUF_ALLOC(buff1, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buff1)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
return -ENOMEM;
|
||||
}
|
||||
NETBUF_ALLOC(buff2, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buff2)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buff2);
|
||||
return -ENOMEM;
|
||||
}
|
||||
uint32_t buff1_size = MAX_PACKET_SIZE;
|
||||
uint32_t buff2_size = MAX_PACKET_SIZE;
|
||||
|
||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
ret = send_raw_socket(buff1, buff1_size);
|
||||
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
ret = send_raw_socket(buff2, buff2_size);
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
NETBUF_FREE(buff1);
|
||||
NETBUF_FREE(buff2);
|
||||
return sent;
|
||||
erret_lc:
|
||||
NETBUF_FREE(buff1);
|
||||
NETBUF_FREE(buff2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipvx = netproto_version(pkt, pktlen);
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
ret = send_raw_ipv4(pkt, pktlen);
|
||||
} else if (ipvx == IP6VERSION) {
|
||||
ret = send_raw_ipv6(pkt, pktlen);
|
||||
} else {
|
||||
printf("proto version %d is unsupported\n", ipvx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lgtrace_addp("raw_sock_send: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
lginfo("delay_packet_send won't work on current youtubeUnblock version");
|
||||
return send_raw_socket(data, data_len);
|
||||
}
|
||||
|
||||
struct instance_config_t instance_config = {
|
||||
.send_raw_packet = send_raw_socket,
|
||||
.send_delayed_packet = delay_packet_send,
|
||||
};
|
||||
|
||||
static int connbytes_pkts(const struct sk_buff *skb) {
|
||||
const struct nf_conn *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
u_int64_t pkts = 0;
|
||||
const struct nf_conn_counter *counters;
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
if (!ct)
|
||||
return -1;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
||||
const struct nf_conn_acct *acct;
|
||||
acct = nf_conn_acct_find(ct);
|
||||
if (!acct)
|
||||
return -1;
|
||||
counters = acct->counter;
|
||||
#else
|
||||
counters = nf_conn_acct_find(ct);
|
||||
if (!counters)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets);
|
||||
|
||||
return pkts;
|
||||
}
|
||||
|
||||
/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */
|
||||
#ifdef RHEL_RELEASE_CODE
|
||||
|
||||
#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
const struct nf_hook_state *state) \
|
||||
|
||||
#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
|
||||
#error "Sorry; this version of RHEL is not supported because it's kind of old."
|
||||
|
||||
#endif /* RHEL_RELEASE_CODE >= x */
|
||||
|
||||
|
||||
/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */
|
||||
#else
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
void *priv, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
unsigned int hooknum, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
#error "Linux < 3.0 isn't supported at all."
|
||||
|
||||
#endif /* LINUX_VERSION_CODE > n */
|
||||
|
||||
#endif /* RHEL or not RHEL */
|
||||
|
||||
|
||||
|
||||
static NF_CALLBACK(ykb_nf_hook, skb) {
|
||||
int ret;
|
||||
|
||||
if ((skb->mark & config.mark) == config.mark)
|
||||
goto accept;
|
||||
|
||||
if (skb->head == NULL)
|
||||
goto accept;
|
||||
|
||||
if (skb->len > MAX_PACKET_SIZE)
|
||||
goto accept;
|
||||
|
||||
if (config.connbytes_limit != 0 && connbytes_pkts(skb) > config.connbytes_limit)
|
||||
goto accept;
|
||||
|
||||
ret = skb_linearize(skb);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Cannot linearize");
|
||||
goto accept;
|
||||
}
|
||||
|
||||
int vrd = process_packet(skb->data, skb->len);
|
||||
|
||||
switch(vrd) {
|
||||
case PKT_ACCEPT:
|
||||
goto accept;
|
||||
case PKT_DROP:
|
||||
goto drop;
|
||||
}
|
||||
|
||||
accept:
|
||||
return NF_ACCEPT;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
|
||||
static struct nf_hook_ops ykb_nf_reg __read_mostly = {
|
||||
.hook = ykb_nf_hook,
|
||||
.pf = NFPROTO_IPV4,
|
||||
.hooknum = NF_INET_POST_ROUTING,
|
||||
.priority = NF_IP_PRI_MANGLE,
|
||||
};
|
||||
|
||||
static struct nf_hook_ops ykb6_nf_reg __read_mostly = {
|
||||
.hook = ykb_nf_hook,
|
||||
.pf = NFPROTO_IPV6,
|
||||
.hooknum = NF_INET_POST_ROUTING,
|
||||
.priority = NF_IP6_PRI_MANGLE,
|
||||
};
|
||||
|
||||
static int __init ykb_init(void) {
|
||||
int ret = 0;
|
||||
ret = init_config(&config);
|
||||
if (ret < 0) goto err;
|
||||
|
||||
ret = open_raw_socket();
|
||||
if (ret < 0) goto err;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
|
||||
for_each_net(n) {
|
||||
ret = nf_register_net_hook(n, &ykb_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net_hook");
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = nf_register_hook(&ykb_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net_hook");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (config.use_ipv6) {
|
||||
ret = open_raw6_socket();
|
||||
if (ret < 0) {
|
||||
config.use_ipv6 = 0;
|
||||
lgwarning("ipv6 disabled!");
|
||||
goto ipv6_fallback;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
for_each_net(n) {
|
||||
ret = nf_register_net_hook(n, &ykb6_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net6_hook");
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = nf_register_hook(&ykb6_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net6_hook");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ipv6_fallback:
|
||||
lginfo("youtubeUnblock kernel module started.\n");
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ykb_destroy(void) {
|
||||
if (config.use_ipv6) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
for_each_net(n)
|
||||
nf_unregister_net_hook(n, &ykb6_nf_reg);
|
||||
#else
|
||||
nf_unregister_hook(&ykb6_nf_reg);
|
||||
#endif
|
||||
close_raw6_socket();
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
for_each_net(n)
|
||||
nf_unregister_net_hook(n, &ykb_nf_reg);
|
||||
#else
|
||||
nf_unregister_hook(&ykb_nf_reg);
|
||||
#endif
|
||||
|
||||
close_raw_socket();
|
||||
|
||||
free_config(config);
|
||||
lginfo("youtubeUnblock kernel module destroyed.\n");
|
||||
}
|
||||
|
||||
module_init(ykb_init);
|
||||
module_exit(ykb_destroy);
|
||||
@@ -1,26 +0,0 @@
|
||||
// Used to register target in iptables
|
||||
#include <stdio.h>
|
||||
#include <xtables.h>
|
||||
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include "ipt_YTUNBLOCK.h"
|
||||
|
||||
#define _init __attribute__((constructor)) _INIT
|
||||
#define __maybe_unused __attribute__((__unused__))
|
||||
|
||||
static void YTKB_help(void) {
|
||||
printf("Youtube Unblock - bypass youtube slowdown DPI in Russia\n");
|
||||
}
|
||||
|
||||
static struct xtables_target ykb_tg_reg = {
|
||||
.name = "YTUNBLOCK",
|
||||
.version = XTABLES_VERSION,
|
||||
.family = NFPROTO_IPV4,
|
||||
.size = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct xt_ytunblock_tginfo)),
|
||||
.help = YTKB_help,
|
||||
};
|
||||
|
||||
void _init(void) {
|
||||
xtables_register_target(&ykb_tg_reg);
|
||||
}
|
||||
70
logging.h
Normal file
70
logging.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
#include "config.h"
|
||||
|
||||
#define LOG_LEVEL (config.verbose)
|
||||
|
||||
#ifdef KERNEL_SPACE
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#define printf pr_info
|
||||
#define perror pr_err
|
||||
|
||||
#define LOG_ERR KERN_ERR
|
||||
#define LOG_INFO KERN_INFO
|
||||
#define LOG_WARN KERN_WARNING
|
||||
|
||||
#define log_message(level, msg, ...) \
|
||||
(printk(level msg, ##__VA_ARGS__))
|
||||
|
||||
#define lgerror(ret, msg, ...) \
|
||||
(log_message(LOG_ERR, msg ": %d\n", ##__VA_ARGS__, ret))
|
||||
|
||||
#else
|
||||
#include <stdio.h> // IWYU pragma: export
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#define log_message(level, msg, ...) \
|
||||
(config.syslog ? (void)(syslog((level), msg, ##__VA_ARGS__)) : (void)(printf(msg, ##__VA_ARGS__)))
|
||||
|
||||
#define lgerror(ret, msg, ...) __extension__ ({ \
|
||||
errno = -(ret); \
|
||||
log_message(LOG_ERR, msg ": %s\n", ##__VA_ARGS__, strerror(errno)); \
|
||||
})
|
||||
#endif /* PROGRAM_SPACE */
|
||||
|
||||
#define lgerr(msg, ...) \
|
||||
(log_message(LOG_ERR, msg, ##__VA_ARGS__))
|
||||
|
||||
#define lgwarning(msg, ...) \
|
||||
(log_message(LOG_WARN, msg, ##__VA_ARGS__))
|
||||
|
||||
|
||||
#define lginfo(msg, ...) \
|
||||
(log_message(LOG_INFO, msg, ##__VA_ARGS__))
|
||||
|
||||
#define print_message(...) \
|
||||
(lginfo(__VA_ARGS__))
|
||||
|
||||
#define lgdebug(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_DEBUG ? log_message(LOG_INFO, msg, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgdebugmsg(msg, ...) lgdebug(msg "\n", ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define lgtrace(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, msg, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgtracemsg(msg, ...) lgtrace(msg "\n", __VA_ARGS__)
|
||||
|
||||
#define lgtrace_start(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, "[TRACE] " msg " ( ", ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgtrace_addp(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, msg", ", ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgtrace_end() \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, ") \n") : (void)0)
|
||||
|
||||
#endif /* LOGGING_H */
|
||||
94
mangle.h
94
mangle.h
@@ -1,66 +1,58 @@
|
||||
#ifndef YU_MANGLE_H
|
||||
#define YU_MANGLE_H
|
||||
|
||||
#ifdef KERNEL_SPACE
|
||||
#include <linux/types.h>
|
||||
typedef __u8 uint8_t;
|
||||
typedef __u32 uint32_t;
|
||||
#include "types.h"
|
||||
#include "tls.h"
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#define PKT_ACCEPT 0
|
||||
#define PKT_DROP 1
|
||||
// Used for section config
|
||||
#define PKT_CONTINUE 2
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
/**
|
||||
* Processes the packet and returns verdict.
|
||||
* This is the primary function that traverses the packet.
|
||||
*/
|
||||
int process_packet(const uint8_t *packet, uint32_t packet_len);
|
||||
|
||||
/* from <netinet/ip.h> */
|
||||
#define IP_RF 0x8000 /* reserved fragment flag */
|
||||
#define IP_DF 0x4000 /* dont fragment flag */
|
||||
#define IP_MF 0x2000 /* more fragments flag */
|
||||
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
||||
#else
|
||||
#define USER_SPACE
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
struct verdict {
|
||||
int gvideo_hello; /* google video hello packet */
|
||||
int sni_offset; /* offset from start of tcp _payload_ */
|
||||
int sni_len;
|
||||
};
|
||||
/**
|
||||
* Processe the TCP packet.
|
||||
* Returns verdict.
|
||||
*/
|
||||
int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len);
|
||||
|
||||
struct verdict analyze_tls_data(const uint8_t *data, uint32_t dlen);
|
||||
|
||||
int ip4_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||
uint32_t payload_offset,
|
||||
uint8_t *frag1, uint32_t *f1len,
|
||||
uint8_t *frag2, uint32_t *f2len);
|
||||
/**
|
||||
* Processes the UDP packet.
|
||||
* Returns verdict.
|
||||
*/
|
||||
int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, uint32_t pktlen);
|
||||
|
||||
int tcp4_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||
uint32_t payload_offset,
|
||||
uint8_t *seg1, uint32_t *s1len,
|
||||
uint8_t *seg2, uint32_t *s2len);
|
||||
|
||||
int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
/**
|
||||
* Sends fake client hello.
|
||||
*/
|
||||
int post_fake_sni(struct fake_type f_type,
|
||||
const void *iph, unsigned int iph_len,
|
||||
const struct tcphdr *tcph, unsigned int tcph_len);
|
||||
|
||||
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph);
|
||||
void ip4_set_checksum(struct iphdr *iph);
|
||||
/**
|
||||
* Splits packet by poses and posts.
|
||||
* Poses are relative to start of TCP payload.
|
||||
* dvs used internally and should be zero.
|
||||
*/
|
||||
int send_tcp_frags(const struct section_config_t *section,
|
||||
const uint8_t *packet, uint32_t pktlen,
|
||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||
|
||||
int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
|
||||
uint8_t *buf, uint32_t *buflen);
|
||||
/**
|
||||
* Splits packet by poses and posts.
|
||||
* Poses are relative to start of TCP payload.
|
||||
* dvs used internally and should be zero.
|
||||
*/
|
||||
int send_ip4_frags(const struct section_config_t *section,
|
||||
const uint8_t *packet, uint32_t pktlen,
|
||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||
#endif /* YU_MANGLE_H */
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/sbin/nft -f
|
||||
# This file
|
||||
|
||||
insert rule inet fw4 mangle_forward tcp dport 443 ct original packets < 20 counter queue num 537 bypass
|
||||
insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
USE_PROCD=1
|
||||
|
||||
START=50
|
||||
STOP=50
|
||||
|
||||
# Openwrt procd script: https://openwrt.org/docs/guide-developer/procd-init-script-example
|
||||
# The program should be put into /usr/bin/
|
||||
# This file should be put into /etc/init.d/
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/bin/youtubeUnblock 537
|
||||
|
||||
procd_set_param nice -20
|
||||
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
|
||||
procd_close_instance
|
||||
}
|
||||
320
quic.c
Normal file
320
quic.c
Normal file
@@ -0,0 +1,320 @@
|
||||
#include "quic.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
/**
|
||||
* Packet number.
|
||||
*/
|
||||
struct quic_pnumber {
|
||||
uint8_t d1;
|
||||
uint8_t d2;
|
||||
uint8_t d3;
|
||||
uint8_t d4;
|
||||
};
|
||||
|
||||
uint64_t quic_parse_varlength(uint8_t *variable, uint64_t *mlen) {
|
||||
if (mlen && *mlen == 0) return 0;
|
||||
uint64_t vr = (*variable & 0x3F);
|
||||
uint8_t len = 1 << (*variable >> 6);
|
||||
|
||||
if (mlen) {
|
||||
if (*mlen < len) return 0;
|
||||
*mlen = len;
|
||||
}
|
||||
|
||||
++variable;
|
||||
for (uint8_t i = 1; i < len; i++) {
|
||||
vr = (vr << 8) + *variable;
|
||||
++variable;
|
||||
}
|
||||
|
||||
return vr;
|
||||
}
|
||||
|
||||
int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len,
|
||||
struct quic_lhdr **qch, uint32_t *qch_len,
|
||||
struct quic_cids *qci,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
if ( raw_payload == NULL ||
|
||||
raw_payload_len < sizeof(struct quic_lhdr))
|
||||
goto invalid_packet;
|
||||
|
||||
struct quic_lhdr *nqch = (struct quic_lhdr *)raw_payload;
|
||||
uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr);
|
||||
uint8_t *cur_rawptr = raw_payload + sizeof(struct quic_lhdr);
|
||||
if (!nqch->fixed) {
|
||||
lgtrace_addp("quic fixed unset");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
uint8_t found = 0;
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
if (ntohl(nqch->version) == supported_versions[i]) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
lgtrace_addp("quic version undefined %d", ntohl(nqch->version));
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
lgtrace_addp("quic version valid %d", ntohl(nqch->version));
|
||||
|
||||
if (left_len < 2) goto invalid_packet;
|
||||
struct quic_cids nqci = {0};
|
||||
|
||||
nqci.dst_len = *cur_rawptr++;
|
||||
left_len--;
|
||||
if (left_len < nqci.dst_len) goto invalid_packet;
|
||||
nqci.dst_id = cur_rawptr;
|
||||
cur_rawptr += nqci.dst_len;
|
||||
left_len -= nqci.dst_len;
|
||||
|
||||
nqci.src_len = *cur_rawptr++;
|
||||
left_len--;
|
||||
if (left_len < nqci.src_len) goto invalid_packet;
|
||||
nqci.src_id = cur_rawptr;
|
||||
cur_rawptr += nqci.src_len;
|
||||
left_len -= nqci.src_len;
|
||||
|
||||
if (qch) *qch = nqch;
|
||||
if (qch_len) {
|
||||
*qch_len = sizeof(struct quic_lhdr) +
|
||||
nqci.src_len + nqci.dst_len;
|
||||
}
|
||||
if (qci) *qci = nqci;
|
||||
if (payload) *payload = cur_rawptr;
|
||||
if (plen) *plen = left_len;
|
||||
|
||||
return 0;
|
||||
|
||||
invalid_packet:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen,
|
||||
const struct quic_lhdr *qch,
|
||||
struct quici_hdr *qhdr,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
if (inplen < 3) goto invalid_packet;
|
||||
struct quici_hdr nqhdr;
|
||||
|
||||
uint8_t *cur_ptr = inpayload;
|
||||
uint32_t left_len = inplen;
|
||||
uint64_t tlen = left_len;
|
||||
|
||||
nqhdr.token_len = quic_parse_varlength(cur_ptr, &tlen);
|
||||
nqhdr.token = cur_ptr + tlen;
|
||||
|
||||
if (left_len < nqhdr.token_len + tlen)
|
||||
goto invalid_packet;
|
||||
cur_ptr += tlen + nqhdr.token_len;
|
||||
left_len -= tlen + nqhdr.token_len;
|
||||
|
||||
tlen = left_len;
|
||||
nqhdr.length = quic_parse_varlength(cur_ptr, &tlen);
|
||||
|
||||
if (left_len != nqhdr.length + tlen &&
|
||||
left_len <= qch->number_length + 1)
|
||||
goto invalid_packet;
|
||||
|
||||
uint32_t packet_number = 0;
|
||||
|
||||
for (uint8_t i = 0; i <= qch->number_length; i++) {
|
||||
packet_number = (packet_number << 8) + *cur_ptr++;
|
||||
left_len--;
|
||||
}
|
||||
|
||||
nqhdr.packet_number = packet_number;
|
||||
|
||||
if (qhdr) *qhdr = nqhdr;
|
||||
if (payload) *payload = cur_ptr;
|
||||
if (plen) *plen = left_len;
|
||||
|
||||
return 0;
|
||||
|
||||
invalid_packet:
|
||||
lgerror(-EINVAL, "QUIC invalid Initial packet");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) {
|
||||
void *iph;
|
||||
uint32_t iph_len;
|
||||
struct udphdr *udph;
|
||||
uint8_t *data;
|
||||
uint32_t dlen;
|
||||
int ret;
|
||||
|
||||
ret = udp_payload_split(payload, *plen,
|
||||
&iph, &iph_len, &udph,
|
||||
&data, &dlen);
|
||||
|
||||
uint32_t ipxv = netproto_version(payload, *plen);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_TTL) {
|
||||
lgtrace_addp("set fake ttl to %d", strategy.faking_ttl);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->ttl = strategy.faking_ttl;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl;
|
||||
} else {
|
||||
lgerror(-EINVAL, "fail_packet: IP version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->frag_off = 0;
|
||||
}
|
||||
|
||||
|
||||
set_ip_checksum(iph, iph_len);
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_UDP_CHECK) {
|
||||
lgtrace_addp("break fake udp checksum");
|
||||
udph->check += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gen_fake_udp(struct udp_fake_type type,
|
||||
const void *ipxh, uint32_t iph_len,
|
||||
const struct udphdr *udph,
|
||||
uint8_t *buf, uint32_t *buflen) {
|
||||
uint32_t data_len = type.fake_len;
|
||||
|
||||
if (!ipxh || !udph || !buf || !buflen)
|
||||
return -EINVAL;
|
||||
|
||||
int ipxv = netproto_version(ipxh, iph_len);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
const struct iphdr *iph = ipxh;
|
||||
|
||||
memcpy(buf, iph, iph_len);
|
||||
struct iphdr *niph = (struct iphdr *)buf;
|
||||
|
||||
niph->protocol = IPPROTO_UDP;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
const struct ip6_hdr *iph = ipxh;
|
||||
|
||||
iph_len = sizeof(struct ip6_hdr);
|
||||
memcpy(buf, iph, iph_len);
|
||||
struct ip6_hdr *niph = (struct ip6_hdr *)buf;
|
||||
|
||||
niph->ip6_nxt = IPPROTO_UDP;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t dlen = iph_len + sizeof(struct udphdr) + data_len;
|
||||
|
||||
if (*buflen < dlen)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf + iph_len, udph, sizeof(struct udphdr));
|
||||
uint8_t *bfdptr = buf + iph_len + sizeof(struct udphdr);
|
||||
|
||||
memset(bfdptr, 0, data_len);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
struct iphdr *niph = (struct iphdr *)buf;
|
||||
niph->tot_len = htons(dlen);
|
||||
niph->id = randint();
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
struct ip6_hdr *niph = (struct ip6_hdr *)buf;
|
||||
niph->ip6_plen = htons(dlen - iph_len);
|
||||
}
|
||||
|
||||
struct udphdr *nudph = (struct udphdr *)(buf + iph_len);
|
||||
nudph->len = htons(sizeof(struct udphdr) + data_len);
|
||||
|
||||
|
||||
udp_fail_packet(type.strategy, buf, &dlen, *buflen);
|
||||
|
||||
*buflen = dlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int detect_udp_filtered(const struct section_config_t *section,
|
||||
const uint8_t *payload, uint32_t plen) {
|
||||
const void *iph;
|
||||
uint32_t iph_len;
|
||||
const struct udphdr *udph;
|
||||
const uint8_t *data;
|
||||
uint32_t dlen;
|
||||
int ret;
|
||||
|
||||
ret = udp_payload_split((uint8_t *)payload, plen,
|
||||
(void **)&iph, &iph_len,
|
||||
(struct udphdr **)&udph,
|
||||
(uint8_t **)&data, &dlen);
|
||||
int udp_dport = ntohs(udph->dest);
|
||||
lgtrace_addp("UDP dport: %d", udp_dport);
|
||||
|
||||
|
||||
if (ret < 0) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (section->udp_filter_quic) {
|
||||
const struct quic_lhdr *qch;
|
||||
uint32_t qch_len;
|
||||
struct quic_cids qci;
|
||||
uint8_t *quic_raw_payload;
|
||||
uint32_t quic_raw_plen;
|
||||
|
||||
lgtrace_addp("QUIC probe");
|
||||
|
||||
ret = quic_parse_data((uint8_t *)data, dlen,
|
||||
(struct quic_lhdr **)&qch, &qch_len, &qci,
|
||||
&quic_raw_payload, &quic_raw_plen);
|
||||
|
||||
if (ret < 0) {
|
||||
lgtrace_addp("QUIC undefined type");
|
||||
goto match_port;
|
||||
}
|
||||
|
||||
lgtrace_addp("QUIC detected");
|
||||
|
||||
uint8_t qtype = qch->type;
|
||||
if (qch->version == QUIC_V1)
|
||||
qtype = quic_convtype_v1(qtype);
|
||||
else if (qch->version == QUIC_V2)
|
||||
qtype = quic_convtype_v2(qtype);
|
||||
|
||||
if (qtype != QUIC_INITIAL_TYPE) {
|
||||
lgtrace_addp("QUIC message type: %d", qtype);
|
||||
goto match_port;
|
||||
}
|
||||
|
||||
lgtrace_addp("QUIC initial message");
|
||||
|
||||
goto approve;
|
||||
}
|
||||
|
||||
match_port:
|
||||
|
||||
for (int i = 0; i < section->udp_dport_range_len; i++) {
|
||||
struct udp_dport_range crange = section->udp_dport_range[i];
|
||||
if (udp_dport >= crange.start && udp_dport <= crange.end) {
|
||||
lgtrace_addp("matched to %d-%d", crange.start, crange.end);
|
||||
goto approve;
|
||||
}
|
||||
}
|
||||
|
||||
skip:
|
||||
return 0;
|
||||
approve:
|
||||
return 1;
|
||||
}
|
||||
141
quic.h
Normal file
141
quic.h
Normal file
@@ -0,0 +1,141 @@
|
||||
#ifndef QUIC_H
|
||||
#define QUIC_H
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to
|
||||
* derive initial secret. It is used for QUIC v1.
|
||||
*/
|
||||
#define QUIC_INITIAL_SALT_V1 \
|
||||
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad" \
|
||||
"\xcc\xbb\x7f\x0a"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* :macro:`NGTCP2_INITIAL_SALT_V2` is a salt value which is used to
|
||||
* derive initial secret. It is used for QUIC v2.
|
||||
*/
|
||||
#define QUIC_INITIAL_SALT_V2 \
|
||||
"\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \
|
||||
"\xf9\xbd\x2e\xd9"
|
||||
|
||||
#define QUIC_INITIAL_TYPE 0
|
||||
#define QUIC_0_RTT_TYPE 1
|
||||
#define QUIC_HANDSHAKE_TYPE 2
|
||||
#define QUIC_RETRY_TYPE 3
|
||||
|
||||
#define QUIC_INITIAL_TYPE_V1 0b00
|
||||
#define QUIC_0_RTT_TYPE_V1 0b01
|
||||
#define QUIC_HANDSHAKE_TYPE_V1 0b10
|
||||
#define QUIC_RETRY_TYPE_V1 0b11
|
||||
#define quic_convtype_v1(type) (type)
|
||||
|
||||
#define QUIC_INITIAL_TYPE_V2 0b01
|
||||
#define QUIC_0_RTT_TYPE_V2 0b10
|
||||
#define QUIC_HANDSHAKE_TYPE_V2 0b11
|
||||
#define QUIC_RETRY_TYPE_V2 0b00
|
||||
#define quic_convtype_v2(type) (((type) + 1) & 0b11)
|
||||
|
||||
#define QUIC_V1 1 // RFC 9000
|
||||
#define QUIC_V2 0x6b3343cf // RFC 9369
|
||||
|
||||
static const uint32_t supported_versions[] = {
|
||||
QUIC_V1,
|
||||
QUIC_V2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Quic Large Header
|
||||
*/
|
||||
struct quic_lhdr {
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
uint8_t number_length:2;
|
||||
uint8_t reserved:2;
|
||||
uint8_t type:2;
|
||||
uint8_t fixed:1;
|
||||
uint8_t form:1;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
uint8_t form:1;
|
||||
uint8_t fixed:1;
|
||||
uint8_t type:2;
|
||||
uint8_t reserved:2;
|
||||
uint8_t number_length:2;
|
||||
#else
|
||||
#error "Undefined endian"
|
||||
#endif
|
||||
uint32_t version;
|
||||
}__attribute__((packed));
|
||||
|
||||
/**
|
||||
* Quic Large Header Ids
|
||||
* (separated from the original header because of varying dst
|
||||
*/
|
||||
struct quic_cids {
|
||||
uint8_t dst_len;
|
||||
uint8_t *dst_id;
|
||||
uint8_t src_len;
|
||||
uint8_t *src_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses QUIС raw data (UDP payload) to quic large header and
|
||||
* quic payload.
|
||||
*
|
||||
* \qch_len is sizeof(qch) + qci->dst_len + qci->src_id
|
||||
* \payload is Type-Specific payload (#17.2).
|
||||
*/
|
||||
int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len,
|
||||
struct quic_lhdr **qch, uint32_t *qch_len,
|
||||
struct quic_cids *qci,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
|
||||
/**
|
||||
* Parses QUIC variable-length integer. (#16)
|
||||
* \variable is a pointer to the sequence to be parsed
|
||||
* (varlen integer in big endian format)
|
||||
*
|
||||
* \mlen Used to signal about variable length and validate left length
|
||||
* in the buffer.
|
||||
*/
|
||||
uint64_t quic_parse_varlength(uint8_t *variable, uint64_t *mlen);
|
||||
|
||||
// quici stands for QUIC Initial
|
||||
|
||||
/**
|
||||
* This structure should be parsed
|
||||
*/
|
||||
struct quici_hdr {
|
||||
uint64_t token_len;
|
||||
uint8_t *token;
|
||||
uint64_t length;
|
||||
uint32_t packet_number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses QUIC initial payload.
|
||||
* \inpayload is a raw QUIC payload (payload after quic large header)
|
||||
*/
|
||||
int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen,
|
||||
const struct quic_lhdr *qch,
|
||||
struct quici_hdr *qhdr,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
// Like fail_packet for TCP
|
||||
int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
|
||||
|
||||
// Like gen_fake_sni for TCP
|
||||
int gen_fake_udp(struct udp_fake_type type,
|
||||
const void *ipxh, uint32_t iph_len,
|
||||
const struct udphdr *udph,
|
||||
uint8_t *buf, uint32_t *buflen);
|
||||
|
||||
int detect_udp_filtered(const struct section_config_t *section,
|
||||
const uint8_t *payload, uint32_t plen);
|
||||
|
||||
#endif /* QUIC_H */
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#define FAKE_SNI_MAXLEN 1500
|
||||
|
||||
static const char fake_sni[] = "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356";
|
||||
static const char fake_sni[] = "\026\003\001\002\000\001\000\001\374\003\003\323[\345\201f\362\200:B\356Uq\355X\315i\235*\021\367\331\272\a>\233\254\355\307/\342\372\265 \275\2459l&r\222\313\361\3729`\376\256\233\333O\001\373\33050\r\260f,\231\035 \324^\000>\023\002\023\003\023\001\300,\3000\000\237\314\251\314\250\314\252\300+\300/\000\236\300$\300(\000k\300#\300'\000g\300\n\300\024\0009\300\t\300\023\0003\000\235\000\234\000=\000<\0005\000/\000\377\001\000\001u\000\000\000\023\000\021\000\000\016www.google.com\000\v\000\004\003\000\001\002\000\n\000\026\000\024\000\035\000\027\000\036\000\031\000\030\001\000\001\001\001\002\001\003\001\004\000\020\000\016\000\f\002h2\bhttp/1.1\000\026\000\000\000\027\000\000\0001\000\000\000\r\0000\000.\004\003\005\003\006\003\b\a\b\b\b\032\b\033\b\034\b\t\b\n\b\v\b\004\b\005\b\006\004\001\005\001\006\001\003\003\003\001\003\002\004\002\005\002\006\002\000+\000\005\004\003\004\003\003\000-\000\002\001\001\0003\000&\000$\000\035\000 \004\224\206\021\256\f\222\266\3435\216\202\342\2573\341\3503\2107\341\023\016\240r|6\000^K\310s\000\025\000\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000";
|
||||
|
||||
static const char fake_sni_old[] = "\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356";
|
||||
|
||||
#endif /*RAW_REPLACEMENTS_H*/
|
||||
|
||||
334
tls.c
Normal file
334
tls.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "types.h"
|
||||
#include "tls.h"
|
||||
#include "config.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static int bruteforce_analyze_sni_str(
|
||||
const struct section_config_t *section,
|
||||
const uint8_t *data, size_t dlen,
|
||||
struct tls_verdict *vrd
|
||||
) {
|
||||
if (section->all_domains) {
|
||||
vrd->target_sni = 1;
|
||||
vrd->sni_len = 0;
|
||||
vrd->sni_offset = dlen / 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) {
|
||||
const char *domain_startp = sne->domain_name;
|
||||
int domain_len = sne->domain_len;
|
||||
|
||||
if (sne->domain_len + dlen + 1 > MAX_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NETBUF_ALLOC(buf, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
return -ENOMEM;
|
||||
}
|
||||
NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int));
|
||||
if (!NETBUF_CHECK(nzbuf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int *zbuf = (void *)nzbuf;
|
||||
|
||||
memcpy(buf, domain_startp, domain_len);
|
||||
memcpy(buf + domain_len, "#", 1);
|
||||
memcpy(buf + domain_len + 1, data, dlen);
|
||||
|
||||
z_function((char *)buf, zbuf, domain_len + 1 + dlen);
|
||||
|
||||
for (unsigned int k = 0; k < dlen; k++) {
|
||||
if (zbuf[k] == domain_len) {
|
||||
vrd->target_sni = 1;
|
||||
vrd->sni_len = domain_len;
|
||||
vrd->sni_offset = (k - domain_len - 1);
|
||||
vrd->sni_target_offset = vrd->sni_offset;
|
||||
vrd->sni_target_len = vrd->sni_len;
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int analyze_sni_str(
|
||||
const struct section_config_t *section,
|
||||
const char *sni_name, int sni_len, const uint8_t *data,
|
||||
struct tls_verdict *vrd
|
||||
) {
|
||||
if (section->all_domains) {
|
||||
vrd->target_sni = 1;
|
||||
goto check_domain;
|
||||
}
|
||||
|
||||
for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) {
|
||||
const char *sni_startp = sni_name + sni_len - sne->domain_len;
|
||||
const char *domain_startp = sne->domain_name;
|
||||
|
||||
if (sni_len >= sne->domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
sne->domain_len)) {
|
||||
vrd->target_sni = 1;
|
||||
vrd->sni_target_offset = (const uint8_t *)sni_startp - data;
|
||||
vrd->sni_target_len = sne->domain_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
check_domain:
|
||||
if (vrd->target_sni == 1) {
|
||||
for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) {
|
||||
const char *sni_startp = sni_name + sni_len - sne->domain_len;
|
||||
const char *domain_startp = sne->domain_name;
|
||||
|
||||
if (sni_len >= sne->domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
sne->domain_len)) {
|
||||
vrd->target_sni = 0;
|
||||
lgdebugmsg("Excluded SNI: %.*s",
|
||||
vrd->sni_len, data + vrd->sni_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define TLS_CONTENT_TYPE_HANDSHAKE 0x16
|
||||
#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
|
||||
#define TLS_EXTENSION_SNI 0x0000
|
||||
#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d
|
||||
|
||||
/**
|
||||
* Processes tls payload of the tcp request.
|
||||
*
|
||||
* data Payload data of TCP.
|
||||
* dlen Length of `data`.
|
||||
*/
|
||||
struct tls_verdict analyze_tls_data(
|
||||
const struct section_config_t *section,
|
||||
const uint8_t *data,
|
||||
uint32_t dlen)
|
||||
{
|
||||
struct tls_verdict vrd = {0};
|
||||
|
||||
size_t i = 0;
|
||||
const uint8_t *data_end = data + dlen;
|
||||
|
||||
if (section->sni_detection == SNI_DETECTION_BRUTE) {
|
||||
bruteforce_analyze_sni_str(section, data, dlen, &vrd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (i + 4 < dlen) {
|
||||
const uint8_t *msgData = data + i;
|
||||
|
||||
uint8_t tls_content_type = *msgData;
|
||||
uint8_t tls_vmajor = *(msgData + 1);
|
||||
uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3));
|
||||
|
||||
if (tls_vmajor != 0x03) goto nextMessage;
|
||||
|
||||
if (i + 5 > dlen) break;
|
||||
|
||||
if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE)
|
||||
goto nextMessage;
|
||||
|
||||
const uint8_t *handshakeProto = msgData + 5;
|
||||
|
||||
if (handshakeProto + 1 >= data_end) break;
|
||||
|
||||
uint8_t handshakeType = *handshakeProto;
|
||||
|
||||
if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO)
|
||||
goto nextMessage;
|
||||
|
||||
const uint8_t *msgPtr = handshakeProto;
|
||||
msgPtr += 1;
|
||||
msgPtr += 3 + 2 + 32;
|
||||
|
||||
if (msgPtr + 1 >= data_end) break;
|
||||
uint8_t sessionIdLength = *msgPtr;
|
||||
msgPtr++;
|
||||
msgPtr += sessionIdLength;
|
||||
|
||||
if (msgPtr + 2 >= data_end) break;
|
||||
uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr);
|
||||
msgPtr += 2;
|
||||
msgPtr += ciphersLength;
|
||||
|
||||
if (msgPtr + 1 >= data_end) break;
|
||||
uint8_t compMethodsLen = *msgPtr;
|
||||
msgPtr++;
|
||||
msgPtr += compMethodsLen;
|
||||
|
||||
if (msgPtr + 2 >= data_end) break;
|
||||
uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr);
|
||||
msgPtr += 2;
|
||||
|
||||
const uint8_t *extensionsPtr = msgPtr;
|
||||
const uint8_t *extensions_end = extensionsPtr + extensionsLen;
|
||||
if (extensions_end > data_end) extensions_end = data_end;
|
||||
|
||||
while (extensionsPtr < extensions_end) {
|
||||
const uint8_t *extensionPtr = extensionsPtr;
|
||||
if (extensionPtr + 4 >= extensions_end) break;
|
||||
|
||||
uint16_t extensionType =
|
||||
ntohs(*(uint16_t *)extensionPtr);
|
||||
extensionPtr += 2;
|
||||
|
||||
uint16_t extensionLen =
|
||||
ntohs(*(uint16_t *)extensionPtr);
|
||||
extensionPtr += 2;
|
||||
|
||||
|
||||
if (extensionPtr + extensionLen > extensions_end)
|
||||
break;
|
||||
|
||||
if (extensionType != TLS_EXTENSION_SNI)
|
||||
goto nextExtension;
|
||||
|
||||
const uint8_t *sni_ext_ptr = extensionPtr;
|
||||
|
||||
if (sni_ext_ptr + 2 >= extensions_end) break;
|
||||
uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr);
|
||||
|
||||
sni_ext_ptr += 2;
|
||||
|
||||
const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen;
|
||||
if (sni_ext_end >= extensions_end) break;
|
||||
|
||||
if (sni_ext_ptr + 3 >= sni_ext_end) break;
|
||||
sni_ext_ptr++;
|
||||
uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr);
|
||||
sni_ext_ptr += 2;
|
||||
|
||||
if (sni_ext_ptr + sni_len > sni_ext_end) break;
|
||||
|
||||
const char *sni_name = (char *)sni_ext_ptr;
|
||||
|
||||
vrd.sni_offset = (uint8_t *)sni_name - data;
|
||||
vrd.sni_target_offset = vrd.sni_offset;
|
||||
vrd.sni_len = sni_len;
|
||||
vrd.sni_target_len = vrd.sni_len;
|
||||
|
||||
analyze_sni_str(section, sni_name, sni_len, data, &vrd);
|
||||
goto out;
|
||||
|
||||
nextExtension:
|
||||
extensionsPtr += 2 + 2 + extensionLen;
|
||||
}
|
||||
nextMessage:
|
||||
i += 5 + message_length;
|
||||
}
|
||||
|
||||
out:
|
||||
return vrd;
|
||||
}
|
||||
|
||||
int gen_fake_sni(struct fake_type type,
|
||||
const void *ipxh, uint32_t iph_len,
|
||||
const struct tcphdr *tcph, uint32_t tcph_len,
|
||||
uint8_t *buf, uint32_t *buflen) {
|
||||
uint32_t data_len = type.fake_len;
|
||||
|
||||
if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) {
|
||||
data_len = (uint32_t)randint() % 1200;
|
||||
}
|
||||
|
||||
if (!ipxh || !tcph || !buf || !buflen)
|
||||
return -EINVAL;
|
||||
|
||||
int ipxv = netproto_version(ipxh, iph_len);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
const struct iphdr *iph = ipxh;
|
||||
|
||||
memcpy(buf, iph, iph_len);
|
||||
struct iphdr *niph = (struct iphdr *)buf;
|
||||
|
||||
niph->protocol = IPPROTO_TCP;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
const struct ip6_hdr *iph = ipxh;
|
||||
|
||||
iph_len = sizeof(struct ip6_hdr);
|
||||
memcpy(buf, iph, iph_len);
|
||||
struct ip6_hdr *niph = (struct ip6_hdr *)buf;
|
||||
|
||||
niph->ip6_nxt = IPPROTO_TCP;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t dlen = iph_len + tcph_len + data_len;
|
||||
|
||||
if (*buflen < dlen)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf + iph_len, tcph, tcph_len);
|
||||
uint8_t *bfdptr = buf + iph_len + tcph_len;
|
||||
|
||||
switch (type.type) {
|
||||
case FAKE_PAYLOAD_DATA:
|
||||
memcpy(bfdptr, type.fake_data, data_len);
|
||||
break;
|
||||
default: // FAKE_PAYLOAD_RANDOM
|
||||
#ifdef KERNEL_SPACE
|
||||
get_random_bytes(bfdptr, data_len);
|
||||
#else /* KERNEL_SPACE */
|
||||
#if _NO_GETRANDOM
|
||||
{
|
||||
int ret = open("/dev/urandom", O_RDONLY);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Unable to open /dev/urandom");
|
||||
return ret;
|
||||
}
|
||||
|
||||
read(ret, bfdptr, data_len);
|
||||
close(ret);
|
||||
}
|
||||
#else /* _NO_GETRANDOM */
|
||||
getrandom(bfdptr, data_len, 0);
|
||||
#endif /* _NO_GETRANDOM */
|
||||
#endif /* KERNEL_SPACE */
|
||||
}
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
struct iphdr *niph = (struct iphdr *)buf;
|
||||
niph->tot_len = htons(dlen);
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
struct ip6_hdr *niph = (struct ip6_hdr *)buf;
|
||||
niph->ip6_plen = htons(dlen - iph_len);
|
||||
}
|
||||
|
||||
fail_packet(type.strategy, buf, &dlen, *buflen);
|
||||
|
||||
*buflen = dlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
34
tls.h
Normal file
34
tls.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef TLS_H
|
||||
#define TLS_H
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
/**
|
||||
* Result of analyze_tls_data function
|
||||
*/
|
||||
struct tls_verdict {
|
||||
int target_sni; /* google video hello packet */
|
||||
int sni_offset; /* offset from start of tcp _payload_ */
|
||||
int sni_target_offset; /* offset of target domain instead of entire sni */
|
||||
int sni_target_len; /* offset of target domain instead of entire sni */
|
||||
int sni_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes the packet and finds TLS Client Hello information inside it.
|
||||
* data pointer points to start of TLS Message (TCP Payload)
|
||||
*/
|
||||
struct tls_verdict analyze_tls_data(const struct section_config_t *section, const uint8_t *data, uint32_t dlen);
|
||||
|
||||
|
||||
/**
|
||||
* Generates the fake client hello message
|
||||
*/
|
||||
int gen_fake_sni(struct fake_type type,
|
||||
const void *iph, uint32_t iph_len,
|
||||
const struct tcphdr *tcph, uint32_t tcph_len,
|
||||
uint8_t *buf, uint32_t *buflen);
|
||||
|
||||
#endif /* TLS_H */
|
||||
133
types.h
Normal file
133
types.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#define _GNU_SOURCE
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#ifdef KERNEL_SPACE
|
||||
#include <linux/errno.h> // IWYU pragma: export
|
||||
#include <linux/string.h> // IWYU pragma: export
|
||||
|
||||
#include <linux/types.h>
|
||||
#else /* USER_SPACE */
|
||||
|
||||
#include <errno.h> // IWYU pragma: export
|
||||
#include <stdint.h> // IWYU pragma: export
|
||||
#include <string.h> // IWYU pragma: export
|
||||
#include <stdlib.h> // IWYU pragma: export
|
||||
|
||||
|
||||
#define _NO_GETRANDOM ((__GLIBC__ <= 2 && __GLIBC_MINOR__ < 25))
|
||||
|
||||
#if !_NO_GETRANDOM
|
||||
#include <sys/random.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
#endif /* SPACES */
|
||||
|
||||
// Network specific structures
|
||||
#ifdef KERNEL_SPACE
|
||||
#include <linux/stddef.h> // IWYU pragma: export
|
||||
#include <linux/net.h> // IWYU pragma: export
|
||||
#include <linux/in.h> // IWYU pragma: export
|
||||
#include <linux/ip.h> // IWYU pragma: export
|
||||
#include <linux/ipv6.h> // IWYU pragma: export
|
||||
#include <linux/tcp.h> // IWYU pragma: export
|
||||
#include <linux/version.h>
|
||||
|
||||
#define free kfree
|
||||
#define malloc(size) kmalloc((size), GFP_KERNEL)
|
||||
|
||||
#define ip6_hdr ipv6hdr
|
||||
|
||||
/* from <netinet/ip.h> */
|
||||
#define IP_RF 0x8000 /* reserved fragment flag */
|
||||
#define IP_DF 0x4000 /* dont fragment flag */
|
||||
#define IP_MF 0x2000 /* more fragments flag */
|
||||
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
||||
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#elif defined(__BIG_ENDIAN)
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BYTE_ORDER __BIG_ENDIAN
|
||||
#else
|
||||
#error "Unsupported endian"
|
||||
#endif
|
||||
|
||||
#define ip6_plen payload_len
|
||||
#define ip6_nxt nexthdr
|
||||
#define ip6_hops hop_limit
|
||||
#define ip6_hlim hop_limit
|
||||
#define ip6_src saddr
|
||||
#define ip6_dst daddr
|
||||
|
||||
#else /* USER_SPACE */
|
||||
#include <arpa/inet.h> // IWYU pragma: export
|
||||
#include <netinet/ip.h> // IWYU pragma: export
|
||||
#include <netinet/ip6.h> // IWYU pragma: export
|
||||
#include <netinet/tcp.h> // IWYU pragma: export
|
||||
#include <netinet/udp.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
#define SFREE(item) do { \
|
||||
free((item)); \
|
||||
(item) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
|
||||
#define max(a,b)__extension__\
|
||||
({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define min(a,b)__extension__\
|
||||
({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#endif /* not a KERNEL_SPACE */
|
||||
|
||||
/* An alternative memory allocation strategy for userspace app */
|
||||
// #define ALLOC_MALLOC
|
||||
|
||||
/**
|
||||
* Use NETBUF_ALLOC and NETBUF_FREE as an abstraction of memory allocation.
|
||||
* Do not use it within expressions, consider these defines as separate statements.
|
||||
*
|
||||
* Use NETBUF_CHECK to check that buffer was properly allocated.
|
||||
*/
|
||||
#ifdef KERNEL_SPACE
|
||||
#include <linux/gfp.h>
|
||||
#define NETBUF_ALLOC(buf, buf_len) __u8* buf = kmalloc(buf_len, GFP_KERNEL);
|
||||
#define NETBUF_CHECK(buf) ((buf) != NULL)
|
||||
#define NETBUF_FREE(buf) kfree(buf);
|
||||
#elif defined(ALLOC_MALLOC)
|
||||
#include <stdlib.h>
|
||||
#define NETBUF_ALLOC(buf, buf_len) __u8* buf = malloc(buf_len);
|
||||
#define NETBUF_CHECK(buf) ((buf) != NULL)
|
||||
#define NETBUF_FREE(buf) free(buf);
|
||||
#else
|
||||
#define NETBUF_ALLOC(buf, buf_len) __u8 buf[buf_len];
|
||||
#define NETBUF_CHECK(buf) (1)
|
||||
#define NETBUF_FREE(buf) ;
|
||||
#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 */
|
||||
60
uspace.mk
60
uspace.mk
@@ -1,3 +1,6 @@
|
||||
#Check for using system libs
|
||||
USE_SYS_LIBS := no
|
||||
|
||||
#Userspace app makes here
|
||||
BUILD_DIR := $(CURDIR)/build
|
||||
DEPSDIR := $(BUILD_DIR)/deps
|
||||
@@ -5,8 +8,14 @@ DEPSDIR := $(BUILD_DIR)/deps
|
||||
CC:=gcc
|
||||
CCLD:=$(CC)
|
||||
LD:=ld
|
||||
override CFLAGS += -Wall -Wpedantic -Wno-unused-variable -I$(DEPSDIR)/include
|
||||
override LDFLAGS += -L$(DEPSDIR)/lib
|
||||
|
||||
ifeq ($(USE_SYS_LIBS), no)
|
||||
override CFLAGS += -I$(DEPSDIR)/include
|
||||
override LDFLAGS += -L$(DEPSDIR)/lib
|
||||
REQ = $(LIBNETFILTER_QUEUE) $(LIBMNL) $(LIBCRYPTO)
|
||||
endif
|
||||
|
||||
override CFLAGS += -DPKG_VERSION=\"$(PKG_FULLVERSION)\" -Wall -Wpedantic -Wno-unused-variable -std=gnu99
|
||||
|
||||
LIBNFNETLINK_CFLAGS := -I$(DEPSDIR)/include
|
||||
LIBNFNETLINK_LIBS := -L$(DEPSDIR)/lib
|
||||
@@ -16,27 +25,25 @@ LIBMNL_LIBS := -L$(DEPSDIR)/lib
|
||||
# PREFIX is environment variable, if not set default to /usr/local
|
||||
ifeq ($(PREFIX),)
|
||||
PREFIX := /usr/local
|
||||
else
|
||||
PREFIX := $(DESTDIR)
|
||||
endif
|
||||
|
||||
export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CFLAGS LIBMNL_LIBS
|
||||
|
||||
APP:=$(BUILD_DIR)/youtubeUnblock
|
||||
|
||||
SRCS := youtubeUnblock.c mangle.c
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c getopt.c
|
||||
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a
|
||||
LIBMNL := $(DEPSDIR)/lib/libmnl.a
|
||||
LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.a
|
||||
|
||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
||||
LIBMNL := $(DEPSDIR)/lib/libmnl.la
|
||||
LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.la
|
||||
#LIBCRYPTO := $(DEPSDIR)/lib64/libcrypto.a
|
||||
|
||||
.PHONY: default all dev dev_attrs prepare_dirs
|
||||
default: all
|
||||
|
||||
run_dev: dev
|
||||
bash -c "sudo $(APP) 537"
|
||||
bash -c "sudo $(APP)"
|
||||
|
||||
dev: dev_attrs all
|
||||
|
||||
@@ -49,6 +56,11 @@ prepare_dirs:
|
||||
mkdir -p $(BUILD_DIR)
|
||||
mkdir -p $(DEPSDIR)
|
||||
|
||||
$(LIBCRYPTO):
|
||||
cd deps/openssl && ./Configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--cross-compile-prefix=$(CROSS_COMPILE_PLATFORM)-,) --no-shared
|
||||
$(MAKE) -C deps/openssl
|
||||
$(MAKE) install_sw -C deps/openssl
|
||||
|
||||
$(LIBNFNETLINK):
|
||||
cd deps/libnfnetlink && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) --enable-static --disable-shared
|
||||
$(MAKE) -C deps/libnfnetlink
|
||||
@@ -59,36 +71,40 @@ $(LIBMNL):
|
||||
$(MAKE) -C deps/libmnl
|
||||
$(MAKE) install -C deps/libmnl
|
||||
|
||||
$(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL)
|
||||
$(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL)
|
||||
cd deps/libnetfilter_queue && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) --enable-static --disable-shared
|
||||
$(MAKE) -C deps/libnetfilter_queue
|
||||
$(MAKE) install -C deps/libnetfilter_queue
|
||||
|
||||
$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL)
|
||||
$(APP): $(OBJS) $(REQ)
|
||||
@echo 'CCLD $(APP)'
|
||||
$(CCLD) $(OBJS) -o $(APP) $(LDFLAGS) -lmnl -lnetfilter_queue
|
||||
$(CCLD) $(OBJS) -o $(APP) $(LDFLAGS) -lmnl -lnetfilter_queue -lpthread
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) config.h
|
||||
$(BUILD_DIR)/%.o: %.c $(REQ) config.h
|
||||
@echo 'CC $@'
|
||||
$(CC) -c $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
install: all
|
||||
install -d $(PREFIX)/bin/
|
||||
install -m 755 $(APP) $(PREFIX)/bin/
|
||||
install -d $(PREFIX)/lib/systemd/system/
|
||||
install -d $(DESTDIR)$(PREFIX)/bin/
|
||||
install -m 755 $(APP) $(DESTDIR)$(PREFIX)/bin/
|
||||
install -d $(DESTDIR)$(PREFIX)/lib/systemd/system/
|
||||
@cp youtubeUnblock.service $(BUILD_DIR)
|
||||
@sed -i 's/$$(PREFIX)/$(subst /,\/,$(PREFIX))/g' $(BUILD_DIR)/youtubeUnblock.service
|
||||
install -m 644 $(BUILD_DIR)/youtubeUnblock.service $(PREFIX)/lib/systemd/system/
|
||||
install -m 644 $(BUILD_DIR)/youtubeUnblock.service $(DESTDIR)$(PREFIX)/lib/systemd/system/
|
||||
|
||||
uninstall:
|
||||
rm $(PREFIX)/bin/youtubeUnblock
|
||||
rm $(PREFIX)/lib/systemd/system/youtubeUnblock.service
|
||||
rm $(DESTDIR)$(PREFIX)/bin/youtubeUnblock
|
||||
rm $(DESTDIR)$(PREFIX)/lib/systemd/system/youtubeUnblock.service
|
||||
-systemctl disable youtubeUnblock.service
|
||||
|
||||
clean:
|
||||
find $(BUILD_DIR) -maxdepth 1 -type f | xargs rm -rf
|
||||
|
||||
distclean: clean
|
||||
rm -rf $(BUILD_DIR)
|
||||
ifeq ($(USE_SYS_LIBS), no)
|
||||
$(MAKE) distclean -C deps/libnetfilter_queue || true
|
||||
$(MAKE) distclean -C deps/libmnl || true
|
||||
$(MAKE) distclean -C deps/libnfnetlink || true
|
||||
|
||||
|
||||
#$(MAKE) distclean -C deps/openssl || true
|
||||
endif
|
||||
|
||||
690
utils.c
Normal file
690
utils.c
Normal file
@@ -0,0 +1,690 @@
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
#include <stdlib.h>
|
||||
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
||||
#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
|
||||
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
|
||||
#else
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24))
|
||||
#include <net/ip6_checksum.h>
|
||||
#include <net/checksum.h>
|
||||
#else
|
||||
#include <net/checksum.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph)
|
||||
{
|
||||
#ifdef KERNEL_SPACE
|
||||
uint32_t tcp_packet_len = ntohs(iph->tot_len) - (iph->ihl << 2);
|
||||
tcph->check = 0;
|
||||
tcph->check = csum_tcpudp_magic(
|
||||
iph->saddr, iph->daddr, tcp_packet_len,
|
||||
IPPROTO_TCP,
|
||||
csum_partial(tcph, tcp_packet_len, 0));
|
||||
#else
|
||||
nfq_tcp_compute_checksum_ipv4(tcph, iph);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ip4_set_checksum(struct iphdr *iph)
|
||||
{
|
||||
#ifdef KERNEL_SPACE
|
||||
iph->check = 0;
|
||||
iph->check = ip_fast_csum(iph, iph->ihl);
|
||||
#else
|
||||
nfq_ip_set_checksum(iph);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph) {
|
||||
#ifdef KERNEL_SPACE
|
||||
tcph->check = 0;
|
||||
tcph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr,
|
||||
ntohs(iph->ip6_plen), IPPROTO_TCP,
|
||||
csum_partial(tcph, ntohs(iph->ip6_plen), 0));
|
||||
#else
|
||||
nfq_tcp_compute_checksum_ipv6(tcph, iph);
|
||||
#endif
|
||||
}
|
||||
|
||||
int set_ip_checksum(void *iph, uint32_t iphb_len) {
|
||||
int ipvx = netproto_version(iph, iphb_len);
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
ip4_set_checksum(iph);
|
||||
} else if (ipvx == IP6VERSION) { // IP6 has no checksums
|
||||
} else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len) {
|
||||
int ipvx = netproto_version(iph, iphb_len);
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
tcp4_set_checksum(tcph, iph);
|
||||
} else if (ipvx == IP6VERSION) {
|
||||
tcp6_set_checksum(tcph, iph);
|
||||
} else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
if (pkt == NULL || buflen < sizeof(struct iphdr)) {
|
||||
lgerror(-EINVAL, "ip4_payload_split: pkt|buflen");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct iphdr *hdr = (struct iphdr *)pkt;
|
||||
if (netproto_version(pkt, buflen) != IP4VERSION) {
|
||||
lgerror(-EINVAL, "ip4_payload_split: ipversion");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t hdr_len = hdr->ihl * 4;
|
||||
uint32_t pktlen = ntohs(hdr->tot_len);
|
||||
if (buflen < pktlen || hdr_len > pktlen) {
|
||||
lgerror(-EINVAL, "ip4_payload_split: buflen cmp pktlen");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iph)
|
||||
*iph = hdr;
|
||||
if (iph_len)
|
||||
*iph_len = hdr_len;
|
||||
if (payload)
|
||||
*payload = pkt + hdr_len;
|
||||
if (plen)
|
||||
*plen = pktlen - hdr_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
struct iphdr *hdr;
|
||||
uint32_t hdr_len;
|
||||
struct tcphdr *thdr;
|
||||
uint32_t thdr_len;
|
||||
|
||||
uint8_t *tcph_pl;
|
||||
uint32_t tcph_plen;
|
||||
|
||||
if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len,
|
||||
&tcph_pl, &tcph_plen)){
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
hdr->protocol != IPPROTO_TCP ||
|
||||
tcph_plen < sizeof(struct tcphdr)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
thdr = (struct tcphdr *)(tcph_pl);
|
||||
thdr_len = thdr->doff * 4;
|
||||
|
||||
if (thdr_len > tcph_plen) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iph) *iph = hdr;
|
||||
if (iph_len) *iph_len = hdr_len;
|
||||
if (tcph) *tcph = thdr;
|
||||
if (tcph_len) *tcph_len = thdr_len;
|
||||
if (payload) *payload = tcph_pl + thdr_len;
|
||||
if (plen) *plen = tcph_plen - thdr_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ip6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
if (pkt == NULL || buflen < sizeof(struct ip6_hdr)) {
|
||||
lgerror(-EINVAL, "ip6_payload_split: pkt|buflen");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct ip6_hdr *hdr = (struct ip6_hdr *)pkt;
|
||||
if (netproto_version(pkt, buflen) != 6) {
|
||||
lgerror(-EINVAL, "ip6_payload_split: ip6version");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t hdr_len = sizeof(struct ip6_hdr);
|
||||
uint32_t pktlen = ntohs(hdr->ip6_plen);
|
||||
if (buflen < pktlen) {
|
||||
lgerror(-EINVAL, "ip6_payload_split: buflen cmp pktlen: %d %d", buflen, pktlen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iph)
|
||||
*iph = hdr;
|
||||
if (iph_len)
|
||||
*iph_len = hdr_len;
|
||||
if (payload)
|
||||
*payload = pkt + hdr_len;
|
||||
if (plen)
|
||||
*plen = pktlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
struct ip6_hdr *hdr;
|
||||
uint32_t hdr_len;
|
||||
struct tcphdr *thdr;
|
||||
uint32_t thdr_len;
|
||||
|
||||
uint8_t *tcph_pl;
|
||||
uint32_t tcph_plen;
|
||||
|
||||
if (ip6_payload_split(pkt, buflen, &hdr, &hdr_len,
|
||||
&tcph_pl, &tcph_plen)){
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
hdr->ip6_nxt != IPPROTO_TCP ||
|
||||
tcph_plen < sizeof(struct tcphdr)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
thdr = (struct tcphdr *)(tcph_pl);
|
||||
thdr_len = thdr->doff * 4;
|
||||
|
||||
if (thdr_len > tcph_plen) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iph) *iph = hdr;
|
||||
if (iph_len) *iph_len = hdr_len;
|
||||
if (tcph) *tcph = thdr;
|
||||
if (tcph_len) *tcph_len = thdr_len;
|
||||
if (payload) *payload = tcph_pl + thdr_len;
|
||||
if (plen) *plen = tcph_plen - thdr_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
void **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
int netvers = netproto_version(pkt, buflen);
|
||||
if (netvers == IP4VERSION) {
|
||||
return tcp4_payload_split(pkt, buflen, (struct iphdr **)iph, iph_len, tcph, tcph_len, payload, plen);
|
||||
} else if (netvers == IP6VERSION) {
|
||||
return tcp6_payload_split(pkt, buflen, (struct ip6_hdr **)iph, iph_len, tcph, tcph_len, payload, plen);
|
||||
} else {
|
||||
lgerror(-EINVAL, "Internet Protocol version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int udp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
struct udphdr **udph,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
struct iphdr *hdr;
|
||||
uint32_t hdr_len;
|
||||
struct udphdr *uhdr;
|
||||
|
||||
uint8_t *ip_ph;
|
||||
uint32_t ip_phlen;
|
||||
|
||||
if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len,
|
||||
&ip_ph, &ip_phlen)){
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
hdr->protocol != IPPROTO_UDP ||
|
||||
ip_phlen < sizeof(struct udphdr)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
uhdr = (struct udphdr *)(ip_ph);
|
||||
if (uhdr->len != 0 && ntohs(uhdr->len) != ip_phlen) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iph) *iph = hdr;
|
||||
if (iph_len) *iph_len = hdr_len;
|
||||
if (udph) *udph = uhdr;
|
||||
if (payload) *payload = ip_ph + sizeof(struct udphdr);
|
||||
if (plen) *plen = ip_phlen - sizeof(struct udphdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udp6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
struct udphdr **udph,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
struct ip6_hdr *hdr;
|
||||
uint32_t hdr_len;
|
||||
struct udphdr *uhdr;
|
||||
|
||||
uint8_t *ip_ph;
|
||||
uint32_t ip_phlen;
|
||||
|
||||
if (ip6_payload_split(pkt, buflen, &hdr, &hdr_len,
|
||||
&ip_ph, &ip_phlen)){
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
hdr->ip6_nxt != IPPROTO_UDP ||
|
||||
ip_phlen < sizeof(struct udphdr)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
uhdr = (struct udphdr *)(ip_ph);
|
||||
if (uhdr->len != 0 && ntohs(uhdr->len) != ip_phlen) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iph) *iph = hdr;
|
||||
if (iph_len) *iph_len = hdr_len;
|
||||
if (udph) *udph = uhdr;
|
||||
if (payload) *payload = ip_ph + sizeof(struct udphdr);
|
||||
if (plen) *plen = ip_phlen - sizeof(struct udphdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udp_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
void **iph, uint32_t *iph_len,
|
||||
struct udphdr **udph,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
int netvers = netproto_version(pkt, buflen);
|
||||
if (netvers == IP4VERSION) {
|
||||
return udp4_payload_split(pkt, buflen, (struct iphdr **)iph, iph_len, udph, payload, plen);
|
||||
} else if (netvers == IP6VERSION) {
|
||||
return udp6_payload_split(pkt, buflen, (struct ip6_hdr **)iph, iph_len, udph, payload, plen);
|
||||
} else {
|
||||
lgerror(-EINVAL, "Internet Protocol version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
// split packet to two ipv4 fragments.
|
||||
int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
uint8_t *frag1, uint32_t *f1len,
|
||||
uint8_t *frag2, uint32_t *f2len) {
|
||||
|
||||
struct iphdr *hdr;
|
||||
const uint8_t *payload;
|
||||
uint32_t plen;
|
||||
uint32_t hdr_len;
|
||||
int ret;
|
||||
|
||||
if (!frag1 || !f1len || !frag2 || !f2len)
|
||||
return -EINVAL;
|
||||
|
||||
if ((ret = ip4_payload_split(
|
||||
(uint8_t *)pkt, buflen,
|
||||
&hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) {
|
||||
lgerror(ret, "ipv4_frag: TCP Header extract error");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (plen <= payload_offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (payload_offset & ((1 << 3) - 1)) {
|
||||
lgerror(-EINVAL, "ipv4_frag: Payload offset MUST be a multiply of 8!");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t f1_plen = payload_offset;
|
||||
uint32_t f1_dlen = f1_plen + hdr_len;
|
||||
|
||||
uint32_t f2_plen = plen - payload_offset;
|
||||
uint32_t f2_dlen = f2_plen + hdr_len;
|
||||
|
||||
if (*f1len < f1_dlen || *f2len < f2_dlen) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
*f1len = f1_dlen;
|
||||
*f2len = f2_dlen;
|
||||
|
||||
memcpy(frag1, hdr, hdr_len);
|
||||
memcpy(frag2, hdr, hdr_len);
|
||||
|
||||
memcpy(frag1 + hdr_len, payload, f1_plen);
|
||||
memcpy(frag2 + hdr_len, payload + payload_offset, f2_plen);
|
||||
|
||||
struct iphdr *f1_hdr = (void *)frag1;
|
||||
struct iphdr *f2_hdr = (void *)frag2;
|
||||
|
||||
uint16_t f1_frag_off = ntohs(f1_hdr->frag_off);
|
||||
uint16_t f2_frag_off = ntohs(f2_hdr->frag_off);
|
||||
|
||||
f1_frag_off &= IP_OFFMASK;
|
||||
f1_frag_off |= IP_MF;
|
||||
|
||||
if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) {
|
||||
f2_frag_off &= IP_OFFMASK;
|
||||
f2_frag_off |= IP_MF;
|
||||
} else {
|
||||
f2_frag_off &= IP_OFFMASK;
|
||||
}
|
||||
|
||||
f2_frag_off += (uint16_t)payload_offset / 8;
|
||||
|
||||
f1_hdr->frag_off = htons(f1_frag_off);
|
||||
f1_hdr->tot_len = htons(f1_dlen);
|
||||
|
||||
f2_hdr->frag_off = htons(f2_frag_off);
|
||||
f2_hdr->tot_len = htons(f2_dlen);
|
||||
|
||||
ip4_set_checksum(f1_hdr);
|
||||
ip4_set_checksum(f2_hdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// split packet to two tcp-on-ipv4 segments.
|
||||
int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
uint8_t *seg1, uint32_t *s1len,
|
||||
uint8_t *seg2, uint32_t *s2len) {
|
||||
|
||||
void *hdr;
|
||||
uint32_t hdr_len;
|
||||
struct tcphdr *tcph;
|
||||
uint32_t tcph_len;
|
||||
uint32_t plen;
|
||||
const uint8_t *payload;
|
||||
int ret;
|
||||
|
||||
if (!seg1 || !s1len || !seg2 || !s2len)
|
||||
return -EINVAL;
|
||||
|
||||
if ((ret = tcp_payload_split((uint8_t *)pkt, buflen,
|
||||
&hdr, &hdr_len,
|
||||
&tcph, &tcph_len,
|
||||
(uint8_t **)&payload, &plen)) < 0) {
|
||||
lgerror(ret, "tcp_frag: tcp_payload_split");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ipvx = netproto_version(pkt, buflen);
|
||||
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
struct iphdr *iphdr = hdr;
|
||||
if (
|
||||
ntohs(iphdr->frag_off) & IP_MF ||
|
||||
ntohs(iphdr->frag_off) & IP_OFFMASK) {
|
||||
lgdebugmsg("tcp_frag: ip4: frag value: %d",
|
||||
ntohs(iphdr->frag_off));
|
||||
lgerror(-EINVAL, "tcp_frag: ip4: ip fragmentation is set");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (plen <= payload_offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t s1_plen = payload_offset;
|
||||
uint32_t s1_dlen = s1_plen + hdr_len + tcph_len;
|
||||
|
||||
uint32_t s2_plen = plen - payload_offset;
|
||||
uint32_t s2_dlen = s2_plen + hdr_len + tcph_len;
|
||||
|
||||
if (*s1len < s1_dlen || *s2len < s2_dlen)
|
||||
return -ENOMEM;
|
||||
|
||||
*s1len = s1_dlen;
|
||||
*s2len = s2_dlen;
|
||||
|
||||
memcpy(seg1, hdr, hdr_len);
|
||||
memcpy(seg2, hdr, hdr_len);
|
||||
|
||||
memcpy(seg1 + hdr_len, tcph, tcph_len);
|
||||
memcpy(seg2 + hdr_len, tcph, tcph_len);
|
||||
|
||||
memcpy(seg1 + hdr_len + tcph_len, payload, s1_plen);
|
||||
memcpy(seg2 + hdr_len + tcph_len, payload + payload_offset, s2_plen);
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
struct iphdr *s1_hdr = (void *)seg1;
|
||||
struct iphdr *s2_hdr = (void *)seg2;
|
||||
s1_hdr->tot_len = htons(s1_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 {
|
||||
struct ip6_hdr *s1_hdr = (void *)seg1;
|
||||
struct ip6_hdr *s2_hdr = (void *)seg2;
|
||||
s1_hdr->ip6_plen = htons(s1_dlen - hdr_len);
|
||||
s2_hdr->ip6_plen = htons(s2_dlen - hdr_len);
|
||||
}
|
||||
|
||||
struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len);
|
||||
struct tcphdr *s2_tcph = (void *)(seg2 + hdr_len);
|
||||
|
||||
s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset);
|
||||
|
||||
set_tcp_checksum(s1_tcph, seg1, hdr_len);
|
||||
set_tcp_checksum(s2_tcph, seg2, hdr_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void z_function(const char *str, int *zbuf, size_t len) {
|
||||
zbuf[0] = len;
|
||||
|
||||
int lh = 0, rh = 1;
|
||||
for (int i = 1; i < (int)len; i++) {
|
||||
zbuf[i] = 0;
|
||||
if (i < rh) {
|
||||
zbuf[i] = zbuf[i - lh];
|
||||
if (rh - i < zbuf[i])
|
||||
zbuf[i] = rh - i;
|
||||
}
|
||||
|
||||
while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]])
|
||||
zbuf[i]++;
|
||||
|
||||
if (i + zbuf[i] > rh) {
|
||||
lh = i;
|
||||
rh = i + zbuf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_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(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) {
|
||||
void *iph;
|
||||
uint32_t iph_len;
|
||||
struct tcphdr *tcph;
|
||||
uint32_t tcph_len;
|
||||
uint8_t *data;
|
||||
uint32_t dlen;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_RAND_SEQ) {
|
||||
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
|
||||
|
||||
tcph->seq = htonl(ntohl(tcph->seq) - (strategy.randseq_offset + dlen));
|
||||
|
||||
lgtrace_addp("%u", ntohl(tcph->seq));
|
||||
} else if (strategy.strategy == FAKE_STRAT_PAST_SEQ) {
|
||||
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
|
||||
tcph->seq = htonl(ntohl(tcph->seq) - dlen);
|
||||
lgtrace_addp("%u", ntohl(tcph->seq));
|
||||
|
||||
} else if (strategy.strategy == FAKE_STRAT_TTL) {
|
||||
lgtrace_addp("set fake ttl to %d", strategy.faking_ttl);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->ttl = strategy.faking_ttl;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl;
|
||||
} else {
|
||||
lgerror(-EINVAL, "fail_packet: IP version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (strategy.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;
|
||||
}
|
||||
|
||||
shift_data(data, dlen, delta);
|
||||
data += delta;
|
||||
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(-EINVAL, "fail_packet: IP version is unsupported");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->frag_off = 0;
|
||||
}
|
||||
|
||||
|
||||
set_ip_checksum(iph, iph_len);
|
||||
set_tcp_checksum(tcph, iph, iph_len);
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_TCP_CHECK) {
|
||||
lgtrace_addp("break fake tcp checksum");
|
||||
tcph->check += 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
207
utils.h
Normal file
207
utils.h
Normal file
@@ -0,0 +1,207 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
#define IP4VERSION 4
|
||||
#define IP6VERSION 6
|
||||
|
||||
/**
|
||||
* Splits the packet to two IP fragments on position payload_offset.
|
||||
* payload_offset indicates the position relatively to start of IP payload
|
||||
* (start of transport header)
|
||||
*/
|
||||
int ip4_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||
uint32_t payload_offset,
|
||||
uint8_t *frag1, uint32_t *f1len,
|
||||
uint8_t *frag2, uint32_t *f2len);
|
||||
|
||||
/**
|
||||
* Splits the packet to two TCP segments on position payload_offset
|
||||
* payload_offset indicates the position relatively to start of TCP payload.
|
||||
*/
|
||||
// int tcp4_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||
// uint32_t payload_offset,
|
||||
// uint8_t *seg1, uint32_t *s1len,
|
||||
// uint8_t *seg2, uint32_t *s2len);
|
||||
int tcp_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||
uint32_t payload_offset,
|
||||
uint8_t *seg1, uint32_t *s1len,
|
||||
uint8_t *seg2, uint32_t *s2len);
|
||||
|
||||
|
||||
/**
|
||||
* Splits the raw packet payload to ip header and ip payload.
|
||||
*/
|
||||
int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
static inline int netproto_version(const uint8_t *pkt, uint32_t buflen) {
|
||||
if (pkt == NULL || buflen == 0)
|
||||
return -1;
|
||||
|
||||
return (*pkt) >> 4;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits the raw packet payload to ip header, tcp header and tcp payload.
|
||||
*/
|
||||
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
/**
|
||||
* Splits the raw packet payload to ip header and ip payload.
|
||||
*/
|
||||
int ip6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
/**
|
||||
* Splits the raw packet payload to ip header, tcp header and tcp payload.
|
||||
*/
|
||||
int tcp6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
int tcp_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
void **iph, uint32_t *iph_len,
|
||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
/**
|
||||
* Splits the raw packet payload to ip header, udp header and udp payload.
|
||||
*/
|
||||
int udp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct iphdr **iph, uint32_t *iph_len,
|
||||
struct udphdr **udph,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
int udp6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
struct udphdr **udph,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
int udp_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
void **iph, uint32_t *iph_len,
|
||||
struct udphdr **udph,
|
||||
uint8_t **payload, uint32_t *plen);
|
||||
|
||||
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph);
|
||||
void ip4_set_checksum(struct iphdr *iph);
|
||||
void ip6_set_checksum(struct ip6_hdr *iph);
|
||||
void tcp6_set_checksum(struct tcphdr *tcph, struct ip6_hdr *iph);
|
||||
|
||||
int set_ip_checksum(void *iph, uint32_t iphb_len);
|
||||
int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_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;
|
||||
};
|
||||
|
||||
struct udp_failing_strategy {
|
||||
unsigned int strategy;
|
||||
uint8_t faking_ttl;
|
||||
};
|
||||
|
||||
struct udp_fake_type {
|
||||
uint16_t fake_len;
|
||||
|
||||
// faking strategy of the fake packet.
|
||||
// Does not support bitmask, pass standalone strategy.
|
||||
// Pass 0 if you don't want any faking procedures.
|
||||
struct udp_failing_strategy strategy;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Does not support bitmask, pass standalone strategy.
|
||||
*/
|
||||
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(const struct section_config_t *section) {
|
||||
struct failing_strategy fl_strat = {
|
||||
.strategy = (unsigned int)section->faking_strategy,
|
||||
.faking_ttl = section->faking_ttl,
|
||||
.randseq_offset = (uint32_t)section->fakeseq_offset
|
||||
};
|
||||
return fl_strat;
|
||||
}
|
||||
|
||||
static inline struct fake_type args_default_fake_type(const struct section_config_t *section) {
|
||||
struct fake_type f_type = {
|
||||
.sequence_len = section->fake_sni_seq_len,
|
||||
.strategy = args_default_failing_strategy(section),
|
||||
};
|
||||
|
||||
switch (section->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 = section->fake_custom_pkt;
|
||||
f_type.fake_len = section->fake_custom_pkt_sz;
|
||||
break;
|
||||
default:
|
||||
f_type.type = FAKE_PAYLOAD_CUSTOM;
|
||||
f_type.fake_data = section->fake_sni_pkt;
|
||||
f_type.fake_len = section->fake_sni_pkt_sz;
|
||||
}
|
||||
|
||||
return f_type;
|
||||
}
|
||||
|
||||
#endif /* UTILS_H */
|
||||
826
youtubeUnblock.c
826
youtubeUnblock.c
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ StandardError=journal
|
||||
StandardOutput=journal
|
||||
StandardInput=null
|
||||
ExecStartPre=iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
||||
ExecStart=$(PREFIX)/bin/youtubeUnblock 537
|
||||
ExecStart=$(PREFIX)/bin/youtubeUnblock
|
||||
ExecStop=iptables -t mangle -D OUTPUT -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
||||
|
||||
[Install]
|
||||
|
||||
Reference in New Issue
Block a user