pcap과 pthread를 이용하여 패킷을 캡쳐 하려고 합니다.
pcap과 pthread를 이용하여 디바이스 수만큼 쓰레드를 생성하고 각각의 쓰레드에서는 해탕 디바이스로 들어오는 패킷을 캡쳐하여 해당디바이스 이름과 src_ip, dst_ip를 출력하고자 합니다.
다음과 같은 환경에서 패킷을 캡쳐하려고 합니다.
- 운영 체제 : RHEL4
- 랜포트 : 2개 (eth0 : 111.111.111.111, eth1 : 222.222.222.222)
1. 디바이스 alias를 얻어와서 얻어온 디바이스만큼 쓰레드를 생성합니다.
2. 쓰레드를 생성할때 인자로 디바이스 alias를 넘겨줍니다.
3. 쓰레드가 시작되면 디바이스 alias로 pcap_open_live()을 사용하여 장치를 열고 패킷캡쳐를 시작합니다.
(NOPROMISCUOUS 모드로 했습니다)
4. 패킷캡쳐는 pcap_next()를 사용하여 무한루프를 돌렸습니다.
특정포트로 들어오는 패킷만 장치이름과 src_ip, dst_ip를 출력합니다.
5. 서버에서 위 프로그램을 실행시켜놓고 다른 2개의 컴퓨터에서 패킷을 발생시켜서 각각의 랜포트로 패킷을 전송합니다. (패킷은 WinSIP 으로 발생시켰습니다)
com1 : 333.333.333.333 -> eth0
com2 : 444.444.444.444 -> eth1
이렇게 전송을하면 서버에서는 다음과 같이 출력이 되어야 합니다.
eth0 333.333.333.333 ---> 111.111.111.111
eth1 444.444.444.444 ---> 222.222.222.222
| | | |
그런데 위와같이 출력이 되다가 이상하게 출력 될때가 있습니다.
eth0 333.333.333.333 ---> 111.111.111.111
eth0 444.444.444.444 ---> 222.222.222.222
이거나 eth0대신 eth1이거나
eth1 333.333.333.333 ---> 111.111.111.111
eth0 444.444.444.444 ---> 222.222.222.222
이거나 아예 아무것도 안찍힐때가 있습니다.
그래서 pcap_compile()을 이용하여 필터링을 걸어줘봤습니다.
쓰레드1(eth0) --- > dst host 111.111.111.111
쓰레드2(eth1) --- > dst host 222.222.222.222
이렇게 해봤더니 출력은
eth0 333.333.333.333 ---> 111.111.111.111
eth1 444.444.444.444 ---> 222.222.222.222
이렇게 되었지만 여전히 안찍히는 경우가 있었습니다. 즉 필터링을 안걸었을때와 같은 결과였습니다.
데이터는 제대로 들어오는것 같긴한데 왜 디바이스 alias가 위와 같이 찍히는지 아무리 찾아봐도 잘 모르겠습니다.
그래서 ifconfig로 promisc를 해지하고 해봐도 똑 같은 결과였습니다.
꼭 제대로된 결과를 찍고싶습니다. 해결책이나 조언 꼭 부탁드리겠습니다.... 꾸벅
혹시나 해서 소스도 같이 올립니다.
#define PROMISCUOUS 1
#define NOPROMISCUOUS 0
#define PCAP_SNAPSHOT 65536
#define PCAP_TIMEOUT 1
#define SIP_PORT 5060
#define PATH "/home/oracle/files/"
//////////////////////////////////////////////////////////////////////////////////////
struct thread_data {
char device_name[255];
char ip_address[255];
};
struct thread_data *data;
int DEV_MAX = 0;
//////////////////////////////////////////////////////////////////////////////////////
void SetDeviceInfo();
void *prepareCapture(void *data);
void Capturing(pcap_t *pd, char *dname);
//////////////////////////////////////////////////////////////////////////////////////
void Capturing(pcap_t *pd, char *dname)
{
unsigned char *p;
struct pcap_pkthdr ph;
int i;
////////////////////////////////////////////////////////////////////////
int j, temp;
struct ether_header *ep;
unsigned short ether_type;
struct iphdr *iph;
struct udphdr *udph;
////////////////////////////////////////////////////////////////////////
for(i=0;;i++)
{
p=(unsigned char *)pcap_next(pd, &ph);
ep = (struct ether_header *)p;
p += sizeof(struct ether_header);
ether_type = ntohs(ep->ether_type);
switch (ether_type){
case ETHERTYPE_IP :
iph = (struct iphdr *) p;
switch(iph->protocol){
case IPPROTO_TCP :
break;
case IPPROTO_UDP :
udph = (struct udphdr *) (p + iph->ihl * 4);
printf("%s\t", dname);
printf("%s -----> ", inet_ntoa(iph->saddr));
printf("%s\n", inet_ntoa(iph->daddr));
break;
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////
void *prepareCapture(void *data)
{
char errbuf[PCAP_ERRBUF_SIZE];
struct thread_data *dat = (struct thread_data*)data;
//////////////////////////////////////////////////////////////////////////////////////
////rule set
char rule[255];
sprintf(rule, "dst port %d", SIP_PORT);
//////////////////////////////////////////////////////////////////////////////////////
////device open and filter set
pcap_t *pd;
struct bpf_program fp;
bpf_u_int32 netp;
bpf_u_int32 maskp;
if(pcap_lookupnet(dat->device_name, &netp, &maskp, errbuf)<0)
{
perror(errbuf);
exit(1);
}
if((pd = pcap_open_live(dat->device_name, PCAP_SNAPSHOT, NOPROMISCUOUS, PCAP_TIMEOUT, errbuf)) == NULL)
{
perror(errbuf);
exit(1);
}
if (pcap_compile(pd, &fp, rule, -1, netp) == -1)
{
printf("compile error\n");
exit(1);
}
if (pcap_setfilter(pd, &fp) == -1)
{
printf("setfilter error\n");
exit(0);
}
////////////////////////////////////////////////////////////////////////////////////////
printf("%s\n", dat->device_name);
printf("%s\n", rule);
Capturing(pd, dat->device_name);
}
//////////////////////////////////////////////////////////////////////////////////////
void SetDeviceInfo()
{
/////////////////////////////////////////////////////////////////////////////////
//디바이스 이름을 얻어온다
char errbuf[PCAP_ERRBUF_SIZE];
int nRet;
pcap_if_t *alldevps;
char *temp_name[255];
nRet=pcap_findalldevs(&alldevps, errbuf);
if(nRet == -1)
{
printf("%s\n", errbuf);
exit(-1);
}
do{
if(alldevps->addresses == NULL)
{
break;
}else{
temp_name[DEV_MAX]=alldevps->name;
DEV_MAX++;
}
}while(alldevps=alldevps->next);
/////////////////////////////////////////////////////////////////////////////////
data=(struct thread_data*)calloc(DEV_MAX, sizeof(struct thread_data));
struct ifreq *ifr;
struct sockaddr_in *sin;
struct sockaddr *sa;
// 이더넷 설정 구조체
struct ifconf ifcfg;
int fd;
int n, m, dev_cnt = 0;
int numreqs = 30;
fd = socket(AF_INET, SOCK_DGRAM, 0);
memset(&ifcfg, 0, sizeof(ifcfg));
ifcfg.ifc_buf = NULL;
ifcfg.ifc_len = sizeof(struct ifreq) * numreqs;
ifcfg.ifc_buf = malloc(ifcfg.ifc_len);
for(;;)
{
ifcfg.ifc_len = sizeof(struct ifreq) * numreqs;
ifcfg.ifc_buf = realloc(ifcfg.ifc_buf, ifcfg.ifc_len);
if (ioctl(fd, SIOCGIFCONF, (char *)&ifcfg) < 0)
{
exit;
}
break;
}
ifr = ifcfg.ifc_req;
for (n = 0; n < ifcfg.ifc_len; n+= sizeof(struct ifreq))
{
for(dev_cnt=0; dev_cnt < DEV_MAX; dev_cnt++)
{
if(strcmp(temp_name[dev_cnt], ifr->ifr_name) == 0)
{
sin = (struct sockaddr_in *)&ifr->ifr_addr;
strcpy(data[dev_cnt].device_name, temp_name[dev_cnt]);
strcpy(data[dev_cnt].ip_address, (char *)inet_ntoa(sin->sin_addr));
}
}
ifr++;
}
}
int main()
{
SetDeviceInfo();
pthread_t *thread;
int thread_cnt;
thread = (pthread_t *)calloc(DEV_MAX, sizeof(pthread_t));
for(thread_cnt=0; thread_cnt
{
if(pthread_create(&thread[thread_cnt], NULL, prepareCapture, &data[thread_cnt]) < 0)//DEV_NAME[thread_cnt]
{
printf("cannot make thread\n");
exit(1);
}
}
for(thread_cnt=0; thread_cnt
{
pthread_join(thread[thread_cnt], NULL);
}
pthread_mutex_destroy(&mutex);
return 1;
}
인용: ..........snip.......
if ((p = (unsigned char*)pcap_next(pd, &ph)) == NULL) {
continue;
}
ep = (struct ether_header *)p;
iph = (struct ip *) (p + sizeof(struct ether_header));
더 이상 처리할 패킷이 없을 경우에도 당연히 건너 뛰는 것이 좋을 듯합니다.
아마 pcap_loop를 쓰는 것이 더 좋을 것 같습니다.
이렇게 쓰려면 struct ip 로 변환하고
printf("%s -----> ", inet_ntoa(iph->ip_src));
printf("%s\n", inet_ntoa(iph->ip_dst));
로 쓸 수 있습니다. 물론 관련된 다른 변수도 바뀌어야 합니다.
디바이스 설정하는 부분도 좀 이상하군요.
+ t = alldevps;
do{
- if(alldevps->addresses == NULL)
+ if(t == NULL)
{
break;
- }else{
- temp_name[DEV_MAX]=alldevps->name;
- printf("alldevps->name : %s\n", alldevps->name);
+ } else {
+ if (strcmp(t->name, "any") == 0) {
+ printf("skip any : %s\n", t->name);
+ continue;
+ }
+ temp_name[DEV_MAX]=t->name;
+ printf("alldevps->name : %s\n", t->name);
DEV_MAX++;
}
- } while (alldevps=alldevps->next);
+ } while (t=t->next);
처럼 t를 임시로 만들어서 쓰고 pcap_freealldevs(alldevps) 로 해제해 줍니다.
메모리 할당만 한 곳이 몇몇 군데 있군요.
Why?
pcap_next로 만드는거랑 pcap_loop과 어떤 차이점이 있는건가요?
질문과는 좀 다른것이지만,
pcap_loop으로는 인자를 주고받거나 하는것이 불편해서,
저는 pcap_next를 많이 쓰는데요,
pcap_next와 pcap_loop은 단지 parsing 펑션에 관한 것 말고 다른 동작상의 차이점도 있는건지요?
Fly to the SKY~~~~~~
"According to your faith, be it unto you!!"
언뜻 보기엔 일단 더
언뜻 보기엔 일단 더 깔끔해 보입니다.
실제 소스를 좀 살펴보니, pcap_loop의 경우,
1. 동시에 여러 개의 패킷을 읽어 올 수 있고,
2. 켭쳐한 파일에서 읽어 올 수 있다는
장점이 있습니다.
pcap_next_ex를 쓴다면 2도 가능합니다.
인용:1. 동시에 여러
다시보니 틀려서 정정합니다. 단순히 읽어오는 패킷 수를 제한하는 것입니다.
그러면 속도면에서는 어떤가요?
그러면 속도면에서는 어떡해 될지 궁금합니다;;
보니까
next_ex는
패킷있음?
없음
패킷있음?
없음
패킷있음?
있음
-> 출력
이런식으로가고
loop는
패킷받는거 대기.{
패킷이 들어오면 핸들러 호출 (핸들러함수에서 출력)
}
같은거 같은데 맞나요?
그리고 dispa? 함수가 하나 더있던거 같던데;
그건또 어떡해되는지요?
역시 네트워크 환경에 따라 달라지는건가요?
댓글을 보니 더 해깔리는 군요.
말을 다르게 보면,
- pcap_next는 패킷을 하나씩을 읽어오는것은 설명으로 알수 있던데,
pcap_loop은 동시에 여러개를 읽는다는 의미가 마치 pcap_next가 패킷을 놓칠수 있다는 의미로 들리는데요.
pcap_loop으로 한것에 비해 , pcap_next가 패킷을 놓칠수도 있는건가요?
Fly to the SKY~~~~~~
"According to your faith, be it unto you!!"
인용:pcap_loop으로
그런 해석을 할 수 있는 경우는 어디에도 없는 것 같습니다.
pcap_next보다 pcap_loop이 더 낳겠다는 것에 대해서.. 궁금해서 입니다.
처음 댓글에도 pcap_loop을 쓰는게 더 낳겠다 하셨고,
그담에도..
1. 동시에 여러 개의 패킷을 읽어 올 수 있고,
2. 켭쳐한 파일에서 읽어 올 수 있다는
장점이 있습니다.
라고 하셨는데....
특히나, 동시에 여러개의 패킷을 읽어 온다는 것이 제가 느낄때는,
어차피 NIC에는 한개씩만 들어오는거 아닌가요?
pcap_loop은 단지 편하게 쓸수있게 제공되는 펑션처럼 받아들이고 있고,
pcap_loop의 기능은 pcap_next나 pcap_next_ex같은걸로 똑같이 구현할수 있다고
생각하고 있었는데요.. 아닌건지?? 정말 궁금합니다.
읽어오는건 논외 이지만,
여튼.. 진짜 궁금해서 그러는건데요..
pcap_loop이 더 낳다고 생각하시는 이유가 궁금합니다 ㅠㅠ
ㅜㅜ
좀더 풀어서 설명이나, 링크 같은걸 걸어주시면.. 좋겠씁니다. ㅠㅠ
설명 좀.. 부탁드립니다.
Fly to the SKY~~~~~~
"According to your faith, be it unto you!!"
인용: 다시보니
왜 이미 정정한 것을 잘못 인용하시죠?
위에서 언급돼 있듯이 본인이 그런 장점이 좋으면 쓰고 필요없으면 안 쓰면됩니다.
잘 동작하는 것을 굳이 바꿀 필요는 없습니다.
댓글 달기