[Network Basic] 기본 C 문법 I ~ II

Chapter 01 ~ 02 기본 문법 I ~ II


01 변수와 자료형

필요성 : 이후 네트워크 패킷을 담기 위한 공간을 만들 때 불필요한 공간을 낭비하지 않기 위해 공부한다.

문제 01-01

char, short, int, long, float, double 그리고 포인터의 크기는 얼마일까?

소스 코드

#include <stdio.h>

int main(void)
{
    printf("char \t: %lu\n", (unsigned long)sizeof(char));
    printf("short \t: %lu\n", (unsigned long)sizeof(short));
    printf("int \t: %lu\n", (unsigned long)sizeof(int));
    printf("long \t: %lu\n", (unsigned long)sizeof(long));
    printf("float \t: %lu\n", (unsigned long)sizeof(float));
    printf("double \t: %lu\n", (unsigned long)sizeof(double));
    printf("point \t: %lu\n", (unsigned long)sizeof(void*));

    return 0;
}

출력

char 	: 1
short 	: 2
int 	: 4
long 	: 8
float 	: 4
double 	: 8
point 	: 8
Press <RETURN> to close this window...
Data Type (unsigned long) Size [byte]
char 1 byte [ 00 ]
short 2 byte [ 00 00 ]
int 4 byte [ 00 00 00 00 ]
long 8 byte [ 00 00 00 00 00 00 00 00 ]
float 4 byte [ 00 00 00 00 ]
double 8 byte [ 00 00 00 00 00 00 00 00 ]
POINTER( * ) 8 byte [ 00 00 00 00 00 00 00 00 ]

∗ 실행 운영체제(OS)의 32/64 bit 차이 혹은 컴파일러의 차이 별로 다른 결과를 반환 할 수 있다.

∗ 위와 같은 차이 때문에 버그가 발생할 가능이 있어서 C99에서는 비트 길이를 고유하게 정의한 데이터 형식을 추가하여 헤더 파일 <stdin.h>를 정의하였다.

stdint.h

자료형 비트 바이트 표현 범위
int8_t 8 1 -128 ~ 127
int16_t 16 2 -32,768 ~ 32,767
int32_t 32 4 -2,147,483,648 ~ 2,147,483,647
int64_t 64 8 263 ~ 263 – 1
uint(bit)_t (bit) (bit) / 8 0 ~ 2bit

참고, 위와 같은 자료형은 typedef "기존 자료형" "새로운 자료형" 형식으로 재정의 한 것 이다.

Typedef를 이용한 자료형 재정의

소스 코드

#include <stdio.h>
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int int16_t;
typedef unsigned short int uint16_t;

int main(void)
{
    int8_t a = 1;
    uint8_t b = 2;
    int16_t c = 3;
    uint16_t d= 4;

    printf("int8_t(signed char)\t:%lu\n", (unsigned long)sizeof(a));
    printf("uint8_t(signed char)\t:%lu\n", (unsigned long)sizeof(b));
    printf("int16_t(signed char)\t:%lu\n", (unsigned long)sizeof(c));
    printf("uint16_t(signed char)\t:%lu\n", (unsigned long)sizeof(d));

    return 0;
}

출력

int8_t(signed char)	:1
uint8_t(signed char)	:1
int16_t(signed char)	:2
uint16_t(signed char)	:2
Press <RETURN> to close this window...

02 연산자

대입 연산자, 나머지 연산자, 형변환 연산자, 복합 대입 연산자, 증감 연산자는 패스

비트 연산자

연산자 사용 예 설명
~ ~a 1의 보수 ( 비트 반전 )
& a & b b에 대한 AND 연산
| a | b a와 b에 대한 OR 연산
^ a ^ b a와 b에 대한 XOR 연산
<< a << 1 1비트를 좌측으로 이동
>> a >> 1 1비트를 우측으로 이동

 소스 코드

#include <stdio.h>

