성엽이
KKIMSSI
성엽이
전체 방문자
오늘
어제
  • 분류 전체보기 (454)
    • :) (2)
    • C프로그래밍이론 (9)
    • C++프로그래밍 (64)
      • STL (1)
    • C# (2)
    • Visual studio 10.0 (9)
    • AT91SAM7S256 (21)
    • 논리회로 (14)
    • AVR2560 (11)
    • TCPIP (16)
    • NetWork (4)
      • Ubuntu Linux (2)
    • Assembly (21)
    • UNIX 프로그래밍 (6)
    • RFID 분석 (1)
    • Win32 API (7)
    • Cortex-M3 (4)
    • Unity (91)
    • Flutter (9)
    • OwnProject (11)
      • It's mine (5)
      • 마인드스톰 실습 (1)
      • 보고서 자료 (2)
      • RPi B+ (2)
    • ETC (25)
      • 상식 (3)
    • MFC (40)
    • PostgeSQL (18)
    • 영상제어 (6)
      • VFW_영상처리 (1)
    • Python (0)
    • Java (30)
      • SpringBoot (2)
      • Javascript (1)
      • JSP (13)
      • Spring (8)
    • Oracle (4)
      • SQL (3)
    • HTML (6)
      • Thymeleaf (1)
      • CSS (1)
      • Bootstrap (3)
    • IDE (1)
      • VS Code (1)
    • Android (2)
    • Privacy Policy (0)
    • MYSQL (2)
      • MariaDB (2)
    • AWS (5)
    • 개인공부 (0)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록
  • 관리자
  • 글쓰기

공지사항

인기 글

태그

  • WINAPI
  • Boot Code 분석
  • MFC
  • ARM Reverse Engineering
  • 문자열 나누기

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
성엽이

KKIMSSI

2013.07.12_SockCapture_소켓으로 패킷캡쳐하기_
TCPIP

2013.07.12_SockCapture_소켓으로 패킷캡쳐하기_

2013. 7. 12. 17:46

 

 

 

 

<인터넷의 전체적인 구조>

 

 

<인터넷 구조 : 프로세스>

