libpcap 예제 기본 사용법
패킷 캡쳐 라이브러리인 libpcap을 설치하고 사용법과 예제 프로그래밍 방법 설명드립니다.
libpcap은 tcpdump 뿐만 아니라 Wireshark/TShark 등에서 네트워크 패킷을 캡쳐하기 위해서 사용되는 라이브러리 입니다.
리눅스 환경을 기준으로 설명드리지만 윈도우 환경일 경우 winpcap 이라는 라이브러리를 이용하여 동일하게 테스트 하실 수 있습니다.
우분투 환경일 경우 아래의 명령어로 패키지를 설치 할 수 있습니다.
$ sudo apt-get install libpcap-dev
그리고 예제에 사용된 전체 소스코드는 아래의 명령어로 다운 받으실 수 있습니다.
$ git clone https://hiseon.me/reps/libpcap-tutorial.git
pcap 파일 구조에 대해 확인 하는 내용은 아래의 페이지를 참고하시기 바랍니다.
네트워크 인터페이스 정보
먼저, 시스템에 설치된 네트워크 인터페이스 정보를 확인 하는 예제입니다.
#include <stdio.h>
#include <pcap.h>
int main(void) {
pcap_if_t *alldevs;
pcap_if_t *d;
int i = 0;
char errbuf[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs(&alldevs, errbuf) < 0) {
printf("pcap_findalldevs error\n");
return 1;
}
if (!alldevs) {
printf("%s\n", errbuf);
}
for (d=alldevs; d; d=d->next) {
printf("%p : %d. %s", d, ++i, d->name);
if (d->description) printf(" (%s)", d->description);
printf("\n");
}
pcap_freealldevs(alldevs);
return 0;
}
위의 예제를 빌드하기 위해서는 -lpcap 이라는 링크 옵션을 추가해야 하는데, 아래의 명령어로 빌드 할 수 있십니다.
gcc -o example example.c -lpcap
시스템에 설치된 네트워크 인터페이스 정보를 확인 하기 위해서는 pcap_findalldevs 함수를 사용하면 됩니다.
pcap_findalldevs 함수를 사용하였을 경우 메모리를 동적으로 할당 하기 때문에 함수 호출 뒤에는 pcap_freealldevs 함수를 호출해 주셔야 합니다.
int pcap_findalldevs(pcap_if_t **, char *);
void pcap_freealldevs(pcap_if_t *);
pcap_if_t 구조체에 네트워크 인터페이스 정보가 저장이 되게 되는데, d->addresses 의 필드를 참고하여 네트워크 인터페이스에 할당된 주소 정보를 추가로 확인 할 수 있습니다. 아래의 내용은 pcap_if_t 구조체의 내용입니다.
/*
* Item in a list of interfaces.
*/
struct pcap_if {
struct pcap_if *next;
char *name; /* name to hand to "pcap_open_live()" */
char *description; /* textual description of interface, or NULL */
struct pcap_addr *addresses;
bpf_u_int32 flags; /* PCAP_IF_ interface flags */
};
typedef struct pcap_if pcap_if_t;
보다 자세한 내용은 git 소스코드를 참고해 주시거나, pcap.h 파일의 내용을 참고하시면 됩니다.
참고로 pcap.h 파일은 아래 위치에 존재하게 됩니다.
/usr/include/pcap/pcap.h
아래의 내용은 인터페이스 주소를 가져오고, 주소를 출력하는 결과 내용입니다.
1 : enp6s0
2 : ppp0
3 : any
4 : lo
5 : docker0
6 : nflog
7 : nfqueue
8 : usbmon1
9 : usbmon2
10 : usbmon3
11 : usbmon4
12 : usbmon5
13 : usbmon6
14 : usbmon7
15 : usbmon8
number : 1
name : enp6s0
address : 192.168.0.66
네트워크 패킷 캡쳐
아래의 내용은 전체 네트워크 인테페이스 정보를 가져오고, 패킷을 수신하는 예제입니다.
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pcap.h>
void packet_handler(u_char *param,
const struct pcap_pkthdr *header, const u_char *pkt_data) {
printf("caplen : %d\n", header->caplen);
printf("len : %d\n", header->len);
}
int main(int argc, char **argv) {
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs;
pcap_if_t *d;
struct pcap_addr *a;
int i = 0;
int no;
if (pcap_findalldevs(&alldevs, errbuf) < 0) {
printf("pcap_findalldevs error\n");
return 1;
}
for (d=alldevs; d; d=d->next) {
printf("%d : %s\n", ++i, (d->description)?(d->description):(d->name));
}
printf("number : ");
scanf("%d", &no);
if (!(no > 0 && no <= i)) {
printf("number error\n");
return 1;
}
for (d=alldevs, i=0; d; d=d->next) {
if (no == ++i) break;
}
if (!(adhandle= pcap_open_live(d->name, 65536, 1, 1000, errbuf))) {
printf("pcap_open_live error %s\n", d->name);
pcap_freealldevs(alldevs);
return -1;
}
pcap_freealldevs(alldevs);
pcap_loop(adhandle, 0, packet_handler, NULL);
pcap_close(adhandle);
return 0;
}
위의 예제에서 가장 중요한 함수가 있는데 바로 아래의 2개의 함수 입니다.
pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf);
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
pcap_open_live 함수는 패킷을 캡쳐하기 위해서 네트워크 디바이스를 오픈 하는 역할을 합니다.
그리고, pcap_loop 함수는 패킷이 수신되었을 경우 호출되는 핸들러 함수를 지정합니다. 핸들러 함수의 원형은 아래와 같습니다.
typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
함수를 통해서 pcap_pkthdr 구조체의 포인터 주소가 전달되는데, pcap_pkthdr 구조체 내용은 아래와 같습니다.
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
pcap_pkthdr 구조체 정보와, 실제 패킷의 포인터 주소값을 이용하여 수신된 패킷을 처리 할 수 있습니다.
위의 예제에서는 수신된 패킷을 단순히 크기만 출력하였지만, 앞으로 패킷 헤더를 함께 분석해 보도록 하겠습니다.
1 : enp6s0
2 : ppp0
3 : Pseudo-device that captures on all interfaces
4 : lo
5 : docker0
6 : Linux netfilter log (NFLOG) interface
7 : Linux netfilter queue (NFQUEUE) interface
8 : USB bus number 1
9 : USB bus number 2
10 : USB bus number 3
11 : USB bus number 4
12 : USB bus number 5
13 : USB bus number 6
14 : USB bus number 7
15 : USB bus number 8
number : 1
caplen : 56
len : 56
caplen : 60
len : 60
프로토콜 헤더
pcap_handler 핸들러 함수에서 호출될때에 전달되는 bytes 포인터 변수의 내용을 프로토콜에 맞춰서 출력하실 수 있습니다. 현재 아래와 같은 프로토콜 종류에 대해한 글들과 예제 소스코드등이 작성되었습니다.
( 본문 인용시 출처를 밝혀 주시면 감사하겠습니다.)