pcap과 pthread를 이용하여 패킷을 캡쳐 하려고 합니다.

soulcrime의 이미지

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;
}

경의 이미지

Quote:

..........snip..................................................................................
p=(unsigned char *)pcap_next(pd, &ph);

ep = (struct ether_header *)p;
p += sizeof(struct ether_header);
ether_type = ntohs(ep->ether_type);
..........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를 쓰는 것이 더 좋을 것 같습니다.

Quote:
printf("%s -----> ", inet_ntoa(iph->saddr));
printf("%s\n", inet_ntoa(iph->daddr));

이렇게 쓰려면 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) 로 해제해 줍니다.
메모리 할당만 한 곳이 몇몇 군데 있군요.

Quote:

pthread_mutex_destroy(&mutex);

Why?
poibe의 이미지

질문과는 좀 다른것이지만,
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도 가능합니다.

경의 이미지

Quote:
1. 동시에 여러 개의 패킷을 읽어 올 수 있고,

다시보니 틀려서 정정합니다. 단순히 읽어오는 패킷 수를 제한하는 것입니다.
익명 사용자의 이미지

그러면 속도면에서는 어떡해 될지 궁금합니다;;
보니까
next_ex는
패킷있음?
없음
패킷있음?
없음
패킷있음?
있음
-> 출력
이런식으로가고
loop는
패킷받는거 대기.{
패킷이 들어오면 핸들러 호출 (핸들러함수에서 출력)
}

같은거 같은데 맞나요?
그리고 dispa? 함수가 하나 더있던거 같던데;
그건또 어떡해되는지요?

역시 네트워크 환경에 따라 달라지는건가요?

poibe의 이미지

말을 다르게 보면,
- pcap_next는 패킷을 하나씩을 읽어오는것은 설명으로 알수 있던데,
pcap_loop은 동시에 여러개를 읽는다는 의미가 마치 pcap_next가 패킷을 놓칠수 있다는 의미로 들리는데요.

pcap_loop으로 한것에 비해 , pcap_next가 패킷을 놓칠수도 있는건가요?

Fly to the SKY~~~~~~

"According to your faith, be it unto you!!"

modestcode의 이미지

Quote:
pcap_loop으로 한것에 비해 , pcap_next가 패킷을 놓칠수도 있는건가요?

그런 해석을 할 수 있는 경우는 어디에도 없는 것 같습니다.
poibe의 이미지

처음 댓글에도 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!!"

modestcode의 이미지

Quote:

다시보니 틀려서 정정합니다. 단순히 읽어오는 패킷 수를 제한하는 것입니다.

왜 이미 정정한 것을 잘못 인용하시죠?
위에서 언급돼 있듯이 본인이 그런 장점이 좋으면 쓰고 필요없으면 안 쓰면됩니다.
잘 동작하는 것을 굳이 바꿀 필요는 없습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.