2011년10월6일_pcap_ICMP헤더 구조체(icmp_ra_addr)를 가리키는 구조체포인터를 사용하여 ping과 traceroute시 패킷의 ICMP헤더를 추출하고 각 Type별 Code와 대응하는 내용(공용체-icmp_hun)을 출력하여 보자.




● /usr/include/netinet/ip_icmp.h

   1: // ICMP 헤더
   2: struct icmp
   3: {
   4:   u_int8_t  icmp_type;    /* type of message, see below */
   5:   u_int8_t  icmp_code;    /* type sub code */
   6:   u_int16_t icmp_cksum;    /* ones complement checksum of struct */
   7:   union
   8:   {
   9:     u_char ih_pptr;        /* ICMP_PARAMPROB */
  10:     struct in_addr ih_gwaddr;    /* gateway address */
  11:     struct ih_idseq        /* echo datagram */
  12:     {
  13:       u_int16_t icd_id;
  14:       u_int16_t icd_seq;
  15:     } ih_idseq;
  16:     u_int32_t ih_void;
  17:  
  18:     /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
  19:     struct ih_pmtu
  20:     {
  21:       u_int16_t ipm_void;
  22:       u_int16_t ipm_nextmtu;
  23:     } ih_pmtu;
  24:  
  25:     struct ih_rtradv
  26:     {
  27:       u_int8_t irt_num_addrs;
  28:       u_int8_t irt_wpa;
  29:       u_int16_t irt_lifetime;
  30:     } ih_rtradv;
  31:   } icmp_hun;            // 공용체의 크기는 4Bytes
  32: #define    icmp_pptr    icmp_hun.ih_pptr
  33: #define    icmp_gwaddr    icmp_hun.ih_gwaddr
  34: #define    icmp_id        icmp_hun.ih_idseq.icd_id
  35: #define    icmp_seq    icmp_hun.ih_idseq.icd_seq
  36: #define    icmp_void    icmp_hun.ih_void
  37: #define    icmp_pmvoid    icmp_hun.ih_pmtu.ipm_void
  38: #define    icmp_nextmtu    icmp_hun.ih_pmtu.ipm_nextmtu
  39: #define    icmp_num_addrs    icmp_hun.ih_rtradv.irt_num_addrs
  40: #define    icmp_wpa    icmp_hun.ih_rtradv.irt_wpa
  41: #define    icmp_lifetime    icmp_hun.ih_rtradv.irt_lifetime
  42:   union
  43:   {
  44:     struct
  45:     {
  46:       u_int32_t its_otime;
  47:       u_int32_t its_rtime;
  48:       u_int32_t its_ttime;
  49:     } id_ts;
  50:     struct
  51:     {
  52:       struct ip idi_ip;    // 옵션을 제외한 IP헤더구조체의 크기는 20Bytes
  53:       /* options and then 64 bits of data */
  54:     } id_ip;
  55:     struct icmp_ra_addr id_radv;
  56:     u_int32_t   id_mask;
  57:     u_int8_t    id_data[1];
  58:   } icmp_dun;        // 공용체의 크기는 20Bytes
  59: #define    icmp_otime    icmp_dun.id_ts.its_otime
  60: #define    icmp_rtime    icmp_dun.id_ts.its_rtime
  61: #define    icmp_ttime    icmp_dun.id_ts.its_ttime
  62: #define    icmp_ip        icmp_dun.id_ip.idi_ip
  63: #define    icmp_radv    icmp_dun.id_radv
  64: #define    icmp_mask    icmp_dun.id_mask
  65: #define    icmp_data    icmp_dun.id_data
  66: };

ICMP헤더의 구조가 복잡해 보이나 알고 보면 간단하다.
우선,

   4:   u_int8_t  icmp_type;    /* type of message, see below */
   5:   u_int8_t  icmp_code;    /* type sub code */
   6:   u_int16_t icmp_cksum;    /* ones complement checksum of struct */

type과 code와 cksum이 있는데 이는 모두 고정크기이고 헤더의 앞부분에 위치하며,
다음에 올 union(공용체) icmp_hun에 들어갈 데이터의 type과 위치와 내용을 선택한다.

  31:   } icmp_hun;            // 공용체의 크기는 4Bytes

