본문 바로가기
네트워크

libpcap 라이브러리 API 정리

by 상레알 2010. 6. 24.
출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/libpcap%C0%BB_%C0%CC%BF%EB%C7%D1_%C7%C1%B7%CE%B1%D7%B7%A1%B9%D6 

이글은 그냥 제가 보려고 한거고 실제로는 위 출처에 잇는 내용입니다. 자세한 설명과 예제는 위 링크에 잘 설명되어 있으니 위 출처에서 보시는게 나을 거 같네요...

1. 디바이스 & 네트웍 정보 관련 API

 [1] int pcap_lookupnet()

int pcap_lookup(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf

네트웍 디바이스에 대한 네트웍 및 mask 번호를 되돌려 준다. 네트웍 번호는 netp에 mask 번호는 maskp에 저장된다. device는 pcap_lookupdev 등을 통해 얻어온 네트웍 디바이스 이름이다. 

에러가 발생할 경우 -1 이 리턴되며, 에러 내용이 errbuf 에 저장된다.

[2] char* pcap_lookupdev

pcap_open_live()와 pcap_lookupnet()에서 사용하기 위한 네트웍 디바이스에 대한 포인터를 되돌려준다.
성공할 경우 "eth0", "eth1" 과 같은 이름을 되돌려 주며 실패할 경우 0을 되돌려 준다.

[3] pcap_datalink

int pcap_datalink(pcap_t *p)

link layer 타입을 되돌려준다. (DLT_EN10MB 과 같은)

2. 패킷 캡쳐 초기화 관련 API

파일 관련 작업을 할때 file descriptor(파일 지정자)를 이용해서 작업 하는것과 마찬가지로, 패킷 캡쳐 관련 작업을 할때에도 packet capture descriptor 를 가지고 작업을 한다.

packet capture descriptor는 pcatp_t*  형으로 선언되어 있다.

[1] pcatp_t *pcap_open_live

pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)

첫번째 인자로 주어지는 네트웍 디바이스 device에 대한 packet capture descriptor(이하 PCD)을 만들기 위한 함수이다. 패킷을 캡춰하는 실질적인 모든일은 pcap_open_live 함수를 호출해서 만들어진 PCD를 이용해서 이루어지게 된다.

linux 커널 2.2 이상의 경우 device 를 "any"혹은 NULL 로 할 경우 모든 네트웍 디바이스에 대해서 패킷 캡쳐가 일어나게 된다.

snaplen은 받아 들일 수 있는 패킷의 최대 크기(byte)이다.

promisc는 네트웍 디바이스를 promiscuous mode 로 할것인지를 결정하기 위해서 사용한다.  promisc 가 1일 경우 promiscuous 모드가 되며, 로컬 네트웍의 모든 패킷을 캡쳐하게 된다. -일경우에는 자기에게만 향하는 패킷을 캡쳐하게 되는데, 몇몇 경우에 있어서 promiscuous 모드로 작동하기도 한다.

to_ms는 읽기 시간초과(time out)지정을 위해서 사용되며 millisecond 단위이다.

ebuf는 pcap_open_live 함수 호출에 문제가 생겼을 경우 에러 메시지를 저장하기 위해서 사용한다. 만약 pcap_open_live 함수 호출시 에러가 발생할경우 NULL를 리턴하고 에러내용을 ebuf에 복사한다.

[2] pcap_t * pcap_opn_offline

pcap_t *pcap_open_offline(char *fname, char *ebuf)

fname 를 가지는 파일로 부터 패킷을 읽어들인다. 만약 fname 이 "-"일 경우 stdin으로 부터 읽어 들인다 .

ebuf는 에러메시지를 저장하기 위해서 사용된다.

패킷 캡쳐 (Read) 관련 API

[1] TCP, IP Ethenet 구조체

패킷 Read 관련 API에서는 패킷을 읽었을때, Demultiplexing 이 되지 않은 완전한 구조의 패킷을 넘겨준다. 그럼으로 최소한 이들 각 패킷의 구조체정보를 알고 있어야 각 계층(Layer)의 데이타를 읽어 올 수 있다.

struct in_addr

인터넷 주소를 저장하기 위해서 사용한다.
/usr/include/netinet/in.h에 정의되어 있다.

typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;
};

uint32_t는 unsigned int의 typedef 자료형이다.
[1] TCP 헤더 구조체

struct tcphdr
  {
    u_int16_t th_sport;     /* source port */
    u_int16_t th_dport;     /* destination port */
    tcp_seq th_seq;     /* sequence number */
    tcp_seq th_ack;     /* acknowledgement number */
#  if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int8_t th_x2:4;       /* (unused) */
    u_int8_t th_off:4;      /* data offset */
#  endif
#  if __BYTE_ORDER == __BIG_ENDIAN
    u_int8_t th_off:4;      /* data offset */
    u_int8_t th_x2:4;       /* (unused) */
#  endif
    u_int8_t th_flags;
#  define TH_FIN    0x01
#  define TH_SYN    0x02
#  define TH_RST    0x04
#  define TH_PUSH   0x08
#  define TH_ACK    0x10
#  define TH_URG    0x20
    u_int16_t th_win;       /* window */
    u_int16_t th_sum;       /* checksum */
    u_int16_t th_urp;       /* urgent pointer */
};