int main(void)
{
    int a = 1;
    int b = 2;

    int c = 3;
    unsigned int d = 3;

    printf(" 0x%08x | 0x%08x = 0x%08x\n", a, b, a|b);
    printf(" 0x%08x & 0x%08x = 0x%08x\n", a, b, a|b);
    printf(" 0x%08x ^ 0x%08x = 0x%08x\n", a, b, a|b);
    printf("~0x%08x = 0x%08x\n", a, ~a);

    printf(" signed int : 0x%08x >> 1 = 0x%08x\n", c, c >> 1);
    printf(" signed int : 0x%08x << 1 = 0x%08x\n", c, c << 1);
    printf(" signed int : 0x%08x >> 1 = 0x%08x\n", ~c, ~c >> 1);

    printf(" unsigned int : 0x%08x >> 1 = 0x%08x\n", d, d >> 1);
    printf(" unsigned int : 0x%08x << 1 = 0x%08x\n", d, d << 1);
    printf(" unsigned int : 0x%08x >> 1 = 0x%08x\n", ~d, ~d >> 1);

    return 0;
}

출력

 0x00000001 | 0x00000002 = 0x00000003
 0x00000001 & 0x00000002 = 0x00000003
 0x00000001 ^ 0x00000002 = 0x00000003
~0x00000001 = 0xfffffffe
 signed int : 0x00000003 >> 1 = 0x00000001
 signed int : 0x00000003 << 1 = 0x00000006
 signed int : 0xfffffffc >> 1 = 0xfffffffe
 unsigned int : 0x00000003 >> 1 = 0x00000001
 unsigned int : 0x00000003 << 1 = 0x00000006
 unsigned int : 0xfffffffc >> 1 = 0x7ffffffe
Press <RETURN> to close this window...

03 조건문

if ~ if else ~ else, switch 패스


04 반복문

for, while, do ~ while 패스


05 무한 반복문

for( (초기화) : : (증감) ), while(1) 패스


06 break, continue

break, continue 패스


07 배열

패스


08 문자와 문자열

패스


09 함수

패스


10 기본 함수

printf(), scanf()

specifier 출력 형태
d 부호 있는 10진수 정수(int)
ld 부호 있는 10진수 정수(long)
u 부호 없는 10진수 정수(int)
lu 부호 없는 10진수 정수(unsigned long)
o 부호 없는 8진수 정수
x 부호 없는 16진수 정수
f 부호 있는 10진수 실수
e E 부호 있는 실수 (지수 표시 e/E)
c 단일 문자
s 문자열
p 16진수의 주소 값

 

11 전처리기


01 파일 포함하기

소스 코드

헤더 파일 : sum.h

int sum(int a, int b){
    return a + b;
}

메인 c 파일 : main.c

#include <stdio.h>
#include "sum.h"

int main(void)
{
    int a = 10, b = 20;
    printf("%d + %d = %d\n",a, b, sum(a,b));

    return 0;
}

출력

10 + 20 = 30
Press <RETURN> to close this window...

02 매크로 정의

정의 방법 #define "치환할 내용" "치환될 내용"

소스 코드

#include <stdio.h>
#define BUFMAX  2048
int main(void)
{
    char buffer[BUFMAX];

    printf("BUFMAX : %d\n",BUFMAX);
    printf("buffer size : %lu\n", (unsigned long)sizeof(buffer));

    return 0;
}

출력

BUFMAX : 2048
buffer size : 2048
Press <RETURN> to close this window...

∗ 미리 정의 되어 있는 매크로 목록

매크로 의미
__FILE__ 소스 코드 파일 이름
__LINE__ 현재 소스 코드 라인 번호
__DATE__ 소스 코드가 컴파일 된 날짜
__TIME__ 소스 코드가 컴파일 된 시각
__TIMESTAMP__ 소스 코드가 컴파일 된 타임 스템프

 소스 코드

#include <stdio.h>

int main(void)
{
    printf("__FILE__\t: %s\n",__FILE__);
    printf("__LINE__\t: %d\n", __LINE__);
    printf("__DATE__\t: %s\n",__DATE__);
    printf("__TIME__\t: %s\n",__TIME__);
    printf("__TIMESTAMP__\t: %s\n",__TIMESTAMP__);

    return 0;
}