처음 공용체의 크기는 멤버들의 크기가 모두 4Bytes이므로 쉽게 구할 수 있다.

  58:   } icmp_dun;        // 공용체의 크기는 20Bytes
  52:       struct ip idi_ip;    // 옵션을 제외한 IP헤더구조체의 크기는 20Bytes

WOW리눅스에 설치된 gcc의 버전이 낮은지 IP헤더의 모든 정보가 표시되어 있지 않고, (최소인 20Bytes밖에 정의 되어 있지 않음)
전에 한 Layer2의 ARP헤더의 Protocol Type도 몇 개 밖에 없었다.
그래서 두 번째의 공용체의 크기는 왕건이인 ip구조체로 정해진다. 

아래와 같이 ICMP헤더의 모양을 그릴 수 있다.

image  

Type과 Code는 모두 1Byte라 빅엔디안 리틀엔디안 따질 필요 없고,
Checksum은 자신을 제외한(이 자리를 0으로 봄) 헤더를 2Bytes로 끊어서 모두 더한 뒤, (유효한 공용체만)
계산 된 결과의 상위 2Bytes를 하위2Bytes에 더하고 다시 캐리가 발생하면 또 상위2Bytes를 더 한다.
그런 1의 보수를 취하면 되는데.. 아무리 계산해도 계산결과가 다른걸 보니 Checksum도 리틀엔디안일 가능이 있다고 본다.
관련 사이트와 문서를 봐도 이 부분은 캐치하지 못 했다.


image

헤더가 복잡하고 아래의 공용체icmp_dun는 아직 쓸 필요성을 못 느껴 상기와 같이 공용체icmp_hun만 보기로 한다.
좀 더 내공이 오르면 그 때 해도 늦지 않아.


