Dork's port

Linux libpcap 을 이용한 packet capture 본문

Develop

Linux libpcap 을 이용한 packet capture

Dork94 2017. 9. 22. 13:03

패킷을 캡쳐할 수 있는 가장 유명한 라이브러리는 아마 pcap Library 일 것이다.  그래서 오늘은 네트워크를 처음 접하는 분들께


pcaplib를 사용 하는 간단한 소스코드를 소개하고자 한다. 네트워크를 처음에 공부할 때에 작성한 코드라 많이 서툴고 버그가 있을 수 도 있다.



#include <iostream>
#include <pcap.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <cstdio>

using namespace std;

#define PROMISCUOUS 1 //Get every packet from Ethernet 
#define NONPROMISCUOUS 0 //Get only mine from Ethernet 

struct ip *iph; //Struct of IP
struct tcphdr *tcph; //Struct of TCP


void callback(u_char *useless, const struct pcap_pkthdr *pkthdr, const u_char *packet);
void printMac(u_int8_t *addr);
int lengthRet(int length, int minusLen);

int main(int argc, char* argv[]) //Device , Filter 
{
	if(argc!=3)
	{
			cout<<"Usage : Pcap [Device Name] [\"Filter\"]"<<endl;
			cout<<"You can fine filter rules at \"www.winpcap.org/docs/docs_40_2/html/group_language.html\""<<endl;
			exit(1);
	}

	char * device = argv[1];
	int ret;	
	char* netAddress;
	char* netMask;
	bpf_u_int32 netp; //IP
	bpf_u_int32 maskp; //Subnet Mask
	char errBuf[PCAP_ERRBUF_SIZE];
	struct in_addr addr;//Struct to save IPv4

	cout<<"Device :"<<device<<endl;
	if(ret = pcap_lookupnet(device,&netp,&maskp,errBuf) <0) //Get Network , Subnet mask about Device
	{						//error => return -1 & error content => errBuf
			perror(errBuf);			//error => print errBuf & exit
			exit(1);
	}
	
	addr.s_addr = netp;
	if((netAddress = inet_ntoa(addr))==NULL)//inet_ntoa => convert ulong type to Dotted-Decimal Notation 
	{
		perror("inet_ntoa");
		exit(1);
	}
	cout<<"Network Address : "<< netAddress <<endl;

	addr.s_addr = maskp;
	netMask = inet_ntoa(addr);//Subnet Mask convert

	cout<<"Subnet Mask : "<<netMask<<endl;

	pcap_t *pcd; //Packet capture descriptor 


	//BUFSIZ is a optimum size (defined in csdio)
	//Get packet capture descriptor from Device
	if((pcd = pcap_open_live(device,BUFSIZ, NONPROMISCUOUS , 1, errBuf))==NULL)
	{
			perror(errBuf);
			exit(1);
	}
	

	struct bpf_program fp;

	
	if(pcap_compile(pcd, &fp, argv[2] , 0, netp)==-1) //Set fp by filter rule(argv[2])
	{
			cout<<"Setfilter error!!!"<<endl;
			exit(1);
	}

	if(pcap_setfilter(pcd, &fp) == -1) //apply packet filter 
	{
			cout<<"Setfilter error"<<endl;
			exit(1);
	}


	pcap_loop(pcd, 0, callback, NULL); //count 1 -> 0 infinity loop

	

	return 0;

}



//packet => recevied pakcet 
void callback(u_char *useless, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{

	struct ether_header *ep;
	unsigned short ether_type;
	int length=pkthdr->len;


	ep = (struct ether_header *)packet; //Save Ethernet Header
	cout<<"Information of Ehernet"<<endl;
	cout<<"Src Mac Address : ";
	printMac(ep->ether_shost);
	
	cout<<"Dest Mak Address : ";
	printMac(ep->ether_dhost);


	cout<<endl<<endl;



	
	ether_type = ntohs(ep->ether_type);// ntohs(network to host short)
									   // network => little endian 
									  

	length = lengthRet(length, sizeof(ep));

	if(ether_type == ETHERTYPE_IP)	//next protocol is IP(0x0800) defined in netinet->if_ether 
	{	

		packet += sizeof(struct ether_header);//To bring IP header
		

		iph = (struct ip *)packet;
		cout<<"Information of IP"<<endl;
		cout<<"Src IP Address : "<<inet_ntoa(iph->ip_src)<<endl;
		cout<<"Dest IP Address : "<<inet_ntoa(iph->ip_dst)<<endl;
		cout<<endl<<endl;
		length = lengthRet(length, sizeof(iph));

		if(iph->ip_p== IPPROTO_TCP) //next protocol is TCP
		{
			packet = packet + iph->ip_hl * 4;
			tcph =(struct tcphdr *)packet;	        //TCP Header
							        //iph->ip_hl => Header length
								//ip_hl is word so ip_hl * 4
								//linux word => 4byte 
			cout<<"Informaiton of TCP"<<endl;
			cout<<"Src Port : "<<ntohs(tcph->source)<<endl;
			cout<<"Dst Port : "<<ntohs(tcph->dest)<<endl;
			cout<<endl<<endl;

			length = lengthRet(length, sizeof(tcphdr));
			packet += sizeof(struct tcphdr); //To print Data Section 
		}


		for(int i=0; i<length;i++) //print data
		{
				if(i%16==0)
						cout<<endl;
				printf("%02x  ", *packet++);
		}

		cout << endl << endl;

	}else{
			cout<<"This Packet is not IP Packet"<<endl;
	}

}



void printMac(u_int8_t *addr)
{
		
	int sizeOfMac=6;//mac address => 48bit
					//mac use hexadecimal number
					//Ex) AB:CD:EF:GH:YJ:KL
					//hexadecimal number use 4bit per 1 num
					//0 0 0 0 => 0
					//1 1 1 1 => F => 15

	for(int i=0; i<sizeOfMac;i++)
	{
			printf("%02x",addr[i]);
			if(i!=sizeOfMac-1)
					printf(":");

	}


	cout<<endl;
}

int lengthRet(int length, int minusLen)
{
	length -= minusLen;
	return length;
}

/************************Information***************************/
//Ethernet & IP Header has a next protocol Info.
//Ethernet => ether_type IP => ip_p 
//***********************Flow chart****************************
//pcap_lookupnet => Get a Device name & Net & Subnet 
//pcap_open_live()=> Make a PCD(Packet Capture Descriptor)
//pcap_compile() //Set fp by filter rule
//pcap_setfilter() //apply packet filter 
//pcap_loop(); //callback a Function by PCD 
//Function









ethernet header부터 ip header , tcpheader까지 parsing  하며 관련된 정보를 출력하는 간단한 프로그램이다.


위의 예제로 네트워크의 헤더 구성은 어떻게 되어있는지, 어떤방식으로 parsing을 하는지 알아 볼 것을 권한다.


또한, pcap은 2계층(이더넷)부터 캡처를 하는 라이브러리로 캡처가 된 시작 포인터는 ethernet헤더이다.


그리고 pcap은 패킷의 흐름을 제어할 수 있는 라이브러리가 아닌 단지 패킷을 들여다보는 (snipping) library임을 명심하자.


패킷의 흐름을 제어하고싶다면, 라우팅테이블을 사용하거나 netfilter사용을 권장한다.


또한, 위의 파일을 컴파일시 라이브러리를 링크해줘야하므로 gcc 또는 g++ 명령어의 마지막에 -lpcap을 적어 링크를 해주길 바라며 linux에서 pcap Library는 아래의 명령어로 설치 가능하다.


# apt-get install libpcap*


Comments