ipv6 support for nfqws

This commit is contained in:
bolvan 2016-02-17 17:54:03 +03:00
parent 42ff15ba30
commit 21452bd4ad
3 changed files with 171 additions and 35 deletions

View File

@ -26,3 +26,7 @@ v4
tpws : added ability to insert extra space after http method : "GET /" => "GET /"
ISP support : TKT support
v5
nfqws : поддержка ipv6 в nfqws

View File

@ -3,9 +3,10 @@
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <linux/ip.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <linux/tcp.h>
#include <netinet/in.h>
//#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
#include <libnetfilter_queue/libnetfilter_queue.h>
@ -15,19 +16,21 @@
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);
}
// move to transport protocol
void proto_skip_ipv4(unsigned char **data,int *len)
{
int l;
l = (**data & 0x0F)<<2;
*data += l;
*len -= l;
}
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)
{
@ -37,6 +40,51 @@ void proto_skip_tcp(unsigned char **data,int *len)
*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)
{
while (len>=blk_len)
@ -60,10 +108,10 @@ static inline bool tcp_synack_segment( const struct tcphdr *tcphdr )
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;
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;
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 )
// Add the padding if the packet lenght is odd
sum += *((uint8_t *)buf);
// Add the pseudo-header
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 += htons(IPPROTO_TCP);
sum += htons(length);
// Add the carries
while (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)
{
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)
@ -144,33 +246,53 @@ struct cbdata_s
bool processPacketData(unsigned char *data,int len,const struct cbdata_s *cbdata)
{
struct iphdr *iphdr = NULL;
struct ip6_hdr *ip6hdr = NULL;
struct tcphdr *tcphdr = NULL;
unsigned char *p;
int len_tcp;
bool bRet = false;
uint8_t proto;
if (proto_check_ipv4(data,len))
{
iphdr = (struct iphdr *) data;
proto = iphdr->protocol;
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;
len_tcp = len;
proto_skip_tcp(&data,&len);
//printf("got TCP packet. payload_len=%d\n",len);
if (cbdata->wsize && tcp_synack_segment(tcphdr))
{
tcp_rewrite_winsize(tcphdr,(uint16_t)cbdata->wsize);
bRet = true;
}
if (cbdata->hostcase && (p = find_bin(data,len,"\r\nHost: ",8)))
{
printf("modifying Host: => host:\n");
p[2]='h'; // "Host:" => "host:"
bRet = true;
}
if (bRet) tcp_fix_checksum(tcphdr,len_tcp,iphdr->saddr,iphdr->daddr);
tcp_rewrite_winsize(tcphdr,(uint16_t)cbdata->wsize);
bRet = true;
}
if (cbdata->hostcase && (p = find_bin(data,len,"\r\nHost: ",8)))
{
printf("modifying Host: => host:\n");
p[2]='h'; // "Host:" => "host:"
bRet = true;
}
if (bRet)
{
if (iphdr)
tcp_fix_checksum(tcphdr,len_tcp,iphdr->saddr,iphdr->daddr);
else
tcp6_fix_checksum(tcphdr,len_tcp,&ip6hdr->ip6_src,&ip6hdr->ip6_dst);
}
}
return bRet;

View File

@ -1,4 +1,4 @@
zapret v.4
zapret v.5
Для чего это надо
-----------------
@ -16,18 +16,29 @@
Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты.
Например, запрос вида "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:".
Кое-где работает добавление дополнительного пробела после метода : "GET /" => "GET /".
Как это реализовать на практике в системе 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.
Однако, возможности редактирования пакетов в iptables сильно ограничены.
Просто так поменять 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.
В принципе этого достаточно.
Однако, при таком воздействии на TCP будет небольшая задержка при установлении соединения.
От 0.5 до 1.5 сек.
Однако, при таком воздействии на TCP будет небольшая задержка.
Чтобы не трогать хосты, которые не блокируются провайдером, можно сделать такой ход.
Создать список заблоченых доменов или скачать его с rublacklist.
Заресолвить все домены в ipv4 адреса. Загнать их в ipset с именем "zapret".
@ -118,7 +128,7 @@ tkt : помогает разделение http запроса на сегме
ТКТ был куплен ростелекомом, используется фильтрация ростелекома.
Поскольку 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
Пример установки на debian 7