출력

__FILE__	: ../main.c
__LINE__	: 6
__DATE__	: Aug  1 2016
__TIME__	: 23:49:44
__TIMESTAMP__	: Mon Aug  1 23:49:44 2016
Press <RETURN> to close this window...

03 매크로 함수

매크로 부분에 간단한 함수를 넣어 대체할 수 있으며 한 함수에서 띄어 쓰기는 허용되지 않는다.

소스 코드

#include <stdio.h>

#define MAX(x,y) (x>y)?(x):(y)
#define DOUBLE(x) x*2

int main(void)
{
    printf("MAX(3,5)\t: %d\n",MAX(3,5));
    printf("DOUBLE(5)\t: %d\n",DOUBLE(5));
    printf("DOUBLE(3+2)\t: %d\n",DOUBLE(3+2));

    return 0;
}

출력

MAX(3,5)	: 5
DOUBLE(5)	: 10
DOUBLE(3+2)	: 7
Press <RETURN> to close this window...

∗ 3번째 DOUBLE(3+2)을 보면 10이 아닌 7이 나오는데 define으로 정의된 함수는 단순 치환으로 이루어져 DOUBLE(3+2)는 3+2*2로 계산되어 7이 나오게 된다.


04 매크로 연산자 “#”

매크로에서 “#”은 해당 내용은 “”(따옴표)가 붙어서 치환되고 “##”연산자는 앞 뒤에 내용을 그대로 붙여 코드로 치환 해준다.

소스 코드

#include <stdio.h>

#define PRINT(x) printf(#x "\n")
#define SAVE(name, num, value) name##num = value


int main(void)
{
    int data1 = 0;
    int data2 = 0;

    PRINT(Hello World!);

    SAVE(data, 1, 100);
    SAVE(data, 2, 3);

    printf("data1 : %d\n", data1);
    printf("data2 : %d\n", data2);

    return 0;
}

출력

Hello World!
data1 : 100
data2 : 3
Press <RETURN> to close this window...

∗ 전 처리 전과 후

전 처리 전 전 처리 후
PRINT(Hello World!); printf(“Hello World!” “\n”);
SAVE(data, 1, 100);
SAVE(data, 2, 3);
data1 = 100;
data2 = 3;

05 조건부 컴파일

전처리기 의미
#if 조건
#ifdef 정의되었을 경우
#ifndef 정의되지 않았을 경우
#else 그 외의 경우
#elif 그 외의 조건일 경우
#endif 조건문 전처리기의 끝

∗ 이를 이용해 중복된 헤더의 선언을 막을 수 있다.


사용 도서 정보

제목 : C로 구현하는 네트워크 해킹 프로그래밍

저자 : 강익선

출판사 : 가메출판사

소스 정보 : www.kame.co.kr

공부 기간 : 최대 1주일

 

[Network][ C ] MAC Address

MAC Address (String > Hex data)


  • 문자열로 이루어진 MAC Address를 Hex(16진수)로 바꾸는 함수를 만든다.
  • 이후 사용하기 쉽게 함수로 만든다.

함수 구조

void mac_to_hex (char * mac, unsigned int * hex_mac)

인자 설명

char * mac : 일정 형식( Ex. 11:22:33:44:55:66 )으로 이루어진 문자열
unsigned int * hex_mac : Hex 형태로 Mac Addresss를  저장할 공간


Code

#include <stdio.h>
#include <pcap.h>

#define ETHER_ADDR_LEN 6

void mac_to_hex (char * mac, unsigned int * hex_mac){
    int i = 0, j = 0;
    char buf[2];        // Save before change Hex

    for (i = 0; i < (ETHER_ADDR_LEN * 2) + 5; i++){
        if(mac[i] == ':' || mac[i] == '-' || mac[i] == ' '){
            sscanf(buf, "%x", (hex_mac+j));     // Change String > Hex
            j++;
        }
        else{
            buf[i % 2] = mac[i];                // Save Hex data
        }
    }
    sscanf(buf, "%x", (hex_mac+j));
}

