QUESTION
recvfrom buffer가 datagram보다 작으면 나머지를 다음 read에서 받을 수 있는가?
UDP는 datagram 경계를 보존한다. receive buffer가 작으면 초과 부분은 잘리고 같은 datagram의 나머지를 다음 recv에서 이어 받을 수 없다. MSG_TRUNC와 recvmsg 반환 길이를 이용해 truncation을 명시적으로 처리한다.
send 성공은 delivery를 보장하지 않는다. loss, duplicate, reorder, path MTU, checksum/ICMP 오류를 application protocol이 필요에 따라 sequence, retry, acknowledgement로 보완한다.
STRUCTURE
구조 그림
각 skb가 datagram 하나다. 16-byte datagram을 8-byte buffer로 읽으면 뒤 8 byte는 다음 recv에 남지 않고 버려진다.
CALL PATH
호출 흐름
stream offset 대신 datagram 하나의 길이와 peer 주소를 state 단위로 다룬다. truncation은 partial progress가 아니라 message 손실이다.
함수 이름을 외우기 위한 그림이 아니다. 반환값, 파일 디스크립터, 메모리 매핑, 대기 큐 가운데 무엇이 다음 단계로 전달되는지 확인한다.
SOURCE COORDINATES
Linux 6.18.37 LTS 소스 위치
glibc 함수에서 멈추지 않고 syscall 구현과 커널 객체가 만나는 파일까지 내려간다. 링크는 동일한 태그의 원본 파일을 가리킨다.
| 파일 | 함수·구조체 | 여기서 볼 것 |
|---|---|---|
| net/ipv4/udp.c | udp_sendmsg(), udp_recvmsg() | datagram 생성과 한 message 단위 receive |
| net/ipv4/udp.c | udp_queue_rcv_skb() | socket receive queue와 drop 조건 |
| net/ipv4/ip_output.c | ip_make_skb(), ip_append_data() | IP packet/fragment/MTU 처리 |
COMPLETE PROGRAM
실행 예제 원본
아래 코드는 설명을 위해 중간 줄을 생략한 의사 코드가 아니다. 파일로 빌드해 실행할 수 있는 최소 예제다.
cc -std=c17 -Wall -Wextra -O2 udp_meta.c -o udp_meta01#define _GNU_SOURCE
02#include <arpa/inet.h>
03#include <stdio.h>
04#include <string.h>
05#include <sys/socket.h>
06#include <unistd.h>
07
08int main(void)
09{
10 int fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
11 struct sockaddr_in local = { .sin_family = AF_INET,
12 .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) } };
13 if (fd < 0 || bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
14 return 1;
15 socklen_t local_len = sizeof(local);
16 getsockname(fd, (struct sockaddr *)&local, &local_len);
17
18 const char payload[] = "0123456789abcdef";
19 if (sendto(fd, payload, sizeof(payload) - 1, 0,
20 (struct sockaddr *)&local, sizeof(local)) < 0)
21 return 1;
22
23 char small[8];
24 struct iovec iov = { .iov_base = small, .iov_len = sizeof(small) };
25 struct msghdr message = { .msg_iov = &iov, .msg_iovlen = 1 };
26 ssize_t n = recvmsg(fd, &message, MSG_TRUNC);
27 printf("datagram=%zd copied=%zu truncated=%s\n", n, sizeof(small),
28 n > (ssize_t)sizeof(small) ? "yes" : "no");
29 close(fd);
30 return n < 0;
31}
CODE NOTES
코드 조각별 설명
SOCK_DGRAM | SOCK_CLOEXECconnection 없이 datagram socket을 만들고 exec 상속을 막는다.
getsockname(fdport 0 bind로 kernel이 고른 ephemeral local port를 읽어 loopback destination으로 사용한다.
sizeof(payload) - 1문자열 terminating NUL은 wire protocol에 포함하지 않는다. datagram 길이는 sendto 호출 하나가 정한다.
char small[8]16-byte datagram보다 작은 receive buffer로 truncation을 의도적으로 만든다.
MSG_TRUNCLinux에서는 실제 datagram 길이를 반환하게 해 buffer보다 컸는지 알 수 있다. 복사된 byte는 buffer 크기뿐이다.
DETAILS
세부 동작
connected UDP는 reliability 기능이 아니다
connect는 default peer를 설정하고 다른 source datagram filtering, send/recv 사용, 일부 asynchronous error 전달을 편하게 한다. handshake나 delivery guarantee를 추가하지 않는다.
peer 변경이 필요하면 connect를 다시 호출하거나 sendto destination을 사용한다.
large datagram은 MTU 문제를 만든다
IP fragmentation은 한 fragment만 잃어도 전체 datagram을 잃고 middlebox에서 제한될 수 있다. DF/path MTU discovery 조건에서는 EMSGSIZE가 돌아올 수 있다.
application은 작은 packet size와 fragmentation/reassembly 상한을 정한다.
queue overflow는 receiver가 모를 수 있다
application이 느리면 socket receive buffer가 가득 차 새 datagram이 drop된다. SO_RXQ_OVFL ancillary data와 system UDP counters로 drop을 관찰할 수 있다.
buffer만 키우기보다 processing batch, CPU affinity, packet rate limit을 함께 조정한다.
OBJECTS
객체와 수명
| 대상 | 언제 생기고 없어지는가 | 확인할 값 |
|---|---|---|
UDP socket | socket/bind에서 endpoint가 생기고 close까지 receive queue를 소유한다 | local/peer, rcvbuf, error queue |
datagram skb | network receive부터 recv/drop까지 message 단위로 존재한다 | length, source, checksum, timestamp |
ancillary metadata | recvmsg control buffer에 복사되어 해당 datagram과 함께 소비된다 | pktinfo, timestamp, overflow |
FAILURE PATH
실패 조건과 오해하기 쉬운 부분
| 겉으로 보이는 현상 | 실제 원인 후보 | 확인 방법 |
|---|---|---|
| payload 뒤가 사라짐 | receive buffer보다 큰 datagram truncation | MSG_TRUNC와 실제 length |
| 가끔 packet 누락 | network 또는 socket queue drop | sequence 번호, netstat, SO_RXQ_OVFL |
| send EMSGSIZE | path MTU보다 큰 datagram/DF | error queue와 discovered MTU |
LAB
직접 확인
- MSG_TRUNC를 제거했을 때 반환 길이와 truncation 검출 가능성을 비교한다.
- SO_TIMESTAMPNS와 IP_PKTINFO를 켜 source/destination interface metadata를 recvmsg control buffer에서 읽는다.
- 작은 SO_RCVBUF와 빠른 sender로 drop을 유도해 sequence gap과 SO_RXQ_OVFL를 기록한다.
./udp_metastrace -e trace=socket,bind,getsockname,sendto,recvmsg,close ./udp_metaPRIMARY REFERENCES