pcap을 이용한 선형 Queue와 Thread문제

mulgo79의 이미지

pcap을 이용하여 패킷 캡쳐 및 분석 툴을 만드는 중입니다.

전혀 이해가 가지 않거나 혹은 저의 실수로 문제가 발생하고 있는것에 대하여

도움을 청하고자 글을 올립니다 아시는 분 도움주시면 감사하겠습니다.

문제는 아래와 같습니다.

패킷을 캡쳐와 분석을 한모듈에서 한번에 하다보면 패킷을 loss가 발생하기

때문에 두개를 띄어 내기 위하여 분석 모듈은 내부 thread로 돌고 있습니다.

1. 캡쳐 모듈
- 패킷을 캡쳐하여 linked list 선형 queue에 enqueue를 하는 동작
2. 분석 모듈
- queue에서 dequeue하여 패킷을 분석하는 동작

위에서 보시는것과 같은 구조입니다.

하지만 문제점은 패킷을 dequeue하는 과정에서 중복되는 아주 똑같은

패킷이 자주 dequeue된다는 점입니다. 첨에는 그려려니 넘겼지만 보다보니

queue의 문제인지 thread의 문제인지 감이 잘 오지 않습니다.

위의 구조를 단일 프로세스로 구성하고 queue과정을 캡쳐 하자마자 queue에 넣고

다시 queue에서 빼서 분석을 하게 되면 정상동작을 하고 있습니다.

하지만 이것을 queue와 thread로 분리하게 되면 이상하게 동작을 하게 됩니다.

예를 들어 아래와 같습니다.

ip만 분석을 했을 경우입니다.

source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.75 destination ip : 210.120.92.94

이것이 thread와 queue의 구조로 돌렸을 경우입니다.

source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.94 destination ip : 210.120.92.75
source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.75 destination ip : 210.120.92.94
source ip : 210.120.92.94 destination ip : 210.120.92.75

이것은 단일 프로세스 구성입니다.

위의 것은 분명히 같은 패킷이 여러번 찍히는데

아래것은 분명히 제가 볼때는 정상동작입니다.

다른것으로도 패킷을 잡아 보았기 때문에 제가 짠 소스가 분명 이상동작하는것은 맞는거 같습니다.

소스도 같이 첨부 하였으니 조금의 아량을 배푸시오 저의 고민을 들어주세요 벌써 3시 30분이 다되가네요

에효 답답해서 잠도 안옵니다. 그럼 꾸벅~~~ ㅠㅠ

powerson의 이미지

일단, 소스가 보이지 않네요. 그리고 한가지, network packet이라는게 ip layer에서만 찍었을 경우에는 상황에 따라 저렇게 보일 가능성은 존재합니다. 혼합되지 않았다? 를 가지고 중복된다! 라고 말씀을 하시기엔, 좀 무리가 따를 거 같습니다.
정말 똑같은 packet인지 아닌지를 판별하기 위해서는, TCP의 sequence number까지 찍어보심이 좋을 거 같습니다.
실제로 중복되는지 안 되는지는 tcp의 sequence number만으로도 바로 알 수 있는 내용입니다.

------------------------------------------------------
아직은 젊다. 모든 것을 할 수 있는 나이란 말이지.

------------------------------------------------------
아직은 젊다. 모든 것을 할 수 있는 나이란 말이지.

mulgo79의 이미지

시퀀스 역시 찍어 보았는데 같은 시퀀스가 찍혔었구요 그걸로 보아서

같은 패킷이 찍혔구나라고 판단되었습니다. 소스는 다시

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "config.h"
#include "extern.h"
#include "ether.h"
#include "ethertype.h"
#include "queue.h"
#include "load_conf.h"

/* default snap length (maximum bytes per packet to capture) */
//#define SNAP_LEN 100
//#define SNAP_LEN 1518
#if 1
struct element {
unsigned int cap_cnt;
char time[24];
u_char *args;
const struct pcap_pkthdr *header;
const u_char *packet;
TAILQ_ENTRY(element) entries;
};
#endif
TAILQ_HEAD(, element) tailq_head;