int main(void){
    unsigned int bymac[ETHER_ADDR_LEN]; // Hex MAC Save here
    char * mac = "11 22 33 44 55 66";   // string MAC save here
    mac_to_hex(mac, bymac);

/* ================TESTing================ */
    int i = 0;
    for(i = 0; i < ETHER_ADDR_LEN - 1; i++){
        printf("%x:", bymac[i]);
    }
    printf("%x\n", bymac[i]);
/* ================TESTing================ */
    return 0;
}

 ∗ 11번째 줄이 MAC Address 구분 기호를 필터링 하는 부분

문자열로 이루어진  MAC 주소를 16진수로 바꾸어 Packet에 넣기 쉽게 만들었다.

[Network][PCAP] ARP Spoofing Packet

ARP Spoofing Packet


목표다

  • 이번 포스트에서 ARP Spoofing Packet에 대해 알아본다.
  • ARP Spoofing Packet을 PCAP을 이용해 직접 만들어 본다.

ARP Packet의 구조

ARP Packet Format

이제 각각에 위치에 값을 넣어 ARP_Spoofing Packet을 만들어 보자.

∗ 표의 왼쪽에서 오른쪽 까지 크기는 총 크기는 32byte ( 0 ~ 31 )이다.

하드웨어 타입 (0x0001) 프로토콜 타입 (0x0806 : ARP)
MAC 주소 크기 ( 6 ) IP 주소 크기 ( 4 ) ARP 옵션 (1 : ARP Reply)
공격자의 MAC 주소 ( 6 byte )
(…이어서) 공격자의 MAC 주소 ( 6 byte ) Gateway의 IP 주소 ( 4 byte )
(…이어서) Gateway의 IP 주소 ( 4 byte ) 목표(Victim)의 MAC 주소 ( 6 byte)
(…이어서) 목표(Victim)의 MAC 주소 ( 6 byte)
목표(Victim)의 IP 주소 ( 4 byte)

위에 표와 같이 packet을 구성하면 Gateway IP에 해당하는 MAC 주소가 공격자로 업데이트 되는  ARP_Spoofing 공격 packet이 완성된다.


Make Packet using libpcap in C

[Network][ C ] IP to BYTE in C

IP 주소를 바이트 형식으로 바꿔주는 함수


함수 구조 : unsigned int inet_addr(char *)

xxx.xxx.xxx.xxx 형식으로 이루어진 문자열을 바이트 형식으로 바꿔줍니다.


예제

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    char * input = "192.168.0.1";
    unsigned int byte_ip;

    byte_ip = inet_addr(input);

    printf("%s => %x \n", input, byte_ip);
    return 0;
}

OUTPUT

192.168.0.1 => 100a8c0 
Press <RETURN> to close this window...

이렇게 바꾼 IP 정보는 Packet의 넣어 전송할 수 있게 된다.

 

[Network] ARP Packet Analisty

ARP 패킷 분석


분석 목표

ARP의 Packet의 구조와 실제 ARP (Request)의 신호를 비교하여 분석한다.
또한 이후 LIBPCAP의 이더넷 구조체를 통해 직접 ARP Pecket 을 만들어든다.


ARP란?

Address Resolution Protocol, 즉 주소 결정 프로토콜로 네트워크 상에서 IP 주소를 MAC 주소로 대응시키기 위해 사용되는 프로토콜이다. 

예시

IP 1.1.1.1이 IP 2.2.2.2에게 IP Pecket을 전송하려고 할 때
IP 2.2.2.2의 물리적 네트워크 주소를 모른다면,
ARP 프로토콜을 사용하여 목적지 2.2.2.2와 브로드캐스팅 MAC 주소인  FFFFFFFFFFFF를 가지는 ARP Request Packet을 네트워크 상에 전송한다.
IP 2.2.2.2는 자신의 IP 주소가 목적지를 표현하는 곳에 있는 Packet을 수신하면 자신의 MAC 주소를 1.1.1.1에게 전달하는 ARP Reply Packet을 전송한다.