● L3_ICMP.c  (이번 주제)

   1: #include "L3_ICMP.h"
   2:  
   3: const void *L3_ICMP(const void *vpData, unsigned int *uipNext)
   4: {    
   5:     const struct icmp *stpICMP = vpData;
   6:  
   7:     // 1. type
   8:     printf("Type: ");
   9:     switch(stpICMP->icmp_type)
  10:     {
  11:         case ICMP_ECHOREPLY:        /* Echo Reply            */
  12:             switch(stpICMP->icmp_code)
  13:             {
  14:                 case ICMP_ECHOREPLY:
  15:                     printf("Code: Echo Reply\n");
  16:                     printf("ID: %d\n", stpICMP->icmp_id);
  17:                     printf("Sequence Number: %d\n", stpICMP->icmp_seq);
  18:  
  19:                     break;
  20:                 default:
  21:                     printf("Unknown\n");
  22:                     break;
  23:             }
  24:             break;
  25:         case ICMP_ECHO:                /* Echo Request */    
  26:             switch(stpICMP->icmp_code)
  27:             {
  28:                 case 0:
  29:                     printf("Code: Echo Request (used to ping)\n");
  30:                     printf("ID: %d\n", stpICMP->icmp_id);
  31:                     printf("Sequence Number: %d\n", stpICMP->icmp_seq);
  32:                     break;
  33:                 default:
  34:                     printf("Unknown\n");
  35:                     break;
  36:             }
  37:             break;            
  38:         case ICMP_UNREACH:            /* dest unreachable, codes: */
  39:             printf("Dest Unreachable\n");
  40:  
  41:             printf("Code: ");            
  42:             switch(stpICMP->icmp_code)
  43:             {
  44:                 case ICMP_UNREACH_NET:            /* bad net */
  45:                     printf("Bad Net\n");
  46:                     break;
  47:                 case ICMP_UNREACH_HOST:        /* bad host */
  48:                     printf("Bad Host \n");
  49:                     break;
  50:                 case ICMP_UNREACH_PROTOCOL:    /* bad protocol */
  51:                     printf("Bad Protocol\n");
  52:                     break;
  53:                 case ICMP_UNREACH_PORT:        /* bad port */
  54:                     printf("Bad Port\n");
  55:                     break;
  56:  
  57:                 case ICMP_UNREACH_NEEDFRAG:    /* IP_DF caused drop */
  58:                     printf("IP_DF caused drop\n");
  59:                     break;
  60:  
  61:                 case ICMP_UNREACH_SRCFAIL:        /* src route failed */
  62:                     printf("src route failed\n");
  63:                     break;
  64:  
  65:                 case ICMP_UNREACH_NET_UNKNOWN:    /* unknown net */
  66:                     printf("unknown net\n");
  67:                     break;
  68:  
  69:                 case ICMP_UNREACH_HOST_UNKNOWN:    /* unknown host */
  70:                     printf("unknown host\n");
  71:                     break;
  72:  
  73:                 case ICMP_UNREACH_ISOLATED:        /* src host isolated */
  74:                     printf("src host isolated\n");
  75:                     break;
  76:  
  77:                 case ICMP_UNREACH_NET_PROHIB:        /* net denied */
  78:                     printf("net denied\n");
  79:                     break;
  80:  
  81:                 case ICMP_UNREACH_HOST_PROHIB:        /* host denied */
  82:                     printf("host denied\n");
  83:                     break;
  84:  
  85:                 case ICMP_UNREACH_TOSNET:            /* bad tos for net */
  86:                     printf("bad tos for net\n");
  87:                     break;
  88:  
  89:                 case ICMP_UNREACH_TOSHOST:            /* bad tos for host */
  90:                     printf("bad tos for host\n");
  91:                     break;
  92:  
  93:                 case ICMP_UNREACH_FILTER_PROHIB:    /* admin prohib */
  94:                     printf("admin prohib\n");
  95:                     break;
  96:  
  97:                 case ICMP_UNREACH_HOST_PRECEDENCE:    /* host prec vio. */
  98:                     printf("host prec vio.\n");
  99:                     break;
 100:  
 101:                 case ICMP_UNREACH_PRECEDENCE_CUTOFF:    /* prec cutoff */
 102:                     printf("prec cutoff\n");
 103:                     break;
 104:  
 105:                 default:
 106:                     printf("Unknown\n");
 107:                     break;
 108:  
 109:                 printf("void: %d\n", stpICMP->icmp_void);        // 출력할 필요는 없으나 확인.
 110:                 printf("Next-Hop MTU: %d\n", stpICMP->icmp_nextmtu);    
 111:             }
 112:             break;
 113:         case ICMP_SOURCEQUENCH:    /* packet lost, slow down */
 114:             printf("Packet Lost, Slow Down.\n");
 115:             break;
 116:         case ICMP_TIMXCEED:            /* time exceeded, code: */    
 117:             printf("Time Exceeded.\n");
 118:             printf("Code: ");            
 119:             switch(stpICMP->icmp_code)
 120:             {
 121:                 case ICMP_TIMXCEED_INTRANS:            /* ttl==0 in transit */
 122:                     printf("Time-to-live exceeded in transit.\n");
 123:                     break;
 124:                 case ICMP_TIMXCEED_REASS:                /* ttl==0 in reass */
 125:                     printf("Fragment reassembly time exceeded.\n");
 126:                     break;
 127:                 default:
 128:                     printf("Unknown\n");
 129:                     break;
 130:             }
 131:             break;
 132:  
 133:         default:
 134:             printf("Unknown\n");
 135:             break;
 136:     }
 137:  
 138:     printf("CheckSum: %d\n",ntohs(stpICMP->icmp_cksum));
 139:  
 140:     return vpData;
 141: }
 142:  


