본문으로 바로가기

이더넷(ethernet) 헤더에 존재하는 3계층 프로토콜 정보중 0x0800에 해당하는 패킷의 다음 역캡슐화 단계인 IPv4의 헤더구조에 대해 알아보자




IPv4 Header



위의 사진이 가장 잘 나와있는것 같아서 참고자료로 가져왔다

위의 구조를 표로 정리해보면 아래와 같다


Field

Size

Version

4bit

Header Length

4bit

Type of Service(Differentiated Service Field)

1byte

Total Length

2byte

Identification

2byte

IP Flags

3bit

Fragment Offset

13bit

Time To Live (TTL)

1byte

Protocol

1byte

Header Checksum

2byte

Source IP Address

4byte

Destination IP Address

4byte

Data

Total Length - Header Length


하나씩 어떠한 기능을 하는지 알아보자


Version

IP 프로토콜이 어떠한 버전인지 명시해준다



Header Length

IP 헤더의 길이를 나타낸다. 특별한 옵션이 따로 없다면 기본적으로 20byte이다.



Defferentiated Service Field (DS)

QoS 보장(통신 품질 보장)을 제공하고자 할 때 사용되는 필드이다. QoS 메커니즘이 복잡했고, 공공 인터넷의 요구에 맞추어 확장되지 못하단 이유로 TOS(Type of Service)에서 대체되었다. TOS에서의 QoS 보장은 통신 흐름마다 제공되었다. 그러나 DS의 경우 흐름을 집계하여 몇가지 클래스에 따라 QoS를 보장하여, 간단하고 확장성 있는 대단위 메커니즘을 지정한다. 이는 음성이나 영상 서비스에서 지연을 줄일 수 있다.



Total Length

IP 헤더의 데이터까지 포함한 전체 IP패킷 데이터를 바이트 단위로 나타낸다



Identification

IP 패킷이 할당받은 ID값



IP Flags

패킷 조각에 대한 정보를 가지고있다

0.. : 패킷의 엔디언 체크

.0. : 패킷의 단편화 여부

..0 : 잔여 패킷 조각 여부



Fragment Offset

패킷 조각 조립시 순서에 대한 정보 표시



Time To Live(TTL)

(초기 설정값 - 도착하기까지 지나온 라우터 수)

최대값은 255지만 64를 초기값으로 권장하고 있고, 해당 패킷이 라우터를 지날 때 마다 1씩 줄어든다. 또한 정해진 TTL 안에 목적지를 찾아가지 못하면 해당 데이터는 폐기된다. 패킷의 무한 순환 방지를 위함이다.



Protocol

패킷이 어떤 프로토콜을 사용하고 있는지 L4(4계층) 프로토콜 정보를 나타낸다



Header Checksum

IP헤더의 오류 검출 기능을 제공한다



Source IP address

출발지 IP주소



Destination IP address

목적지 IP주소






IP 헤더의 구조가 어떻게 이루어져 있는지 확인했으니 IP 헤더를 분석하는 사용사 정의 함수를 만들어보자. 패킷 길이정보를 담고있는 두 필드를 이용해서 데이터도 출력해보자



analysis.h


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
void datacpy(u_char* src, const u_char* data, int size){
    int i;
    for(i=0; i<size; i++) src[i] = data[i];
}
 
