본문 바로가기
[ ★ ]Study/Network

RAW 소켓

by nroses-taek 2017. 6. 12.

TCP, UDP 서버 & 클라이언트 프로그램과 달리 RAW 소켓은 사용자가 직접 구현해야 되는 소켓이라고 볼 수 있다.

TCP, UDP는 소켓 관련 라이브러리 및 운영체제에서 프로토콜 헤더를 작성하여 데이터를 전송하기 때문에 직접 프로토콜 헤더를 작성하지 않아도 데이터를 송수신할 수 있었다. 단순하게 데이터를 송수신하는 기능을 구현하기에는 이와 같이 프로토콜 헤더를 자동으로 만들어주는 것이 편리하다.

하지만 프로토콜 헤더를 직접 다루어야 하는 경우도 있기 때문에 RAW 소켓을 공부할 필요가 있다.

RAW소켓이란 소켓을 생성할 때 지정하는 옵션을 이용하여 데이터를 전송할 때 직접 프로토콜 헤더를 만들고 수신할 때도 프로토콜 헤더를 포함하여 수신하겠다는 의미로 소켓을 생성하는 것이다.

추가로 윈도우에서 RAW 소켓 사용이 제한되기 때문에 Packet Capture 라이브러리를 이용해야된다.

 

리눅싀에서의 RAW 소켓 생성 순서.

1. RAW 소켓 생성

2. 소켓 옵션 변경(선택 사항)

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
setsockopt( sockfd, IPPROTO_IP, IP_HDRINCL, (char *)&value, sizeof(value) );

위와 같이 소켓을 생성한 뒤에 옵션을 변경하면 데이터를 전송하기 위하여 실제 IP헤더를 포함한 모든 프로토콜 헤더를 직접 작성해 주어야 한다. 또한, 데이터를 수신하게 될 때도 프로토콜 헤더를 포함하여 데이터가 수신하게 된다.

위와 같이 소켓을 생성하였다고 하더라도 OSI 7 Layer 중 2Layer에 해당되는 데이터 링크 계층의 프레임 헤더는 운영체제에서 자동으로 만들어준다.

sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL);

 

위의 내용과 같이  소켓을 생성하면 데이터 링크 계층에 해당하는 프레임 헤더까지 직접 만들어 전송할 수 있을 뿐더러 데이터 링크 계층의 데이터까지 모두 수신할 수 있다.

추가로 소켓 디스크립터는 파일 디스크립터와 동일하게 처리되므로 파일에서 처리되는 입출력 관련 함수를 동일하게 소켓 디스크립터에 적용할 수 있다. 이러한 점을 이용하면 sendto 및 recvfrom 함수를 대신하여 더 편리하게 송수신할 수 있다.

추가로 RAW 소켓은 다른 사용자가 주고받는 데이터도 수신할 수 있을 뿐만 아니라 프로토콜 헤더를 조작하여 전송할 수 있으므로 리눅스에서는 보안상의 이유로 root 권한으로만 RAW 소켓 프로그램을 실행할 수 있다.

 

read 함수와 recvfrom 함수의 차이점

read 함수를 이용하여 소켓으로부터 수신 데이터를 읽는다면 부가적인 정보를 제외하고 순수한 데이터 정보만을 읽을 수 있다.

하지만, recvfrom 함수를 이용하는 경우에는 어떤 네트워크 인터페이스로부터 어떤 데이터가 수신되었는지 부가적인 정보까지 확인해 볼 수 있다.

 

기존의 소켓을 이용하여 데이터를 주고받을 때는 구조체 sockaddr_in을 이용하여 IP주소와 포트 정보 등을 저장하여 사용하였지만, 위의 옵션으로 RAW소켓을 생성하는 경우 구조체 sockaddr_in 대신에 구조체 sockadd_ll 형식의 정보로 데이터가 수신된다.

구조체 sockaddr_ll은 /usr/include/linux/if_packet.h 파일에 정의되어있다.


#include <linux/types.h>

struct sockaddr_pkt {
    unsigned short spkt_family;
    unsigned char spkt_device[14];
    __be16 spkt_protocol;
};

struct sockaddr_ll {
    unsigned short    sll_family;
    __be16        sll_protocol;
    int        sll_ifindex;
    unsigned short    sll_hatype;
    unsigned char    sll_pkttype;
    unsigned char    sll_halen;
    unsigned char    sll_addr[8];
};

 

PF_PACKET 옵션으로 소켓을 생성하는 경우 sll_family의 값은 언제나 PF_PACKET이 된다. sll_protocol은 네트워크 표준 Ethernet 프로토콜 타입으로 <linux/if_ether.h>헤더 파일에 정의되어 있다.

sll_ifindex의 경우 네트워크 인터페이스의 인덱스 번호이다. 0은 모든 네트워크 인터페이스를 의미하게 되는데 0값은 오직 네트워크 인터페이스를 바인딩할 때만 유효한다.

sll_hatype의 경우 헤더의 형태르 의미하며, sll_pkttype의 경우 패킷의 타입을 의미한다. sllpkttype 값은 위의 헤더파일에 정의되어 있는데 일부 내용에 대한 매크로의 의미와 값들은 다음 표의 내용과 같다. sll_addr과 sll_halen의 경우 물리 계층의 주소와 길이를 의미한다.

매크로

의미

PACKET_HOST

0

로컬 호스트로 수신되는 패킷

PACKET_BROADCAST

1

물리 계층의 브로드캐스팅 패킷

PACKET_MULTICAST

2

물리 계층의 멀티캐스팅 패킷

PACKET_OTHERHOST

3

다른 호스트로 수신되는 패킷

(Promiscuous 모드일 경우)

 

패킷을 보낼 때는 sll_family, sll_addr, sll_halen, sll_ifindex 필드만이 사용되며 그 외의 필드는 반드시 0으로 설정되어야 한다. 그리고 sll_hatype과 sll_pkttype의 경우 패킷을 받을 때만 설정되며 네트워크 인터페이스를 바인딩할 때는 sll_protocol과 sll_ifindex가 사용된다.

그 외의 필드는 반드시 0으로 설정되어야 한다. 그리고 sll_hatype과 sll_pkttype의 경우 패킷을 받을 때만 설정되며 네트워크 인터페이스를 바인딩할 때는 sll_protocol과 sll_ifindex가 사용된다.

 

'[ ★ ]Study > Network' 카테고리의 다른 글

pcap_addr 구조체  (0) 2017.06.13
pcap_findalldevs 함수  (0) 2017.06.13
UDP 클라이언트 프로그램  (1) 2017.06.12
UDP 서버 예제 프로그램  (0) 2017.06.12
UDP 프로토콜  (0) 2017.06.12

댓글