[C 프로젝트] 패킷 분석기 Part 5. IPv4 프로토콜 스택 분석 구현

이번 강의는 IPv4 프로토콜 스택을 분석하여 구현하는 실습입니다.

프로젝트에서 사용할 PCAP 파일 다운로드 (PCAP 파일이면 다른 파일도 관계 없어요.)

현재 PCAP 파일 구조와 ehternet 프로토콜 스택 분석 부분은 구현한 상태입니다.

IPv4 헤더를 스택 구조에 맞게 정의하고 패킷의 정보를 출력하는 내용입니다.

기술적인 특별한 사항이 없습니다. 동영상 강의를 참고하세요.

  • PcapFile.h
#pragma once
#include 
#pragma comment(lib,"ws2_32")
#include 
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;

typedef struct _PFHeader PFHeader;
struct _PFHeader //패킷 파일 헤더
{
    uint magic;//0xA1B2C3D4
    ushort major;
    ushort minor;
    uint gmt_to_local;
    uint timestamp;
    uint max_caplen;
    uint linktype;
};

typedef struct _PACKETHEADER PackHeader;
struct _PACKETHEADER //패킷 헤더
{
    uint captime;//second
    uint caputime;//u second
    uint caplen;
    uint packlen;
};

#define PF_MAGIC 0xA1B2C3D4
#define LT_ETHER   0x01
void ParseEthers(FILE *fp);
int ParsePCapFile(FILE *fp, PFHeader *pfh);
  • PcapFile.c
#include "PcapFile.h"
#include "EHEther.h"
char buffer[0x100000];
void ViewPCapFile(PFHeader *pfh);
void ViewPacketHeaderInfo(PackHeader *ph, int pno);

void ParseEthers(FILE *fp)
{
    PackHeader ph = { 0 };
    int pno = 0;
    while (fread(&ph, sizeof(PackHeader), 1, fp) == 1)
    {
        pno++;
        ViewPacketHeaderInfo(&ph, pno);
        fread(buffer, sizeof(uchar), ph.caplen, fp);
        ParseEther(buffer, ph.caplen);
    }
}

int ParsePCapFile(FILE *fp, PFHeader *pfh)
{
    fread(pfh, sizeof(PFHeader), 1, fp);
    if (pfh->magic != PF_MAGIC)
    {
        return 0;
    }
    ViewPCapFile(pfh);
    return 1;
}
void ViewPCapFile(PFHeader *pfh)
{
    printf("=========== PCAP File 헤더 정보 ============\n");
    printf("\t 버전:%d.%d\n", pfh->major, pfh->minor);
    printf("\t최대 캡쳐 길이:%d bytes\n", pfh->max_caplen);
}
void ViewPacketHeaderInfo(PackHeader *ph, int pno)
{
    printf("!!! <%4d th> 프레임 !!!\n", pno);
    printf("패킷:%6d bytes 캡쳐:%6d\n", ph->packlen, ph->caplen);
}
  • EHEther.h
#pragma once
#include "PcapFile.h"

#define L3_IPv4 0x0800
#define L3_ARP 0x0806

typedef struct  _EtherHeader EtherHeader;
struct  _EtherHeader
{
    uchar dst_mac[6];
    uchar src_mac[6];
    ushort l3type;
};

void ParseEther(uchar *buffer, uint len);
  • EHEther.c
#include "EHEther.h"
#include "IPv4.h"
void ViewEther(EtherHeader *eh);
void ParseEther(uchar *buffer, uint len)
{
    EtherHeader *eh = (EtherHeader *)buffer;
    uchar *next = buffer + sizeof(EtherHeader);
    len = len - sizeof(EtherHeader);

    ViewEther(eh);
    switch (ntohs(eh->l3type))
    {
    case L3_IPv4: ParseIPv4(next, len); break;
    case L3_ARP: printf("ARP: to be defined\n"); break;
    default: printf("Not support\n");	break;
    }
}

