Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重写字符串匹配为 parser #147

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,6 @@ jobs:
run: |
pip install -r scripts/requirements.txt --break-system-packages

- name: Enable ipv6
uses: fscarmen/[email protected]
with:
stack: dual
mode: client

- name: Configure ipv6
run: |
echo "net.ipv6.conf.all.disable_ipv6=0" | sudo tee -a /etc/sysctl.conf
Expand All @@ -121,18 +115,20 @@ jobs:

ping6 -c 4 ::1 || { echo "::1 is not reachable"; exit 1; }

- name: Run unit tests
- name: Run Integration test
run: |
cmake -S . -B build -DUA2F_BUILD_TESTS=OFF -DUA2F_ENABLE_UCI=OFF -DUA2F_ENABLE_ASAN=ON ${{ matrix.cache }}
cmake --build build
sudo ./build/ua2f --version
ldd ./build/ua2f
sudo -E $(which python3) scripts/test.py ./build/ua2f

- name: Upload log
uses: actions/upload-artifact@v4
with:
name: syslog-cache-${{ matrix.cache }}
path: /var/log/syslog
if: always()
run: |
echo -e "\`\`\`log\n" >> $GITHUB_STEP_SUMMARY
cat /var/log/syslog >> $GITHUB_STEP_SUMMARY
echo -e "\n\`\`\`" >> $GITHUB_STEP_SUMMARY

build:
name: Build ${{ matrix.arch }}
Expand Down Expand Up @@ -170,6 +166,7 @@ jobs:
ARCH: ${{ matrix.arch }}-23.05.5
FEEDNAME: packages_ci
PACKAGES: openwrt
V: sc

