Вопрос по mbuf+pfil

Gunsleader

Кто-нибудь пробовал использовать PFIL для обработки пакетов? Я так понимаю он весит хук на интерфейс и дает доступ к мбуф, Таким образом можно менять трафик. Но кто-нибудь пробовал создать новый фрейм для посылки(сгенерить лишний фрейм в сетевом обмене)? Возможно ли это средствами pful+mbuf?
Ось Фрибсд

pitrik2

весит
о ужас
не весит а вешает

sergey_m

Это можно сделать, но не средствами pfil. То есть в обработчике pfil ты можешь аллоцировать новый mbuf chain, создать пакет и отправить его. Но только нельзя его отправлять напрямую, то есть вызывать ip_output или ip_input это приведёт к куче скрытых проблем, таких как LOR или рекурсия. Правильно сделать это через netisr_dispatch.

sergey_m

Я так понимаю он весит хук на интерфейс
Не на интерфейс, а на весь IP input и весь IP output.

Gunsleader

Спасибо за ответ в понедельник почитаю маны что это такое =)

Gunsleader

Еще забавная особенность
выходящий пакет(сниф с помощью вайршарка) IP header:
45 00 00 41 2c ba 40 00 40 06 89 7d c0 a8 02 02 c0 a8 01 2d
Пакет который получает пфил IP header
45 00 41 00 2C BA 00 40 40 06 89 7D C0 A8 02 02 C0 A8 01 2D
Кто-нибудь может объяснить сей еффект?(жирные числа перевернуты)

sergey_m

ntohs делается до поступления в pfil.

Gunsleader

Написал корректный подсчет чексуммы IP & TCP Headers. Если модифицировать только содержимое пакетов, то они проходят нормально и приходят серерву измененными. Но не могу заставить сервер получить пакеты с другой длины.
Шлю 2 пакета с клиента(сервет просто ожидает ресив и на экран выводит)
packet1: TestMessage.
packet2: 2nd message
применяю простейший код для модификации длины: struct mbuff** m
data это просто указатель на содержание пакета в мбуфф.
Код апплаится только на 1й пакет.
TEST[0]='G';
TEST[1]='\0';
int res=m_append(*m,2,TEST);
m_fixhdr(*m);
printf("res cames from m_append:%d \n",res);
printf("new data string is %s \n",data);
iph->ip_len+=2;//modifying IP header length
Затем пересчитываю чексуммы и заменяю их.
Сервер получает "Test Message.G\0d message" И отвечает с аск+25. (т.е пакет дошел чексуммы корректные) но число символов такое же как пакет1+пакет2. Нет дополнительных 2х символов
Выглядит как-будто я просто по чужой памяти пишу но не могу понять, в чем ошибка.
Спасибо :)

sergey_m

Непонятно объясняешь. Местами не ясно о каком пакете речь о mbuf или о TCP? Также непонятно о каком заголовке речь mbuf или TCP. Приведи больший кусок кода и объясни что хотел получить.

Gunsleader

Server - машинка с пфил - Client
Генерирую следующий TCP обмен между клиент -сервер.
C --- SYN --->S
C<---SYN,ACK---S
C----ACK--->S
C----push, data="Test message." --->S
C----push,fin, data="2nd message" --->S
C<---- ACK+=25 ----- S
На каждый ТСР пакет вызывается pfil filter(struct mbuf **m,...). Вот об этой функции я и говорил.
Я проверяю что пакет меня интересующий и пробую менять поле data с пересчетом TCP & IP chtcksum. Если не менять длину пакета, то всё работает как я хочу.
Пытаюсь изменить длину пакета с data="Test message." следующим кодом(только на этот пакет)
TEST[0]='G';
TEST[1]='\0';
int res=m_append(*m,2,TEST);
m_fixhdr(*m);
printf("res cames from m_append:%d \n",res);
printf("new data string is %s \n",data);
iph->ip_len+=2;//modifying IP header length
И получаю странные результаты. На сервер приходит "Test Message.G\0d message"
Как правильно изменить длину фрейма?