void ViewMac(const char *msg, uchar *base);
void ViewEther(EtherHeader *eh)
{
    printf("★★★★★     ethernet header    ★★★★★\n");
    ViewMac("dest", eh->dst_mac);
    ViewMac("source", eh->src_mac);
    printf("\tL3Type:%#x\n", ntohs(eh->l3type));
    printf("\t(IPv4:0x0800 ARP:0x0806)\n");
}
void ViewMac(const char *msg, uchar *base)
{
    printf("\t%s", msg);
    printf("%02x", base[0]);
    for (int i = 1; i < 6; i++)
    {
        printf(":%02x", base[i]);
    }
    printf("\n");
}
  • IPv4.h
#pragma once
#include "PcapFile.h"

#define PRO_ICMPv4 0x01
#define PRO_IGMP    0x02
#define PRO_TCP     0x06
#define PRO_UDP     0x11
#define PRO_OSPF    0x59

typedef struct _IPv4Header IPv4Header;
struct _IPv4Header
{    
    uchar hlen : 4;
    uchar version : 4;
    uchar tos;
    ushort tlen;
    ushort id;
    ushort fl_off;
#define DONT_FRAG(x) (x&0x40)
#define MORE_FRAG(x) (x&0x20)
#define FRAG_OFF(x) ntohs(x&0xFF1F)
    uchar ttl;
    uchar protocol;
    ushort checksum;
    uint srcaddr;
    uint dstaddr;
};

void ParseIPv4(uchar *base, uint len);
  • IPv4.c
#include "IPv4.h"

void PrintIPv4(uint addr)
{
    uchar *up = (uchar *)&addr;
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d.", up[i]);
    }
    printf("%d", up[i]);

}
void ParseIPv4(uchar *base, uint len) 
{
    IPv4Header *iph = (IPv4Header *)base;
    printf("☆☆☆☆☆     IPv4 Header    ☆☆☆☆☆☆\n");
    printf("\n version:%d\n", iph->version);
    printf("\n hlen:%d bytes\n", iph->hlen * 4);
    printf("\t total length :%d bytes\n", ntohs(iph->tlen));
    printf("\t id:%d\n", ntohs(iph->id));

    if (DONT_FRAG(iph->fl_off))
    {
        printf("\t Don't Fragment\n");
    }
    if (MORE_FRAG(iph->fl_off))
    {
        printf("\t More Fragment\n");
    }
    printf("\t offset:%d bytes\n", FRAG_OFF(iph->fl_off));
    printf("\t Time To Live:%d\n", iph->ttl);
    printf("\t Protocol :%d\n", iph->protocol);
    printf("\t ICMP:%d IGMP:%d TCP:%d UDP:%d OSPF:%d\n", 
        PRO_ICMPv4, PRO_IGMP, PRO_TCP, PRO_UDP, PRO_OSPF);
    printf("\t src:");
    PrintIPv4(iph->srcaddr);
    printf("\t");
    PrintIPv4(iph->dstaddr);
    printf("\n");
    uchar *next = base + (iph->hlen * 4);
    len = len - (iph->hlen * 4);
    switch (iph->protocol)
    {
    case PRO_ICMPv4: printf("\t to be defined\n"); break;
    case PRO_TCP: printf("\t to be defined\n");  break;
    default: printf("\t Not supported\n"); break;
    }
}
  • Program.c
//https://ehpub.co.kr
#include 
#include 
#include 
#include "PcapFile.h"
int main()
{
    setlocale(LC_ALL, "KOREAN");
    char fname[256 + 1] = "";
    printf("분석할 pcap 파일명:");
    scanf_s("%s", fname, sizeof(fname));

    FILE *fp = 0;
    fopen_s(&fp, fname, "rb");
    if (fp == 0)
    {
        perror("파일 열기 실패");
        system("pause");
        return 0;
    }

    PFHeader pfh = { 0, };
    if (ParsePCapFile(fp, &pfh) == 0)
    {
        printf("PCAP 파일이 아닙니다\n");
        fclose(fp);
        system("pause");
        return 0;
    }

    switch (pfh.linktype)
    {
    case LT_ETHER: ParseEthers(fp); break;
    default: printf("Not Support\n"); break;
    }
    fclose(fp);
    system("pause");
    return 0;
}