WireGuard 내부의 IPIP: 아무도 예산에 넣지 않은 추가 20바이트
IPIP는 WireGuard 내부에서 20바이트를 더 쓴다. 살아 있는 터널에서 멈춘 전송을 단편화 탓으로 돌리기 전에 올바른 계층에서 예산을 계산하라.
작은 ping이 하루 종일 터널을 지나고 SSH 배너가 도착한 뒤, 파일 전송이 시작되는 순간 세션이 멈춘다. 이 패턴은 잘못된 계층에서 계산한 바이트 예산을 가리킨다. 바깥 터널은 자기 오버헤드만 보고 설계됐고 안쪽 몫은 아무도 빼지 않았다. 중첩된 IPIP 패킷은 WireGuard에게 그저 또 하나의 IP 패킷이며, 그래서 예약한 적 없는 바이트를 소비한다. 수정은 산수이고, 세 개의 명령과 한 개의 표에 들어간다.
그것을 드러내는 증상
단편화 문제는 크기로 자신을 알리며, 주소로는 결코 알리지 않는다. 상대는 56바이트 ping에 한 시간 동안 응답하다가 1400바이트에서 조용해진다. 터널은 올라가 있고 경로는 바뀌지 않았다. 핸드셰이크는 들어가고 첫 번째 완전한 TCP 윈도는 들어가지 않으며, 건강해 보인 지 몇 초 만에 전송이 멈춘다.
무엇이든 고치기 전에 상한을 측정하라. 측정하지 않은 상한은 두 번 쫓아가게 된다. 의심스러운 크기보다 아래에서 do-not-fragment 비트를 켠 프로브를 보내고, ping -M do -s 1000 10.9.0.2, 이미 너무 크다는 것을 아는 크기에서 반복하라. 목적지가 그대로인데 반전이 페이로드 크기를 따라간다면 라우팅은 제 일을 하고 있고, 예산에서 헤더 하나가 빠진 것이다.
모든 헤더를 한 번씩, 올바른 계층에서 세라
터널 인터페이스가 아니라 경로의 가장 작은 MTU에서 시작하라. 벽은 양쪽 끝보다 상류에 있다. WireGuard는 IPv4 외부 전송에 60바이트를 쓴다. IP에 20, UDP에 8, 자체 헤더에 32이며, 그 전송이 IPv6이면 80바이트다. 1420바이트 wg0는 1500바이트 경로가 실제로 전달할 수 있는 1440 아래에 20바이트 여유를 둔다. 그래서위키에서 복사한 숫자가 여기에도 맞는다. 스택 전체를 다이어그램에 그려라. 다음에 MTU를 바꾸는 사람이 그렇지 않으면 새로운 둥근 숫자를 고르고 당신의 버그를 물려받는다.
| Layer | Bytes | Remaining |
|---|---|---|
| Path MTU on the wired link | none yet | 1500 |
| WireGuard outer: IP, UDP, WG header | 60 | 1440 |
| Slack kept for safety | 20 | 1420 (wg0) |
| IPIP header inside the tunnel | 20 | 1400 (ipip0) |
| Original IPv4 and TCP headers | 40 | 1360 payload |
다섯 번째 행이 예산이 틀어지는 곳이다. 중첩 인터페이스가 나르는 1400바이트에는 원래 패킷 자체의 40바이트 헤더가 이미 포함되어 있다. 그중 파일을 읽는 프로세스에 도달하는 것은 1360바이트뿐이다. 그 열이 없는 다이어그램이 권하듯 1400을 애플리케이션 페이로드로 취급하면, 안쪽 장치는 움직이지 않은 벽 앞에서 40바이트나 더 후해진다.
두 MTU를 따로 설정하라
캐리어와 중첩 장치는 별개의 객체이므로 각자 자기 값이 필요하다. 중첩 장치는 캐리어에서 아무것도 물려받지 않으므로 이 순서로 적용하라:
ip link set wg0 mtu 1420
ip tunnel add ipip0 mode ipip remote 203.0.113.9 local 198.51.100.4
ip link set ipip0 mtu 1400
기억할 만한 규칙: IPIP 터널이 나르는 패킷이 IPv4이면 안쪽 MTU = 바깥 MTU - 20, 안쪽 패킷의 어느 한쪽이 IPv6이면 - 40이다. 중첩 헤더가 20바이트에서 40으로 자라기 때문이다. 그 뺄셈에 다른 것은 들어가지 않는다. WireGuard의 오버헤드는 1420을 고를 때 이미 지불됐고, 그것을 두 번 청구하면 완벽하게 동작하면서 아무도 측정하지 않는 동안 느리게 남는 터널이 만들어진다. 두 장치가 둥근 1400을 나르는 배포가 바로 그 실수의 지문이다.
중첩 패킷의 어느 쪽이든 IPv6이면 위의 모든 숫자가 무효가 되므로 비슷한 사이트에서 숫자를 복사하지 말고 다시 계산하라. IPv6가 바깥 경로나 원래 데이터그램에 나타나는 순간 빼는 헤더는 각각 40바이트가 되고, 페이로드 숫자는 정확히 그만큼 떨어진다.
아마 필요 없는 방화벽 규칙
두 WireGuard 피어 사이에서 회선 위에 있는 유일한 것은 UDP이므로, 프로토콜 4, 즉 RFC 2003이 IPv4-in-IPv4 캡슐화에 부여한 번호에 대한 인바운드 규칙은 중첩 경로를 만들지도, 유지하지도 않는다. IPIP 헤더는 암호화된 페이로드 안에 살고, 어떤 경계 방화벽도 그것을 보지 못한다. 프로토콜 4를 열어도 호스트로 들어가는 허용된 길이 하나 늘어날 뿐이다.
원래 패킷의 출발지와 목적지를 언급하는 정책은 캡슐화 해제 이후, 터널 인터페이스와 라우팅 테이블에 속한다. 거기에 두면 경계 규칙은 닫힌 채로 있을 수 있다.
WireGuard가 실제로 검증하는 것
AllowedIPs는 WireGuard가 나르는 패킷의 출발지를 판단하고, 그것이 나르는 것은 IPIP로 캡슐화된 패킷이다. 허용된 IPIP 출발지는 안쪽 헤더가 당신이 라우팅하고 싶지 않은 호스트를 지목해도 수용된다. 따라서 원래 출발지는 피어가 당신을 위해 확인한 것이 아니며, 그 안쪽 주소 쌍은 캡슐화 해제 후 자체적인 강제가 필요하다.
래퍼는 라우팅 구조를 제공하지, 두 번째 암호 계층을 제공하지 않는다. WireGuard 안에서 IPIP를 추가 보호라고 부르는 것은 다른 설계를 설명하는 일이다.
한 번의 ping이 아니라 사다리로 검증하라
계산한 경계를 양쪽에서 넘나드는 크기를 시험하고, 두 장치에 대해 커널이 무엇을 믿는지 확인하라. 1360바이트 페이로드에 40바이트 헤더를 더해 1400이 되면 아래의 첫 번째 에코는 들어가고 두 번째는 상한에서 한 바이트 벗어나 앉는다:
ping -M do -s 1372 10.9.0.2
ping -M do -s 1373 10.9.0.2
wg show wg0
ip -d link show ipip0
상한 아래에서 깨끗하게 통과하고 그 위의 첫 크기에서 실패하면 산수가 끝에서 끝까지 성립한다는 뜻이다. 그런 다음 원래 멈췄던 전송을 다시 실행하라. 합성 프로브는 예산을 증명할 뿐 송신자 자신의 쓰기 크기에 대해서는 아무 말도 하지 않는다. 들어간다고 증명한 크기에서도 중첩 경로가 깨진다면, MTU에서 열 바이트를 더 깎기 전에 먼 끝의 캡슐화 해제와 경로를 점검하라.