개요
유저 프로그램에서는 setsockopt() 함수를 통해 소켓에 특정 소켓옵션을 부여해줄 수 있다.
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
setsockopt() 함수는 총 5개의 매개변수를 입력받는다.
각 매개변수의 역할은 아래와 같다.
- sockfd : 소켓을 가르키는 소켓 디스크립터
- level : 소켓 옵션을 적용시킬 네트워크 스택 레벨 (Ex. SOL_SOCKET, IPPROTO_IP, IPPROTO_TCP 등)
- optname : 소켓 옵션 이름
- optval : 소켓 옵션 값
- optlen : 소켓 옵션 값 길이
setsockopt()는 fd가 가르키는 소켓의 특정 소켓 옵션을 부여해주는 함수이며, 필요하다면 새로운 소켓 옵션을 커널에 등록시켜서 자신이 원하는 소켓 옵션을 소켓에 부여해줄 수 있다.
본 페이지에서는 소켓 레벨(SOL_SOCKET)에 새로운 소켓 옵션을 추가하는 방법을 설명한다.
사용된 커널은 4.19.x 버전이다.
필자는 새로운 소켓 옵션을 통해 기존 목적지 주소를 loopback(127.0.0.1) 주소로 변환시키는 것을 진행해보았다. 그러한 과정은 아래에 설명되어 있다.
커널의 SOL_SOCKET 레벨에 새로운 소켓 옵션 추가
1. 새로운 소켓 옵션 이름 추가
# 파일경로 : include/uapi/asm-generic/socket.h
SOL_SOCKET에 적용시킬 수 있는 기존 소켓 옵션 이름은 21개가 있다.
기존 소켓 옵션 값과 겹치지 않는 값으로 새로운 소켓 옵션 이름을 작성해준다.
필자는 SO_INSLAB이라는 22번 소켓 옵션을 추가하였다.
2. 새로운 소켓 옵션에 따른 동작 구현
# 파일경로 : net/core/sock.c
유저 프로그램에서 setsockopt()의 level 매개변수에 SOL_SOCKET을 입력하였을 시 커널에서는 소켓 레벨에 옵션을 부여해주기 위해 sock_setsockopt()를 호출한다. (만약, 유저 프로그램에서 setsockopt()의 level에 IPPROTO_TCP를 입력하였을 경우에는 do_tcp_setsockopt()를 호출한다.)
그 후 유저 프로그램으로부터 얻어온 optname에 해당하는 case문으로 이동해 optname에 맞는 코드를 실행시킨다.
필자는 optname으로 SO_INSLAB(22)이 입력되었을 경우 optval의 값을 검사해 "enable"인 경우 sock 구조체의 INSLAB 변수를 1로 flag 시키고 아니면 INSLAB을 0으로 flag 시키도록 하였다.
미리 sock 구조체에 INSLAB 이라는 int형 변수를 선언해두어서 위 코드를 실행할 수 있는 것이다.
아래는 sock 구조체에 추가한 INSLAB 변수이다.
# 파일경로 : include/net/sock.h
여기까지 진행되었다면 유저프로그램에서 setsockopt(fd, SOL_SOCKET, SO_INSLAB, "enable", 6)를 호출 시 커널에서는 sock_setsockopt()를 실행하여 fd가 가르키는 소켓의 sock 구조체의 INSLAB 값을 1로 flag 시킬 것이다.
이제 sock 구조체의 INSLAB 값이 1로 flag된 소켓들의 목적지 주소를 loopback주소로 변경하는 방법을 설명한다.
새로운 소켓 옵션이 추가된 소켓의 특정 동작 구현
# 파일경로 : net/socket.c
위 캡처본에 보이는 __sys_sendto() 함수는 UDP 소켓에서 사용하는 함수이며 UDP 소켓에서 메시지를 송신(send)할 때 사용하는 함수이다.
다시 말해 필자는 sock 구조체의 INSLAB 값이 1로 flag된 소켓이 __sys_sendto()를 호출할 시 빨간 박스와 같이 목적지 주소를 127.0.0.1로 변환하는 커널 코드를 구현하였다.
커널 컴파일 진행
앞서 구현된 코드는 커널에서 동작하는 코드임으로 커널이 새롭게 컴파일 되어야 한다.
본 페이지에서는 커널 컴파일 관련해서는 작성하지 않을 것이며, 만약 필요로한다면 아래 링크를 참조하길 바란다.
- https://heekangpark.blogspot.com/2019/01/compile-linux-kernel-05.html
- https://forum.ubuntu-kr.org/viewtopic.php?t=26625
- https://www.slideshare.net/he4722/ss-71711590
- https://sandarabong.tistory.com/63
실험
# 유저 프로그램 코드
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SO_INSLAB 22
int main()
{
int ret, sock;
char* enable = "enable";
char* disable = "disable";
char* buff = "hello";
struct sockaddr_in servaddr;
sock = socket(PF_INET, SOCK_DGRAM, 0);
ret = setsockopt(sock, SOL_SOCKET, SO_INSLAB, enable, strlen(enable));
//ret = setsockopt(sock, SOL_SOCKET, SO_INSLAB, disable, strlen(disable));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(55555);
servaddr.sin_addr.s_addr = inet_addr("xxx.xxx.xx.xx");
sendto(sock, (const char*)buff, strlen(buff), 0,
(const struct sockaddr *)&servaddr, sizeof(servaddr));
close(sock);
return 0;
}
위 UDP Sender C언어 코드를 실행시켰으며 setsockopt()를 통해 SO_INSLAB을 "enable"하고 "disable"해보았다.
해당 코드의 실행 결과는 아래와 같으며 tcpdump를 통해 시스템 내부에서 진행되는 udp 패킷을 캡처한 것이다.
tcpdump에서 캡처된 패킷은 총 2개의 패킷이며 위 1번 패킷은 SO_INSLAB이 enable된 실행결과이며 아래는 disable된 실행결과이다.
1번 결과는 소켓이 setsockopt()로 인해 SO_INSLAB이 enable되어 sock 구조체의 INSLAB 값이 1로 flag 되었음으로 목적지 주소가 loopback(127.0.0.1) 주소로 변환되어 전송되는 것이다.
반대로 2번 결과는 소켓이 setsockopt()로 인해 SO_INSLAB이 disable되어 sock 구조체의 INSLAB 값이 0으로 flag 되었음으로 목적지 주소가 기존 주소(모자이크로 표시)로 전송되어 지는 것이다.
끝으로..
본 페이지에서는 간단히 소켓 레벨(SOL_SOCKET)에 새로운 소켓 옵션(SO_INSLAB)을 추가해보고 해당 소켓 옵션에 따라 커널에서 특정 동작을 수행하게끔 구현해보았다.
Ref
'Linux' 카테고리의 다른 글
Linux 커널버전 4.x, 5.x의 proc 파일 시스템 예제 코드 (0) | 2022.08.30 |
---|---|
Linux 커널의 TC(Traffic Control) 구조 (0) | 2022.07.15 |
여러 네트워크 인터페이스(NIC) 중 특정 인터페이스를 통한 통신 방법 (in Linux) (0) | 2022.01.12 |
iperf3 사용법 (0) | 2021.12.26 |
C언어 NIC(네트워크 인터페이스 카드) IP 확인 소스코드 (0) | 2021.08.24 |