본문 바로가기
  • AI (Artificial Intelligence)
Skills/Network

WireShark 구현 (2) - 랜 케이블의 이더넷 패킷 출력 구현

by 로샤스 2014. 5. 9.

1. 랜 케이블의 이더넷 패킷 출력 구현

 

첫 번째 미션은 랜케이블에서 돌아다니는 패킷인 이더넷의 실체를 확인해보는 것이다. 먼저 이더넷 개념을 다시한번 숙지하자.

 

이더넷은 데이터링크계층에서 동작하는 프로토콜로써 이더넷프레임이라고도 하며, 다수의 PC를 연결한 환경인 LAN상에서 동작된다.

 

송신하는 PC가 없는 경우 B의 MAC(랜카드 주소)를 수신자로 세팅하여 전송하게 되며, 이때 B를 제외한 모든 PC는 자신의 MAC주소가 아니기 때문에 해당 패킷을 폐기한다. 이와 같이 데이터를 보내는 것은 자유지만, 다른 PC에서 보낸 패킷과의 충돌이 발생할 수 있으므로 LAN 케이블을 감시하여 패킷이 없는 경우에만 전송이 가능하다.

 

 

 

이와 같은 통신방식을 CSMA/CD라고 불리는데, 해당 방식은 IEEE 802.3에 표준화된 방식으로, 이더넷에 접속되어 있는 장치들은 언제라도 데이터를 전송할 수가 있으나, 단 회선이 사용되지 않는 경우에만 데이터를 전송하게 된다. 만약 충돌이 발생된 경우에는 일정시간동안 기다린 후 다시 데이터를 전송하게 된다.

 

이더넷 프레임의 구조

 

LAN 케이블에는 다음의 그림과 같은 모양의 데이터가 전기적 신호로 만들어져 돌아다니게 되는데, 통신의 시작임을 알리는 동기신호와 상대방의 LAN카드 주소(목적지), 나의 LAN 카드주소(발신지), 패킷유형(다음에 따라오는 프로토콜) 그리고 프로토콜 종류에 따라 IP헤더, TCP헤더(만약 PING과 같은 프로그램을 사용하는 경우에는 IP헤더 다음에 ICMP헤더가 따라 붙으며, TCP/UDP 헤더는 없다) 그리고 메일과 같은 어플리케이션 사용시 메일 내용 등이 포함되는 DATA, 마지막으로 송, 수신중에 에러가 발생되었는지를 확인하기 위한 CRC체크섬이 합쳐진 모습을 가지고 있다.

 

 

 

이제 LAN상에 이더넷패킷이 있는 지를 확인하는 간단한 프로그램을 만들어보자.

 

소스 : basic_dump.c

목적 : 네트워크 상에 이더넷 프레임 존재 시 카운팅과 함께 "이더넷 패킷이 존재한다는 메시지"를 화면에 출력한다.

결과물 : 마지막 스샷 참고

 

프로그램의 기본구조는 PCAP 라이브러리에서 제공되는 함수를 절차대로 구성하여 패킷을 수집한다.

 

 

 

 

(1) pcap_findalldevs() 함수 

pc에 존재하는 네트워크 디바이스(랜 카드) 전체를 검색하여 리스트를 만드는 함수이며, 첫 번째 옵션인 alldevs에는 디바이스를 검색하는 첫 번째 디바이스 정보를 저장하며, 두 번째 옵션인 errbuf는 에러 발생 시 에러 정보가 저장된다. 디바이스 검색 시 에러가 발생되는 경우 -1를 리턴한다. 

 

#include <pcap/pcap.h> - /usr/include/pcap/pcap.h

int pcap_findalldevs(pcap_if_t ** alldevs, char *errbuf)

 

(2) pcap_freealldevs() 함수

pcap_findalldevs() 함수를 통해 선택한 랜카드에 에러가 발생되는 경우 pcap_findalldevs() 함수를 통해 랜카드 정보 리스트를 해제하는 역할을 한다. 사용되는 첫 번째 옵션은 디바이스 정보 리스트를 지정한다.

 

#include <pcap/pcap.h> - /usr/include/pcap/pcap.h

void pcap_freealldevs(pcap_if_t * alldevs)

 

 

(3) pcap_open_live() 함수

pcap_findalldevs()를 통해 선택한 랜카드를 이용하여 네트워크 패킷을 수집하기 위한 방법을 설정한다.

 

#include <pcap/pcap.h> - /usr/include/pcap/pcap.h

pcap_t * pcap_open_live(const char* device, int snaplen, int promisc, int to_ms, char *ebuf)

 

• device : 선택된 랜카드

• snaplen : 수신할 패킷 길이를 정의

