NAME

CMSG_ALIGN, CMSG_SPACE, CMSG_NXTHDR, CMSG_FIRSTHDR - 보조 데이터에 접근하기

SYNOPSIS

#include <sys/socket.h>

struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh,
                            struct cmsghdr *cmsg);
size_t CMSG_ALIGN(size_t length);
size_t CMSG_SPACE(size_t length);
size_t CMSG_LEN(size_t length);
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);

DESCRIPTION

이 매크로들을 이용해 소켓 페이로드에 포함되지 않는 제어 메시지(보조 데이터라고도 함)를 생성하고 접근한다. 그런 제어 정보로는 패킷을 받은 인터페이스, 드물게 쓰이는 여러 헤더 필드들, 상세한 오류 설명, 파일 디스크립터, 유닉스 크리덴셜 집합 등이 있을 수 있다. 예를 들어 제어 메시지를 사용해 IP 옵션 같은 추가 헤더 필드를 보낼 수 있다. sendmsg(2)를 호출해서 보조 데이터를 보내고 recvmsg(2)를 호출해서 받는다. 자세한 내용은 각 매뉴얼 페이지를 보라.

보조 데이터는 cmsghdr 구조체에 데이터를 덧붙인 것의 배열이다. 사용할 수 있는 제어 메시지 종류에 대해선 프로토콜별 맨 페이지를 보라. 소켓별 보조 버퍼 최대 크기를 /proc/sys/net/core/optmem_max로 설정할 수 있다. socket(7) 참고.

cmsghdr 구조체는 다음처럼 정의돼 있다.

struct cmsghdr {
    size_t cmsg_len;    /* 데이터 바이트 수. 헤더 포함.
                           (POSIX에서는 타입이 socklen_t) */
    int    cmsg_level;  /* 기원 프로토콜 */
    int    cmsg_type;   /* 프로토콜별 종류 */
/* 이어서:
    unsigned char cmsg_data[]; */
};

cmsghdr 구조체 열에 절대 직접 접근하지 말아야 한다. 대신 다음 매크로들을 쓰면 된다.

(가령 sendmsg(2)로 보낼) 일련의 cmsghdr 구조체를 담을 버퍼를 초기화할 때는 CMSG_NXTHDR()의 올바른 동작을 위해 먼저 버퍼를 0으로 채우는 게 좋다.

보조 데이터를 만들려면 먼저 msghdrmsg_controllen 멤버를 제어 메시지 버퍼의 길이로 설정해야 한다. 그리고 그 msghdrCMSG_FIRSTHDR()를 써서 첫 번째 제어 메시지를 얻고 CMSG_NXTHDR()로 이후 메시지들을 얻는다. 각 제어 메시지에서 (CMSG_LEN()으로) cmsg_len을, 다른 cmsghdr 헤더 필드들을, 그리고 CMSG_DATA()를 써서 데이터 부분을 채운다. 마지막으로 msghdrmsg_controllen 필드를 버퍼 내 모든 제어 메시지 길이의 CMSG_SPACE()의 합으로 설정해야 한다. msghdr에 대한 더 자세한 내용은 recvmsg(2)를 보라.

CONFORMING TO

이 보조 데이터 모델은 POSIX.1g 초안, 4.4BSD-Lite, RFC 2292에서 기술하는 IPv6 고급 API, SUSv2를 따른다. CMSG_FIRSTHDR(), CMSG_NXTHDR(), CMSG_DATA()는 POSIX-1.2008에 명세돼 있다. CMSG_SPACE()CMSG_LEN()은 다음 POSIX 릴리스(Issue 8)에 포함될 예정이다.

CMSG_ALIGN()은 리눅스 확장이다.

NOTES

이식성을 위해선 여기서 설명하는 매크로들만 이용해 보조 데이터에 접근해야 한다. CMSG_ALIGN()은 리눅스 확장이므로 이식성이 있어야 하는 프로그램에서는 사용하지 말아야 한다.

리눅스에서 CMSG_LEN(), CMSG_DATA(), CMSG_ALIGN()은 (인자가 상수라고 하면) 상수 식이고, 따라서 전역 변수 크기를 선언하는 데 그 값을 쓸 수 있다. 하지만 이식성이 없을 수 있다.

EXAMPLES

다음 코드는 받은 보조 버퍼에서 IP_TTL 옵션을 찾는다.

struct msghdr msgh;
struct cmsghdr *cmsg;
int received_ttl;

/* msgh의 보조 데이터 받기 */

for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
        cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
    if (cmsg->cmsg_level == IPPROTO_IP
            && cmsg->cmsg_type == IP_TTL) {
        memcpy(&receive_ttl, CMSG_DATA(cmsg), sizeof(receive_ttl));
        break;
    }
}

if (cmsg == NULL) {
    /* 오류: IP_TTL을 켜지 않았거나 버퍼가 작거나 I/O 오류 */
}

아래 코드는 SCM_RIGHTS를 이용해 유닉스 도메인 소켓 상으로 파일 디스크립터 배열을 전달한다.

struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
int myfds[NUM_FD];  /* 전달할 파일 디스크립터들을 담음 */
char iobuf[1];
struct iovec io = {
    .iov_base = iobuf,
    .iov_len = sizeof(iobuf)
};
union {         /* 보조 데이터 버퍼. 적절히 정렬되게
                   하기 위해 공용체로 감싼다 */
    char buf[CMSG_SPACE(sizeof(myfds))];
    struct cmsghdr align;
} u;

msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(myfds));
memcpy(CMSG_DATA(cmsg), myfds, sizeof(myfds));

SEE ALSO

recvmsg(2), sendmsg(2)

RFC 2292


2021-03-22