컴퓨터공부/Linux & Unix

3. 사용 예

achivenKakao 2006. 9. 2. 08:56

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