void dataprint(const u_char* data,int size){
    while(size--printf("%.2x",*data++);
}
 
 
////////////////////////////////////// Layer 2 //////////////////////////////////////////
 
int Ethernet_Header(const u_char* data){
    printf("[Ethernet_Header]");
    u_char dst_mac[6];
    u_char src_mac[6];
    u_char type[2];
    
    datacpy(dst_mac, data, 6);
    data+=6;
 
    datacpy(src_mac, data, 6);
    data+=6;
 
    datacpy(type, data, 2);
    data+=2;
 
    printf("\n - Dst Mac Addr  : ");
    dataprint(dst_mac,6);
    printf("\n - Src Mac Addr  : ");
    dataprint(src_mac,6);
    printf("\n - Ethernet Type : ");
    dataprint(type,2);
 
    printf("\n");
    
    return ((int)type[0]<<8+ (int)type[1];
}
 
 
 
///////////////////////////////////// Layer 3 /////////////////////////////////////////
 
u_short IPv4_Header(const u_char*data){
    printf("[IPv4_Header]");
    u_char ver; //4bit
    u_char hlen; //4bit - header len
    u_char dsf; //Differentiated Services Field
    u_char tlen[2]; //total len
    u_char id[2]; 
    u_char flags; //3bit
    u_char offset[2]; //13bit
    u_char ttl;
    u_char protocol;
    u_char chksum[2];
    u_char src_ip[4];
    u_char dst_ip[4];
    u_char *etc;
    u_short datalen;
    
    datacpy(&ver, data, 1);
    ver = ver>>4;
    datacpy(&hlen, data, 1);
    hlen = hlen<<4;
    hlen = hlen>>4;
    data +=1;
    
    datacpy(&dsf, data, 1);
    data +=1;
 
    datacpy(tlen, data, 2);
    data +=2;
 
    datacpy(id, data, 2);
    data +=2;
 
    datacpy(&flags, data, 1);
    flags = flags>>5;
    flags = flags<<5;
    datacpy(offset, data, 2);
    offset[0= offset[0]<<3;
    offset[0= offset[0]>>3;
    data +=2;
 
    datacpy(&ttl, data, 1);
    data +=1;
 
    datacpy(&protocol, data, 1);
    data +=1;
 
    datacpy(chksum, data, 2);
    data +=2;
 
    datacpy(src_ip, data, 4);
    data +=2;
    
    datacpy(dst_ip, data, 4);
    data +=4;
    
 
    datalen = (((u_short)tlen[0<< 8+ (u_short)tlen[1])-((u_short)hlen<<2);
    if(datalen!=0){
        etc = (u_char*)malloc(datalen);
        datacpy(etc, data, datalen);
    }
    
    printf("\n - Version         : ");
    dataprint(&ver,1);
    printf("\n - Header Length   : ");
    dataprint(&hlen,1);
    printf("\n - DFS             : ");
    dataprint(&dsf,1);
    printf("\n - Total Length    : ");
    dataprint(tlen,2);
    printf("\n - Identification  : ");
    dataprint(id,2);
    printf("\n - Flags           : ");
    dataprint(&flags,1);
    printf("\n - Fragment Offset : ");
    dataprint(offset,2);
    printf("\n - Time To Live    : ");
    dataprint(&ttl,1);
    printf("\n - Protocol        : ");
    dataprint(&protocol,1);
    printf("\n - Header Checksum : ");
    dataprint(chksum,2);
    printf("\n - Source IP       : ");
    dataprint(src_ip,4);
    printf("\n - Destination IP  : ");
    dataprint(dst_ip,4);
    printf("\n - Data            : ");
    dataprint(etc, datalen);
    free(etc);    
    printf("\n");
 
}
 
cs


- 지난 포스팅에서 다뤘던 이더넷(ethernet) 헤더의 부분을 조금 수정했다(상위 계층 프로토콜 정보를 받아오기 위함)

- DATA 부분은 동적할당으로 받아왔다

- 형 변환때문에 조금 머리아팠다

- 밑에 다른 함수들을 정의만 해놓아서 위의 코드에 담지 않았다




sniffer.c


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include<stdio.h>
#include<pcap.h>
#include<arpa/inet.h>
#include"analysis.h"
 
void callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
 
int loop;
 
int main(){
    pcap_if_t *alldevsp;
    pcap_if_t *d;
 
    char errbuf[PCAP_ERRBUF_SIZE];
    
    bpf_u_int32 netp;
    bpf_u_int32 maskp;
    int ret,i=1;
    struct in_addr addr;
    char *net;
 
    pcap_t *pcd;
    
    /*
    int pcap_findalldevs
    - pcap_if_t **alldevsp
    - char *errbuf
    */
    printf("====== pcap_findalldevs ======\n");
    ret = pcap_findalldevs(&alldevsp,errbuf);
    if(ret == -1){
        printf("pcap_findalldev ERR : %s\n",errbuf);
        return -1;
    }
    
    for(d=alldevsp; d; d=d->next){
        printf("%d. %s", i++, d->name);
        if(d->description){
            printf("(%s)\n",d->description);
        }else{
            printf("(None)\n");
        }
    }
 
    printf("Select device number > ");
    scanf("%d",&i);
    i--;
 
    for(d=alldevsp;i>0;d=d->next, i--);
 
 
    /*
    int pcap_lookupnet
    - const char *device
    - bpf_u_int32 *netp
    - bpf_u_int32 *maskp
    - char *errbuf
    */
    printf("====== pcap_lookupnet ======\n");
    ret = pcap_lookupnet(d->name, &netp, &maskp, errbuf);
    if(ret==-1){
        printf("pcap_lookupnet ERR : %s\n",errbuf);
        pcap_freealldevs(alldevsp);
        return -1;
    }
    printf("dev   : %s\n",d->name);
    addr.s_addr = netp;
    net = inet_ntoa(addr);
    if(net ==NULL){
        printf("inet_ntoa ERR");
        pcap_freealldevs(alldevsp);
        return -1;
    }
    printf("netp  : %s\n",net);
    addr.s_addr = maskp;
    net = inet_ntoa(addr);
    if(net==NULL){
        printf("inet_ntoa ERR");
        pcap_freealldevs(alldevsp);
        return -1;
    }
    printf("maskp : %s\n",net);
 
    /*
    pcap_t *pcap_open_live
    - const char *device
    - int snaplen 
    - int promisc 
    - int to_ms
    - char *errbuf
    */
    printf("====== pcap_open_live ======\n");
    pcd = pcap_open_live(d->name, 10000011000, errbuf);
    if(pcd==NULL){
        printf("pcap_open_live ERR : %s\n",errbuf);
        pcap_freealldevs(alldevsp);
        return -1;
    }
    printf("File Descriptor : %d\n",pcap_fileno(pcd));
    pcap_freealldevs(alldevsp);
 
    /*
    int pcap_loop
    - pcap_t *p
    - int cnt
    - pcap_handler callback
    - u_char *user
    */
    printf("======== pcap_loop ========\n");
    pcap_loop(pcd, 0, callback, NULL);
    pcap_close(pcd);
    return 0;
}
 
void callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes){
    int i=0;
    u_short L3;
    loop++;
    printf("\n=============== Packet no.%d(%4d byte) ===============\n",loop,h->len);
    printf("\n");
    //Layer 1
    L3 = Ethernet_Header(bytes);
    bytes+=14;
    //Layer 2
    switch(L3){
        case 0x0800 : IPv4_Header(bytes); break;
        case 0x0806 : ARP_Header(); break;
        default : printf("Can't support this detail protocol");
    }
}
 
cs


위 코드를 보면 대충 짐작할 수 있겠지만 다음 포스팅은 ARP 프로토콜이다




실행화면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
root@g0pher-virtual-machine:/home/g0pher/complete# ./sniffer 
====== pcap_findalldevs ======
1. ens33(None)
2. any(Pseudo-device that captures on all interfaces)
3. lo(None)
4. nflog(Linux netfilter log (NFLOG) interface)
5. nfqueue(Linux netfilter queue (NFQUEUE) interface)
6. usbmon1(USB bus number 1)
7. usbmon2(USB bus number 2)
Select device number > 1
====== pcap_lookupnet ======
dev   : ens33
netp  : 192.168.233.0
maskp : 255.255.255.0
====== pcap_open_live ======
File Descriptor : 3
======== pcap_loop ========
 
=============== Packet no.1122 byte) ===============
 
[Ethernet_Header]
 - Dst Mac Addr  : 005056c00008
 - Src Mac Addr  : 000c29aad30b
 - Ethernet Type : 0800
[IPv4_Header]
 - Version         : 04
 - Header Length   : 05
 - DFS             : 10
 - Total Length    : 006c
 - Identification  : c0d8
 - Flags           : 40
 - Fragment Offset : 0000
 - Time To Live    : 40
 - Protocol        : 06
 - Header Checksum : 25cc
 - Source IP       : c0a8e984
 - Destination IP  : e984c0a8
 - Data            : e9010cf1919035eca599cfb47c
 
=============== Packet no.2(  60 byte) ===============
 
[Ethernet_Header]
 - Dst Mac Addr  : 000c29aad30b
 - Src Mac Addr  : 005056c00008
 - Ethernet Type : 0800
[IPv4_Header]
 - Version         : 04
 - Header Length   : 05
 - DFS             : 00
 - Total Length    : 0028
 - Identification  : 59df
 - Flags           : 40
 - Fragment Offset : 0000
 - Time To Live    : 80
 - Protocol        : 06
 - Header Checksum : 4d19
 - Source IP       : c0a8e901
 - Destination IP  : e901c0a8
 - Data            : e98a016774d04a3dbd000fc82ed
 
=============== Packet no.3(  90 byte) ===============
 
[Ethernet_Header]
 - Dst Mac Addr  : 005056c00008
 - Src Mac Addr  : 000c29aad30b
 - Ethernet Type : 0800
[IPv4_Header]
 - Version         : 04
 - Header Length   : 05
 - DFS             : 10
 - Total Length    : 004c
 - Identification  : c0d9
 - Flags           : 40
 - Fragment Offset : 0000
 - Time To Live    : 40
 - Protocol        : 06
 - Header Checksum : 25eb
 - Source IP       : c0a8e984
 - Destination IP  : e984c0a8
 - Data            : 0100161a7a38d90fb541600109621413
 
=============== Packet no.4122 byte) ===============
 
[Ethernet_Header]
 - Dst Mac Addr  : 005056c00008
 - Src Mac Addr  : 000c29aad30b
 - Ethernet Type : 0800
[IPv4_Header]
 - Version         : 04
 - Header Length   : 05
 - DFS             : 10
 - Total Length    : 006c
 - Identification  : c0da
 - Flags           : 40
 - Fragment Offset : 0000
 - Time To Live    : 40
 - Protocol        : 06
 - Header Checksum : 25ca
 - Source IP       : c0a8e984
 - Destination IP  : e984c0a81cd732a82
 - Data            : e9018e1fbf22ef7afd4b3bff9b1d82
cs


data 부분은 포스팅 과정 중 길이를 줄여서 크기가 안맞을 수 있다. 당황하지 말자