ARP Packet 분석

ARP Packet Format

ARP Packet

 색깔 별로 ARP 패킷을 대응 시킨 파일이다.

앞에 12 byte는 목적지 MAC과 근원지 MAC주소를 담은 Network 계층 데이터이고
뒤에 24 byte는 단지 Packet의 크기를 맞추기 위해 생성된 Padding 부분이다.


Libpcap

Ethernet header

#define ETHER_ADDR_LEN 6

struct ethhdr
{
    unsigned char   h_dest[ETHER_ADDR_LEN];   /* destination eth addr */
    unsigned char   h_source[ETHER_ADDR_LEN]; /* source ether addr    */
    unsigned short  h_proto;                  /* packet type ID field */
};

ARP Packet Format

ARP header

/* 		ARP Header, (assuming Ethernet+IPv4)           	   */ 
#define ARP_REQUEST 1   		/* ARP Request             */ 
#define ARP_REPLY 2     		/* ARP Reply               */
#define ARP_HDR_ADDR_LEN 6 		/* MAC Address Length	   */
#define ARP_IP_ADDR_LEN 4 		/* IP Address Length	   */

typedef struct arphdr { 
    u_int16_t htype;    		/* Hardware Type           */ 
    u_int16_t ptype;    		/* Protocol Type           */ 
    u_char hlen;        		/* Hardware Address Length */ 
    u_char plen;        		/* Protocol Address Length */ 
    u_int16_t oper;     		/* Operation Code          */ 
    u_char sha[ARP_HDR_ADDR_LEN];	/* Sender hardware address */ 
    u_char spa[ARP_IP_ADDR_LEN];	/* Sender IP address       */ 
    u_char tha[ARP_HDR_ADDR_LEN];	/* Target hardware address */ 
    u_char tpa[ARP_IP_ADDR_LEN];	/* Target IP address       */ 
}arphdr_t; 

 

[Network] Ethernet Analisty

Ethernet 분석


분석 목표

Ethernet에 담겨있는 이야기를 풀어 본다.

OSI 7 Layer에 대한 내용을 선행 해야함.


Ethernet?

이더넷(Ethernet)은 네트워킹의 한 방식으로 CSMA/CD혹은 토큰링(Token ring)라는 프로토콜을 이용해 통신한다.

통신하기위한 길이 하나 있는데 이때,
CSMA/CD는 눈치 게임이고 (동시에 접근하면 충돌이 일어남), 토큰링 방식은 차례차례 사용해 겹칠일이 없는 것이다.

최근 가장 많이 사용하는 방식은 CSMA/CD방식이라고 한다.


How ethernet packet look like?

응용 계층 부터 네트워크 계층 까지 Header가 붙는 모습

위 이미지를 보면 제일 위에 있는 Layer 7 (응용 계층)부터 ~ Layer 2,3 (네트워크 계층)까지 전송을 위해 Header가 붙는 모습을 나타넨 이미지 이다.

전부다 보면서 이해하면 좋지만 하나만 집중하자면 우리가 눈여겨 볼 것은 모든 헤더가 붙은 네트워크 계층이다.

Ehternet frame

목적지 MAC Addr 근원지 MAC Addr Type    DATA    CRC
8 byte 6 byte 2 byte 4 byte

앞에 preamble 라고 해서 물리 계층에 Header가 붙지만 지금 네트워크 부분만을 보고있으므로 패스

위 표에서 보면 알다 시피 목적지 MAC과 근원지 MAC주소가 나오고 Type에 따라 뒤에 나오는 데이터를 어떤건지 설명해 주는데 링크에 종류가 나열되어있다.

다만 우리가 주로 사용할 것만을 나열하면

  • IPv4 : 0x0800
  • ARP : 0x0806

이렇게 두가지 인데 Type의 2byte가 0x0800이면 이후 뒤에 오는 내용이 IP정보가 온다는 뜻이다.

