네트워크 보안/네트워크

Netlink Sockets # How to use

uzguns 2024. 11. 3. 21:41

Netlink는 Linux에서 kernel space와 user space 간의 통신을 위해 설계된 특별한 IPC (Inter-Process Communication)입니다. Netlink를 사용하면 커널과 사용자 간 데이터를 주고받을 수 있으며, socket programming 방식을 사용하기 때문에 기존의 소켓 프로그래밍과 유사하게 사용할 수 있습니다. Netlink는 아래와 같은 특징과 장점이 있습니다.

Netlink의 장점
구현이 간단함

Netlink는 kernel과 user space 사이의 통신을 구현할 때, 기존의 system calls, ioctl, proc file system 등을 사용하지 않고도 쉽게 구현할 수 있습니다. Netlink를 사용하면 추가하고자 하는 API와 protocols family만 netlink.h에 정의하면 되므로, 더 간편하고 안전합니다.
비동기화 방식 (Asynchronous)

Netlink는 socket 통신의 특성을 활용하여 기본적으로 비동기 방식으로 동작합니다. 이는 동기 방식에 비해 여러 가지 장점을 제공하며, 개발자가 따로 비동기화 구현을 신경 쓸 필요가 없습니다.
동적 로딩 지원 (Dynamic Loading)

다른 IPC 방식(system call, ioctl 등)은 커널 컴파일 시점에 API가 고정되어야 하는 반면, Netlink는 모듈처럼 동적으로 로드하여 사용할 수 있습니다. 따라서 커널의 재컴파일 없이도 기능을 추가하거나 수정할 수 있습니다.
멀티캐스트 방식 지원

일반적인 IPC는 하나의 프로세스에만 메시지를 전송할 수 있는 반면, Netlink는 멀티캐스트 방식을 지원하여 동일한 메시지를 여러 프로세스에 동시에 전달할 수 있습니다.
양방향 통신 (Duplex)

Netlink는 단방향(Simplex) 통신과 달리 양방향(Duplex) 통신을 지원합니다. 이를 통해 user space에서 kernel space로 요청을 보낼 수 있을 뿐만 아니라, kernel space에서 user space로도 메시지를 전송할 수 있습니다.
배우기 쉬움

Netlink는 BSD 소켓 스타일의 API 구조를 따르고 있어, 기존 소켓 프로그래밍에 익숙한 개발자라면 쉽게 배울 수 있습니다.
Netlink의 구조
Address Family

Netlink 소켓은 AF_NETLINK라는 주소 패밀리를 사용하여 kernel과의 통신을 설정합니다.
Protocols

다양한 Netlink 프로토콜이 존재하며, 이는 Netlink 소켓의 동작 방식을 결정합니다. 예를 들어, NETLINK_ROUTE는 네트워크 라우팅 관련 정보에 사용되고, NETLINK_GENERIC은 일반적인 용도로 사용할 수 있습니다.
Netlink 사용법
Netlink Socket API

Netlink는 기존 소켓 API와 유사한 방식으로 사용합니다.
소켓 생성 (socket())

socket(AF_NETLINK, SOCK_RAW, protocol)을 호출하여 Netlink 소켓을 생성합니다. 여기서 protocol은 사용할 Netlink 프로토콜을 지정합니다.
소켓 설정 (bind())

생성한 소켓을 bind() 함수로 설정하여, 커널에서 user space로 메시지를 받을 수 있도록 소켓을 연결합니다.
메시지 전송 (sendmsg())

sendmsg() 함수를 통해 Netlink 메시지를 커널로 전송할 수 있습니다. 이 때, Netlink 메시지 형식을 사용하여 전송할 데이터를 포함시켜야 합니다.

 

Netlink 사용 개요
Netlink는 Linux 커널과 사용자 공간 간의 통신을 위한 IPC 방식으로, AF_NETLINK address family를 사용하여 커널과 데이터를 주고받을 수 있습니다. 다른 IPC 방식과 달리 Netlink는 커널 모듈과 사용자 애플리케이션 간에 동적, 비동기, 멀티캐스트 방식의 양방향 통신을 지원하며 사용법이 간편합니다. 주로 라우팅, 네트워크 모니터링, SELinux 이벤트 알림 등에서 활용됩니다.