● L2_IP.c (Layer2계층의 헤더를 분석하고 다음 헤더의 위치를 반환하여 L3_ICMP.c가 분석할 위치를 알려줌.)

   1: #include "L2_IP.h"
   2:  
   3: const void *L2_IP(const void *vpData, unsigned int *uipNext)
   4: {
   5:     const struct ip *stpIP = vpData;
   6:     unsigned short usCheckSum = 0;
   7:     //unsigned int A = 0x00FF00FF;        //test
   8:  
   9:     printf("┌────────────────────────────┐\n");
  10:  
  11:     // 1. 주소버전과 헤더 길이
  12:     printf("│ Version: IPv%-43d│\n", stpIP->ip_v);
  13:     printf("│ Header Length: %2d * 4Bytes = %2dBytes%19c│\n", stpIP->ip_hl, stpIP->ip_hl * 4, ' ');
  14:  
  15:     // 2. 서비스형태
  16:     printf("│ Type Of Service: ");
  17:  
  18:     switch(IPTOS_TOS(stpIP->ip_tos))        // 1Byte로 서비스형태를  나타냄.
  19:     {
  20:         case IPTOS_LOWDELAY:
  21:             printf("%-38s", "IPTOS_LOWDELAY");    // FTP서버접속시 타입
  22:             break;
  23:  
  24:         case IPTOS_THROUGHPUT:
  25:             printf("%-38s", "IPTOS_THROUGHPUT");
  26:             break;
  27:  
  28:         case IPTOS_RELIABILITY:
  29:             printf("%-38s", "IPTOS_RELIABILITY");
  30:             break;
  31:  
  32:         case IPTOS_LOWCOST:    // MINCOST와 같음.
  33:             printf("%-38s", "IPTOS_LOWCOST");
  34:             break;
  35:         
  36:         default:
  37:             printf("%-38s", "Unknown");
  38:             break;
  39:     }
  40:  
  41:     printf("│\n");
  42:  
  43:     // 3. 패킷 전체의 길이
  44:     printf("│ Total Length: %5dBytes = %2dKBytes%20c│\n", stpIP->ip_len, stpIP->ip_len / 1024, ' ');
  45:  
  46:     // 4. 조각ID
  47:     printf("│ Fragment Identification: %-30d│\n", stpIP->ip_id);
  48:  
  49:     // 5. 조각Offset
  50:     printf("│ Don't Fragment Flag: %-34s│\n", (ntohs(stpIP->ip_off) & IP_DF) ? "Yes" : "No");    //빅엔디안으로 전환. 삼항 연산자
  51:     printf("│ More Fragment Flag: %-35s│\n", (ntohs(stpIP->ip_off) & IP_MF) ? "Yes" : "No");    //빅엔디안으로 전환. 삼항 연산자
  52:     printf("│ Fragment Offset: %-38d│\n", stpIP->ip_off & IP_OFFMASK);
  53:  
  54:     // 6. 패킷수명 Time To Live
  55:     printf("│ Time To Live: %-41d│\n", stpIP->ip_ttl);
  56:  
  57:     // 7. 프로토콜 (다음 Layer)
  58:     printf("│ Protocols: ");
  59:     switch(stpIP->ip_p)
  60:     {
  61:         case IPPROTO_IP:
  62:             printf("%-44s", "Dummy protocol for TCP.");
  63:             break;
  64: /*
  65:         case IPPROTO_HOPOPTS:    //값이 IPPROTO_IP와 같음. ipv6용인듯. 버전6에 대한 처리해야?
  66:             printf("IPv6 Hop-by-Hop options.\n");
  67:             break;
  68: */
  69:         case IPPROTO_ICMP:
  70:             printf("%-44s", "Internet Control Message Protocol.");
  71:             break;
  72:  
  73:         case IPPROTO_IGMP:
  74:             printf("%-44s", "Internet Group Management Protocol.");
  75:             break;
  76:  
  77:         case IPPROTO_IPIP:
  78:             printf("%-44s", "IPIP tunnels (older KA9Q tunnels use 94).");
  79:             break;
  80:  
  81:         case IPPROTO_TCP:
  82:             printf("%-44s", "Transmission Control Protocol.");
  83:             break;
  84:  
  85:         case IPPROTO_EGP:
  86:             printf("%-44s", "Exterior Gateway Protocol.");
  87:             break;
  88:  
  89:         case IPPROTO_PUP:
  90:             printf("%-44s", "PUP protocol.");
  91:             break;
  92:  
  93:         case IPPROTO_UDP:
  94:             printf("%-44s", "User Datagram Protocol.");
  95:             break;
  96:  
  97:         case IPPROTO_IDP:
  98:             printf("%-44s", "XNS IDP protocol.");
  99:             break;
 100:  
 101:         case IPPROTO_TP:
 102:             printf("%-44s", "SO Transport Protocol Class 4.");
 103:             break;
 104:  
 105:         case IPPROTO_IPV6:
 106:             printf("%-44s", "IPv6 header.");
 107:             break;
 108:  
 109:         case IPPROTO_ROUTING:
 110:             printf("%-44s", "IPv6 routing header.");
 111:             break;
 112:  
 113:         case IPPROTO_FRAGMENT:
 114:             printf("%-44s", "IPv6 fragmentation header.");
 115:             break;
 116:  
 117:         case IPPROTO_RSVP:
 118:             printf("%-44s", "Reservation Protocol.");
 119:             break;
 120:  
 121:         case IPPROTO_GRE:
 122:             printf("%-44s", "General Routing Encapsulation.");
 123:             break;
 124:  
 125:         case IPPROTO_ESP:
 126:             printf("%-44s", "encapsulating security payload.");
 127:             break;
 128:  
 129:         case IPPROTO_AH:
 130:             printf("%-44s", "authentication header.");
 131:             break;
 132:  
 133:         case IPPROTO_ICMPV6:
 134:             printf("%-44s", "ICMPv6.");
 135:             break;
 136:  
 137:         case IPPROTO_NONE:
 138:             printf("%-44s", "IPv6 no next header.");
 139:             break;
 140:  
 141:         case IPPROTO_DSTOPTS:
 142:             printf("%-44s", "IPv6 destination options.");
 143:             break;
 144:  
 145:         case IPPROTO_MTP:
 146:             printf("%-44s", "Multicast Transport Protocol.");
 147:             break;
 148:  
 149:         case IPPROTO_ENCAP:
 150:             printf("%-44s", "Encapsulation Header.");
 151:             break;
 152:  
 153:         case IPPROTO_PIM:
 154:             printf("%-44s", "Protocol Independent Multicast.");
 155:             break;
 156:  
 157:         case IPPROTO_COMP:
 158:             printf("%-44s", "Compression Header Protocol.");
 159:             break;
 160:  
 161:         case IPPROTO_RAW:
 162:             printf("%-44s", "Raw IP packets.");
 163:             break;
 164:  
 165:         default:
 166:             printf("%-44s", "Unknown.");
 167:             break;
 168:     }
 169:  
 170:     printf("│\n");
 171:     *uipNext = stpIP->ip_p;        // L3 프로토콜 포지션
 172:  
 173:     // 8. IP Header CheckSum
 174:     printf("│ Checksum: %-45d│\n", ntohs(stpIP->ip_sum), ' ');
 175:     // 채크섬 확인
 176:     //usCheckSum = in_chsum((unsigned short *)vpData, stpIP->ip_hl * 4);    /*stpIP->ip_hl * 4*/
 177:     //printf("│ Checksum Cal: %-41d│\n", usCheckSum, ' ');
 178:  
 179:     // 9. 출발지 IP주소 32bit
 180:     printf("│ Source IPv4 Address: %-34s│\n", inet_ntoa(stpIP->ip_src.s_addr));
 181:  
 182:     // 10. 목적지 IP주소 32bit
 183:     printf("│ Destination IPv4 Address: %-29s│\n", inet_ntoa(stpIP->ip_dst.s_addr));
 184:  
 185:     // A. 방향
 186:     printf("│ Direction: [%-15s] -> ", inet_ntoa(stpIP->ip_src.s_addr));
 187:     printf("[%-15s]%6c│\n", inet_ntoa(stpIP->ip_dst.s_addr), ' ');
 188:     printf("└────────────────────────────┘\n");
 189:  
 190:     //test
 191:     //putchar('\n');
 192:     //printf("Test IP: %s\n", inet_ntoa(*((struct in_addr *)&A)));
 193:     //printf("Test IP: %s\n", inet_ntoa(A));
 194:  
 195:  
 196:  
 197:     putchar('\n');    //터미널창 보기 좋게 하기 위해 개행
 198:  
 199:     vpData = (unsigned char *)vpData + stpIP->ip_hl * 4;
 200:  
 201:     return vpData;
 202: }
 203:  
 204: unsigned short in_chsum(unsigned short *addr, int len)
 205: {
 206:     int nleft = len;
 207:     int sum = 0;
 208:     int temp = 0;
 209:     unsigned short *w = addr;
 210:     unsigned short answer = 0;
 211:  
 212:     // 우리 알고리즘은 simple?
 213:     while(nleft > 1)
 214:     {
 215:         sum += *w++;
 216:         nleft -= 2;
 217:     }
 218:  
 219:     // mop up an odd byte, if necessary
 220:     if(1 == nleft)
 221:     {
 222:         *(unsigned char *)(&answer) = *(unsigned char *)w;
 223:         sum += answer;
 224:     }
 225:  
 226:     // add back carry outs from top 16 bits to low 16 bits.
 227:     temp = sum >> 16;
 228:     sum = temp + (sum & 0xFFFF);    // add hi 16 to low 16
 229:     sum += (sum >> 16);    // add carry
 230:     answer = ~sum;        // truncate to 16 bits     
 231:  
 232:     return answer;
 233: }
 234:  

