3. 사용 예
실제 libpcap을 이용하는 많은 응용이 있습니다. 위에서 언급한 tcpdump가 그 대표적인 예이며, 이것 외에 많은 네트워크 모니터링 툴, 공개 네트워크 IDS(Intrusion Detection System) 인 Snort, 패킷을 캡쳐하는 많은 응용들이 libpcap을 이용하여 다양한 OS에 포팅되어 있습니다. www.tcpdump.org/related.html에 가면 응용의 예들을 볼 수 있습니다.
그럼 libpcap을 이용한 간단한 예를 보이겠습니다. 아래 소스는 ip기반 tcp, udp, icmp의 패킷을 잡아서 각 프로토콜 필드별로 프린트해주는 소스입니다.
먼저 IP, TCP, UDP의 각 HEADER FORMAT을 참조하시기 바랍니다.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Internet Datagram Header
<RFC791>
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
TCP Header Format
<RFC793>
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source | Destination |
| Port | Port |
+--------+--------+--------+--------+
| | |
| Length | Checksum |
+--------+--------+--------+--------+
|
| data octets ...
+---------------- ...
User Datagram Header Format
<RFC768>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <pcap/pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#define PROMISCUOUS 1
struct iphdr *iph;
struct tcphdr *tcph;
struct udphdr *udph;
struct icmp *icmph;
static pcap_t *pd;
int sockfd;
int pflag;
int rflag;
int eflag;
int cflag;
int chcnt;
char *device, *filter_rule;
void packet_analysis(unsigned char *, const struct pcap_pkthdr *,
const unsigned char *);
struct printer {
pcap_handler f;
int type;
};
/* datalink type에 따른 불리어질 함수들의
목록들을 갖는 구조체
Data-link level type codes.
#define DLT_NULL 0 no link-layer encapsulation
#define DLT_EN10MB 1 Ethernet (10Mb)
#define DLT_EN3MB 2 Experimental Ethernet (3Mb)
#define DLT_AX25 3 Amateur Radio AX.25
#define DLT_PRONET 4 Proteon ProNET Token Ring
#define DLT_CHAOS 5 Chaos
#define DLT_IEEE802 6 IEEE 802 Networks
#define DLT_ARCNET 7 ARCNET
#define DLT_SLIP 8 Serial Line IP
#define DLT_PPP 9 Point-to-point Protocol
#define DLT_FDDI 10 FDDI
#define DLT_ATM_RFC1483 11 LLC/SNAP encapsulated atm
#define DLT_RAW 12 raw IP
#define DLT_SLIP_BSDOS 13 BSD/OS Serial Line IP
#define DLT_PPP_BSDOS 14 BSD/OS Point-to-point Protocol
bpf.h 라는 헤더화일에 위와 같은 내용으로 정의되어 있다. */
static struct printer printers[] = {
{ packet_analysis, DLT_IEEE802 },
{ packet_analysis, DLT_EN10MB },
{ NULL, 0 },
};
/* datalink type에 따라 수행될 함수를 결정하게 된다.
이는 pcap_handler라는 함수형 포인터의 값으로 대입된다. */
static pcap_handler lookup_printer(int type)
{
struct printer *p;
for(p=printers; p->f; ++p)
if(type == p->type)
return p->f;
perror("unknown data link type");
}
/* pcap_loop()에 의해 패킷을 잡을 때마다 불려지는 함수
pcap_handler가 이 함수를 포인터하고 있기 때문이다 */
void packet_analysis(unsigned char *user, const struct pcap_pkthdr *h,
const unsigned char *p)
{
int j, temp;
unsigned int length = h->len;
struct ether_header *ep;
unsigned short ether_type;
unsigned char *tcpdata, *udpdata,*icmpdata, temp_char;
register unsigned int i;
chcnt = 0;
if(rflag) {
while(length--) {
printf("%02x ", *(p++));
if( (++chcnt % 16) == 0 ) printf("\n");
}
fprintf(stdout, "\n");
return;
}
length -= sizeof(struct ether_header);
// ethernet header mapping
ep = (struct ether_header *)p;
// ethernet header 14 bytes를 건너 뛴 포인터
p += sizeof(struct ether_header);
// datalink type
ether_type = ntohs(ep->ether_type);
printf("\n");
// Ethernet frame이 IEEE802인경우 ether_type필드가 길이필드가 된다.
if(ether_type <= 1500) {
;
/*while(length--) {
if(++is_llchdr <= 3) {
fprintf(stdout,"%02x",*p++);
continue;
}
if(++next_line == 16) {
next_line = 0;
printf("\n");
}
printf("%02x",*p++);
}*/
}
else
{
if(eflag) {
printf("\n\n=================== Datalink layer ===================\n");
for(j=0; j<ETH_ALEN; j++) {
printf("%X", ep->ether_shost[j]);
if(j != 5) printf(":");
}
printf(" ------> ");
for(j=0; j<ETH_ALEN; j++){
printf("%X", ep->ether_dhost[j]);
if(j != 5) printf(":");
}
printf("\nether_type -> %x\n", ntohs(ep->ether_type));
}
iph = (struct iphdr *) p;
i = 0;
if (ntohs(ep->ether_type) == ETHERTYPE_IP) { // ip 패킷인가?
printf("\n\n=================== IP HEADER ===================\n");
printf("%s -----> ", inet_ntoa(iph->saddr));
printf("%s\n", inet_ntoa(iph->daddr));
printf("Version: %d\n", iph->version);
printf("Herder Length: %d\n", iph->ihl);
printf("Service: %#x\n",iph->tos);
printf("Total Length: %d\n", ntohs(iph->tot_len));
printf("Identification : %d\n", ntohs(iph->id));
printf("Fragment Offset: %d\n", ntohs(iph->frag_off));
printf("Time to Live: %d\n", iph->ttl);
printf("Checksum: %d\n", ntohs(iph->check));
if(iph->protocol == IPPROTO_TCP) {
tcph = (struct tcphdr *) (p + iph->ihl * 4);
// tcp data는
tcpdata = (unsigned char *) (p + (iph->ihl*4) + (tcph->doff * 4));
printf("\n\n=================== TCP HEADER ===================\n");
printf("Source Port: %d\n", ntohs(tcph->source));
printf("Destination Port: %d\n", ntohs(tcph->dest));
printf("Sequence Number: %d\n", ntohl(tcph->seq));
printf("Acknowledgement Number: %d\n", ntohl(tcph->ack_seq));
printf("Data Offset: %d\n", tcph->doff);
printf("Window: %d\n", ntohs(tcph->window));
printf("URG:%d ACK:%d PSH:%d RST:%d SYN:%d FIN:%d\n",
tcph->urg, tcph->ack, tcph->psh, tcph->rst,
tcph->syn, tcph->fin, ntohs(tcph->check),
ntohs(tcph->urg_ptr));
printf("\n=================== TCP DATA(HEX) =================\n");
chcnt = 0;
for(temp = (iph->ihl * 4) + (tcph->doff * 4); temp <= ntohs(iph->tot_len) - 1; temp++) {
printf("%02x ", *(tcpdata++));
if( (++chcnt % 16) == 0 ) printf("\n");
}
if (pflag) {
tcpdata = (unsigned char *) (p + (iph->ihl*4) + (tcph->doff * 4));
printf("\n=================== TCP DATA(CHAR) =================\n");
for(temp = (iph->ihl * 4) + (tcph->doff * 4); temp <= ntohs(iph->tot_len) - 1; temp++) {
temp_char = *tcpdata;
if ( (temp_char == 0x0d) && ( *(tcpdata+1) == 0x0a ) ) {
fprintf(stdout,"\n");
tcpdata += 2;
temp++;
continue;
}
temp_char = ( ( temp_char >= ' ' ) && ( temp_char < 0x7f ) )? temp_char : '.';
printf("%c", temp_char);
tcpdata++;
}
}
printf("\n>>>>> End of Data >>>>>\n");
}
else if(iph->protocol == IPPROTO_UDP) {
udph = (struct udphdr *) (p + iph->ihl * 4);
udpdata = (unsigned char *) (p + iph->ihl*4) + 8;
printf("\n==================== UDP HEADER =====================\n");
printf("Source Port : %d\n",ntohs(udph->source));
printf("Destination Port : %d\n", ntohs(udph->dest));
printf("Length : %d\n", ntohs(udph->len));
printf("Checksum : %x\n", ntohs(udph->check));
printf("\n=================== UDP DATA(HEX) ================\n");
chcnt = 0;
for(temp = (iph->ihl*4)+8; temp<=ntohs(iph->tot_len) -1; temp++) {
printf("%02x ", *(udpdata++));
if( (++chcnt % 16) == 0) printf("\n");
}
udpdata = (unsigned char *) (p + iph->ihl*4) + 8;
if(pflag) {
printf("\n=================== UDP DATA(CHAR) ================\n");
for(temp = (iph->ihl*4)+8; temp<=ntohs(iph->tot_len) -1; temp++) {
temp_char = *udpdata;
if ( (temp_char == 0x0d) && ( *(udpdata+1) == 0x0a ) ) {
fprintf(stdout,"\n");
udpdata += 2;
temp++;
continue;
}
temp_char = ( ( temp_char >= ' ' ) && ( temp_char < 0x7f ) )? temp_char : '.';
printf("%c", temp_char);
udpdata++;
}
}
printf("\n>>>>> End of Data >>>>>\n");
}
else if(iph->protocol == IPPROTO_ICMP) {
icmph = (struct icmp *) (p + iph->ihl * 4);
icmpdata = (unsigned char *) (p + iph->ihl*4) + 8;
printf("\n\n=================== ICMP HEADER ===================\n");
printf("Type : %d\n", icmph->icmp_type);
printf("Code : %d\n", icmph->icmp_code);
printf("Checksum : %02x\n", icmph->icmp_cksum);
printf("ID : %d\n", icmph->icmp_id);
printf("Seq : %d\n", icmph->icmp_seq);
printf("\n=================== ICMP DATA(HEX) =================\n");
chcnt = 0;
for(temp = (iph->ihl * 4) + 8; temp <= ntohs(iph->tot_len) - 1; temp++) {
printf("%02x ", *(icmpdata++));
if( (++chcnt % 16) == 0 ) printf("\n");
}
printf("\n>>>>> End of Data >>>>>\n");
}
}
}
}
void sig_int(int sig)
{
printf("Bye!!\n");
pcap_close(pd);
close(sockfd);
exit(0);
}
void usage(void)
{
fprintf(stdout," Usage : noh_pa filter_rule [-pch]\n");
fprintf(stdout," -p : 데이타를 문자로 출력한다.\n");
fprintf(stdout," -c : 주어진 숫자만큼의 패킷만 덤프한다\n");
fprintf(stdout," -e : datalink layer를 출력한다.\n");
fprintf(stdout," -r : 잡은 패킷을 생으로 찍는다.\n");
fprintf(stdout," -h : 사용법\n");
}
int main(int argc, char *argv[])
{
struct bpf_program fcode;
pcap_handler printer;
char ebuf[PCAP_ERRBUF_SIZE];
int c, i, snaplen = 1514, size, packetcnt;
bpf_u_int32 myself, localnet, netmask;
unsigned char *pcap_userdata;
filter_rule = argv[1]; /* example : "src host xxx.xxx.xxx.xxx and tcp port 80" */
signal(SIGINT,sig_int);
opterr = 0;
if(argc-1 < 1) {
usage();
exit(1);
}
while( (c = getopt(argc, argv,"i:c:pher")) != -1) {
switch(c) {
case 'i' :
device = optarg;
break;
case 'p' :
pflag = 1;
break;
case 'c' :
cflag = 1;
packetcnt = atoi(optarg);
if(packetcnt <= 0) {
fprintf(stderr,"invalid number %s",optarg);
exit(1);
}
break;
case 'e' :
eflag = 1;
break;
case 'r' :
rflag = 1;
break;
case 'h' :
usage();
exit(1);
}
}
if (device == NULL ) {
if ( (device = pcap_lookupdev(ebuf) ) == NULL) {
perror(ebuf);
exit(-1);
}
}
fprintf(stdout, "device = %s\n", device);
pd = pcap_open_live(device, snaplen, PROMISCUOUS, 1000, ebuf);
if(pd == NULL) {
perror(ebuf);
exit(-1);
}
i = pcap_snapshot(pd);
if(snaplen < i) {
perror(ebuf);
exit(-1);
}
if(pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
perror(ebuf);
exit(-1);
}
setuid(getuid());
if(pcap_compile(pd, &fcode, filter_rule, 0, netmask) < 0) {
perror(ebuf);
exit(-1);
}
if(pcap_setfilter(pd, &fcode) < 0) {
perror(ebuf);
exit(-1);
}
fflush(stderr);
printer = lookup_printer(pcap_datalink(pd));
pcap_userdata = 0;
if(pcap_loop(pd, packetcnt, printer, pcap_userdata) < 0) {
perror("pcap_loop error");
exit(-1);
}
pcap_close(pd);
exit(0);
}
http://www.unix.co.kr/data/kldp_org/KoreanDoc/html/Libpcap-KLDP/example.html