아래 이미지를 보면 Type 부분이 0x0800이고 뒤에오는 정보가 IP인것을 알 수 있다.

IPv4 헤더

다음 포스트에선 ARP에 대해서 알아보자.

[BoB] Pcap tutorial

PCAP TUTORIAL

작동 환경 및 IDE에 대한 설명은 링크 참고

아마 삭제될 가능성 농후한 글입니다
해당 글은 제가 참고용으로만 사용되며 딱히 도움이 되지 않을 것 같습니다.


CODE

#include <stdio.h>
#include <pcap.h>

int main(int argc, char *argv[]){
    char *dev, errbuf[PCAP_ERRBUF_SIZE];

    dev = pcap_lookupdev(errbuf);
    if(dev == NULL){
        fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
        return 2;
    }
    printf("Device: %s\n", dev);
    return 0;
}

분석

pcap.h : pcap의 function, structuer 등을 사용하기 위한 header

char 변수
*dev : device info를 저장 하기 위한 변수errbuf [PCAP_ERRBUF_SIZE] : 미리 정의된 PCAP_ERRBUF_SIZE 만큼의 Error을 저장하기 위한 변수

pcap_lookupdev(errbuf) : pcap.h에서 제공하는 Network device 중 첫 번째의 정보를 return 하는 함수, ERROR를 저장할 배열의 주소를 인자로 입력 받는다.

fprintf에 관한 설명 링크stderr에 대한 설명 링크


출력 (리눅스 기준)

Device: eth0

[BoB][Network] libpcap(winpcap) programing

[BoB] Network

프로그램 중 네트워크 패킷을 다루기 위하 프로그래밍 공부를 하고 있습니다.
기초는 넘어가고 필요한 것만 바로 진행하기에
따라가기 위해 정리보단 결과물 위주로 올릴 것 같습니다.

개발 환경

Virtual Machine : Virtual Box
OS : Ubuntu LTS 14.04 Desktop
Process : i5-6200U x 2
RAM : 4GB (4096MB)
VRAM : 128MB
HDD : 13GB

IDE : QT (설치 방법 및 사용법)


설치 Package

>> apt-get install g++
>> apt-get install libpcap*


pcap QT에서 사용하기

(P.S. project를 만들때 non-qt 를 클릭해 C 혹은 C++ 프로젝트로 생성)

프로젝트명.pro파일에 LIBS += -lpcap 을 추가 후 컴파일


패킷에서 정보 추출하는 프로그램 (출처)

#include <sys/time.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <pcap/pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>

#define PROMISCUOUS 1
#define NONPROMISCUOUS 0

// IP 헤더 구조체
struct ip *iph;

// TCP 헤더 구조체
struct tcphdr *tcph;

// 패킷을 받아들일경우 이 함수를 호출한다.  
// packet 가 받아들인 패킷이다.
void callback(u_char *useless, const struct pcap_pkthdr *pkthdr, 
                const u_char *packet)
{
    static int count = 1;
    struct ether_header *ep;
    unsigned short ether_type;    
    int chcnt =0;
    int length=pkthdr->len;

    // 이더넷 헤더를 가져온다. 
    ep = (struct ether_header *)packet;

    // IP 헤더를 가져오기 위해서 
    // 이더넷 헤더 크기만큼 offset 한다.   
    packet += sizeof(struct ether_header);

    // 프로토콜 타입을 알아낸다. 
    ether_type = ntohs(ep->ether_type);

    // 만약 IP 패킷이라면 
    if (ether_type == ETHERTYPE_IP)
    {
        // IP 헤더에서 데이타 정보를 출력한다.  
        iph = (struct ip *)packet;
        printf("IP 패킷\n");
        printf("Version     : %d\n", iph->ip_v);
        printf("Header Len  : %d\n", iph->ip_hl);
        printf("Ident       : %d\n", ntohs(iph->ip_id));
        printf("TTL         : %d\n", iph->ip_ttl); 
        printf("Src Address : %s\n", inet_ntoa(iph->ip_src));
        printf("Dst Address : %s\n", inet_ntoa(iph->ip_dst));

        // 만약 TCP 데이타 라면
        // TCP 정보를 출력한다. 
        if (iph->ip_p == IPPROTO_TCP)
        {
            tcph = (struct tcphdr *)(packet + iph->ip_hl * 4);
            printf("Src Port : %d\n" , ntohs(tcph->source));
            printf("Dst Port : %d\n" , ntohs(tcph->dest));
        }

        // Packet 데이타 를 출력한다. 
        // IP 헤더 부터 출력한다.  
        while(length--)
        {
            printf("%02x", *(packet++)); 
            if ((++chcnt % 16) == 0) 
                printf("\n");
        }
    }
    // IP 패킷이 아니라면 
    else
    {
        printf("NONE IP 패킷\n");
    }
    printf("\n\n");
}    