//struct element *item;
//struct element *tmp;

int snap_len = SNAP_LEN;
int32_t thiszone;
static u_int packets_captured;
static u_int packets_completed;
int num_packets = -1; /* number of packets to capture */
char *dev = NULL; /* capture device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
struct bpf_program fp; /* compiled filter program (expression) */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
char *localnet;
int localnet_cnt = 0;
pcap_t *handle; /* packet capture handle */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//pthread_cond_t thread_cond = PTHREAD_COND_INITIALIZER;

char ip_class;
int class_flag;
struct db_cfg *db_cfg;

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
void print_app_usage(void);
int subnet_mask(char *netaddr);
static RETSIGTYPE cleanup(int);
static void info();
RETSIGTYPE requestinfo();
void *decodeThread(void *arg);
struct session *session;

/*
* print help text
*/
void
print_app_usage(void)
{
printf("Usage: %s [interface]\n", APP_NAME);
printf("\n");
printf("Options:\n");
printf(" interface Listen on for packets.\n");
printf("\n");

return;
}

/*
* dissect/print packet
*/
void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
#if 1
//if(packets_captured >= 1000) return;
packets_captured++;
struct element *item;
if((item = malloc(sizeof(*item))) == NULL) {
return;
}
item->args = args;
item->header = header;
item->packet = packet;
item->cap_cnt = packets_captured;
strcpy(item->time, timestamp(&header->ts, item->time));
TAILQ_INSERT_TAIL(&tailq_head, item, entries);
//struct element *tmp = TAILQ_FIRST(&tailq_head);
//TAILQ_REMOVE(&tailq_head, tmp, entries);
//packet_analysis(session, item->time, item->args, item->header, item->packet);
//packet_analysis(item->time, item->args, item->header, item->packet);
//free(item);

//free(item);
//pthread_mutex_unlock(&mutex);
//timestamp(&item->header->ts);
//printf("\n");
#endif
#if 0
packets_captured++;
struct element item;
#if 0
if((item = malloc(sizeof(*item))) == NULL) {
return;
}
#endif
item.args = args;
item.header = header;
item.packet = packet;
//item->time = timestamp(&header->ts);
//strcpy(item->time, timestamp(&header->ts));
strcpy(item.time, timestamp(&header->ts, item.time));

pthread_t p_thread;
pthread_create(&p_thread, NULL, &test, (void *)&item);
//packet_analysis(session, item->time, item->args, item->header, item->packet);
#endif

return;
}

static RETSIGTYPE
cleanup(int signo _U_)
{
if(handle != NULL && pcap_file(handle) == NULL) {
putchar('\n');
info(1);
}
exit(1);
}

void info()
{
struct pcap_stat stat;

if(pcap_stats(handle, &stat) < 0) {
printf("pcap_stats : %s\n", pcap_geterr(handle));
return;
}

printf("%u packets captured\n", packets_captured);
printf("%u packets received by filter\n", stat.ps_recv);
printf("%u packets dropped by kernel\n", stat.ps_drop);
printf("\n Capture complete.\n");
}

void sig_int(int sig)
{
printf("Bye!!\n");
info();
pcap_close(handle);
exit(EXIT_FAILURE);
}

#if 1
void
*decodeThread(void *arg)
{
int th_id = (int)arg;
int cnt = 0;
struct element *tmp = NULL;
char time[24];
u_char *args;
const struct pcap_pkthdr *header;
const u_char *packet;
#if 1
struct session *session;
if((session = (struct session *)malloc(sizeof(*session))) == NULL) {
printf("session malloc failed\n");
exit(EXIT_FAILURE);
}
session->info = db_cfg;
while(cnt < 3) {
if(db_connection(session) != 0) {
cnt++;
continue;
}
else {
cnt = 0;
break;
}
}
#endif

#if 1
while(1) {
#if 1
pthread_mutex_lock(&mutex);
//printf("lock\n");
//pthread_cond_timedwait(&thread_cond, &mutex, &timeout);
//printf("thr_id : %d\n", th_id);
#if 0
if(TAILQ_EMPTY(&tailq_head)) {
pthread_mutex_unlock(&mutex);
usleep(100000);
//usleep(100%(th_id+1));
tmp = NULL;
printf("tailq_empty\n");
continue;
}
#endif
//pthread_mutex_lock(&mutex);
//thread_cond
//tmp = TAILQ_FIRST(&tailq_head);
//pthread_mutex_lock(&mutex);
//printf("lock\n");
if((tmp = TAILQ_FIRST(&tailq_head))) {
TAILQ_REMOVE(&tailq_head, tmp, entries);
strcpy(time, tmp->time);
args = tmp->args;
header = tmp->header;
packet = tmp->packet;
cnt = tmp->cap_cnt;
free(tmp);
pthread_mutex_unlock(&mutex);
//printf("thread - %d\n", th_id);
//printf("unlock\n");
packet_analysis(session, time, args, header, packet, cnt);
//packet_analysis(session, tmp->time, tmp->args, tmp->header, tmp->packet);
//a = pthread_cond_timedwait(&thread_cond, &mutex, &timeout);
//pthread_mutex_unlock(&mutex);
usleep(1000);
//usleep(100*(th_id+1));
//usleep(100);
#if 0
if(tmp != NULL) {
free(tmp);
}
#endif
}
else {
pthread_mutex_unlock(&mutex);
sleep(1);
//usleep(1000*(th_id+1));
}
tmp = NULL;
//usleep(100*(th_id+1));
//pthread_mutex_unlock(&mutex);
#endif
//usleep(1000);
}
#endif
}
#endif

int
subnet_mask(char *netaddr)
{
int ret = 0;
if(strcmp(netaddr, "255.0.0.0") == 0) {
//printf("A Classs\n");
class_flag = 1;
ip_class = 'A';
}
else if(strcmp(netaddr, "255.255.0.0") == 0) {
//printf("B Classs\n");
class_flag = 2;
ip_class = 'B';
}
else if(strcmp(netaddr, "255.255.255.0") == 0) {
//printf("C Classs\n");
class_flag = 3;
ip_class = 'C';
}
else {
//printf("Unknown Classs\n");
ret = 1;
}
return ret;
}

int
main(int argc, char **argv)
{
struct in_addr mask_addr, net_addr;
/* check for capture device name on command-line */
dev = argv[1];
if (argc == 2) {
dev = argv[1];
}
else if (argc > 3) {
fprintf(stderr, "error: unrecognized command-line options\n");
print_app_usage();
exit(EXIT_FAILURE);
}
else {
/* find a capture device if not specified on command-line */
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
exit(EXIT_FAILURE);
}
}

// db conf load
if((db_cfg = (struct db_cfg *)malloc(sizeof(*db_cfg))) == NULL) {
fprintf(stderr, "db_cfg malloc failed\n");
exit(EXIT_FAILURE);
}
if((db_cfg = (struct db_cfg *)load_db(db_cfg)) == NULL) {
fprintf(stderr, "db config file load failed\n");
exit(EXIT_FAILURE);
}
#if 0
if((session = (struct session *)malloc(sizeof(*session))) == NULL) {
printf("db session malloc failed\n");
exit(EXIT_FAILURE);
}
session->info = db_cfg;
db_connection(session);
#endif

thiszone = gmt2local(0);
TAILQ_INIT(&tailq_head);
#if 1
int thr_id;
pthread_t p_thread[atoi(argv[2])];
int stat;
int i;
#if 1
//pthread_mutex_init(&mutex, NULL);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
for(i=0; i thr_id = pthread_create(&p_thread[i], NULL, &decodeThread, (void *)i);
//thr_id = pthread_create(&p_thread[i], NULL, &decodeThread, (void *)&i);
}
#endif
#endif

signal(SIGINT, sig_int);
/* get network number and mask associated with capture device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
#if 1
mask_addr.s_addr = mask;
net_addr.s_addr = net;
if((localnet = (char *)malloc(strlen(inet_ntoa(net_addr)))) == NULL) {
printf("localnet malloc failed\n");
exit(EXIT_FAILURE);
}
//memset(localnet, 0x00, strlen(inet_ntoa(net_addr)));
//sprintf(localnet, "%s\n", inet_ntoa(net_addr));
//printf("netmask : %s\n", inet_ntoa(mask_addr));
//printf("localnet : %s", localnet);
//free(localnet);
#endif
// IP Class (A Class, B Class, C Class)
mask_addr.s_addr = mask;
if(subnet_mask(inet_ntoa(mask_addr)) != 0) {
fprintf(stderr, "unknown subnetmask\n");
exit(EXIT_FAILURE);
}
printf("IP Class : %c Class\n", ip_class);

if(local_net(inet_ntoa(net_addr)) != 0) {
printf("worng localnet\n");
free(localnet);
exit(EXIT_FAILURE);
}
printf("localnet : %s", localnet);

/* print capture info */
printf("Device: %s\n", dev);
if(num_packets < 0) {
printf("Number of packets: Limited \n");
}
else {
printf("Number of packets: %d\n", num_packets);
}

/* open capture device */
handle = pcap_open_live(dev, SNAP_LEN, 1, 0, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
exit(EXIT_FAILURE);
}

/* make sure we're capturing on an Ethernet device [2] */
if (pcap_datalink(handle) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet\n", dev);
exit(EXIT_FAILURE);
}

/* compile the filter expression */
if (pcap_compile(handle, &fp, NULL, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s\n", pcap_geterr(handle));
exit(EXIT_FAILURE);
}

/* apply the compiled filter */
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s\n", pcap_geterr(handle));
exit(EXIT_FAILURE);
}

/* now we can set our callback function */
pcap_loop(handle, 0, got_packet, NULL);
//pcap_dispatch(handle, 0, got_packet, NULL);
#if 1
//pthread_join(p_thread[0], (void *)&stat);
//pthread_join(p_thread[1], (void *)&stat);
#if 1
for(i=0; i pthread_join(p_thread[i], (void *)&stat);
//pthread_join(p_thread[i], (void *)&stat);
}
//stat = pthread_mutex_destroy(&mutex);
#endif
#endif

/* cleanup */
pcap_freecode(&fp);
pcap_close(handle);

return 0;
}

mulgo79의 이미지

제 메신져 아이디입니다

mulgo1004@nate.com

살려주세요 ㅠㅠ

grassman의 이미지

<code> </code> 태그를 사용하는게 좋을 듯 합니다.

그리고.. thread를 쓸 때는 Queue의 삽입과 제거 동작 역시 mutex 등으로 보호해 줘야 합니다.

ymir의 이미지

전역 변수가 무쟈게 많군요.. ;;
별도의 쓰레드에서도 전역변수에 접근하는 부분이 있다면..
해당 변수는 가급적 volatile 로 선언하시는게 만수무강에 좋습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

mulgo79의 이미지

thread의 문제도 아니었고, queue의 lock문제도 결국은 아니었던것 같습니다.

문제는 다름 아닌 포인터 였습니다.

포인터 복사를 하고 있어서 포인터가 변하면서 기존의 데이터가 찍혔던것으로

보여져서 수정을 했더니 sequence number도 맞고 다른것 전부 정상적으로 작동하고 있습니다.

이젠 패킷을 분석해야겠습니다. db에 적재했으니 분석 모듈을 만들어서

in cps/out cps, in packet/out packet, connection,

내부 호스트 끼리의 주고 받는 패킷인가, 내부/외부 간의 패킷인가

내부 호스트 들중에 client동작을 하는 것인가 server 동작을 하는것인가

서버호스트의 os판별(ttl값으로 판별 가능하다고 하더군요)

아무튼 우선은 저정도를 해야 겠군요 아웅~~~ 졸려..

다들 그럼 좋은 저녁이 아니구나 새벽되세요 ^^

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.