● L1_Ethernet.c (Layer1계층의 헤더를 분석하고 다음 헤더의 위치를 반환하여 L2_IP.c가 분석할 위치를 알려줌.)

   1: #include "L1_Ethernet.h"
   2:  
   3: const void *L1_Ethernet(const void *vpData, unsigned int *uipNext)
   4: {
   5:     const struct ether_header *stpEth = vpData;
   6:  
   7:     printf("┌────────────────────────────┐\n");
   8:     printf("│ Direction : ");
   9:     // 이더넷 헤더 출력 MAC + type ID
  10:     printf("[%02X:%02X:%02X:%02X:%02X:%02X] -> "
  11:         "[%02X:%02X:%02X:%02X:%02X:%02X] "
  12:         , stpEth->ether_shost[0]
  13:         , stpEth->ether_shost[1]
  14:         , stpEth->ether_shost[2]
  15:         , stpEth->ether_shost[3]
  16:         , stpEth->ether_shost[4]
  17:         , stpEth->ether_shost[5]    
  18:         , stpEth->ether_dhost[0]
  19:         , stpEth->ether_dhost[1]
  20:         , stpEth->ether_dhost[2]
  21:         , stpEth->ether_dhost[3]
  22:         , stpEth->ether_dhost[4]
  23:         , stpEth->ether_dhost[5]);    // MAC주소 출력
  24:  
  25:     printf("│\n");
  26:     
  27:     //printf("%04X\n", ntohs(stpEth->ether_type));        //type ID출력
  28:                                                 //개발에 필요한 핵심기술 확인
  29:                                                 // 스텁코드 stub code
  30:  
  31:     // 프로토콜ID에 분기
  32:     printf("│ protocol ID : ");
  33:     
  34:     switch(ntohs(stpEth->ether_type))    //프로토콜ID검사
  35:     {
  36:         case ETHERTYPE_PUP:
  37:             printf("%-41s", "Xerox PUP");
  38:             break;
  39:          case ETHERTYPE_IP:
  40:             printf("%-41s", "IP");
  41:             break;
  42:  
  43:         case ETHERTYPE_ARP:
  44:             printf("%-41s", "Address resolution");
  45:             break;
  46:  
  47:         case ETHERTYPE_REVARP:
  48:             printf("%-41s", "Reverse ARP");
  49:             break;
  50:  
  51:         default:
  52:             printf("%-41s", "UnKnown");
  53:             break;
  54:     }
  55:  
  56:     printf("│\n");
  57:     printf("└────────────────────────────┘\n");
  58:  
  59:     *uipNext = ntohs(stpEth->ether_type);    // L2프로토콜 종류 ID
  60:     vpData = stpEth + 1;        // 헤더 전체 크기 다음 주소로 이동 구조체 크기만큼 리턴
  61:     
  62:     return vpData;        // 다음 헤더 처리를 위해 다음 헤더 처리의 시작 주소를 리턴.
  63: }
  64:  