[2] IP 헤더 구조체

struct ip
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ip_hl:4;       /* header length */
    unsigned int ip_v:4;        /* version */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned int ip_v:4;        /* version */
    unsigned int ip_hl:4;       /* header length */
#endif
    u_int8_t ip_tos;            /* type of service */
    u_short ip_len;         /* total length */
    u_short ip_id;          /* identification */
    u_short ip_off;         /* fragment offset field */
#define IP_RF 0x8000            /* reserved fragment flag */
#define IP_DF 0x4000            /* dont fragment flag */
#define IP_MF 0x2000            /* more fragments flag */
#define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
    u_int8_t ip_ttl;            /* time to live */
    u_int8_t ip_p;          /* protocol */
    u_short ip_sum;         /* checksum */
    struct in_addr ip_src, ip_dst;  /* source and dest address */
  };

[3] Ethernet 헤더 구조체

struct ethhdr
{
    unsigned char   h_dest[ETH_ALEN];   /* destination eth addr */
    unsigned char   h_source[ETH_ALEN]; /* source ether addr    */
    unsigned short  h_proto;            /* packet type ID field */
};

ip, tcp 헤더 파일은 /usr/include/netinet 밑에서 찾을 수 있으며, ethernet 헤더 파일은 /usr/include/linux/if_ether.h 에서 찾을 수 있다.

Ethernet 헤더와 IP 헤더의 경우 demultiplexing 과정을 거치기 위해서 사우이 Layer 의 프로토콜 타입을 지정하고 있음을 알 수 있다. Ethernet 헤더의 h_proto 와 IP 헤더의 ip_p 가 각 상위 Layer의 프로토콜 타입을 알려주기 위해서 사용된다.

부연설명을하자면 운영체제가 패킷을 받으면 가장 먼저 Link 레이어를 거치는데 Link 레이어에서는 Ethernet 헤더를 분석해서 패킷이 Network 레이어로 전달되는 패킷인지 확인해서 Network 레이어로 전달된다면 해당 패킷이 IP패킷인지 아니면 ICMP , IGMP 와 같은 패킷인지를 검사한후 Network 레이어의 알맞은 처리루틴으로 보낼것이다. Network 레이어에서는 패킷을 받은 다음 자신의 프로토콜 헤더를 검사해서 이 패킷이 Transport 레이어로 전달되는 패킷인지 확인하고, Transport 레이어로 전달된다면 UDP인지, TCP 인지를 확인한 다음에 Transport 레이어의 적당한 처리루틴으로 패킷을 던질 것이다. 최후에는 TCP 헤더만 남게 되는데, TCP 헤더의 PORT를 검사해서 어떤 어플리케이션에게 전달되어야 하는지를 최종 결정하게 된다.


[4] u_char *pcap_next

uchar *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

다음 패킷에 대한 포인터를 리턴한다.

우리는 이 패킷을 읽음으로써 패킷의 정보를 얻어올수 있다. 실제로 이 함수를 이용해서 패킷 캡쳐와 관련된 모든 일을 할 수 있다. 나머지 패킷캡쳐와 관련된 함수들은 (pcap_loop 와 같은) 이 함수의 기능 추가 버전이라고 볼수 있다.

[5] pcap_loop

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

P 는 PCD 이며, cnt는 패킷 캡쳐를 몇번에 걸쳐서 할것인지를 결정하기 위해서 사용하낟. 만약 0이 지정되면 계속 패킷을 받아들이게 된다. callback는 패킷이 들어왔을떄 실행하는 함수의 포인터 이다. 보통은 패킷 필터링과 관련된 함수가 실행될것이다.

[6] pcap_dispatch

int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

pcap_loop와 거의 비슷하다고 한다.


패킷 필터링 관련 API

[1] pcap_compile

int pcap_compile(pcap_t *p, struct bpf_program *frp, char *str, int optimize, bpf_u_int32 netmask)

들어오는 패킷을 필터링해서 받아들이기 위해서 사용한다. 예를 들어 tcpdump에서 port80으로 오는 패킷만을 캡쳐하기 위해서 다음과 같이 사용하는걸 보았을 것이다.
--------------------------------------------------------------------------
$ tcpdump port 80
Kernel filter, protocol ALL, TURBO mode (575 frames), datagram packet socket
tcpdump: listening on all devices
--------------------------------------------------------------------------
tcpdump 명령 실행시킬때 뒤에 준옵션인 port80 이 filter rule이며, str 아규먼트를 통해서 전달된다.
fp  bfp_program 구조체의 포인터이며 pcap_compile 에 의해서 채워진다. netmask는 로컬 네트의 netmask 이다.

filter rule 에대한 내용은 tcpdump의 man 페이지에 상세하게 나와 있으니 참고하기 바란다.


[2] pcap_setfilter

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

pcap_compile을 통해서 지정된 필터를 적용시키기 위해서 사용되며 앞으로 들어오는 패킷에 대햇서는 이 필터룰에 의해서 필터링 된다.