패킷 캡쳐는 네트웍 상에서 돌아다니는 패킷을 들여다 보는 걸
말한다. 패킷 캡쳐라는 어감상 패킷을 "잡는"게 아닌가 라고
생각할수 있지만, 패킷을 "잡지"는 않고 단지 들여다만 볼 뿐이다.
만약 여러분의 호스트가 포함된 네트웍을 관리하는 라우터가
일반적인(스위칭이 아닌) 라우터라면, 내부로 향하는 모든
패킷은 브로드캐스팅(Broadcasting) 된다. 이는 스위칭 라우터가
아닌한은 모든 로컬네트웍의 패킷을 들여다 볼수 있음을
의미하기도 한다. 어쨋든 이경우 운영체제는 자신에게 도착된
패킷중 목적지가 자신인 패킷만을 처리해서 Application Layer 까지
올려 보내게 된다.
libpcap 을 사용하면 이러한 패킷의 캡쳐가 가능해진다.
인터넷 상의 패킷은 상대방에게 보낼경우 encapuslation
과정을 거치고, 받은 패킷에 대해서는 demultiplexing
과정을 거친다는 것을 알고 있을것이다 -
TCP/IP 개요(3) 참고 -
libpcap 을 사용해서 캡쳐한 패킷은 demultiplexing 과정을
거치기 전의 패킷이다. 이렇게 해서 캡쳐한 패킷은
각 프로토콜 단위로(구조체) 읽어서 처리하면 된다.
다음은 encapuslation&demultiplexing 과정이다.
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 모드가 되며,
로컬 네트웍의 모든 패킷을 캡쳐하게 된다. 0 일경우
에는 자기에게만 향하는 패킷을 캡쳐하게 되는데, 몇몇
경우에 있어서 promiscuous 모드로 작동하기도 한다.
to_ms 는 읽기 시간초과(time out)
지정을 위해서 사용되며 millisecond 단위이다.
ebuf 는 pcap_open_live 함수 호출에
문제가 생겼을경우 에러 메시지를 저장하기 위해서
사용한다. 만약 pcap_open_live 함수 호출시 에러가
발생할경우 NULL 을 리턴하고 에러내용을 ebuf 에 복사한다.
이번장에서는 실제 패킷을 캡쳐하는 관련 API 들에 대해서
알아볼것이다. 이 패킷 캡처 관련 API 를 제대로 이해하고
사용하기 위해서는 TCP/IP와 이더넷 프로토콜의 구조에
대해서 어느정도 이해를 해야 한다. 그럼으로 API 를 다루기
전에 이들 대표적인 프로토콜들에 대한 헤더 정보에
대해서 간략하게 먼저 알아보도록 하겠다.
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 */
};
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 를 검사해서
어떤 어플리케이션에게 전달되어야 하는지를 최종
결정하게 된다.
libpcap 를 이용한 프로그래밍
윤 상배
dreamyun@yahoo.co.kr
1절. 소개
이번 강좌는 libpcap 을 사용한 패킷 캡춰에 대한 내용이다.
2절. Libpcap 기본
2.1절. Libpcap 에 대하여
Libpcap(이하 pcap)은 "Portable Packet Capturing Library"의 줄임말이며, 해석그대로 "간단하게 패킷을 캡쳐하기 위한 함수모음(라이브러리)" 이다.
물론 pcap 외에도 패킷캡쳐를 위한 도구들이 있기는 하지만, 대부분의 경우 운영체제에 종속적이여서, 운영체제별로 코드를 다시 짜야 한다는 불편함이 있다. 대표적인 도구로는 SOCK_PACKET, LSF, SNOOP, SNIT 등이 있다.
이에 비해 pcap 는 운영체제에 상관없이 범용적으로 사용가능한 API를 제공해줌으로, 공용프로그램 혹은 공용라이브러리의 제작이 가능하도록 도와준다. 또한 간단하게 사용가능한 사용자 레벨 라이브러리이다.
libpcap 를 이용한 가장 대표적인 프로그램이 tcpdump 와 SAINT 와 같은 프로그램들이다.
또한 상용 IDS [1] 제품의 상당수가 패킷분석을 위해서 libpcap 을 사용하고 있다.
2.2절. libpcap 의 설치
여러분이 Unix 계열 운영체제를 사용하고 있다면, 거의 대부분 tcpdump 를 곧바로 사용할수 있을것이다. tcpdump 를 사용할수 있다는 것은 그 기반이 되는 libpcap 역시 설치되어 있다는 말이 된다.
그러나 만약의 경우 설치가 되어 있지 않다면 tcpdump.org 에서 받아서 컴파일후 설치하기 바란다.
컴파일 하기가 귀찮다면 그리고 레드헷이나 데비안 계열의 리눅스 사용자라면 해당 패키지를 배포하는 ftp 사이트에서 다운 받아서 설치하면 된다. 솔라리스 운영체제 라면 www.sunfreeware.com 에서 패키지를 받아서 설치하기 바란다.
2.3절. 패킷 캡쳐의 기본이해
패킷 캡쳐는 네트웍 상에서 돌아다니는 패킷을 들여다 보는 걸 말한다. 패킷 캡쳐라는 어감상 패킷을 "잡는"게 아닌가 라고 생각할수 있지만, 패킷을 "잡지"는 않고 단지 들여다만 볼 뿐이다.
만약 여러분의 호스트가 포함된 네트웍을 관리하는 라우터가 일반적인(스위칭이 아닌) 라우터라면, 내부로 향하는 모든 패킷은 브로드캐스팅(Broadcasting) 된다. 이는 스위칭 라우터가 아닌한은 모든 로컬네트웍의 패킷을 들여다 볼수 있음을 의미하기도 한다. 어쨋든 이경우 운영체제는 자신에게 도착된 패킷중 목적지가 자신인 패킷만을 처리해서 Application Layer 까지 올려 보내게 된다.
libpcap 을 사용하면 이러한 패킷의 캡쳐가 가능해진다. 인터넷 상의 패킷은 상대방에게 보낼경우 encapuslation 과정을 거치고, 받은 패킷에 대해서는 demultiplexing 과정을 거친다는 것을 알고 있을것이다 - TCP/IP 개요(3) 참고 - libpcap 을 사용해서 캡쳐한 패킷은 demultiplexing 과정을 거치기 전의 패킷이다. 이렇게 해서 캡쳐한 패킷은 각 프로토콜 단위로(구조체) 읽어서 처리하면 된다. 다음은 encapuslation&demultiplexing 과정이다.
그림 1. Encapuslation & demultiplexing
2.4절. 패킷 캡쳐의 응용
패킷 캡쳐는 여러가지 목적으로 사용될수 있다. NIDS(Network Intrusion Detection System) 프로그램이 가장 대표적인 응용이며, 네트웍 트래픽 감시, 네트웍 디버깅을 위한 용도로 사용가능하다.
3절. libpcap 프로그래밍
이번장에서는 libpcap 에서 필수적으로 사용되는 중요 API 에 대해서 알아볼것이다.
3.1절. 디바이스&네트웍 정보 관련 API
3.1.1절. int pcap_lookupnet()
네트웍 디바이스에 대한 네트웍 및 mask 번호를 되돌려준다. 네트웍 번호는 netp에 mask 번호는 maskp에 저장된다. device는 pcap_lookupdev 등을 통해 얻어온 네트웍 디바이스 이름이다.
에러가 발생할경우 -1 이 리턴되며, 에러 내용이 errbuf 에 저장된다.
3.1.2절. char* pcap_lookupdev
pcap_open_live() 와 pcap_lookupnet() 에서 사용하기 위한 네트웍 디바이스에 대한 포인터를 되돌려준다. 성공할 경우 "eth0", "eth1" 과 같은 이름을 되돌려주며 실패할경우 0을 되돌려준다.
3.1.3절. pcap_datalink
3.1.4절. 예제
예제 : pcap.c
다음은 실행결과이다.
3.2절. 패킷 캡쳐 초기화 관련 API
파일관련 작업을 할때 file descriptor(파일지정자)를 이용해서 작업하는것과 마찬가지로, 패킷 캡쳐관련 작업을 할때에도 packet capture descriptor 를 가지고 작업을 한다.
packet capture descriptor 는 pcatp_t * 형으로 선언되어 있다.
3.2.1절. pcatp_t *pcap_open_live
linux 커널 2.2 이상의 경우 device 를 "any" 혹은 NULL로 할경우 모든 네트웍디바이스에 대해서 패킷 캡쳐가 일어나게 된다.
snaplen은 받아들일수 있는 패킷의 최대 크기(byte)이다.
promisc 는 네트웍 디바이스를 promiscuous mode 로 할것인지를 결정하기 위해서 사용한다. promisc 가 1일경우 promiscuous 모드가 되며, 로컬 네트웍의 모든 패킷을 캡쳐하게 된다. 0 일경우 에는 자기에게만 향하는 패킷을 캡쳐하게 되는데, 몇몇 경우에 있어서 promiscuous 모드로 작동하기도 한다.
to_ms 는 읽기 시간초과(time out) 지정을 위해서 사용되며 millisecond 단위이다.
ebuf 는 pcap_open_live 함수 호출에 문제가 생겼을경우 에러 메시지를 저장하기 위해서 사용한다. 만약 pcap_open_live 함수 호출시 에러가 발생할경우 NULL 을 리턴하고 에러내용을 ebuf 에 복사한다.
3.2.2절. pcap_t *pcap_open_offline
ebuf 는 에러메시지를 저장하기 위해서 사용된다.
3.3절. 패킷 캡쳐(Read) 관련 API
이번장에서는 실제 패킷을 캡쳐하는 관련 API 들에 대해서 알아볼것이다. 이 패킷 캡처 관련 API 를 제대로 이해하고 사용하기 위해서는 TCP/IP와 이더넷 프로토콜의 구조에 대해서 어느정도 이해를 해야 한다. 그럼으로 API 를 다루기 전에 이들 대표적인 프로토콜들에 대한 헤더 정보에 대해서 간략하게 먼저 알아보도록 하겠다.
3.3.1절. TCP,IP,Eternet 구조체
패킷 Read 관련 API에서는 패킷을 읽었을때, Demultiplexing 이 되지 않은 완전한 구조의 패킷을 넘겨준다. 그럼으로 최소한 이들 각 패킷의 구조체 정보를 알고 있어야 각 계층(Layer)의 데이타를 읽어올수 있다.
다음은 TCP, IP, Eternet 구조체정보이다.
tcp 헤더 구조체
IP 헤더 구조체
ETHERNET 헤더 구조체
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 를 검사해서 어떤 어플리케이션에게 전달되어야 하는지를 최종 결정하게 된다.
3.3.2절. u_char *pcap_next
우리는 이 패킷을 읽음으로써 패킷의 정보를 얻어올수 있다. 실지로 이 함수를 이용해서 패킷캡쳐와 관련된 모든 일을 할수 있다. 나머지 패킷캡쳐와 관련된 함수들은 (pcap_loop 같은) 이 함수의 기능 추가버젼 이라고 볼수 있다.
3.3.3절. pcap_loop
callback 는 패킷이 들어왔을때 실행하는 함수의 포인터이다. 보통은 패킷필터링과 관련된 함수가 실행될것이다.
3.3.4절. pcap_dispatch
3.4절. 패킷 필터링 관련 API
3.4.1절. pcap_compile
fp bfp_program 구조체의 포인터이며 pcap_compile 에 의해서 채워진다. netmask는 로컬 네트의 netmask 이다.
filter rule 에 대한 내용은 tcpdump 의 man 페이지에 상세하게 나와 있으니 참고하기 바란다.
3.4.2절. pcap_setfilter
4절. 예제 코드
pcap 의 기본적인 API 를 살펴봤으니 직접 코드를 작성해 보도록 하겠다.
예제 : pcap_test.c
컴파일 방법은 아래와 같다.
5절. 결론
이상 간단하게 libpcap 의 사용방법에 대해서 알아보았다. 이번 글에서는 libpcap 의 사용법에만 초첨을 맞추고 있는데, 다음번 강좌에서는 몇가지 "응용" 에 대해서 알아보도록 하겠다.
주석
Intrusion Detection System 의 줄임말 이며, 침입탐지 시스템을 말한다. 네트웍 침입탐지를 위한 NIDS, 호스트 침입탐지를 위한 HIDS 로 나눌수 있다. 일반적으로 IDS 라고 하면 네트웍 침입 탐지 시스템을 말한다.
Recent Posts
Archive Posts
Tags