sergey_m

Приведи больше кода, будет понятнее.

Gunsleader

Вот код фильтра из пфил(код не релиз, я просто пытаюсь понять пока, что и как работает)

static int
filter_first(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,struct inpcb *inp)
{
pointer = (**m).m_hdr.mh_data;
iph = (struct ip *char*)pointer;
if (iph->ip_p!=6) return 0; //ignore all non TCP segments
if (!(
iph->ip_src.s_addr==ip1) && (iph->ip_dst.s_addr==ip2 ||
iph->ip_src.s_addr==ip2) && (iph->ip_dst.s_addr==ip1
return 0; //ignore all non ip1-ip2 segments
//lets do 3 way connection packets
tcph = (struct tcphdr*char*)iph + sizeof(struct ip;
unsigned char tf=tcph->th_flags;
//SYN&ACK (client initiated connection)
if (tf&TH_SYN && tf&TH_ACK)
{
server_seq=server_ack=client_seq=client_ack=0;
start_seq=tcph->th_seq;
start_ack=tcph->th_ack;
printf("connection between %u and %u established\n",iph->ip_src.s_addr,iph->ip_dst.s_addr);
printf("connection ip1<->ip2 established with seq=%u ack=%u\n",start_seq,start_ack);
}
if (tf&TH_PUSH)
{
//ok lets try dup packet
printf("calling dup\n");
ilen = (/*htons*/(iph->ip_len) - iph->ip_hl*4) + (tcph->th_off*4;
char * data=(char*char*)tcph + (tcph->th_off*4;
if (data[0]){};
if (ilen>0)// && data[0]=='T')
{
printf("test message packet detected\n");
printf("next %ld nextpkt %ld\n"quad_t*m)->m_hdr.mh_nextquad_t*m)->m_hdr.mh_nextpkt);
int len = iph->ip_hl*4;
printf("dumping packet len is %d\n",len);
for (int i=0;i<len;i++) printf("%02X "unsigned char)pointer[i]);
printf("\n");

unsigned short* ips=(unsigned short*) pointer;
//unsigned short ocs=ips[5];
printf("old IP checksum was %04X\n",ips[5]);
tcp_cksum(iph,tcph,data);

//Modifying Packet
// Вот тут я пытаюсь изменить длину первого пакета(начинается с Т)
if (data[0]=='T')
{
TEST[0]='G';
TEST[1]='G';
int res=m_append(*m,2,TEST);
m_fixhdr(*m);
printf("res cames from m_append:%d \n",res);
printf("new data string is %s \n",data);
iph->ip_len+=2;

}
//тут я тестил что просто изменение содержания пакетов работает(так тестил чексуммы)
//data[0]='Q';
//Restoring(recalculating) IP TCP checksums lengths and stuff
//lets recalculate ilen first
ilen = (/*htons*/(iph->ip_len) - iph->ip_hl*4) + (tcph->th_off*4;
ips[5]=0;
unsigned short ncs=cksum(pointer,iph->ip_hl*4);
printf("new IP checksum %02X\n",ncs);
ips[5]=ncs;

// Корявый код нужно переделать
printf("old TCP cksum was %04X\n",tcph->th_sum);
tcph->th_sum=0; //get rid of old checksum
memcpy(TEST,tcph,32);
memcpy(TEST+32,data,ilen);

for (int i=0;i<4;i++)
{
src_addr[i]=pointer[i+12];
dst_addr[i]=pointer[i+16];
}
for (int i=0;i<ilen+32;i++) tbuff2[i]=TEST[i];
unsigned short tsum=tcp_sum_calc(ilen+32, src_addr,dst_addr, ilen%2 , tbuff2);
printf("new TCP checksum %04X\n",htons(tsum;
tcph->th_sum=htons(tsum);

//data[0]='Q';
//struct mbuf* temp = (*m)->m_hdr.mh_nextpkt;
//(*m)->m_hdr.mh_nextpkt=mc2;
//mc2->m_hdr.mh_nextpkt=temp;
}
}
return 0;
}

sergey_m

Много кривого, не могу не откомментить.
Во-первых, ты не проверяешь, а если в mbuf данные нужной тебе длины вообще. Во-вторых, не проверяешь есть ли они в первом буфере цепочки, а ведь только в этом случае будет работать mtod. Конечно, IP стек уже позаботился о том, чтобы IP заголовок был там. Но вот о TCP заголовке он не позаботился. Поэтому нужно делать проверку и если условие не выполняется, то делать m_pullup. И конечно проверять не вернуло ли оно NULL.
Далее:

pointer = (**m).m_hdr.mh_data;
iph = (struct ip *char*)pointer;

iph = mtod(*m, struct ip *);

if (iph->ip_p!=6) return 0; //ignore all non TCP segments

!= IPPROTO_TCP

tcph = (struct tcphdr*char*)iph + sizeof(struct ip;

В заголовке могут быть ip options, поэтому:
*m = m_pullup(*m, iph->ip_hl << 2 + sizeof(struct tcphdr;
проверить что *m != NULL
 tcph = (struct tcphdr *mtod(*m, char *) + iph->ip_hl << 2);
(кстати далее ты ilen вычисляешь правильно)
	char * data=(char*char*)tcph + (tcph->th_off*4; 

Вот тут уже могут начаться большие проблемы с тем, что данные идут не в первом члене цепочки mbuf, и более того, m_pullup может не удастся так, чтобы весь пакет был единым последовательным куском памяти. Поэтому, для того, чтобы эта программа была полноценной, придётся написать ещё много кода, который будет шариться по цепочкам mbuf. Пока же, на коротких пакетах всё будет работать и так. Но хотя бы надо пытаться сделать m_pullup на нужную тебе величину.

int res=m_append(*m,2,TEST);
m_fixhdr(*m);

AFAIK, m_fixhdr здесь излишний. m_append пересчитывает длину.
P.S. Смотрю дальше.

sergey_m

Я кажется понял, что ты хочешь. Так не получится. :)
Шлю 2 пакета с клиента(сервет просто ожидает ресив и на экран выводит)
packet1: TestMessage.
packet2: 2nd message
...
Сервер получает "Test Message.G\0d message" И отвечает с аск+25. (т.е пакет дошел чексуммы корректные) но число символов такое же как пакет1+пакет2. Нет дополнительных 2х символов
Конечно так и должно быть. Ведь th_seq ты не увеличил на 2 в следующем сегменте. Вот получатель и прочитал из второго сегмента не с самого начала, а с третьего октета, ведь предыдущие два он уже получил в первом сегменте.
Ты конечно можешь увеличить th_seq во втором сегменте, и наверное даже это получится. Но только в вакууме. Как только изменятся размеры пакетов, тайминги, появятся потери, то всё это перестанет работать. И не забываем о том, что mbuf может быть не contigous и m_pullup не сможет выполниться. Короче говоря, для реализации этой задачи нужно написать мини-tcp-стек. И если ты хочешь что бы он работал всегда и безглючно, то размер его будет приближаться к полноценному стеку. Поэтому такие задачи намного лучше решать в userland. Там сокеты уже позаботились о contigous memory и о том, чтобы ты не знал что такое фрагментация, потери, повторная пересылки и дупликаты.

Gunsleader

Да про ак и сек сам понял уже =) Спасибо за советы. Просто не могу найти нормальную документацию вот и тыкаюсь как котёнок. Не подскажете откуда вы это всё знаете?
Задача которая стоит сейчас написать прогрумму которая будет достаточно быстро просматривать трафик идущий через нее и модфицировать трафик(блочить изменять добавлять новые пакеты). Разве есть подобные решения в userspace?

sergey_m

Просто не могу найти нормальную документацию вот и тыкаюсь как котёнок. Не подскажете откуда вы это всё знаете?
Во-первых, man 9 mbuf.
И также читать исходники. Например нам интересно, как правильно разбирать ip заголовок. На эту тему можно почитать sys/netinet/ip_input.c функцию ip_input. Также в системе есть три пакетных фильтра, и все они тоже разбирают ip заголовок. Значит можно почитать и их: sys/netinet/ipfw/ip_fw2.c, функция ipfw_chk sys/contrib/pf/net/pf.c функция pf_test.
Задача которая стоит сейчас написать прогрумму которая будет достаточно быстро просматривать трафик идущий через нее и модфицировать трафик(блочить изменять добавлять новые пакеты). Разве есть подобные решения в userspace?
Если речь о модификации TCP, то только безумец будет делать это в ядре. Если не безумец, то это должен быть человек(команда) готовый к написанию своего TCP стека.
Поэтому лучше сразу делать в user land. Если речь идёт о каком-то контент-фильтре, то скорее всего сама контент-фильтрация будет самой тяжёлой функцией, а вовсе не пересылка данных из приложения в ядро и назад. Поэтому не стоит париться по поводу производительности сокетов.

Gunsleader

лучше сразу делать в user land. Если речь идёт о каком-то контент-фильтре, то скорее всего сама контент-фильтрация будет самой тяжёлой функцией, а вовсе не пересылка данных из приложения в ядро и назад. Поэтому не стоит париться по поводу производительности
Вы можете назвать(посоветовать) готовые решения? Просто производительность важна так как будет примерно трафик от активных 1-2х тысяч соединений. Или что почитать о userland спобах решениях. Не до конца представляю как можно сделать в userland контроль трафика который идет не тебе

Sharp

Если под фильтрацией подразумевается защита от вирусов, то это либо http://www.snort.org/, либо http://www.juniper.net/us/en/products-services/security/isg-...
Если http, то либо http://www.citrix.com/English/ps2/products/product.asp?conte... , либо http://www.f5.com/products/big-ip/product-modules/local-traf...
Какая то конечная цель?

sergey_m

Это называется transparent proxy. С помощью ipfw fwd (например) трафик попадает локальному приложению вместо нормального назначения, приложение устанавливает соединение с нормальным назначением и прокачивает трафик через себя.

Gunsleader

Сейчас стоит задача на определенный TCP фрейм (клиент->сервер) сделать так, чтобы сервер получил 2 TCP фрейма в тот же сокет(сэмулировать что клиент послал 2 фрейма).
пытаюсь сделать следующий код(m - mbuf**) на интересующий меня фрейм:
struct mbuf* myown = m_dup*mM_DONTWAIT);
(**m).m_hdr.m_nextpkt = myown;
iph->ip_id++; //change id of previous frame
//recalc all checksums
Но если повесить tcpdump на эту машину видно что дополнительного фрейма не возникает.
Поглядел в ip_input.c и не нанешл обработки поля (*m).mhdr.m_nextpkt (смотрел ниже кода
if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_IN, NULL) != 0)
return;
if (m == NULL) /* consumed by filter */
return;
Получается, чтобы сгенерировать дополнительный фрейм нужно менять ядро? Или есть более простые способы?
PS читаю ман по netisr(9)

sergey_m

m_nextpkt так применять нельзя. Функции, работающие с IP пакетами ожидают получения на вход только одного пакета. Поле m_nextpkt используется только для организации очередей в интерфейсах, в сокетах, в очереди протоколов, точнее "доменов". Стек, в частности IP стек, берёт один пакет из очереди, и передаёт его на обработку, в частности ip_input.
О том, как посылать сгенерированные в пакетном фильтре пакеты, я писал в одном из первых постов.

Gunsleader

Пропустил. спасибо. Сейчас как раз про netisr и читаю

Gunsleader


if data[0]=='A') && (dd==0
{
dd=1;
printf("got non zero A type PUSH frame trying to dupe\n");
//struct mbuf* myown = m_get(M_WAIT,EXT_CLUSTER);
//printf("alloc returns %ud\n"unsigned long)myown);

//*************** MYOWN frame ******************
myown=m_dup*mM_DONTWAIT);
printf("alloc returns %lu\n"unsigned long)myown);
unsigned char* pointer_ = (*myown).m_hdr.mh_data;
struct ip* iph_ = (struct ip *char*)pointer_;
struct tcphdr* tcph_ = (struct tcphdr*char*)iph_ + sizeof(struct ip;
int ilen_ = (/*htons*/(iph_->ip_len) - iph_->ip_hl*4) + (tcph_->th_off*4;
unsigned char * data_=(char*char*)tcph_ + (tcph_->th_off*4;
printf("my own length is %d with %c\n",ilen_,data_[0]);
data_[0]='C';
//recheck IP checksum
unsigned short* ips_=(unsigned short*) pointer_;
ips_[5]=0; //delete old_checksum(IP)
unsigned short ncs_=cksum(pointer_,iph_->ip_hl*4);
ips_[5]=ncs_;
//recheck TCP checksum
printf("old TCP cksum was %04X\n",tcph_->th_sum);
tcph_->th_sum=0; //get rid of old checksum
tcph_->th_seq+=100;
//tcph_->th_ack+=100;
iph_->ip_id+=1;
memcpy(TEST,tcph_,32);
memcpy(TEST+32,data_,ilen_);
for (int i=0;i<4;i++)
{
src_addr[i]=pointer_[i+12];
dst_addr[i]=pointer_[i+16];
}
for (int i=0;i<ilen+32;i++) tbuff2[i]=TEST[i];
unsigned short tsum_=tcp_sum_calc(ilen+32, src_addr,dst_addr, ilen_%2 , tbuff2);
printf("new TCP checksum %04X\n",htons(tsum_;
tcph_->th_sum=htons(tsum_);
int res = netisr_dispatch(NETISR_IP,myown);
//int res = netisr_queue(NETISR_IP,myown);
printf("netisr_dispatch returns %d\n",res);

//return 0;
//***************** OLD FRAME **************************
//mark and modifying packet
data[0]='B'; //fixed
//recheck IP checksum
unsigned short* ips=(unsigned short*) pointer;
ips[5]=0; //delete old_checksum(IP)
unsigned short ncs=cksum(pointer,iph->ip_hl*4);
ips[5]=ncs;
//recheck TCP checksum
printf("old TCP cksum was %04X\n",tcph->th_sum);
tcph->th_sum=0; //get rid of old checksum
memcpy(TEST,tcph,32);
memcpy(TEST+32,data,ilen);
for (int i=0;i<4;i++)
{
src_addr[i]=pointer[i+12];
dst_addr[i]=pointer[i+16];
}
for (int i=0;i<ilen+32;i++) tbuff2[i]=TEST[i];
unsigned short tsum=tcp_sum_calc(ilen+32, src_addr,dst_addr, ilen%2 , tbuff2);
printf("new TCP checksum %04X\n",htons(tsum;
tcph->th_sum=htons(tsum);
//ncs=ncs_;

}

Не проверяю много вещей(выделение памяти и т.п. пока всё еще пытаюсь разобраться в целом как это работает.
Вот делаю так, на сервер не приходит доп фрейм, я как то неправильно копирую цепочку mbuf?
Доходит только обычный фрейм измененный( я хочу изменить один на B и отправить дополнительный на C) длина фрейма 100.
PS неправильно длину заголовка считаю, пока это не так важно так как опций нет.

sergey_m

Смотри tcpdumpом на самой машине с пакетным фильтром, а не на сервере. Кстати надо делать netisr_queue потому что dispatch может приводит к рекурсивному выполнению IP стека.

Gunsleader

Пробовал и isr_queue и смотрел tcpdump на машине с фильтром почему то дополнительного фрейма нет.
UPD вру при queue происходит чтото странное, буду разбираться
Оставить комментарий
Имя или ник:
Комментарий: