pcap을 이용한 선형 Queue와 Thread문제
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분이 다되가네요
에효 답답해서 잠도 안옵니다. 그럼 꾸벅~~~ ㅠㅠ
일단, 소스가 보이지
일단, 소스가 보이지 않네요. 그리고 한가지, network packet이라는게 ip layer에서만 찍었을 경우에는 상황에 따라 저렇게 보일 가능성은 존재합니다. 혼합되지 않았다? 를 가지고 중복된다! 라고 말씀을 하시기엔, 좀 무리가 따를 거 같습니다.
정말 똑같은 packet인지 아닌지를 판별하기 위해서는, TCP의 sequence number까지 찍어보심이 좋을 거 같습니다.
실제로 중복되는지 안 되는지는 tcp의 sequence number만으로도 바로 알 수 있는 내용입니다.
------------------------------------------------------
아직은 젊다. 모든 것을 할 수 있는 나이란 말이지.
------------------------------------------------------
아직은 젊다. 모든 것을 할 수 있는 나이란 말이지.
시퀀스도 같아요 ㅠㅠ
시퀀스 역시 찍어 보았는데 같은 시퀀스가 찍혔었구요 그걸로 보아서
같은 패킷이 찍혔구나라고 판단되었습니다. 소스는 다시
#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;
}
아참 혹시라도 불쌍한 저에게 조언을 주실 분 메신져좀 ㅠㅠ
제 메신져 아이디입니다
mulgo1004@nate.com
살려주세요 ㅠㅠ
소스가 짤린데다 옆으로 붙어서 보기 힘들군요.
<code> </code> 태그를 사용하는게 좋을 듯 합니다.
그리고.. thread를 쓸 때는 Queue의 삽입과 제거 동작 역시 mutex 등으로 보호해 줘야 합니다.
음...
전역 변수가 무쟈게 많군요.. ;;
별도의 쓰레드에서도 전역변수에 접근하는 부분이 있다면..
해당 변수는 가급적 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 』
ㅠㅠ 드뎌 해결했습니다.
thread의 문제도 아니었고, queue의 lock문제도 결국은 아니었던것 같습니다.
문제는 다름 아닌 포인터 였습니다.
포인터 복사를 하고 있어서 포인터가 변하면서 기존의 데이터가 찍혔던것으로
보여져서 수정을 했더니 sequence number도 맞고 다른것 전부 정상적으로 작동하고 있습니다.
이젠 패킷을 분석해야겠습니다. db에 적재했으니 분석 모듈을 만들어서
in cps/out cps, in packet/out packet, connection,
내부 호스트 끼리의 주고 받는 패킷인가, 내부/외부 간의 패킷인가
내부 호스트 들중에 client동작을 하는 것인가 server 동작을 하는것인가
서버호스트의 os판별(ttl값으로 판별 가능하다고 하더군요)
아무튼 우선은 저정도를 해야 겠군요 아웅~~~ 졸려..
다들 그럼 좋은 저녁이 아니구나 새벽되세요 ^^
댓글 달기