송신측 프로토콜 스택에서의 작업

 

  • 어플리케이션 계층은 트랜스포트 계층으로 메시지를 보냅니다.
  • 트랜스포트 계층은 네트워크 계층으로 세그먼트를 보냅니다.
  • 네트워크 계층은 링크 계층으로 데이터그램을 보냅니다.
  • 링크 계층은 물리적 계층으로 프레임을 보냅니다.
  • 물리적 계층은 수신측 물리적 계층으로 프레임의 비트들을 보냅니다.
  •  

    수신측 프로토콜 스택에서는 다음과 같은 작업들이 실행됩니다.

     

  • 물리적 계층은 송신측 물리적 계층으로부터 프레임의 비트들을 받습니다.
  • 링크 계층은 물리적 계층으로부터 프레임들을 받습니다.
  • 네트워크 계층은 링크 계층으로부터 데이터그램을 받습니다.
  • 트랜스포트 계층은 네트워크 계층으로부터 세그먼트를 받습니다.
  • 어플리케이션 계층은 트랜스포트 계층으로부터 메시지를 받습니다.

     

     


     
  •  Ethernet.c

     #include "Ethernet.h"


    int L1_Ethernet(void *vp)
    {
      // 헤더 구조체 포인터.
      struct ether_header *stEth=(struct ether_header*)vp;  

      printf("==                         Source      ->     Destination\n");            
      /* source ether addr    */
      printf("== MAC Address    : [%02X:%02X:%02X:%02X:%02X:%02X]->",
          stEth->ether_shost[0],
          stEth->ether_shost[1],  
          stEth->ether_shost[2],
          stEth->ether_shost[3],  
          stEth->ether_shost[4],  
          stEth->ether_shost[5]);

      /* destination eth addr */
      printf("[%02X:%02X:%02X:%02X:%02X:%02X]\n",
          stEth->ether_dhost[0],
          stEth->ether_dhost[1],  
          stEth->ether_dhost[2],
          stEth->ether_dhost[3],  
          stEth->ether_dhost[4],  
          stEth->ether_dhost[5]);
      //printf("----- %s\n",ether_ntoa( (struct ether_addr *)stEth->ether_shost) );
      //구조체를 직접 찝어줘서 ether_host (LAN 장치번호)를 알수있다.

      /* packet type ID field */
      printf("== Packet type    : ");
      switch(ntohs(stEth->ether_type))
      {
        case ETHERTYPE_PUP:
          printf("Xerox PUP\n");
          break;
        case ETHERTYPE_IP:
          printf("IP\n");
          break;
        case ETHERTYPE_ARP:
          printf("Address resolution\n");
          break;
        case ETHERTYPE_REVARP:
          printf("Revers ARP\n");
          break;
        default :
          printf("Unknown %04X\n",ntohs(stEth->ether_type));
          break;
      }
      
      return ntohs(stEth->ether_type);
    }



     L2_IP.c

     #include "L2_IP.h"


    // CRC check sum이라는 이론을 함수로 만들어놓음._ 데이타의 손상이 있는지를 찾아주는 함수.
    unsigned short smart_cksum(void *vp_data, unsigned int ui_Len)
    {
      unsigned long ul_sum;
      for(ul_sum =0; ui_Len > 0; ui_Len=ui_Len-2)
      {  
        ul_sum = ul_sum + *((unsigned short *)vp_data);
        vp_data = (unsigned short *)vp_data + 1;
      }
      ul_sum = (ul_sum>>16) + (ul_sum&0xffff);
      ul_sum = ul_sum + (ul_sum>>16);
      return ~ul_sum;
    }

    // Layer 2 의 IP 안에 구조파악.
    int L2_IP(void *vp)
    {
      struct ip *st_IP = vp;  // IP 헤더.
      unsigned short us_checksum;

      printf("== IP Version           : IPv%d\n",
          (st_IP->ip_v));
      printf("== IP Header size       : %d bytes\n",
          (st_IP->ip_hl)*4);
      // Type of Service를 출력. 
      printf("== Type of Service      : LOWDELAY        [%s]\n",
          ( 0 == ((st_IP->ip_tos) & IPTOS_LOWDELAY) ? "NO" : "YES" )   );
      printf("==                      : Throughput      [%s]\n", 
          ( 0 == ((st_IP->ip_tos) & IPTOS_THROUGHPUT) ? "NO" : "YES" )   );

      printf("==                      : Reliability     [%s]\n",
          ( 0 == ((st_IP->ip_tos) & IPTOS_RELIABILITY) ? "NO" : "YES" )   );

      printf("==                      : Low Cost        [%s]\n",
          ( 0 == ((st_IP->ip_tos) & IPTOS_LOWCOST) ? "NO" : "YES" )   );

      printf("==                      : Min Cost        [%s]\n",
          ( 0 == ((st_IP->ip_tos) & IPTOS_MINCOST) ? "NO" : "YES" )   );

      // Total Length
      printf("== Total Length         : %d bytes\n",
          ntohs(st_IP->ip_len));

      printf("== Identification       : %d\n",
          ntohs(st_IP->ip_id));

      // Flag and Fragment
      printf("== Flag                 : More Fragment   [%s]\n",
          ( 0 == (ntohs(st_IP->ip_off) & IP_MF) ? "NO" : "YES" )   );

      printf("==                      : Don't Fragment  [%s]\n",
          ( 0 == (ntohs(st_IP->ip_off) & IP_DF) ? "NO" : "YES" )   );

      printf("== Fragmentation OFFSET : %d\n",
          ntohs(st_IP->ip_off) & IP_OFFMASK );

      // Time to live
      printf("== Time to live         : %d sec\n", st_IP->ip_ttl);

      // L3 Protocol
      printf("== IP Protocol          : ");  
      switch(st_IP->ip_p)
      {
        case IPPROTO_IP :  
          printf("Dummy protocol for TCP\n");
          break;
        case IPPROTO_ICMP :
          printf("Internet Control Message Protocol\n");
          break;
        case IPPROTO_IGMP :
          printf("Internet Group Management Protocol\n");
          break;
        case IPPROTO_IPIP :
          printf("IPIP tunnels (older KA9Q tunnels use 94)\n");
          break;
        case IPPROTO_TCP :
          printf("Transmission Control Protocol\n");
          break;
        case IPPROTO_EGP :
          printf("Exterior Gateway Protocol\n");
          break;
        case IPPROTO_PUP :
          printf("PUP protocol\n");
          break;
        case IPPROTO_UDP :
          printf("User Datagram Protocol\n");
          break;
        case IPPROTO_IDP :
          printf("XNS IDP protocol\n");
          break;
        case IPPROTO_TP :
          printf("SO Transport Protocol Class\n");
          break;
        case IPPROTO_IPV6 :
          printf("IPv6 header\n");
          break;
        case IPPROTO_ROUTING :
          printf("IPv6 routing header\n");
          break;
        case IPPROTO_FRAGMENT :
          printf("IPv6 fragmentation header\n");
          break;
        default :
          break;
      }

      //printf("== IP Header   : %04X\n", st_IP->ip_sum);  // 실제 들어가는 checksum.
      //st_IP->ip_sum = 0X0000;  // 2byte의 데이터 비우고 받기위해서 0x0000을 넣어줌.
      //printf("== Smart cksum : %04X\n", smart_cksum(st_IP,20));  // 함수로 맞춰본.

      us_checksum = st_IP->ip_sum;
      st_IP->ip_sum = 0x0000;  
      printf("== CheckSum Check       : %s\n", (    us_checksum == smart_cksum(st_IP,(st_IP->ip_hl)*4)  ) ? "OK" : "Failed" );

      // IP Address ( Source IP Address -> Destination IP Address )
      // inet_ntoa 함수는 4byte의 정수를 네트워크에 문자열로 바꿔 
      // Static 변수로 버퍼에 저장해주므로, 따로 불러서 덮어써야만 값이 바껴서 제대로 나옴.
      printf("== IP Address           : [%s]->",inet_ntoa(st_IP->ip_src));
      printf("[%s]\n",inet_ntoa(st_IP->ip_dst)); 

      return (st_IP->ip_p);

    }



     SockCap.c

     // Socket Data 를 recvfrom으로 데이터 수신후 분석 //

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/ioctl.h>
    #include <linux/if.h>
    #include <linux/if_ether.h>
    #include "HexaView.h"
    #include "Ethernet.h"
    #include "L2_IP.h"

    typedef struct _L1_map
    {
      unsigned int uiType;
      int (*L1Func)(void *);
    }L1_map;

    // 구조체를 L1_list[]에 각각 넣어줌. 2차원 배열 uiType <->  ETHERTYPE_? , int (*L1Func)(void *) <-> 함수주소 (ex: L2_IP)
    L1_map L1_list[] ={
            {ETHERTYPE_PUP,    0},
            {ETHERTYPE_IP,    L2_IP},  
            {ETHERTYPE_ARP,    0},
            {ETHERTYPE_REVARP, 0},
            {0xFFFFFFFF,    0}
            };

    int main()
    {
      int iSock;      
      struct ifreq stIf;
      int iRet;
      struct sockaddr_in st_pktInfo;
      unsigned int ui_Len = sizeof(st_pktInfo);
      unsigned char uc_Buffer[1500];  // Packet의 용량제한범위. ethernet이니까!
      L1_map *stpL1;  // stpL1은 L1_map 구조체를 가리키고있는 포인터.
      int iProto;

      // SOCK_RAW 생소켓!, 가공되어있지않은 소켓.
      iSock = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));
      // usr/include <asm/socket.h> line56 <linux/if_ether.h> line74

      // 윈도우에서 cls 와 비슷.
      write (1, "\033[1;1H\033[2J", 10);

      if(0 > iSock)
      {
        printf("socket() failed\n");
        return 0;
      }
      // 랜카드 정보를 Socket 하고 연결 시킴.
      strcpy(stIf.ifr_name, "eth0");

       // 저수준 입출력 장치에 모두에 쓰임.
      iRet = ioctl(iSock, SIOCGIFFLAGS, &stIf);  
      if( 0 > iRet )
      {
        printf("ioclt() failed\n");
        close(iSock);
        return 0;
      }

      // 랜카드 플래그를 다 건져올리는 모드 PROMISC모드로 바꿈.
      stIf.ifr_flags = stIf.ifr_flags | IFF_PROMISC; 

      // PROMISC 모드로 랜카드 설정이 바꿈.
      iRet = ioctl(iSock, SIOCSIFFLAGS, &stIf);  
      if( 0 > iRet )
      {
        printf("ioclt() failed\n");
        close(iSock);
        return 0;
      }

      // 읽어들인 byte 수.
      iRet = recvfrom(iSock, uc_Buffer, 1500, 0, (struct sockaddr *)&st_pktInfo, &ui_Len);
      
      // Ethernet 장치번호, Packet type ID : Layer 1
      // iProto에 Packet Type 을 반환받음.
      iProto = L1_Ethernet(uc_Buffer);
      
      // L1_map을 가리키고있는 stpL1에다가
      // 전역변수로 지정해둔 L1_list[]의 주소, L1_list를 넣어줌.
      // stpL1은 현재 L1_list를 가리키고있음.
      stpL1 = L1_list;
      
      while(1)
      { // L1_Ethernet() 의 반환값(ETHERTYPE_?)이 무엇이든 있을때 반복문 나감.
        if(iProto == (stpL1->uiType))
        {
          break;
        }

          // 전역변수 L1_list의 끝까지 도달하면 반복문 나감.
        if(0xFFFFFFFF == (stpL1->uiType))
        {
          break;  
        }
        ++stpL1; // L1_list의 배열 한칸씩 옮겨감.
      }

       // Packet Type 이 건져와지면 실행
      if(NULL != (stpL1->L1Func))
      { 
         stpL1->L1Func(uc_Buffer+14);
      }

      // 받은 데이터 uc_Buffer, recvfrom으로 수신받은 byte만큼
      // HexaView에 보냄.
      HexaView(uc_Buffer, iRet);

      close(iSock);
      
      return 0;
    }

    void HexaView(unsigned char *ucP, unsigned int isize)
    {
      int iCnt;
      int iloop;
      
      printf("----------------------------------------"
        "----------------------------------\n");
      printf("Address  \t\t\tHexa\t\t\t      ASCII\n"); 
      printf("\t ");

      
      for(iCnt=0 ; iCnt <= 15 ; iCnt++)
      {
        printf("%02X ", iCnt);
      }

      putchar('\n');

      printf("----------------------------------------"
        "----------------------------------\n");
      
      if( 0 == isize%16 )
      {
        isize = isize / 16;  
      }
      else
      {
        isize = (isize/16) + 1;    
      }
      
      for(iloop=0; iloop < isize; iloop++)
      {
        printf("%08X ", ucP); 
      
        for( iCnt=0 ; iCnt <= 15 ; iCnt++)
        {
          printf("%02X ", *(ucP+iCnt));
        }

        for( iCnt=0 ; iCnt <= 15 ; iCnt++ )
        {
          if(0 == *(ucP+iCnt))
          {
            printf(".");
          }
          else if( 32 > *(ucP+iCnt))
          {
            printf(".");
          }
          else if( 127 < *(ucP+iCnt))
          {
            printf(".");      
          }
          else
          {
            printf("%c", *(ucP+iCnt)); 
          }
        }
        putchar('\n');
        ucP = ucP + 16;
      }
    }

     

     

     

    Raw socket
    1. Layer3까지 조작이 가능하다.
    2. 일반적인 network stack을 이용한다.
    3. 커널이 처리하기 전에 먼저 빼올 수 있다.
    4. sockaddr_in을 사용한다.

                            <네트워크스택구조>


    Packet socket
    1. Layer2까지 조작이 가능하다.
    2. network stack을 skip하고 device driver로 전송된다.
    3. 커널이 처리하기 전에 먼저 빼올 수 있다.
    4. sockaddr_ll을 사용한다.

     

     

    <데이터 전송시 네트워크스택>

     

     

    <데이터 수신시 네트워크스택>

     참조사이트

    http://helloworld.naver.com/helloworld/47667
     

    //////SockCap 파일들//////

    Ethernet.c


    Ethernet.h


    HexaView.c


    HexaView.h


    L2_IP.c


    L2_IP.h


    SockCap_up.c

    //////////////////////////

    저작자표시 (새창열림)

    'TCPIP' 카테고리의 다른 글

    2013.08.05_Window에서 Packet Capture 하기 연습  (0) 2013.08.05
    2013.07.10_Thread를 이용한 Server 구현.  (0) 2013.07.10
    2013.07.09_TCP/IP를 Thread로 통신_  (0) 2013.07.09
    2013.07.08_Inter Process Communication(IPC)_활용.  (0) 2013.07.09
    2013.07.04_멀티태스킹 테스트  (0) 2013.07.05
      'TCPIP' 카테고리의 다른 글
      • 2013.08.05_Window에서 Packet Capture 하기 연습
      • 2013.07.10_Thread를 이용한 Server 구현.
      • 2013.07.09_TCP/IP를 Thread로 통신_
      • 2013.07.08_Inter Process Communication(IPC)_활용.
      성엽이
      성엽이

      티스토리툴바