쿠키의 저장소

Raw IP, Raw Socket 본문

Network

Raw IP, Raw Socket

모카치노쿠키 2018. 1. 4. 17:26

Linux Network Kernel을 공부하다보니 Raw IP라는 용어가 나와서 찾아본 김에 정리하는 글입니다.

보통 Raw IP라고 말하기보다는 Raw Socket이라는 말을 더 많이 쓰는 것 같습니다.

먼저 위키피디아에는 다음과 같이 정의가 정리되어 있네요.

"raw 소켓은 어느 특정한 프로토콜 용의 전송 계층 포매팅 없이
인터넷 프로토콜 패킷을 직접적으로 주고 받게 해주는 인터넷 소켓이다."

무슨 말인지 조금 어렵기도 한데 쉽게 풀이하자면 이렇습니다.

"커널 네트워크 스택 처리과정을 일부 스킵하고 유저 어플리케이션에서 직접 처리하겠다."

막상 쓰고보니 딱히 쉽게 풀이가 된 것 같지는 않지만... 아무튼 부연 설명을 덧붙여 보자면 이렇습니다.

원래 리눅스 상에서 패킷의 헤더를 조작하는 동작은 커널 수준에서만 가능합니다.

즉, 유저스페이스에서는 아무리 뭔 짓을 해도 헤더의 내용은 바꿀 수 없다는 것이죠.

그렇다면 여기서 잠깐! 패킷의 헤더를 바꿀 일이 있나요?

네! 있습니다. NAT나 spoofing 등이 그것이죠.

리눅스에서 iptables 명령어를 이용하여 목적지 주소나 출발지 주소를 바꾸는 것 처럼 패킷의 헤더를 바꾸는 일이 필요할 때가 있습니다.

이 때, 패킷의 헤더를 바꾸는 작업이 커널에서만 가능하기 때문에 프로그래머 입장에서는 할 수 있는 작업이 한정적이라는 단점이 있습니다.

이러한 단점을 극복하기 위해서 Raw Socket을 열어서 커널의 네트워크 스택을 거치지 않고 바로 유저스페이스로 패킷을 올려버리고 몇몇 설정을 통해 패킷의 헤더를 유동적으로 수정할 수 있습니다.

<출처 : http://slideplayer.com/slide/7851824/>

Raw Socket의 동작을 그림으로 나타내면 다음과 같습니다. 위의 이미지는 출처에 명시한 발표자료에서 가져온 것입니다.

해당 자료에 의하면 원래는 패킷을 수신하면 Protocol family handling routine을 거친 후에 IP, TCP/UDP 처리 등을 거쳐 application에 전달이 되지만 Raw Socket은 복잡한 처리 루틴을 제끼고 패킷을 바로 User land application으로 올려 처리할 수 있습니다.

이 때, 그림에서는 PF_PACKET 모드를 사용하게 되는데 PF_PACKET은 패킷을 L2 레이어에서 직접 송수신하는 소프트웨어 인터페이스입니다.

즉 커널에서의 어떠한 처리도 필요로 하지 않고, 유저스페이스에서 모든 것을 처리할 수 있도록 도와줍니다. 이 때, 커널 메모리의 포인터를 전달받아 해당 메모리에 직접 작업을 하므로 별도의 메모리 카피가 일어나지 않아(=Zero Copy) 성능상 유리할 수 있습니다.

위의 경우처럼 PF_PACKET은 유저스페이스에서 L2 레이어인 이더넷 프레임 헤더까지 수정을 하고싶은 경우에 사용하게 됩니다.

만일 굳이 이더넷 프레임 헤더는 수정할 일이 없고 IP 헤더만 수정이 필요한 경우에는 AF_INET 모드를 사용하면 IP 헤더까지만 수정이 됩니다.

사용법의 차이는 다음과 같습니다.

PF_PACKET 모드 :
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

AF_INET 모드 :
sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); // IPPROTO_RAW 인자는 IP_HDRINCL을 enable 시켜서 커널에 IP 헤더를 수정하지 말 것을 요청합니다.
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&value, sizeof(value)); // IP_HDRINCL을 enable 시켜서 커널에 IP 헤더를 수정하지 말 것을 요청합니다.

Raw Socket을 열어서 패킷을 받아온 이후에는 L2, L3, L4 레이어의 패킷의 값을 직접 채워줘야 합니다.

만일 AF_INET 모드에서 L4 레이어 프로토콜의 정보만을 수정하고 IP 헤더는 변경하지 않는다면 IPPROTO_RAW 대신 IPPROTO_TCP나 IPPROTO_UDP 인자를 넣어 해당 프로토콜의 정보에 해당하는 헤더값만을 수정 가능하도록 할 수도 있습니다.



Comments