• promisc : 랜카드 특성을 바꾸어 모든 네트워크 패킷을 수집하는 Promiscuous모드를 위해 1로세팅

• to_ms : 패킷이 있는 경우 매번 동작하지 않고, 여러 패킷을 한꺼번에 처리하기 위한 시간

• ebuf : 에러 발생 시에 에러정보를 저장하는 공간

 

(4) pcap_loop() 함수

선택된 랜카드로 패킷을 연속적으로 수집하는 역할을 한다.

 

#include <pcap/pcap.h> - /usr/include/pcap/pcap.h

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 

pcap_dispatch()와 유사하다. 에러가 발생하거나 요구된 갯수 만큼의 패킷을 받아들이고 리턴한다. pcap_dispatch()와 다르게 타임 아웃이 발생해도 리턴되지 않는다. cnt 값이 음수면 에러가 발생하기 전까지 계속 수행한다. 

 

• pcap_t *p : 패킷 캡쳐 descriptor 

• int cnt : 읽을 패킷의 갯수 

• pcap_handler callback : 패킷을 처리할 루틴 

• u_char *user : 사용자 데이터  

 

(5) pcaket_handler() 함수

 

#include <pcap/pcap.h> - /usr/include/pcap/pcap.h

void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *);

 

 

• 첫번째는 pcap_dispatch()나 pcap_loop()를 호출할 때 전달하는 u_char 포인터

• 두번째는 패킷의 정보를 기지고 있는 pcap_pkthdr 구조체

• 세번째는 패킷의 데이터를 가리키고 있는 u_char 포인터 

 

basic_dump.c


 

 #include <pcap/pcap.h>

 

#include <stdlib.h>

 

/* 패킷이 캡처 됬을때, 호출되는 콜백 함수 */

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

 

int main()

{

pcap_if_t *alldevs;

pcap_if_t *d;

int inum;

int i=0;

pcap_t *adhandle ; // 랜카드를 검색하여 랜카드 정보를 저장하고 있으며, errbuf는 에러정보를 저장한다.

char errbuf[PCAP_ERRBUF_SIZE];

 

/* 네트워크 다바이스 목록을 가져온다. */

/* alldevs에 리스트 형태로 저장되며, 에러시 errbuf에 에러 내용 저장 */

if(pcap_findalldevs(&alldevs, errbuf) == -1)

{

fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);

exit(1);

}

/* 네트워크 다바이스명을 출력한다. */
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}

/* 에러 처리 */
if(i==0)
{
//printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
printf("\nNo interfaces found! Make sure LibPcap is installed.\n");
return -1;
}

/* 캡처할 네트워크 디바이스 선택 */
printf("Enter the interface number (1-%d):",i);
scanf("%d", &inum);

/* 입력값의 유효성 판단 */
if(inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
/* 장치 목록 해제 */
pcap_freealldevs(alldevs);
return -1;

 

 

}

 

printf("\nlistening on %s...\n", d->description);

 

/* 장치 목록 해제 */

pcap_freealldevs(alldevs);

 

/* 캡처 시작 */

pcap_loop(adhandle,      // pcap_open_live통해 얻은 네트워크 디바이스 핸들

0,     // 0 : 무한루프

// 양의정수 : 캡처할 패킷수 

packet_handler,  // 패킷이 캡처됬을때, 호출될 함수 핸들러 

NULL);           // 콜백함수로 넘겨줄 파라미터 

 

pcap_close(adhandle);    // 네트워크 디바이스 핸들 종료

return 0;

}

 

/* 패킷이 캡처 됬을때, 호출되는 콜백 함수 */

void packet_handler(u_char *param,                    //파라미터로 넘겨받은 값 

const struct pcap_pkthdr *header, //패킷 정보 

const u_char *pkt_data)           //실제 캡처된 패킷 데이터

{

 

struct tm *ltime;

char timestr[16];

time_t local_tv_sec;

 

//출력 포맷 설정 

local_tv_sec = header->ts.tv_sec;

ltime=localtime(&local_tv_sec);
strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);

// 캡처된 시간 / 패킷크기 출력
printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);

/* pcap_pkthdr 구조체 */
//header->ts     //캡처시간
//header->len    //실제 패킷크기 
//header->caplen //캡처된 패킷 크기 
 }

 

 


 

 

 

gcc basicdump.c -lpcap

sudo ./a.out

 

- 결과 -

 

 



 

 

 

 

 

 

<이미지 출처 - cafe.naver/sec>

 

 

 

 

 

 

 

출처 : http://blog.naver.com/hjs20613?Redirect=Log&logNo=140188632025

 

 

 

 

 

댓글