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헤더의 모양을 그릴 수 있다.
Type과 Code는 모두 1Byte라 빅엔디안 리틀엔디안 따질 필요 없고,
Checksum은 자신을 제외한(이 자리를 0으로 봄) 헤더를 2Bytes로 끊어서 모두 더한 뒤, (유효한 공용체만)
계산 된 결과의 상위 2Bytes를 하위2Bytes에 더하고 다시 캐리가 발생하면 또 상위2Bytes를 더 한다.
그런 1의 보수를 취하면 되는데.. 아무리 계산해도 계산결과가 다른걸 보니 Checksum도 리틀엔디안일 가능이 있다고 본다.
관련 사이트와 문서를 봐도 이 부분은 캐치하지 못 했다.
헤더가 복잡하고 아래의 공용체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비슷)
일단 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는 제어메세지이므로 최대한 간단하게 해야 하기 때문이 아닐까 한다.
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은 안부를 묻는 행위라고 생각하면 된다. “밥은 먹고 다니냐?)
//나머지는 나중에 –_-;
● 첨부 - 실행결과와 분석;
Type ICMP_ECHO인 Echo요청~ http://www.yahoo.co.kr 에 ping을 보냄.
ECHOREPLY- http://www.yahoo.co.kr 에서 ping에 대한 서버(?)에서 그에 대한 응답을 한 것.
ICMP_TIMXCEED – 이건 www.zdorov.com.ua 에 traceroute하여 얻은 결과인데 어디서 패킷이 사망했는지는 모르겠다. (땀;;;)
내용 중 첫 2Bytes인 ID가 뭔지 궁금해 나의 실제IP주소로 ping을 보내고,
다른 터미널에서 ps -al명령으로 PID를 확인하고 Echo Request패킷의 ID를 확인해 보니 똑 같았다.
마치며..주의해야 될 점은 네트워크 패킷 중 일부는 리틀엔디안일 수 있다는 것이다.
나머지 Type은 실험할 방법이 생각나면 그 때 그때 해보고 분석하겠당~
전체소스코드다운로드: 20111006_protocol_pcap.zip |
'내장형하드웨어 > 일일보고서' 카테고리의 다른 글
2011년10월10일_비트맵파일구조(Bitmap File Format) (0) | 2011.10.11 |
---|---|
2011년10월10일_소코반 게임 기초 (미완성) (0) | 2011.10.11 |
2011년9월28일_pcap_이더넷구조체를 가리키는 구조체포인터를 사용하여 캡쳐한 패킷의 헤더를 추출하자. (2) | 2011.09.29 |
2011년9월28일_winAPI_대화상자_컨트롤과의 통신 (0) | 2011.09.29 |
2011년9월28일_winAPI_대화상자_컨트롤과의 통신 (0) | 2011.09.28 |