int main(int argc, char **argv)
{
    char *dev;
    char *net;
    char *mask;

    bpf_u_int32 netp;
    bpf_u_int32 maskp;
    char errbuf[PCAP_ERRBUF_SIZE];
    int ret;
    struct pcap_pkthdr hdr;
    struct in_addr net_addr, mask_addr;
    struct ether_header *eptr;
    const u_char *packet;

    struct bpf_program fp;     

    pcap_t *pcd;  // packet capture descriptor

    // 사용중인 디바이스 이름을 얻어온다. 
    dev = pcap_lookupdev(errbuf);
    if (dev == NULL)
    {
        printf("%s\n", errbuf);
        exit(1);
    }
    printf("DEV : %s\n", dev);

    // 디바이스 이름에 대한 네트웍/마스크 정보를 얻어온다. 
    ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);
    if (ret == -1)
    {
        printf("%s\n", errbuf);
        exit(1);
    }

    // 네트웍/마스트 정보를 점박이 3형제 스타일로 변경한다. 
    net_addr.s_addr = netp;
    net = inet_ntoa(net_addr);
    printf("NET : %s\n", net);

    mask_addr.s_addr = maskp;
    mask = inet_ntoa(mask_addr);
    printf("MSK : %s\n", mask);
    printf("=======================\n");

    // 디바이스 dev 에 대한 packet capture 
    // descriptor 를얻어온다.   
    pcd = pcap_open_live(dev, BUFSIZ,  NONPROMISCUOUS, -1, errbuf);
    if (pcd == NULL)
    {
        printf("%s\n", errbuf);
        exit(1);
    }    

    // 컴파일 옵션을 준다.
    if (pcap_compile(pcd, &fp, argv[2], 0, netp) == -1)
    {
        printf("compile error\n");    
        exit(1);
    }
    // 컴파일 옵션대로 패킷필터 룰을 세팅한다. 
    if (pcap_setfilter(pcd, &fp) == -1)
    {
        printf("setfilter error\n");
        exit(0);    
    }

    // 지정된 횟수만큼 패킷캡쳐를 한다. 
    // pcap_setfilter 을 통과한 패킷이 들어올경우 
    // callback 함수를 호출하도록 한다. 
    pcap_loop(pcd, atoi(argv[1]), callback, NULL);
}

실행 방법 및 결과

[[email protected] pcap_test]# ./pcap_test -1 "port 80"
DEV : eth0
NET : 192.168.100.0
MSK : 255.255.255.0
=======================
IP 패킷
Version     : 4
Header Len  : 5
Ident       : 51804
TTL         : 64
Src Address : 192.168.100.130
Dst Address : 218.234.19.87
Src Port : 4996
Dst Port : 80
45000034ca5c400040065cfbc0a86482
daea1357138400502e7a303b2e7cc456
801021f0badb00000101080a0014e136
22631d7b485454502f312e3120323030
204f

IP 패킷
Version     : 4
Header Len  : 5
Ident       : 41787
TTL         : 54
Src Address : 218.234.19.87
Dst Address : 192.168.100.130
Src Port : 80
Dst Port : 4996