- name: Move created packages to project dir
run: cp bin/packages/${{ matrix.arch }}/packages_ci/*.ipk . || true
Expand Down
12 changes: 10 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ add_executable(ua2f
src/config.c
src/third/nfqueue-mnl.c)

target_link_libraries(ua2f mnl netfilter_queue pthread nfnetlink)

if (UA2F_ENABLE_UCI)
add_compile_definitions(UA2F_ENABLE_UCI=1)
target_link_libraries(ua2f uci)
Expand All @@ -129,13 +127,23 @@ else ()
message(STATUS "Cache is auto.")
endif ()

set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "")
set(BUILD_STATIC_LIBS ON CACHE INTERNAL "")
add_subdirectory(src/third/llhttp-release-v9.2.1)

target_link_libraries(ua2f mnl netfilter_queue pthread nfnetlink llhttp_static)

install(TARGETS ua2f RUNTIME DESTINATION bin)

if (UA2F_BUILD_TESTS)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()

FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ uci commit ua2f
## TODO

- [ ] pthread 支持,由不同线程完成入队出队
- [ ] 重写正则匹配为 parser
- [ ] 以连接为单位维护 parser 状态
- [x] 重写字符串匹配为 parser
- [ ] 添加 UA 正则匹配

## License

Expand Down
38 changes: 17 additions & 21 deletions scripts/test.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import atexit
import http.server
import json
import logging
import os
import socket
import socketserver
import subprocess
import sys
import threading
import time
from http.server import HTTPServer, BaseHTTPRequestHandler

import requests
from fake_useragent import UserAgent
from tqdm import tqdm
from fastapi import FastAPI, Request
from fastapi.responses import Response
from fastapi.staticfiles import StaticFiles
from tqdm import tqdm
from uvicorn import Config, Server

ua = UserAgent()
Expand All @@ -30,10 +21,11 @@
async def root(request: Request):
user_agent = request.headers.get("user-agent")

code = 200
if not all(c == 'F' for c in user_agent):
return Response(status_code=400)
code = 400

return Response(content=str(len(user_agent)).encode())
return Response(status_code=code, content=str(user_agent).encode())

def start_server():
config4 = Config(app=app, host="127.0.0.1", port=PORT, access_log=False)
Expand All @@ -48,19 +40,23 @@ def start_server():
t6.start()

def start_ua2f(u: str):
p = subprocess.Popen([u])
atexit.register(lambda: p.kill())
r = os.system(u)
if r != 0:
print(f"UA2F failed with exit code {r}")
exit(-1)


def setup_iptables():
os.system(f"sudo iptables -A OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")
os.system(f"sudo ip6tables -A OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")

os.system(f"iptables -A OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")
os.system(f"ip6tables -A OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")

def cleanup_iptables():
os.system(f"sudo iptables -D OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")
os.system(f"sudo ip6tables -D OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")
os.system(f"iptables -D OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")
os.system(f"ip6tables -D OUTPUT -p tcp --dport {PORT} -j NFQUEUE --queue-num 10010")

def assert_equal(actual, expected):
if actual != expected:
raise AssertionError(f"Assertion failed: Expected {expected!r}, but got {actual!r}")

if __name__ == "__main__":
if os.name != 'posix':
Expand Down Expand Up @@ -89,15 +85,15 @@ def cleanup_iptables():
"User-Agent": nxt
})
assert response.ok
assert response.text == str(len(nxt))
assert len(response.text) == len(nxt)

for i in tqdm(range(4096)):
nxt = ua.random
response = requests.get(f"http://[::1]:{PORT}", headers={
"User-Agent": nxt
})
assert response.ok
assert response.text == str(len(nxt))
assert len(response.text) == len(nxt)

# clean
cleanup_iptables()
55 changes: 36 additions & 19 deletions src/cache.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#include "cache.h"
#include "third/uthash.h"

#include <assert.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/syslog.h>
Expand All @@ -9,56 +7,75 @@
#include <string.h>
#include <time.h>

#include "cache.h"

#include "third/uthash.h"

pthread_rwlock_t cacheLock;

struct cache *not_http_dst_cache = NULL;
static int check_interval;
struct cache *dst_cache = NULL;

typedef struct {
int check_interval;
pthread_rwlock_t* lock;
struct cache** table;
} thread_args;

_Noreturn static void* check_cache(void* arg) {
pthread_detach(pthread_self());

const __auto_type args = (thread_args *)arg;
const __auto_type lock = args->lock;
const __auto_type check_interval = args->check_interval;
const __auto_type cache_ptr = args->table;
free(args);

_Noreturn static void* check_cache(void* arg __attribute__((unused))) {
while (true) {
pthread_rwlock_wrlock(&cacheLock);
pthread_rwlock_wrlock(lock);

const time_t now = time(NULL);
struct cache *cur, *tmp;

HASH_ITER(hh, not_http_dst_cache, cur, tmp) {
HASH_ITER(hh, *cache_ptr, cur, tmp) {
if (difftime(now, cur->last_time) > check_interval) {
HASH_DEL(not_http_dst_cache, cur);
HASH_DEL(*cache_ptr, cur);
free(cur);
}
}

pthread_rwlock_unlock(&cacheLock);
pthread_rwlock_unlock(lock);

sleep(check_interval);
}
}

void init_not_http_cache(const int interval) {
check_interval = interval;

if (pthread_rwlock_init(&cacheLock, NULL) != 0) {
syslog(LOG_ERR, "Failed to init cache lock");
exit(EXIT_FAILURE);
}
syslog(LOG_INFO, "Cache lock initialized");

pthread_t cleanup_thread;
const __auto_type ret = pthread_create(&cleanup_thread, NULL, check_cache, NULL);

thread_args* arg = malloc(sizeof(thread_args));
arg->check_interval = interval;
arg->lock = &cacheLock;
arg->table = &dst_cache;

const __auto_type ret = pthread_create(&cleanup_thread, NULL, check_cache, arg);
if (ret) {
syslog(LOG_ERR, "Failed to create cleanup thread: %d", ret);
syslog(LOG_ERR, "Failed to create cleanup thread: %s", strerror(ret));
exit(EXIT_FAILURE);
}
syslog(LOG_INFO, "Cleanup thread created");

pthread_detach(cleanup_thread);
}

bool cache_contains(struct addr_port target) {
pthread_rwlock_wrlock(&cacheLock);

struct cache *s;
HASH_FIND(hh, not_http_dst_cache, &target, sizeof(struct addr_port), s);
HASH_FIND(hh, dst_cache, &target, sizeof(struct addr_port), s);
if (s != NULL) {
s->last_time = time(NULL);
}
Expand All @@ -73,11 +90,11 @@ void cache_add(struct addr_port addr_port) {

struct cache *s;

HASH_FIND(hh, not_http_dst_cache, &addr_port, sizeof(struct addr_port), s);
HASH_FIND(hh, dst_cache, &addr_port, sizeof(struct addr_port), s);
if (s == NULL) {
s = malloc(sizeof(struct cache));
memcpy(&s->target.addr, &addr_port, sizeof(struct addr_port));
HASH_ADD(hh, not_http_dst_cache, target.addr, sizeof(struct addr_port), s);
HASH_ADD(hh, dst_cache, target.addr, sizeof(struct addr_port), s);
}
s->last_time = time(NULL);

Expand Down
5 changes: 2 additions & 3 deletions src/cache.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#ifndef UA2F_CACHE_H
#define UA2F_CACHE_H

#include <stdbool.h>
#include <time.h>
#include <pthread.h>
#include <stdbool.h>

#include "third/nfqueue-mnl.h"
#include "third/uthash.h"
Expand All @@ -19,7 +18,7 @@ struct cache {
UT_hash_handle hh;
};

extern struct cache *not_http_dst_cache;
extern struct cache *dst_cache;
extern pthread_rwlock_t cacheLock;

void init_not_http_cache(int interval);
Expand Down
56 changes: 27 additions & 29 deletions src/handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,32 +177,6 @@ enum {
IP_UNK = 0,
};

static bool ipv4_set_transport_header(struct pkt_buff *pkt_buff) {
struct iphdr *ip_hdr = nfq_ip_get_hdr(pkt_buff);
if (ip_hdr == NULL) {
return false;
}

if (nfq_ip_set_transport_header(pkt_buff, ip_hdr) < 0) {
syslog(LOG_ERR, "Failed to set ipv4 transport header");
return false;
}
return true;
}

static bool ipv6_set_transport_header(struct pkt_buff *pkt_buff) {
struct ip6_hdr *ip_hdr = nfq_ip6_get_hdr(pkt_buff);
if (ip_hdr == NULL) {
return false;
}

if (nfq_ip6_set_transport_header(pkt_buff, ip_hdr, IPPROTO_TCP) < 0) {
syslog(LOG_ERR, "Failed to set ipv6 transport header");
return false;
}
return true;
}

int get_pkt_ip_version(const struct nf_packet *pkt) {
if (pkt->has_conntrack) {
return pkt->orig.ip_version;
Expand Down Expand Up @@ -242,19 +216,43 @@ void handle_packet(const struct nf_queue *queue, const struct nf_packet *pkt) {
// will this happen?
send_verdict(queue, pkt, get_next_mark(pkt, false), NULL);
syslog(LOG_WARNING, "Received unknown ip packet %x. You may set wrong firewall rules.", pkt->hw_protocol);
goto end;
}

if (type == IPV4) {
assert(ipv4_set_transport_header(pkt_buff));
const __auto_type ip_hdr = nfq_ip_get_hdr(pkt_buff);
if (ip_hdr == NULL) {
send_verdict(queue, pkt, (struct mark_op){false, 0}, NULL);
syslog(LOG_WARNING, "Failed to get ipv4 header. You may set wrong firewall rules.");
goto end;
}

if (nfq_ip_set_transport_header(pkt_buff, ip_hdr) < 0) {
send_verdict(queue, pkt, (struct mark_op){false, 0}, NULL);
syslog(LOG_WARNING, "Failed to set ipv4 transport header. You may set wrong firewall rules.");
goto end;
}

count_ipv4_packet();
} else if (type == IPV6) {
assert(ipv6_set_transport_header(pkt_buff));
const __auto_type ip_hdr = nfq_ip6_get_hdr(pkt_buff);
if (ip_hdr == NULL) {
send_verdict(queue, pkt, (struct mark_op){false, 0}, NULL);
syslog(LOG_WARNING, "Failed to get ipv6 header. You may set wrong firewall rules.");
goto end;
}

if (nfq_ip6_set_transport_header(pkt_buff, ip_hdr, IPPROTO_TCP) < 0) {
send_verdict(queue, pkt, (struct mark_op){false, 0}, NULL);
syslog(LOG_WARNING, "Failed to set ipv6 transport header. You may set wrong firewall rules.");
goto end;
}

count_ipv6_packet();
}

const __auto_type tcp_hdr = nfq_tcp_get_hdr(pkt_buff);
if (tcp_hdr == NULL) {
// This packet is not tcp, pass it
send_verdict(queue, pkt, (struct mark_op){false, 0}, NULL);
syslog(LOG_WARNING, "Received non-tcp packet. You may set wrong firewall rules.");
goto end;
Expand Down
1 change: 1 addition & 0 deletions src/third/llhttp-release-v9.2.1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
libllhttp.pc
Loading
Loading