XFRM Reference Guide
XFRM Reference Guide
리눅스 XFRM 서브시스템을 깊이 있게 이해하고 싶은 개발자와 사용자를 위한 레퍼런스 가이드예요. 특히 IPsec에서 Cilium이 어떻게 XFRM을 활용하는지, 정책·상태 객체가 어떻게 동작하는지 자세히 다뤄요.
본문
Note: 이 문서 섹션은 리눅스 XFRM 서브시스템을 이해하고 싶은 개발자와 사용자를 대상으로 해요. 이 레퍼런스 가이드를 읽는 것이 Cilium을 더 폭넓게 이해하는 데 도움이 될 수는 있지만, Cilium을 사용하기 위한 필수 요건은 아니에요. 보다 높은 수준의 소개는 Getting Started 가이드와 eBPF Datapath를 참고하세요.
개요 (Overview)
리눅스 커널에서의 IPsec 암호화는 XFRM에 의존해요. XFRM은 암호화에서 압축에 이르기까지 패킷 변환(transformation)을 위한 IP 프레임워크예요. 정책(policy)과 상태(state) 객체 집합으로 구성되며, IPsec에서 이들은 각각 Security Policy와 Security Association에 해당해요.
XFRM 정책과 상태 (XFRM Policies and States)
높은 수준에서 보면 XFRM 정책은 어떤 트래픽을 허용하고 거부할지 정의하고, 상태는 암호화와 복호화를 어떻게 수행할지를 정의해요. 정책은 방향(out, in, fwd), CIDR을 포함한 소스·목적지 IP 주소, 패킷 마크(mark)로 매칭할 수 있어요. 예를 들어 다음 정책은 어떤 소스 IP 주소든 가진, 10.56.1.X 목적지 IP 주소와 0xcb93eXX 패킷 마크를 가진 egress 패킷을 매칭해요. 여기서처럼 정책은 기본적으로 트래픽을 허용하는 방향으로 동작해요.
src 0.0.0.0/0 dst 10.56.1.0/24
dir out priority 0
mark 0xcb93e00/0xffffff00
[...]
상태는 이와 비교적 비슷하지만, 방향에 무관하고 정확한 IP 주소(또는 전체를 매칭하려면 0.0.0.0)만 매칭할 수 있어요. 다음 상태는 IP 주소가 10.56.0.17 -> 10.56.1.238인 패킷에 적용되며, 마크는 위와 동일해요. 터널 모드 IPsec의 경우 이 IP 주소들은 외부(outer) IP 주소에 해당해요. 수신(ingress)되는 암호화 패킷에 대해서는 SPI도 사용돼요(아래에서 설명해요).
src 10.56.0.17 dst 10.56.1.238
proto esp spi 0x00000003 reqid 1 mode tunnel
replay-window 0
mark 0xcb93e00/0xffffff00 output-mark 0xe00/0xffffff00
aead rfc4106(gcm(aes)) 0x6254fced5f7a5ea9401b9015ecf10d65eac51a69 128
anti-replay context: seq 0x0, oseq 0x36, bitmap 0x00000000
sel src 0.0.0.0/0 dst 0.0.0.0/0
이 상태가 암호화를 수행해야 하는지 복호화를 수행해야 하는지를 명시하는 부분이 없다는 걸 눈치채셨을 거예요. 그 이유는 실제로 둘 다 수행할 수 있기 때문이에요. 앞서 말했듯이 상태는 트래픽의 방향에 무관하므로, 같은 상태를 이론적으로는 암호화와 복호화 양쪽에 사용할 수 있어요. 무엇을 할지는 스택의 어느 지점에서 상태가 매칭되는지에 따라 결정돼요(예: 수신 시 복호화).
정책 템플릿 (Policy Templates)
XFRM 정책은 일반적으로 아래처럼 템플릿도 함께 정의해요:
src 0.0.0.0/0 dst 10.56.1.0/24
dir out priority 0
mark 0xcb93e00/0xffffff00
tmpl src 10.56.0.17 dst 10.56.1.238
proto esp spi 0x00000003 reqid 1 mode tunnel
이 템플릿을 어떻게 사용하는지는 방향에 따라 달라요. egress 트래픽의 경우 템플릿은 수행할 인코딩(캡슐화)을 정의해요. 예를 들어 위 템플릿은 패킷을 IP 헤더와 ESP 헤더로 캡슐화해요. IP 헤더는 IP 주소 10.56.0.17과 10.56.1.238을 갖게 되고, ESP 헤더는 SPI 3을 갖게 돼요.
반면 ingress와 forwarded 트래픽에서 템플릿은 추가 필터 역할을 해요. 예를 들어 다음 XFRM 정책은 패킷이 0xd00/0xf00과 매칭되는 패킷 마크를 가진 데 더해, 외부 IP 주소가 10.56.1.238과 10.56.0.17인 ESP 패킷일 때만 허용해요.
src 0.0.0.0/0 dst 10.56.0.0/24
dir in priority 0
mark 0xd00/0xf00
tmpl src 10.56.1.238 dst 10.56.0.17
proto esp reqid 1 mode tunnel
여기처럼 터널 모드를 사용할 때는 XFRM OUT 정책의 템플릿과 매칭되는 XFRM 상태가 항상 있어야 한다는 점에 주의하세요. 그 이유는 egress에서 상태가 템플릿이 적용된 뒤에 매칭되기 때문이에요. 그 경우 XFRM 상태와 템플릿 사이에 IP 주소, SPI, 프로토콜, 모드, reqid가 모두 일치해야 해요.
XFRM 패킷 흐름 (XFRM Packet Flows)
다음 다이어그램은 XFRM 요소를 보라색으로 표시한 일반적인 Netfilter 패킷 흐름을 나타내요.
Egress 패킷 흐름 (Egress Packet Flow)
egress에서 패킷은 먼저 "XFRM OUT policy" 블록 중 하나를 만나요. 이 시점에 XFRM OUT 정책에 대한 조회가 수행돼요. 매칭된 것이 있으면 패킷은 "XFRM encode" 블록으로 가고, 템플릿(예: 캡슐화)이 적용된 뒤 XFRM 상태와 매칭돼요. 상태가 발견되면 그 정보로 패킷을 암호화해요.
암호화된 패킷은 다시 OUTPUT과 POSTROUTING 체인을 통과해요.
Ingress 패킷 흐름 (Ingress Packet Flow)
ingress에서 암호화된 패킷(예: ESP 패킷)은 INPUT 체인을 거친 뒤 "XFRM decode"를 만나요.
터널 모드에서 암호화된 패킷은 일반적으로 서버의 IP 주소 중 하나를 외부 목적지 주소로 가지므로 자동으로 INPUT 체인으로 라우팅돼야 해요. 그렇지 않다면 INPUT 체인으로 패킷을 리다이렉트하도록 IP 라우트를 추가해야 할 수 있어요. 예를 들어 Cilium은 tc-bpf ingress에서 IPsec 트래픽을 식별해 특별한 값으로 마크하고, 그 값을 이용해 해당 패킷들을 INPUT 체인으로 재라우팅해요.
"XFRM decode"에서 패킷이 XFRM 상태와 매칭되면 그 상태의 정보를 이용해 복호화(디캡슐화·복호화)돼요. 매칭은 소스·목적지 주소, 마크, SPI, 프로토콜을 기준으로 해요. 복호화 오류(예: 잘못된 키)가 발생하면 패킷은 드롭되고 오류 카운터가 증가해요.
다이어그램에 나와 있듯이 복호화가 일어나는 데 패킷과 매칭되는 XFRM 정책은 필수가 아니지만(바로 "XFRM decode"로 가요), 패킷이 로컬 프로세스로 진행되거나 FORWARD 체인을 통과하려면 필요해요. 선택적 템플릿(즉 level use)을 가진 XFRM 정책은 복호화된 모든 패킷을 통과시켜요. 암호화된 적이 없어서 "XFRM decode"를 거치지 않는 트래픽은 기본적으로 허용돼요.
패킷이 복호화된 뒤에는 처음 수신된 인터페이스에서 온 것처럼 스택에 재순환(recirculate)돼요. 더 정확히는 tc 계층 이전에 재순환되어, 패킷이 tc-bpf 훅에 두 번 보이게 돼요(복호화 전 한 번, 후 한 번). 재순환 시 패킷 마크는 보존되므로, 복호화·재순환된 패킷을 식별하고 추적할 수 있어요.
ip xfrm 출력 설명 (Output Description of ip xfrm)
출력은 iproute2-6.1.0 기준이에요. 최신 버전에서는 필드가 더 추가될 가능성이 커요. 예를 들어 XFRM 상태는 최신 커널(v6.10+)에서 dir 필드를 갖는데, 언젠가 ip xfrm state 출력에 나타날 가능성이 있어요.
ip xfrm 출력에서 정책은 생성 날짜 순으로 정렬되며, 새 정책이 위에 있어요. 이는 패킷에 매칭되는 두 정책의 우선순위가 같을 때 최신 정책이 사용되기 때문에 중요해요.
$ ip xfrm policy
# - `src 0.0.0.0/0` is the CIDR to match against the source IP address
# - `dst 0.0.0.0/0` is the CIDR to match against the destination IP address
src 0.0.0.0/0 dst 0.0.0.0/0 uid 0
# - `dir fwd` states the direction. It defines where in the Linux stack this policy will be
# used, between ingress, egress, and forwarding.
# - `action allow` is the action to take on matching packets. Packets can only be allowed
# through (by default) or dropped.
# - `index 18` is used to differentiate between different policies which might have the
# same or overlapping selectors. If not given or if it already exists, it is
# automatically (re-)generated (cf., `xfrm_gen_index`). The three LSBs encode the
# direction (ex., 1 for `XFRM_POLICY_OUT`). The MSBs are simply incremented by one (that
# is, the index is incremented by 8) until a free index is found.
# - `priority 2975` states the priority for this policy in case multiple could match the
# packet. 0 is the highest priority.
# - `share any` is always set to `any` and unused today
# (https://elixir.bootlin.com/linux/v6.9.5/source/net/xfrm/xfrm_user.c#L1914).
# - `flag (0x00000000)` set of flags for XFRM policies. Only `XFRM_POLICY_ICMP` (0x2) is
# supported at the moment; `XFRM_POLICY_LOCALOK` (0x1) is not implemented (anymore?).
# When `XFRM_POLICY_ICMP` is given, the policy will also apply to ICMP packet with a
# payload packet that matches the policy's selector.
dir fwd action allow index 18 priority 2975 share any flag (0x00000000)
lifetime config:
# Various limits and expiration times for the policy, based on the number of bytes
# received, the number of packets received, the time since the policy was added, or the
# time since the policy was last matched by a packet. When a soft limit or expiration
# time is reached, a notification is sent to userspace via netlink
# (`struct xfrm_user_expire`). When a hard limit or expiration time is reached, the
# policy is deleted.
limit: soft (INF)(bytes), hard (INF)(bytes)
limit: soft (INF)(packets), hard (INF)(packets)
expire add: soft 0(sec), hard 0(sec)
expire use: soft 0(sec), hard 0(sec)
lifetime current:
# Counters for bytes and packets matched by this policy, to be used if limits have
# been set.
0(bytes), 0(packets)
# Timestamps for when the policy was added and when it was last matched by a packet, to
# be used if expiration times have been set.
add 2024-06-17 11:24:49 use 2024-06-17 11:25:01
# - `src 0.0.0.0` See Policy Templates for how this field is used.
# - `dst 10.92.0.164` See Policy Templates for how this field is used.
tmpl src 0.0.0.0 dst 10.92.0.164
# - `proto esp` See Policy Templates for how this field is used.
# - `spi 0x00000000(0)` See Policy Templates for how this field is used.
# - `reqid 1(0x00000001)` See Policy Templates for how this field is used.
# - `mode tunnel` See Policy Templates for how this field is used.
proto esp spi 0x00000000(0) reqid 1(0x00000001) mode tunnel
# - `level use` is the nonsensical way to indicate this template is optional, the
# alternative being `level required`. If no XFRM state matching the template is
# found, the template will be skipped if optional. Otherwise, the packet will be
# dropped with `XfrmInTmplMismatch`.
# - `share any` is not implemented and will always be `any`.
level use share any
# - `enc-mask ffffffff` Bit mask defining the list of allowed encryption algorithms.
# See Encryption algorithms in include/uapi/linux/pfkeyv2.h for the list of possible
# values.
# - `auth-mask ffffffff` Bit mask defining the list of allowed authentication
# algorithms. See Authentication algorithms in include/uapi/linux/pfkeyv2.h for the
# list of possible values.
# - `comp-mask ffffffff` Non-implemented bit mask (was probably defined for compression
# algorithms).
enc-mask ffffffff auth-mask ffffffff comp-mask ffffffff
$ ip xfrm state
# - `src 10.92.1.189` is the IP address to match against the packets' source IP addresses.
# - `dst 10.92.0.164` is the IP address to match against the packets' destination IP addresses.
src 10.92.1.189 dst 10.92.0.164
# - `proto esp` states the IPsec protocol to use.
# - `spi 0x00000000(0)` is the Security Parameter Index. A tag to distinguish between
# multiple IPsec streams that may be using different algorithms and/or keys. Particularly
# useful during key rotations.
# - `reqid 1(0x00000001)` is an ID only used to ensure the XFRM policy template and the
# state match. It doesn't seem to be used for anything else in the kernel.
# - `mode tunnel` states whether the packet is encapsulated (`tunnel`) or if the ESP header
# is simply added to the existing packet (`transport`).
proto esp spi 0x00000003(3) reqid 1(0x00000001) mode tunnel
# - `replay-window 0` size of the replay window used for the anti-replay checks (i.e.,
# toleration setting).
# - `seq 0x000000000`
# - `flag (0x000000000)` holds various flags including `XFRM_STATE_ESN` (0x80) for ESN
# mode.
replay-window 0 seq 0x00000000 flag (0x00000000)
# - `mark 0x4db50d00/0xffff0f00` are the value and mask used to match against the packets'
# marks.
# - `output-mark 0xd00/0xffffff00` are the value and mask to apply to the packets' marks
# after they have been encrypted or decrypted.
mark 0x4db50d00/0xffff0f00 output-mark 0xd00/0xffffff00
# - `aead rfc4106(gcm(aes))` are the type and name of algorithm in use.
# - `0x856f15d0ccabe682286b4286bccf5d595b88b168 (160 bits)` is the key and its size. It's
# of course sensitive information that should be treated as such.
# - `128` is the ICV length. Which lengths are supported depends on the algorithm in use.
aead rfc4106(gcm(aes)) 0x856f15d0ccabe682286b4286bccf5d595b88b168 (160 bits) 128
# - `seq 0x0` holds the current receive-side sequence number, for the anti-replay check.
# - `oseq 0x0` is the last emitted sequence number. If this number overflows (on 32-bits),
# packets are dropped and the error counter `XfrmOutStateSeqError` is increased. In ESN
# mode, this sequence number is coded on 64-bits.
# - `bitmap 0x00000000` tracks the sequence numbers that have already been seen in the replay
# window.
anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
# - `sel src 0.0.0.0/0 dst 0.0.0.0/0` is an additional filter applying to the decrypted
# packets, to ensure the inner packets are coming and going where you expect.
# - `uid 0` this field appears to be unused (`user` in `struct xfrm_selector`).
sel src 0.0.0.0/0 dst 0.0.0.0/0 uid 0
lifetime config:
# Various limits and expiration times for the state, based on the number of bytes
# received, the number of packets received, the time since the state was added, or the
# time since the state was last used for a packet. When a soft limit or expiration time
# is reached, a notification is sent to userspace via netlink
# (`struct xfrm_user_expire`). When a hard limit or expiration time is reached, the state
# is deleted.
limit: soft (INF)(bytes), hard (INF)(bytes)
limit: soft (INF)(packets), hard (INF)(packets)
expire add: soft 0(sec), hard 0(sec)
expire use: soft 0(sec), hard 0(sec)
lifetime current:
# Counters for bytes and packets matched by this policy, to be used if limits have been
# set.
20124(bytes), 83(packets)
# Timestamps for when the policy was added and when it was last matched by a packet, to
# be used if expiration times have been set.
add 2024-06-17 11:15:48 use 2024-06-17 11:16:02
stats:
# - `replay-window 0` is incremented whenever a packet is received with a sequence number
# outside the window.
# - `replay 0` is incremented whenever a packet is received with a sequence number in the
# replay window that was already observed.
# - `failed 0` (full name `integrity_failed` on kernel's side) is incremented when the
# checksums for authentication or encryption headers are incorrect.
# `XfrmInStateProtoError` is always incremented when this counter is incremented.
replay-window 0 replay 0 failed 0
XFRM 오류 (XFRM Errors)
모든 XFRM 오류는 패킷 드롭에 해당해요. 일부는 상태별 카운터 증가와도 연관돼요. 이 오류 카운터를 /proc/net/xfrm_stat에서 보려면 CONFIG_XFRM_STATISTICS가 필요해요.
- XfrmInError: 암호화 중 커널이 메모리 할당에 실패했을 때 발생해요.
- XfrmInBufferError:
- 패킷이 너무 많은 XFRM 상태를 거칠 때 발생해요. 최대값은
XFRM_MAX_DEPTH(6)로 정해져 있어요. - 패킷에 적용되는 XFRM 정책 템플릿이 너무 많을 때 발생해요. 최대값도
XFRM_MAX_DEPTH(6)예요.
- 패킷이 너무 많은 XFRM 상태를 거칠 때 발생해요. 최대값은
- XfrmInHdrError:
- 패킷의 SPI 부분이 잘못된 형식일 때 발생해요.
- 외부 IP 헤더가 잘못된 형식일 때 발생해요.
- XfrmInNoStates: INPUT 체인으로 수신되는 AH 또는 ESP 패킷과 매칭되는 XFRM IN 상태가 없을 때 발생해요.
- XfrmInStateProtoError:
- AH 또는 ESP 체크섬이 올바르지 않을 때 발생해요.
- 패킷의 IPsec 프로토콜(예: AH, ESP)이 XFRM 상태가 지정한 프로토콜과 일치하지 않을 때 발생해요.
- 아래에 나열된 모든 프로토콜별 오류(예:
esp_input에서 발생하는 오류)도 포함해요. - 복호화/암호화가 실패할 때(예: XFRM IN 상태에 지정된 키가 패킷이 암호화된 키와 일치하지 않을 때) 발생해요.
- 프로토콜 헤더(예: ESP)나 트레일러가 잘못된 형식일 때 발생해요.
- 암호화/복호화를 수행할 메모리가 부족할 때 발생해요.
- XfrmInStateModeError: 패킷이 IPsec 터널 모드인데 매칭된 XFRM 상태가 transport 모드일 때 발생해요.
- XfrmInStateSeqError: anti-replay 검사가 패킷을 거부했을 때 발생해요. 시퀀스 번호가 윈도우 밖에 있어서 실패했다면 관련 XFRM 상태의 replay-window 카운터가 증가해요. 이미 본 시퀀스 번호여서 실패했다면 대신 replay 카운터가 증가해요.
- XfrmInStateExpired: 상태가 만료(hard limits)된 시점과 실제 삭제 사이에 지연이 있을 수 있어요. 그 사이에 ingress에서 매칭되는 패킷은
XfrmInStateExpired로 드롭돼요. - XfrmInStateMismatch:
- XFRM 상태의 캡슐화 프로토콜(
ip xfrm state의 encap 필드에 있는espinudp등)이 패킷의 캡슐화 프로토콜과 일치하지 않을 때 발생해요. - 복호화된 패킷이 사용된 XFRM 상태의 셀렉터(
sel필드)와 일치하지 않을 때 발생해요.
- XFRM 상태의 캡슐화 프로토콜(
- XfrmInStateInvalid: 수신 패킷이 삭제 중이거나 만료된 XFRM 상태와 매칭됐을 때 발생해요.
- XfrmInTmplMismatch:
- 패킷이 선택 불가(non-optional) 템플릿을 가진 XFRM 정책과 매칭됐는데, 그 템플릿이 패킷 복호화에 사용된 XFRM 상태 중 어떤 것과도 매칭되지 않을 때 발생해요(네, 패킷은 여러 번 복호화될 수 있어요).
- 모드가
tunnel인 XFRM 상태가 패킷에 사용됐는데 어떤 XFRM 정책 템플릿과도 매칭되지 않을 때 발생해요.
- XfrmInNoPols: 수신 패킷이 어떤 XFRM 정책과도 매칭되지 않고 기본 동작이 block으로 설정돼 있을 때 발생해요. 기본 XFRM 정책 동작을 보거나 설정하려면
ip xfrm policy {get,set}default를 사용하세요. - XfrmInPolBlock: 패킷이 action이 block인 XFRM IN 정책과 매칭됐을 때 발생해요.
- XfrmOutError:
- 암호화 중 커널이 메모리 할당에 실패했을 때 발생해요.
- 어떤 경우에는 암호화할 패킷이 잘못된 형식일 때 발생해요.
- XfrmOutBundleCheckError: 사용되지 않아요.
- XfrmOutNoStates: 패킷이 XFRM OUT 정책과 매칭됐지만 정책 템플릿과 매칭되는 XFRM 상태를 찾지 못했을 때 발생해요.
- XfrmOutStateProtoError: 프로토콜 특정(예: ESP) 암호화 오류가 발생했을 때 발생해요.
- XfrmOutStateModeError: 패킷이 캡슐화되면 MTU를 초과하는데 분할(fragment)되면 안 될 때 발생해요.
- XfrmOutStateSeqError: XFRM 상태의 출력 시퀀스 번호(oseq)가 최댓값에 도달했을 때 발생해요. ESN 모드가 아니면
UINT32_MAX예요. - XfrmOutStateExpired: 상태가 만료(hard limits)된 시점과 실제 삭제 사이에 지연이 있을 수 있어요. 그 사이에 egress에서 매칭되는 패킷은
XfrmOutStateExpired로 드롭돼요. - XfrmOutPolBlock: 패킷이 action이 block인 XFRM OUT 정책과 매칭됐을 때 발생해요.
- XfrmOutPolDead: 사용되지 않아요. 삭제 과정에 있는 XFRM 상태에 대해서는 대신
XfrmOutStateInvalid가 보고돼요. - XfrmOutPolError:
- 패킷에 적용되는 XFRM 정책 템플릿이 너무 많을 때 발생해요. 최대값은
XFRM_MAX_DEPTH(6)예요. - 매칭된 XFRM 정책의 선택 불가 템플릿에 대한 XFRM 상태를 찾지 못했을 때 발생해요.
- 패킷에 적용되는 XFRM 정책 템플릿이 너무 많을 때 발생해요. 최대값은
- XfrmFwdHdrError: FWD 정책 검사를 통과할 때 패킷이 잘못된 형식일 때 발생해요.
- XfrmOutStateInvalid: egress 패킷이 삭제 중이거나 만료된 XFRM 상태와 매칭됐을 때 발생해요.
- XfrmOutStateDirError: 조회 중 발견된 XFRM 상태의 방향이 정의되어 있는데
XFRM_SA_DIR_OUT이 아닐 때 발생해요. 커널 v6.10 이상에서만 발생해요. - XfrmInStateDirError: 조회 중 발견된 XFRM 상태의 방향이 정의되어 있는데
XFRM_SA_DIR_IN이 아닐 때 발생해요. 커널 v6.10 이상에서만 발생해요.
성능 고려 사항 (Performance Considerations)
이 섹션에서는 XFRM 정책과 상태를 담는 데이터 구조를 설명해요. 수많은 상태와 정책을 다룰 때 유용한데, 이 정보가 인덱싱을 개선하고 조회를 빠르게 하는 데 도움이 되기 때문이에요. 수천 개의 정책과 상태를 다룰 때는 조회 비용이 암호화/복호화 비용에 비해서도 무시할 수 없는 수준이 될 수 있어요.
XFRM 정책 데이터 구조 (Data Structure for XFRM Policies)
XFRM 정책은 여러 개의 레드-블랙 트리와 해시 테이블로 이루어진 상당히 복잡한 데이터 구조에 저장돼요. 루트에는 모든 것이 네트워크 네임스페이스, IP 패밀리, 방향, 인터페이스(XFRM 인터페이스를 사용하는 경우)로 인덱싱된 확장 가능한 해시 테이블에 들어 있어요. 이 확장 가능한 해시 테이블의 각 엔트리는 여러 개의 레드-블랙 트리를 담고 있으며, 그 트리들이 XFRM 정책을 보관해요. 이 엔트리들은 xfrm_pol_inexact_bin 구조로 표현돼요.
xfrm_pol_inexact_bin이 (현재 IP 패밀리, 네임스페이스, 방향 기준으로) 검색되면, 그 레드-블랙 트리들 각각을 소스·목적지 IP 주소로 조회해요. root_s 트리는 소스 IP 주소로 정렬된 정책을 담고 있고, root_d 트리는 목적지 IP 주소로 정렬된 정책을 담아요. 또한 root_d 트리의 리프 노드는 소스 IP 주소로 정렬된 정책을 가진 다른 트리도 담고 있어요. 이를 통해 root_s와 root_d로의 조회가 리프 노드들에서 세 개의 후보 (src_ip; dst_ip) 정책 목록을 반환할 수 있어요:
root_s에서 온 (src_ip; any) 후보 목록.root_d에서 온 (any; dst_ip) 후보 목록.root_d의 리프 노드가 가리키는 트리들에서 온 (src_ip; dst_ip) 후보 목록.
이 세 가지 후보 XFRM 정책 목록은 xfrm_pol_inexact_bin 엔트리에 직접 저장된 (any; any) 후보 목록으로 완성돼요.
참고로 XFRM 정책은 소스·목적지 CIDR에 따라 네 후보 목록 중 하나에만 존재해요.
이 네 후보 XFRM 정책 목록이 평가돼요. 커널은 각 목록을 순회하면서 패킷과 매칭되는 가장 높은 우선순위(가장 낮은 우선순위 번호) 후보를 찾아요. 두 정책이 매칭되고 우선순위가 같으면 최신 것을 선호해요. 패킷 마크와 정책 마크의 비교도 이 후보의 선형 평가 동안에만 수행돼요.
XFRM 상태 데이터 구조 (Data Structure for XFRM States)
XFRM 상태는 네 개의 해시 테이블로 구성되며, 인덱싱에 사용되는 XFRM 필드와 목적이 서로 달라요:
net->xfrm.state_bydst는 소스·목적지 IP 주소와 reqid로 인덱싱돼요.net->xfrm.state_bysrc는 소스·목적지 IP 주소로만 인덱싱돼요.net->xfrm.state_byspi는 목적지 IP 주소, SPI, 프로토콜로 인덱싱돼요.net->xfrm.state_byseq는 시퀀스 번호로만 인덱싱돼요.
net->xfrm.state_byspi는 수신 패킷에 대한 XFRM 상태 조회에 사용돼요. 각 XFRM 상태가 자신만의 SPI를 갖도록 권장되고(RFC4301, 4.1절 참고) 암호화된 패킷이 SPI를 담고 있으므로 검색을 빠르게 하기에 합리적이에요.
XFRM 정책 템플릿에 해당하는 XFRM 상태를 (암호화 전에) 검색할 때는 net->xfrm.state_bydst를 사용해요. 인덱싱 정보가 XFRM 정책 템플릿이 제공하는 것이기 때문에 합리적이에요. 이 해시 테이블은 일반적으로 모든 XFRM 상태를 순회할 때(예: 플러시할 때)도 사용되지만, 그런 용도로는 어떤 해시 테이블이든 상관없어요.
net->xfrm.state_bysrc와 net->xfrm.state_byseq는 갱신할 XFRM 상태 조회, 사용자의 netlink 쿼리 응답, 새 상태 추가 전 기존 상태 확인 같은 다양한 관리 작업에 사용돼요.