Netlink의 구조
Address Family

Netlink 소켓은 AF_NETLINK address family를 사용합니다. 이를 통해 커널과의 통신을 설정합니다.
Protocols

Netlink에서 사용하는 프로토콜은 kernel/include/uapi/linux/netlink.h에 정의되어 있습니다. 보통 kernel/include/linux/netlink.h를 include 하여 사용할 수 있습니다.
Netlink 프로토콜 목록 예시:
NETLINK_ROUTE: 라우팅 및 네트워크 장치 관련 정보
NETLINK_FIREWALL: 방화벽 설정
NETLINK_SOCK_DIAG: 소켓 모니터링
NETLINK_XFRM: IPsec 관련
NETLINK_SELINUX: SELinux 이벤트 알림
NETLINK_GENERIC: 일반적인 용도 등
Netlink 사용법
Netlink Socket API

Netlink 소켓은 기존 소켓 API (socket(), sendmsg(), recvmsg(), close())로 접근할 수 있습니다.
소켓 생성: socket()

socket(AF_NETLINK, SOCK_RAW, protocol)을 호출하여 Netlink 소켓을 생성합니다. 여기서 protocol은 Netlink의 특정 프로토콜을 나타냅니다.
소켓 설정: bind()

소켓을 생성한 후 bind() 함수를 호출하여 소켓을 설정합니다. 필요한 정보를 struct sockaddr_nl 구조체에 입력하여 사용합니다.
struct sockaddr_nl의 주요 필드
nl_family: AF_NETLINK로 설정
nl_pid: 프로세스 ID. get_pid() 또는 pthread_self()로 가져올 수 있습니다.
nl_groups: 멀티캐스트 그룹 번호. 여러 프로세스에 메시지를 동시에 전달하여 시스템 부하를 줄일 수 있습니다.

struct sockaddr_nl nl_addr;
nl_addr.nl_family = AF_NETLINK;
nl_addr.nl_pid = get_pid(); // 또는 `pthread_self() << get_pid()`
bind(fd, (struct sockaddr*)&nl_addr, sizeof(nl_addr));

 

메시지 전송: sendmsg()

Netlink를 통한 통신을 택배에 비유하면:
socket()을 사용하여 Netlink 소켓을 생성
bind()로 송신자의 주소 설정
sendmsg()로 상대방 주소와 메시지 포장 후 발송
recvmsg()로 상대방의 답장 대기
메시지 전송 시 사용되는 구조체는 struct msghdr입니다.

struct msghdr {
    void *msg_name;         // 주소 정보
    socklen_t msg_namelen;  // 주소 길이
    struct iovec *msg_iov;  // 전송할 메시지 벡터
    size_t msg_iovlen;      // 벡터 요소 수
    void *msg_control;      // 부가 데이터
    size_t msg_controllen;  // 부가 데이터 길이
    int msg_flags;          // 수신된 메시지의 플래그
};

 

메시지 전송 절차

msg_name: struct sockaddr_nl 구조체를 연결하여 수신자 주소 정보를 설정
msg_iov: struct iovec 구조체를 사용하여 메시지 헤더와 실제 데이터를 저장
절차 요약:
struct sockaddr_nl로 수신자 정보 입력
struct iovec로 전송할 메시지와 정보 설정
struct msghdr에 위 두 구조체를 연결
sendmsg() 호출로 메시지 전송
Netlink의 장점
간단한 구현: 커널과 사용자 공간 간 통신을 추가할 때 많은 수정을 요구하지 않음
비동기 처리: 기본적으로 비동기 방식으로 통신
동적 로딩: 커널 모듈처럼 동적 로드 가능
멀티캐스트 지원: 여러 프로세스에 동시에 메시지 전송 가능
양방향 통신: 사용자와 커널 간 양방향 통신 지원
배우기 쉬움: BSD 소켓 스타일 API 구조로 직관적