● main.c (프로그램의 시작)

   1: #include <stdio.h>
   2: #include <pcap/pcap.h>
   3: #include "HexaView.h"
   4: #include "L1_Ethernet.h"
   5: #include "L2_IP.h"
   6: #include "L2_ARP.h"
   7: #include "L3_ICMP.h"
   8:  
   9: int main()
  10: {
  11:     char errbuf[PCAP_ERRBUF_SIZE];        //에러가 나면 여기에 기록됨.
  12:     char *cpNIC_Name;    //NIC는 장치 (Network Interface Card)
  13:     pcap_t *stpNIC;        // 구조체포인터
  14:     struct pcap_pkthdr stInfo;        // 정보를 담을 구조체
  15:     const u_char *ucpData;    // 데이터
  16:     unsigned int uiNext;
  17:     
  18:     
  19:     // 1. 네트웍 장치명 읽어오기
  20:     cpNIC_Name = pcap_lookupdev(errbuf);    // 장치이름을 알아오고,
  21:     if(NULL == cpNIC_Name)    // 에러난 경우 에러버퍼에 에러가 난 이유를 저장.
  22:     {
  23:         printf(errbuf);        
  24:         putchar('\n');
  25:         exit(-1);        //exit()가 나은가?
  26:         //return -1;        //리턴이 나은가?
  27:     }
  28:     
  29:     printf(cpNIC_Name);
  30:     putchar('\n');
  31:     
  32:  
  33:     // 2. 네트웍 장치 열기
  34:     //                         장치명, 나중에 설명, 에러버퍼   
  35:     stpNIC = pcap_open_live(cpNIC_Name, 1500, 1, 0, errbuf);
  36:     if(NULL == stpNIC)    //에러가 난 경우
  37:     {
  38:         printf(errbuf);
  39:         putchar('\n');
  40:         exit(-2);
  41:         //return -2;
  42:     }
  43:  
  44:     printf("패킷캡쳐 오픈 successful(성공)\n");
  45:     
  46:     // 3. 패킷분석을 위해 패킷캡쳐 
  47:     ucpData = pcap_next(stpNIC, &stInfo);
  48:  
  49:     // 4. 패킷내용을 출력
  50:     HexaView(ucpData, 160);
  51:     //MSDFunction(ucpData, 10);
  52:  
  53:     // 5. 이더넷 헤더 Layer1
  54:     ucpData = L1_Ethernet(ucpData, &uiNext);    // L1헤더만 출력하고 L2헤더 출력 위해 포인터 옮김
  55:                                             // uiNext에 L2 프로토콜번호(ID) 담김
  56:     // 6. Layer2. IP, ARP...
  57:     switch(uiNext)    //프로토콜ID검사
  58:     {
  59:         case ETHERTYPE_PUP:
  60:             break;
  61:         case ETHERTYPE_IP:
  62:             ucpData = L2_IP(ucpData, &uiNext);    //L1헤더만 출력하고 L2헤더 출력 위해 포인터 옮김
  63:             break;
  64:         case ETHERTYPE_ARP:
  65:         case ETHERTYPE_REVARP:    // 패킷이 같으므로 붙임.
  66:             ucpData = L2_ARP(ucpData, &uiNext);    //L1헤더만 출력하고 L2헤더 출력 위해 포인터 옮김
  67:             break;
  68:         default:
  69:             break;
  70:     }
  71:  
  72:     // 7. Layer3. 
  73:     switch(uiNext)
  74:     {
  75:         case IPPROTO_IP:
  76:             break;
  77:         case IPPROTO_ICMP:
  78:             ucpData = L3_ICMP(ucpData, &uiNext);    //L1헤더만 출력하고 L2헤더 출력 위해 포인터 옮김
  79:             break;
  80:         case IPPROTO_IGMP:
  81:             break;
  82:         case IPPROTO_IPIP:
  83:             break;
  84:         case IPPROTO_TCP:
  85:             //ucpData = L3_TCP(ucpData, &uiNext);
  86:             break;
  87:         case IPPROTO_EGP:
  88:             break;
  89:         case IPPROTO_PUP:
  90:             break;
  91:         case IPPROTO_UDP:
  92:             //ucpData = L3_UDP(ucpData, &uiNext);
  93:             break;
  94:         case IPPROTO_IDP:
  95:             break;
  96:         case IPPROTO_TP:
  97:             break;
  98:         case IPPROTO_IPV6:
  99:             break;
 100:         case IPPROTO_ROUTING:
 101:             break;
 102:         case IPPROTO_FRAGMENT:
 103:             break;
 104:         case IPPROTO_RSVP:
 105:             break;
 106:         case IPPROTO_GRE:
 107:             break;
 108:         case IPPROTO_ESP:
 109:             break;
 110:         case IPPROTO_AH:
 111:             break;
 112:         case IPPROTO_ICMPV6:
 113:             break;
 114:         case IPPROTO_NONE:
 115:             break;
 116:         case IPPROTO_DSTOPTS:
 117:             break;
 118:         case IPPROTO_MTP:
 119:             break;
 120:         case IPPROTO_ENCAP:
 121:             break;
 122:         case IPPROTO_PIM:
 123:             break;
 124:         case IPPROTO_COMP:
 125:             break;
 126:         case IPPROTO_RAW:
 127:             break;
 128:         default:
 129:             break;
 130:     }
 131:  
 132:     // last. 열린 네트웍장치를 닫는다.
 133:     pcap_close(stpNIC);        
 134:     
 135:     return 0;
 136: }
 137:  


