nftables에 설정한 마크는 한 부류의 트래픽을 터널로 보내야 하는데, 트래픽은 계속 물리 인터페이스로 나간다. 규칙 집합은 단독으로 보면 올바르다. 흔히 일어난 일은 다른 무언가가 같은 마크를 기록했거나, 라우팅 규칙이 새 값이 더는 만족하지 못하는 마스크와 일치하는 것이다.

확인 1: 커널이 일치시키는 규칙을 읽어라

방화벽이 아니라 정책 규칙에서 시작하라. 커널은 우선순위 순서로 규칙을 검토하고 첫 일치가 테이블을 결정한다. 규칙 자체에서 fwmark 값과 마스크를 읽고, 설정한 마크를 가진 패킷이 실제로 그것과 일치하는지 확인하라. 완전한 32비트 값을 기대하는 규칙은 단 하나의 비트만 가진 패킷과 일치하지 않는다.

그다음 그 규칙이 어느 테이블을 고르는지, 테이블에 무엇이 있는지 확인하라. 빈 테이블을 가리키는 규칙은 조회 실패를 낳고, 많은 시스템에서 이는 패킷이 다음 규칙으로 떨어져 물리 인터페이스로 나간다는 뜻이다.

두 명령이 이것을 정리한다: 규칙 목록과, 그 테이블에서 목적지에 대한 경로 조회다.

확인 2: 패킷 마크인가 연결 마크인가

nftables는 패킷에 마크를 설정할 수 있고 연결 추적 항목에도 마크를 설정할 수 있으며, 이들은 서로 다른 객체다. 패킷 마크는 패킷의 처리 단계 동안 살아 있고, 연결 마크는 흐름 동안 유지되며 의도적으로 복사해야 한다. 하나를 설정하고 다른 하나를 기대하는 것은 흔한 절반짜리 수정이다.

라우팅 규칙이 패킷 마크를 근거로 삼는다면, 경로의 뒤에서 연결 마크를 복원해도 그 결정에는 아무 효과가 없다, 결정은 이미 내려졌기 때문이다.

라우팅 신호를 어느 객체가 소유하는지 정하고, 라우팅 결정이 내려지는 지점에서 그것을 설정하라.

확인 3: 그 마크를 또 누가 기록하는가

WireGuard 자체 도구는 자신의 UDP 패킷에 마크를 붙여 암호화된 전송이 자기가 속한 터널로 다시 라우팅되지 않게 한다. 규칙 집합이 같은 필드에 무관한 값을 할당하면 그 제외가 사라지고 터널의 바깥 트래픽이 자기 자신으로 들어가려 한다. 증상은 연결된 뒤 트래픽을 통과시키지 않는 터널이다.

마크는 다른 방화벽 도구, 컨테이너 네트워킹, 다른 목적의 정책 라우팅에서도 쓰이므로, 두 하위 시스템이 필드 전체를 기록하는 것은 각각 개별적으로 옳더라도 충돌이다.

하나 더 추가하기 전에 기록자들을 나열하라.

확인 4: 마스크가 무엇을 남길지 정한다

마크는 32비트 필드이고, 마스크는 여러 소유자가 그것을 나누는 방식이다. 마스크와 함께 값을 설정하면 소유하지 않은 비트가 보존되고, 필드 전체를 설정하면 다른 모든 이의 비트가 파괴된다. 필드를 건드리는 모든 규칙에서, 자신이 쓰지 않은 규칙까지 포함해 마스크를 읽어라.

비트 할당을 규칙 집합 옆에 기록하라.

그렇지 않으면 다음 사람이 이미 소유자가 있는 값을 재사용하게 되고, 여기서의 충돌은 주소 지정 버그인데도 라우팅 버그처럼 보인다. 패킷 메타데이터에 관한 nftables 문서가 마크 연산 자체를 설명한다.

확인 5: 조회가 아니라 실제 흐름으로 검증하라

단순한 경로 조회는 마크를 지니지 않으므로 실제 패킷과 다른 경로를 보여 줄 수 있다. 관심 있는 트래픽을 생성하고 카운터, 연결 추적 항목, 실제로 나가는 인터페이스를 관찰하라. 너무 넓은 마크는 성공해 보이면서 제외를 깨뜨리기 때문에, 포함된 부류와 제외된 부류를 모두 시험하라. 포함된 부류가 동작하고 제외된 부류도 터널로 들어간다면, 마크는 너무 좁은 것이 아니라 너무 넓은 것이다.

새 흐름과 기존 흐름이 정당하게 다른 경로를 탈 수 있으므로, 활성 세션 중에 새로 고침을 강제하려고 연결 상태를 비우는 것은 피하라.

마크, 마스크, 테이블을 한곳에 기록하면 다음 편집이 검토 가능해진다.

관련 글