Skip to content

cmsg(3)

Seonghun Lim edited this page Oct 13, 2019 · 4 revisions

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 구조체 열에 절대로 직접 접근하지 말아야 한다. 대신 다음 매크로들을 사용하라.

  • CMSG_FIRSTHDR()는 전달받은 msghdr에 연계된 보조 데이터 버퍼의 첫 번째 cmsghdr에 대한 포인터를 반환한다. 버퍼에 cmsghdr가 들어갈 공간이 없으면 NULL을 반환한다.

  • CMSG_NXTHDR()는 전달받은 cmsghdr 다음의 유효한 cmsghdr를 반환한다. 버퍼에 충분한 공간이 남아 있지 않으면 NULL을 반환한다.

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

  • CMSG_ALIGN()은 길이를 받아서 필요 정렬을 감안한 길이를 반환한다. 상수 식이다.

  • CMSG_SPACE()는 전달받은 데이터 길이의 페이로드를 가진 보조 항목이 차지하는 바이트 수를 반환한다. 상수 식이다.

  • CMSG_DATA()cmsghdr의 데이터 부분에 대한 포인터를 반환한다.

  • CMSG_LEN()cmsghdr 구조체의 cmsg_len 멤버에 저장할 값을 반환하는데, 필요한 정렬까지 고려한 값이다. 데이터 길이를 인자로 받는다. 상수 식이다.

보조 데이터를 만들려면 먼저 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_ALIGN()은 리눅스 확장이다.

NOTES

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

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

EXAMPLE

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

struct msghdr msgh;
struct cmsghdr *cmsg;
int *ttlptr;
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) {
        ttlptr = (int *) CMSG_DATA(cmsg);
        received_ttl = *ttlptr;
        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(int) * NUM_FD);
memcpy(CMSG_DATA(cmsg), myfds, NUM_FD * sizeof(int));

SEE ALSO

recvmsg(2), sendmsg(2)

RFC 2292


2019-03-06

Clone this wiki locally