● 프로그램 구조 (Software Diagram비슷)

image

일단 pcap라이브러리 pcap_next()를 사용해 흘러다니는 패킷을 캡쳐하고, (가공된 데이터)
L1_Ethernet()로 제일 처음 나오는 14Bytes 고정크기의 헤더를 분석하여,
출발지/도착지 MAC주소와 다음에 올 L2헤더의 프로토콜을 알아낸다.
이번 실험은 ICMP에 관한 것이므로 L2계층은 IP이다.
L2_IP()는 IPv4와 IPv6와 같은 주소체계(?) 버전과 L2헤더의 길이, TOS, 패킷 전체 길이, 조각ID, 조각Offset, 패킷수명, L3계층 프로토콜등을 알 수 있다.
L3_ICMP()는 처음 오는 1Byte의 Type과 다음 1Byte의 Code를 이용해 체크섬 다음에 오는 4Bytes의 내용이 무엇인가 알 수 있다.
이렇게 type과 code를 이용해 내용을 선택하는 이유는 아마도 옛날 네트워크의 환경이 별로 좋지 못 하여 패킷의 크기를 최대한 줄여야 하고 ICMP는 제어메세지이므로 최대한 간단하게 해야 하기 때문이 아닐까 한다.


image 

type과 code로 공용체icmp_hun에 들어갈 내용이 선택되는데,
상기의 도식은 그 중 일부만 보고 표시한 것이다. (프로그램도 일부만 분석한다.)
ECHOREPLY는 ECHO request의 응답으로 ping명령을 사용사용하여 네트워크에 연결된 라우터나 호스트로 제어패킷을 보내면,
수신 받은 쪽에서도 돌려준다. (일부 서버와 호스트는 무시하더라..사실 이 부분은 잘 모르겠다.)
암튼 Type이 ICMP_ECHOREPLY면 Code는 무조건 0이 되어야 하는데 Code도 ICMP_ECHOREPLY로 분기했다. (도식은 오타로 0이 추가)
이 때 내용은 첫 2Bytes가 ping이나 traceroute 프로세스의 PID이고,
SeqNum은 Sequence Number로 ping시에 붙여지는 제어패킷의 순서번호이다.
(그냥 ping은 안부를 묻는 행위라고 생각하면 된다. “밥은 먹고 다니냐?)

//나머지는 나중에 –_-;



● 첨부 - 실행결과와 분석;

image

Type ICMP_ECHO인 Echo요청~ http://www.yahoo.co.kr 에 ping을 보냄.


image 

ECHOREPLY- http://www.yahoo.co.kr 에서 ping에 대한 서버(?)에서 그에 대한 응답을 한 것.



image

ICMP_TIMXCEED – 이건  www.zdorov.com.ua 에 traceroute하여 얻은 결과인데 어디서 패킷이 사망했는지는 모르겠다. (땀;;;)




image

내용 중 첫 2Bytes인 ID가 뭔지 궁금해 나의 실제IP주소로 ping을 보내고,
다른 터미널에서 ps -al명령으로 PID를 확인하고 Echo Request패킷의 ID를 확인해 보니 똑 같았다.

마치며..주의해야 될 점은 네트워크 패킷 중 일부는 리틀엔디안일 수 있다는 것이다.
         나머지 Type은 실험할 방법이 생각나면 그 때 그때 해보고 분석하겠당~

전체소스코드다운로드: 20111006_protocol_pcap.zip