mirror of
https://github.com/bol-van/zapret.git
synced 2024-12-04 15:40:52 +03:00
ipv6 support for nfqws
This commit is contained in:
parent
42ff15ba30
commit
21452bd4ad
@ -26,3 +26,7 @@ v4
|
|||||||
|
|
||||||
tpws : added ability to insert extra space after http method : "GET /" => "GET /"
|
tpws : added ability to insert extra space after http method : "GET /" => "GET /"
|
||||||
ISP support : TKT support
|
ISP support : TKT support
|
||||||
|
|
||||||
|
v5
|
||||||
|
|
||||||
|
nfqws : поддержка ipv6 в nfqws
|
||||||
|
170
nfq/nfqws.c
170
nfq/nfqws.c
@ -3,9 +3,10 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <linux/ip.h>
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include <netinet/in.h>
|
//#include <netinet/in.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/netfilter.h> /* for NF_ACCEPT */
|
#include <linux/netfilter.h> /* for NF_ACCEPT */
|
||||||
#include <libnetfilter_queue/libnetfilter_queue.h>
|
#include <libnetfilter_queue/libnetfilter_queue.h>
|
||||||
@ -15,19 +16,21 @@
|
|||||||
|
|
||||||
bool proto_check_ipv4(unsigned char *data,int len)
|
bool proto_check_ipv4(unsigned char *data,int len)
|
||||||
{
|
{
|
||||||
return len && (data[0] & 0xF0)==0x40 &&
|
return len>=20 && (data[0] & 0xF0)==0x40 &&
|
||||||
len>=((data[0] & 0x0F)<<2);
|
len>=((data[0] & 0x0F)<<2);
|
||||||
}
|
}
|
||||||
|
// move to transport protocol
|
||||||
void proto_skip_ipv4(unsigned char **data,int *len)
|
void proto_skip_ipv4(unsigned char **data,int *len)
|
||||||
{
|
{
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
l = (**data & 0x0F)<<2;
|
l = (**data & 0x0F)<<2;
|
||||||
*data += l;
|
*data += l;
|
||||||
*len -= l;
|
*len -= l;
|
||||||
}
|
}
|
||||||
bool proto_check_tcp(unsigned char *data,int len)
|
bool proto_check_tcp(unsigned char *data,int len)
|
||||||
{
|
{
|
||||||
return len>=((data[12] & 0xF0)>>2);
|
return len>=20 && len>=((data[12] & 0xF0)>>2);
|
||||||
}
|
}
|
||||||
void proto_skip_tcp(unsigned char **data,int *len)
|
void proto_skip_tcp(unsigned char **data,int *len)
|
||||||
{
|
{
|
||||||
@ -37,6 +40,51 @@ void proto_skip_tcp(unsigned char **data,int *len)
|
|||||||
*len -= l;
|
*len -= l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool proto_check_ipv6(unsigned char *data,int len)
|
||||||
|
{
|
||||||
|
return len>=40 && (data[0] & 0xF0)==0x60 &&
|
||||||
|
(len-40)>=htons(*(uint16_t*)(data+4)); // payload length
|
||||||
|
}
|
||||||
|
// move to transport protocol
|
||||||
|
// proto_type = 0 => error
|
||||||
|
void proto_skip_ipv6(unsigned char **data,int *len,uint8_t *proto_type)
|
||||||
|
{
|
||||||
|
int hdrlen;
|
||||||
|
uint8_t HeaderType;
|
||||||
|
|
||||||
|
*proto_type = 0; // put error in advance
|
||||||
|
|
||||||
|
HeaderType = (*data)[6]; // NextHeader field
|
||||||
|
*data += 40; *len -= 40; // skip ipv6 base header
|
||||||
|
while(*len>0) // need at least one byte for NextHeader field
|
||||||
|
{
|
||||||
|
switch(HeaderType)
|
||||||
|
{
|
||||||
|
case 0: // Hop-by-Hop Options
|
||||||
|
case 60: // Destination Options
|
||||||
|
case 43: // routing
|
||||||
|
if (*len<2) return; // error
|
||||||
|
hdrlen = 8+((*data)[1]<<3);
|
||||||
|
break;
|
||||||
|
case 44: // fragment
|
||||||
|
hdrlen = 4;
|
||||||
|
break;
|
||||||
|
case 59: // no next header
|
||||||
|
return; // error
|
||||||
|
default:
|
||||||
|
// we found some meaningful payload. it can be tcp, udp, icmp or some another exotic shit
|
||||||
|
*proto_type = HeaderType;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (*len<hdrlen) return; // error
|
||||||
|
HeaderType = **data;
|
||||||
|
// advance to the next header location
|
||||||
|
*len -= hdrlen;
|
||||||
|
*data += hdrlen;
|
||||||
|
}
|
||||||
|
// we have garbage
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char *find_bin(unsigned char *data,int len,const void *blk,int blk_len)
|
unsigned char *find_bin(unsigned char *data,int len,const void *blk,int blk_len)
|
||||||
{
|
{
|
||||||
while (len>=blk_len)
|
while (len>=blk_len)
|
||||||
@ -60,10 +108,10 @@ static inline bool tcp_synack_segment( const struct tcphdr *tcphdr )
|
|||||||
tcphdr->fin == 0;
|
tcphdr->fin == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr)
|
uint16_t tcp_checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr)
|
||||||
{
|
{
|
||||||
const uint16_t *buf=buff;
|
const uint16_t *buf=buff;
|
||||||
uint16_t *ip_src=(void *)&src_addr, *ip_dst=(void *)&dest_addr;
|
uint16_t *ip_src=(uint16_t *)&src_addr, *ip_dst=(uint16_t *)&dest_addr;
|
||||||
uint32_t sum;
|
uint32_t sum;
|
||||||
int length=len;
|
int length=len;
|
||||||
|
|
||||||
@ -79,6 +127,7 @@ uint16_t checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_
|
|||||||
if ( len & 1 )
|
if ( len & 1 )
|
||||||
// Add the padding if the packet lenght is odd
|
// Add the padding if the packet lenght is odd
|
||||||
sum += *((uint8_t *)buf);
|
sum += *((uint8_t *)buf);
|
||||||
|
|
||||||
// Add the pseudo-header
|
// Add the pseudo-header
|
||||||
sum += *(ip_src++);
|
sum += *(ip_src++);
|
||||||
sum += *ip_src;
|
sum += *ip_src;
|
||||||
@ -86,6 +135,7 @@ uint16_t checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_
|
|||||||
sum += *ip_dst;
|
sum += *ip_dst;
|
||||||
sum += htons(IPPROTO_TCP);
|
sum += htons(IPPROTO_TCP);
|
||||||
sum += htons(length);
|
sum += htons(length);
|
||||||
|
|
||||||
// Add the carries
|
// Add the carries
|
||||||
while (sum >> 16)
|
while (sum >> 16)
|
||||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
@ -96,7 +146,59 @@ uint16_t checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_
|
|||||||
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr)
|
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr)
|
||||||
{
|
{
|
||||||
tcp->check = 0;
|
tcp->check = 0;
|
||||||
tcp->check = checksum(tcp,len,src_addr,dest_addr);
|
tcp->check = tcp_checksum(tcp,len,src_addr,dest_addr);
|
||||||
|
}
|
||||||
|
uint16_t tcp6_checksum(const void *buff, int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
|
||||||
|
{
|
||||||
|
const uint16_t *buf=buff;
|
||||||
|
const uint16_t *ip_src=(uint16_t *)src_addr, *ip_dst=(uint16_t *)dest_addr;
|
||||||
|
uint32_t sum;
|
||||||
|
int length=len;
|
||||||
|
|
||||||
|
// Calculate the sum
|
||||||
|
sum = 0;
|
||||||
|
while (len > 1)
|
||||||
|
{
|
||||||
|
sum += *buf++;
|
||||||
|
if (sum & 0x80000000)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
if ( len & 1 )
|
||||||
|
// Add the padding if the packet lenght is odd
|
||||||
|
sum += *((uint8_t *)buf);
|
||||||
|
|
||||||
|
// Add the pseudo-header
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *(ip_src++);
|
||||||
|
sum += *ip_src;
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *(ip_dst++);
|
||||||
|
sum += *ip_dst;
|
||||||
|
sum += htons(IPPROTO_TCP);
|
||||||
|
sum += htons(length);
|
||||||
|
|
||||||
|
// Add the carries
|
||||||
|
while (sum >> 16)
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
|
||||||
|
// Return the one's complement of sum
|
||||||
|
return (uint16_t)(~sum);
|
||||||
|
}
|
||||||
|
void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
|
||||||
|
{
|
||||||
|
tcp->check = 0;
|
||||||
|
tcp->check = tcp6_checksum(tcp,len,src_addr,dest_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcp_rewrite_winsize(struct tcphdr *tcp,uint16_t winsize)
|
void tcp_rewrite_winsize(struct tcphdr *tcp,uint16_t winsize)
|
||||||
@ -144,33 +246,53 @@ struct cbdata_s
|
|||||||
bool processPacketData(unsigned char *data,int len,const struct cbdata_s *cbdata)
|
bool processPacketData(unsigned char *data,int len,const struct cbdata_s *cbdata)
|
||||||
{
|
{
|
||||||
struct iphdr *iphdr = NULL;
|
struct iphdr *iphdr = NULL;
|
||||||
|
struct ip6_hdr *ip6hdr = NULL;
|
||||||
struct tcphdr *tcphdr = NULL;
|
struct tcphdr *tcphdr = NULL;
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
int len_tcp;
|
int len_tcp;
|
||||||
bool bRet = false;
|
bool bRet = false;
|
||||||
|
uint8_t proto;
|
||||||
|
|
||||||
if (proto_check_ipv4(data,len))
|
if (proto_check_ipv4(data,len))
|
||||||
{
|
{
|
||||||
iphdr = (struct iphdr *) data;
|
iphdr = (struct iphdr *) data;
|
||||||
|
proto = iphdr->protocol;
|
||||||
proto_skip_ipv4(&data,&len);
|
proto_skip_ipv4(&data,&len);
|
||||||
if (iphdr->protocol==6 && proto_check_tcp(data,len))
|
}
|
||||||
|
else if (proto_check_ipv6(data,len))
|
||||||
|
{
|
||||||
|
ip6hdr = (struct ip6_hdr *) data;
|
||||||
|
proto_skip_ipv6(&data,&len,&proto);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// not ipv6 and not ipv4
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proto==IPPROTO_TCP && proto_check_tcp(data,len))
|
||||||
|
{
|
||||||
|
tcphdr = (struct tcphdr *) data;
|
||||||
|
len_tcp = len;
|
||||||
|
proto_skip_tcp(&data,&len);
|
||||||
|
//printf("got TCP packet. payload_len=%d\n",len);
|
||||||
|
if (cbdata->wsize && tcp_synack_segment(tcphdr))
|
||||||
{
|
{
|
||||||
tcphdr = (struct tcphdr *) data;
|
tcp_rewrite_winsize(tcphdr,(uint16_t)cbdata->wsize);
|
||||||
len_tcp = len;
|
bRet = true;
|
||||||
proto_skip_tcp(&data,&len);
|
}
|
||||||
//printf("got TCP packet. payload_len=%d\n",len);
|
if (cbdata->hostcase && (p = find_bin(data,len,"\r\nHost: ",8)))
|
||||||
if (cbdata->wsize && tcp_synack_segment(tcphdr))
|
{
|
||||||
{
|
printf("modifying Host: => host:\n");
|
||||||
tcp_rewrite_winsize(tcphdr,(uint16_t)cbdata->wsize);
|
p[2]='h'; // "Host:" => "host:"
|
||||||
bRet = true;
|
bRet = true;
|
||||||
}
|
}
|
||||||
if (cbdata->hostcase && (p = find_bin(data,len,"\r\nHost: ",8)))
|
if (bRet)
|
||||||
{
|
{
|
||||||
printf("modifying Host: => host:\n");
|
if (iphdr)
|
||||||
p[2]='h'; // "Host:" => "host:"
|
tcp_fix_checksum(tcphdr,len_tcp,iphdr->saddr,iphdr->daddr);
|
||||||
bRet = true;
|
else
|
||||||
}
|
tcp6_fix_checksum(tcphdr,len_tcp,&ip6hdr->ip6_src,&ip6hdr->ip6_dst);
|
||||||
if (bRet) tcp_fix_checksum(tcphdr,len_tcp,iphdr->saddr,iphdr->daddr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bRet;
|
return bRet;
|
||||||
|
30
readme.txt
30
readme.txt
@ -1,4 +1,4 @@
|
|||||||
zapret v.4
|
zapret v.5
|
||||||
|
|
||||||
Для чего это надо
|
Для чего это надо
|
||||||
-----------------
|
-----------------
|
||||||
@ -16,18 +16,29 @@
|
|||||||
Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты.
|
Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты.
|
||||||
Например, запрос вида "GET / HTTP/1.1\r\nHost: kinozal.tv......"
|
Например, запрос вида "GET / HTTP/1.1\r\nHost: kinozal.tv......"
|
||||||
мы посылаем 2 частями : сначала идет "GET ", затем "/ HTTP/1.1\r\nHost: kinozal.tv.....".
|
мы посылаем 2 частями : сначала идет "GET ", затем "/ HTTP/1.1\r\nHost: kinozal.tv.....".
|
||||||
Как заставить систему разбивать запрос на 2 части ? Подменить поле tcp window size
|
|
||||||
на первом входящем TCP пакете с SYN,ACK. Тогда клиент подумает, что сервер установил
|
|
||||||
для него маленький window size и первый сегмент с данными отошлет не более указанной длины.
|
|
||||||
В следующем пакете мы не будем менять ничего, поэтому клиент это поймет так,
|
|
||||||
что сервер увеличил window size, и все пойдет как обычно.
|
|
||||||
|
|
||||||
Другие DPI спотыкаются, когда заголовок "Host:" пишется в другом регистре : например, "host:".
|
Другие DPI спотыкаются, когда заголовок "Host:" пишется в другом регистре : например, "host:".
|
||||||
Кое-где работает добавление дополнительного пробела после метода : "GET /" => "GET /".
|
Кое-где работает добавление дополнительного пробела после метода : "GET /" => "GET /".
|
||||||
|
|
||||||
Как это реализовать на практике в системе linux
|
Как это реализовать на практике в системе linux
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Как заставить систему разбивать запрос на части ? Можно прогнать всю TCP сессию
|
||||||
|
через transparent proxy, а можно подменить поле tcp window size на первом входящем TCP пакете с SYN,ACK.
|
||||||
|
Тогда клиент подумает, что сервер установил для него маленький window size и первый сегмент с данными
|
||||||
|
отошлет не более указанной длины. В последующих пакетах мы не будем менять ничего.
|
||||||
|
Дальнейшее поведение системы по выбору размера отсылаемых пакетов зависит от реализованного
|
||||||
|
в ней алгоритма. Опыт показывает, что linux первый пакет всегда отсылает не более указанной
|
||||||
|
в window size длины, остальные пакеты до некоторых пор шлет не более max(36,указанный_размер).
|
||||||
|
После некоторого количества пакетов срабатывает механизм window scaling и начинает
|
||||||
|
учитываться фактор скалинга, размер пакетов становится не более max(36,указанный_рамер << scale_factor).
|
||||||
|
Не слишком изящное поведение, но поскольку на размеры входящик пакетов мы не влияем,
|
||||||
|
а объем принимаемых по http данных обычно гораздо выше объема отсылаемых, то визуально
|
||||||
|
появятся лишь небольшие задержки.
|
||||||
|
Windows ведет себя в аналогичном случае гораздо более предсказуемо. Первый сегмент
|
||||||
|
уходит указанной длины, дальше window size меняется в зависимости от значения,
|
||||||
|
присылаемого в новых tcp пакетах. То есть скорость почти сразу же восстанавливается
|
||||||
|
до возможного максимума.
|
||||||
|
|
||||||
Перехватить пакет с SYN,ACK не представляет никакой сложности средствами iptables.
|
Перехватить пакет с SYN,ACK не представляет никакой сложности средствами iptables.
|
||||||
Однако, возможности редактирования пакетов в iptables сильно ограничены.
|
Однако, возможности редактирования пакетов в iptables сильно ограничены.
|
||||||
Просто так поменять window size стандартными модулями нельзя.
|
Просто так поменять window size стандартными модулями нельзя.
|
||||||
@ -42,8 +53,7 @@ iptables -t raw -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j N
|
|||||||
так и маршрутизируемые пакеты. То есть решение одинаково работает как на клиенте,
|
так и маршрутизируемые пакеты. То есть решение одинаково работает как на клиенте,
|
||||||
так и на роутере. На роутере на базе PC или на базе OpenWRT.
|
так и на роутере. На роутере на базе PC или на базе OpenWRT.
|
||||||
В принципе этого достаточно.
|
В принципе этого достаточно.
|
||||||
Однако, при таком воздействии на TCP будет небольшая задержка при установлении соединения.
|
Однако, при таком воздействии на TCP будет небольшая задержка.
|
||||||
От 0.5 до 1.5 сек.
|
|
||||||
Чтобы не трогать хосты, которые не блокируются провайдером, можно сделать такой ход.
|
Чтобы не трогать хосты, которые не блокируются провайдером, можно сделать такой ход.
|
||||||
Создать список заблоченых доменов или скачать его с rublacklist.
|
Создать список заблоченых доменов или скачать его с rublacklist.
|
||||||
Заресолвить все домены в ipv4 адреса. Загнать их в ipset с именем "zapret".
|
Заресолвить все домены в ipv4 адреса. Загнать их в ipset с именем "zapret".
|
||||||
@ -118,7 +128,7 @@ tkt : помогает разделение http запроса на сегме
|
|||||||
ТКТ был куплен ростелекомом, используется фильтрация ростелекома.
|
ТКТ был куплен ростелекомом, используется фильтрация ростелекома.
|
||||||
Поскольку DPI не отбрасывает входящую сессию, а только всовывает свой пакет, который приходит раньше ответа от настоящего сервера,
|
Поскольку DPI не отбрасывает входящую сессию, а только всовывает свой пакет, который приходит раньше ответа от настоящего сервера,
|
||||||
блокировки так же обходятся без применения "тяжелой артиллерии" следующим правилом :
|
блокировки так же обходятся без применения "тяжелой артиллерии" следующим правилом :
|
||||||
iptables -t raw -I PREROUTING -p tcp --sport 80 -m string --hex-string "Location: http://95.167.13.50" --algo bm -j DROP --from 40 --to 300
|
iptables -t raw -I PREROUTING -p tcp --sport 80 -m string --hex-string "|0D0A|Location: http://95.167.13.50" --algo bm -j DROP --from 40 --to 300
|
||||||
Ростелеком : см tkt
|
Ростелеком : см tkt
|
||||||
|
|
||||||
Пример установки на debian 7
|
Пример установки на debian 7
|
||||||
|
Loading…
Reference in New Issue
Block a user