NAME

nft - 패킷 필터링 및 분류를 위한 nftables 프레임워크의 관리 도구

SYNOPSIS

nft [ -nNscaeSupyjt ] [ -I directory ] [ -f filename | -i | cmd ...]
nft -h
nft -v

DESCRIPTION

nft는 리눅스 커널 nftables 프레임워크의 패킷 필터링 및 분류 규칙을 설정하고 관리하고 조사하는 데 쓰는 명령행 도구다. 그 리눅스 커널 서브시스템을 nf_tables라고 하는데, 여기서 'nf'는 Netfilter를 나타낸다.

OPTIONS

옵션 요약 전체를 보려면 nft --help를 실행하면 된다.

-h, --help
도움말 메시지와 전체 옵션을 보여 준다.
-v, --version
버전을 보여 준다.
-n, --numeric
출력을 완전히 숫자로만 찍는다.
-s, --stateless
규칙과 상태 객체의 상태 정보를 생략한다.
-N, --reversedns
DNS 역질의를 통해 IP 주소를 이름으로 변환한다. 네트워크 트래픽을 발생시키므로 목록 표시가 느려질 수 있다.
-S, --service
/etc/services에 정의된 대로 포트 번호를 서비스 이름으로 변환한다.
-u, --guid
/etc/passwd/etc/group에 정의된 대로 숫자로 된 UID/GID를 이름으로 변환한다.
-p, --numeric-protocol
제4계층 프로토콜을 숫자로 표시한다.
-y, --numeric-priority
기본 체인 우선순위를 숫자로 표시한다.
-c, --check
변경 사항을 실제 적용하지 않고 명령 유효성만 확인한다.
-a, --handle
출력 내용에서 객체 핸들을 보여 준다.
-e, --echo
addinsert, replace 명령으로 룰셋에 항목을 집어넣을 때 nft monitor처럼 알림을 찍는다.
-j, --json
JSON 형식으로 출력한다. 스키마 설명은 libnftables-json(5)을 보라.
-I, --includepath directory
포함 파일을 찾을 디렉터리 목록에 디렉터리 directory를 추가한다. 이 옵션은 여러 번 지정할 수 있다.
-f, --file filename
filename에서 입력을 읽어 들인다. filename-이면 stdin에서 읽는다.
-i, --interactive
대화형 readline CLI에서 입력을 읽어 들인다. quit으로 빠져나갈 수 있다. EOF 표시를 쓸 수도 있는데, 보통 CTRL-D이다.
-T, --numeric-time
시각, 요일, 시간 값을 숫자로 보인다.
-t, --terse
출력에서 집합 내용물을 생략한다.

입력 파일 형식

구문 규약

행 단위로 입력을 파싱 한다. 개행 문자 바로 앞의 행 마지막 문자가 따옴표로 감싸지 않은 백슬래시(\)일 때는 다음 행을 계속 이어진 것처럼 처리한다. 한 행에서 여러 명령을 세미콜론(;)으로 구분할 수 있다.

해시 기호(#)로 주석이 시작된다. 그 행의 나머지 문자들을 모두 무시한다.

식별자는 알파벳 문자(a-z,A-Z)로 시작해서 0개 이상의 알파벳 문자(a-z,A-Z), 숫자(0-9), 슬래시(/), 백슬래시(\), 밑줄(_), 마침표(.) 문자가 온다. 다른 문자를 쓰거나 키워드와 충돌하는 식별자는 큰따옴표(")로 감싸 줘야 한다.

파일 포함하기

include filename

include 문을 써서 다른 파일을 포함할 수 있다. 포함 파일을 찾을 디렉터리들을 -I/--includepath 옵션으로 지정할 수 있다. 또한 경로 앞에 './'를 붙여서 현재 작업 디렉터리에 위치한 파일을 (즉 상대 경로로) 포함하도록 강제하거나 '/'를 써서 절대 경로로 파일 위치를 나타낼 수도 있다.

-I/--includepath를 지정하지 않으면 nft는 컴파일 시점에 지정된 기본 디렉터리를 이용한다. -h/--help 옵션을 통해 그 기본 디렉터리를 알아낼 수 있다.

include 문은 일반적인 셸 와일드카드 기호(*,?,[])를 지원한다. include 문에 와일드카드 기호를 쓴 경우에는 include 문에 일치하는 파일이 없어도 오류가 아니다. 그래서 include "/etc/firewall/rules/*" 같은 문으로 포함한 디렉터리가 비어 있을 수도 있다. 와일드카드에 걸린 항목들은 알파벳 순서로 올라온다. 마침표(.)로 시작하는 파일은 include 문에 걸리지 않는다.

심볼 변수

define variable = expr
$variable

define 문을 써서 심볼 변수를 정의할 수 있다. 변수 참조는 식이며 이를 이용해 다른 변수를 초기화 할 수 있다. 정의 유효 범위는 현재 블록과 그 안에 포함된 모든 블록이다.

심볼 변수 사용하기

define int_if1 = eth0
define int_if2 = eth1
define int_ifs = { $int_if1, $int_if2 }

filter input iif $int_ifs accept

주소 패밀리

주소 패밀리에 따라 어떤 종류의 패킷이 처리되는지 정해진다. 각 주소 패밀리별로 커널 패킷 처리 경로의 특정 지점들에 소위 훅이 있어서 그 훅에 대한 규칙이 존재하면 nftables를 호출한다.

ip
IPv4 주소 패밀리
ip6
IPv6 주소 패밀리
inet
인터넷(IPv4/IPv6) 주소 패밀리
arp
ARP 주소 패밀리, IPv4 ARP 패킷 처리
bridge
브리지 주소 패밀리, 브리지 장치를 통과하는 패킷 처리
netdev
netdev 주소 패밀리, 진입점에서 패킷 처리

모든 nftables 객체는 주소 패밀리별 네임스페이스 안에 존재하며, 그래서 모든 식별자에는 주소 패밀리가 포함돼 있다. 주소 패밀리 없이 식별자를 지정하면 기본적으로 ip 패밀리를 쓴다.

IPv4/IPv6/Inet 주소 패밀리

IPv4/IPv6/Inet 주소 패밀리는 IPv4 패킷, IPv6 패킷, 그리고 두 종류 모두를 다룬다. 네트워크 스택의 패킷 처리 단계 다섯 곳에 훅이 있다.

표 1: IPv4/IPv6/Inet 주소 패밀리 훅

설명
prerouting 시스템에 들어오는 모든 패킷이 prerouting 훅에서 처리된다. 라우팅 처리 전에 호출되며 이르게 필터링을 하거나 라우팅에 영향을 주는 패킷 속성을 바꾸는 데 쓰인다.
input 로컬 시스템으로 전달되는 패킷이 input 훅에서 처리된다.
forward 다른 호스트로 전달되는 패킷이 forward 훅에서 처리된다.
output 로컬 프로세스가 보내는 패킷이 output 훅에서 처리된다.
postrouting 시스템을 떠나는 모든 패킷이 postrouting 훅에서 처리된다.

ARP 주소 패밀리

ARP 주소 패밀리는 시스템이 받고 보내는 ARP 패킷들을 다룬다. 클러스터링을 위해 ARP 패킷을 조작하는 데 흔히 쓴다.

표 2: ARP 주소 패밀리 훅

설명
input 로컬 시스템으로 전달되는 패킷이 input 훅에서 처리된다.
output 로컬 시스템에서 보내는 패킷이 output 훅에서 처리된다.

브리지 주소 패밀리

브리지 주소 패밀리는 브리지 장치를 통과하는 이더넷 패킷을 다룬다.

지원하는 훅 목록은 위의 IPv4/IPv6/Inet 주소 패밀리와 동일하다.

Netdev 주소 패밀리

Netdev 주소 패밀리는 진입점(ingress)에서 패킷을 처리한다.

표 3: Netdev 주소 패밀리 훅

설명
ingress 시스템에 들어오는 모든 패킷이 이 훅에서 처리된다. 제3계층 프로토콜 핸들러 전에 호출되며 이른 필터링이나 폴리싱에 쓸 수 있다.

룰셋

{list | flush} ruleset [family]

현재 커널 내에 위치한 테이블, 체인 등의 세트 전체를 나타내는 데 ruleset 키워드를 쓴다. 다음 ruleset 명령이 있다.

list
사람이 읽기 좋은 형식으로 룰셋을 출력한다.
flush
룰셋 전체를 비운다. iptables와 달리 모든 테이블과 그 안에 담긴 모든 걸 제거한다는 점에 유의해야 한다. 실질적으로 빈 룰셋이 되며, 어떤 패킷 필터링도 일어나지 않게 되므로 커널에서는 수신한 모든 유효 패킷을 받아들인다.

listflush를 특정 주소 패밀리로 한정할 수도 있다. 유효한 패밀리 이름의 목록은 위의 "주소 패밀리" 절을 보라.

설계상 list ruleset 명령의 출력을 nft -f 입력으로 쓸 수 있다. 실질적으로 iptables-saveiptables-restore에 대응한다.

테이블

{add | create} table [family] table [{ flags flags ; }]
{delete | list | flush} table [family] table
list tables [tables]
delete table [family] handle handle

테이블은 체인, 집합, 상태 객체를 담는 컨테이너다. 주소 패밀리와 이름으로 식별된다. 주소 패밀리는 ip, ip6, inet, arp, bridge, netdev 중 하나여야 한다. inet 주소 패밀리는 하이브리드 IPv4/IPv6 테이블을 만드는 데 쓰는 가상 패밀리다. meta expression nfproto 키워드를 쓰면 패킷이 (IPv4와 IPv6 중) 어느 패밀리 맥락에 있는지 검사할 수 있다. 주소 패밀리를 지정하지 않으면 기본으로 ip를 쓴다. add와 create의 유일한 차이는 지정한 테이블이 이미 존재하는 경우에 전자는 오류를 반환하지 않는 반면 create은 오류를 반환한다는 점이다.

표 4: 테이블 플래그

플래그 설명
dormant 테이블을 더이상 평가하지 않는다. (기본 체인들을 등록 해제한다.)

테이블 추가, 변경, 삭제

# 대화형으로 nft 시작
nft --interactive

# 새 테이블 생성
create table inet mytable

# 새 기본 체인 추가: 입력 패킷 받기
add chain inet mytable myin { type filter hook input priority 0; }

# 체인에 카운터 하나 추가
add rule inet mytable myin counter

# 테이블을 잠시 비활성화 -- 더는 규칙들이 평가되지 않음
add table inet mytable { flags dormant; }

# 테이블을 다시 활성화
add table inet mytable
add
지정한 이름으로 지정한 패밀리에 새 테이블 추가.
delete
지정한 테이블 삭제.
list
지정한 테이블의 모든 체인 및 규칙 나열.
flush
지정한 테이블의 모든 체인 및 규칙 비우기.

체인

{add | create} chain [family] table chain [{ type type hook hook [device device] priority priority ; [policy policy ;] }]
{delete | list | flush} chain [family] table chain
list chains [family]
delete chain [family] table handle handle
rename chain [family] table chain newname

체인은 규칙들을 담는 컨테이너다. 두 가지 종류가 있는데, 기본 체인과 일반 체인이다. 기본 체인은 네트워킹 스택에서 패킷이 진입하는 지점이다. 일반 체인은 점프 대상으로 쓸 수 있으며 규칙들로 구조를 만드는 데 쓴다.

add
지정한 테이블에 새 체인 추가. 훅과 우선순위 값을 지정하면 기본 체인으로 만들어서 네트워킹 스택에 연결한다.
create
add 명령과 비슷하되 체인이 이미 존재하면 오류를 반환한다.
delete
지정한 체인 삭제. 체인에 어떤 규칙도 없어야 하고 점프 대상으로 쓰이고 있지 않아야 한다.
rename
지정한 체인의 이름 변경.
list
지정한 체인의 모든 규칙 나열.
flush
지정한 체인의 모든 규칙 비우기.

기본 체인에선 type, hook, priority 매개변수가 필수다.

표 5: 지원하는 체인 타입

타입 패밀리 설명
filter 모두 모두 긴가민가할 때 쓰면 되는 표준 체인 타입.
nat ip, ip6, inet prerouting, input, output, postrouting 이 체인 타입에서는 conntrack 항목에 따라 네트워크 주소 변환을 수행한다. 연결의 첫 번째 패킷만 실제로 이 체인을 거친다. 체인 규칙에서는 일반적으로 생성되는 conntrack 항목의 세부 사항(예를 들어 NAT 문)을 규정한다.
route ip, ip6 output 패킷이 이 체인 타입을 거치고서 허용되는 경우에 IP 헤더의 관련 부분이 변경됐으면 라우트 검색을 새로 수행한다. 이를 이용해 가령 nftables에서 정책 라우팅을 할 수 있다.

위에 설명한 특별한 경우들(가령 nat에서 forward 훅을 지원하지 않거나 route에서 output 훅만 지원하는 것) 외에도 신경써야 할 특이 사항이 두 가지 더 있다.

priority 매개변수는 같은 hook 값의 체인들을 거치는 순서를 나타내는 부호 있는 정수 값 또는 표준 우선순위 이름을 받는다. 순서는 오름차순이다. 즉 낮은 우선순위 값이 높은 값보다 우선도가 높다.

표준 우선순위 값들 대신 쉽게 기억할 수 있는 이름을 쓸 수 있다. 모든 이름이 각 패밀리의 모든 훅에서 통하는 건 아니지만 (아래 호환성 표 참고) 그래도 그 숫자 값은 체인 우선순위 지정에 사용할 수 있다.

그 이름과 값들은 기본 체인 등록 때 xtables에서 쓰는 우선순위에 따라 정의되고 사용 가능해진다.

대부분의 패밀리에서 같은 값을 쓰지만 브리지는 다른 값을 쓴다. 값과 호환성을 기술하는 다음 두 표를 참고하라.

표 6: 표준 우선순위 이름, 패밀리, 훅 호환성 표

이름 패밀리
raw -300 ip, ip6, inet 모두
mangle -150 ip, ip6, inet 모두
dstnat -100 ip, ip6, inet prerouting
filter 0 ip, ip6, inet, arp, netdev 모두
security 50 ip, ip6, inet 모두
srcnat 100 ip, ip6, inet postrouting

표 7: 브리지 패밀리의 표준 우선순위 이름과 훅 호환성

이름
dstnat -300 prerouting
filter -200 모두
out 100 output
srcnat 300 postrouting

이 표준 이름에 간단한 산술 연산(더하기와 빼기)을 해서 상대 우선순위를 쉽게 지정할 수도 있다. 가령 mangle - 5-155를 나타낸다. 값을 찍을 때도 표준 값에서 10 넘게 차이가 나지 않으면 그런 식으로 찍는다.

기본 체인에는 체인 policy를 설정할 수도 있다. 즉 안에 담긴 규칙들에서 명시적으로 허용하지도 않고 거부하지도 않은 패킷이 어떻게 되는가이다. 지원하는 정책 값은 accept(기본)와 drop이다.

규칙

{add | insert} rule [family] table chain [handle handle | index index] statement ... [comment comment]
replace rule [family] table chain handle handle statement ... [comment comment]
delete rule [family] table chain handle handle

지정한 테이블의 체인에 규칙이 추가된다. 패밀리를 지정하지 않으면 ip 패밀리를 쓴다. 일군의 문법 규칙에 따라 식(expression)과 문(statement)이라는 두 가지 요소로 규칙이 구성된다.

add와 insert 명령에서는 선택적으로 위치 지정이 가능한데, 기존 규칙의 handle이나 (0에서 시작하는) index로 지정한다. 내부적으로는 항상 handle로 규칙 위치를 나타내며 index에서 변환하는 건 사용자 공간에서 이뤄진다. 때문에 변환이 이뤄진 후 동시에 룰셋 변경이 일어나는 경우 영향이 있을 수도 있다. 즉 참조 대상 규칙 앞에서 규칙이 삽입 내지 삭제되면 실제 규칙 인덱스가 바뀔 수 있다. 그리고 참조 대상 규칙이 삭제되면 유효하지 않은 handle을 준 경우처럼 명령을 커널에서 거부한다.

comment는 한 단어거나 큰 따옴표(")로 감싼 여러 단어 문자열이며 실제 규칙과 관련된 메모를 하는 데 쓸 수 있다. 주의: 규칙 추가 시 bash를 쓴다면 따옴표에 이스케이프를 해 줘야 한다. 예: \"enable ssh for servers\".

add
문 목록으로 나타낸 새 규칙을 추가. 위치를 지정하지 않으면 지정한 체인에 규칙을 덧붙이고, 지정한 경우에는 지정한 규칙 뒤에 규칙을 삽입한다.
insert
add와 같되 체인의 처음이나 지정한 규칙 앞에 규칙을 삽입.
replace
add와 비슷하되 지정한 규칙을 교체.
delete
지정한 규칙 삭제.

ip 테이블 input 체인에 규칙 추가

nft add rule filter output ip daddr 192.168.0.0/24 accept # 'ip filter' 상정
# 같은 명령을 좀 더 길게 쓰기
nft add rule ip filter output ip daddr 192.168.0.0/24 accept

inet 테이블에서 규칙 삭제

# nft -a list ruleset
table inet filter {
        chain input {
                type filter hook input priority 0; policy accept;
                ct state established,related accept # handle 4
                ip saddr 10.1.1.1 tcp dport ssh accept # handle 5
          ...
# 핸들이 5인 규칙 삭제하기
# nft delete rule inet filter input handle 5

집합

nftables에는 두 가지 집합 개념이 있다. 익명 집합은 따로 이름이 없는 집합이다. 집합을 쓰는 규칙을 만들 때 집합 멤버들을 중괄호로 감싸고 쉼표로 원소들을 구분한다. 그 규칙이 제거되면 집합도 제거된다. 이 집합은 갱신이 불가능하다. 즉 익명 집합은 일단 선언하고 나면 그 익명 집합을 쓰는 규칙을 제거/변경하지 않고는 변경할 수 없다.

익명 집합 이용해 특정 서브넷 및 포트 허용하기

nft add rule filter input ip saddr { 10.0.0.0/8, 192.168.0.0/16 } tcp dport { 22, 443 } accept

기명 집합은 규칙에서 참조하기 전에 먼저 정의해야 한다. 익명 집합과 달리 언제든 기명 집합의 원소를 추가하거나 제거할 수 있다. 규칙에서 집합 이름 앞에 @를 붙여서 집합을 참조한다.

기명 집합 이용해 주소 및 포트 허용하기

nft add rule filter input ip saddr @allowed_hosts tcp dport @allowed_ports accept

집합 allowed_hosts와 allowed_ports가 먼저 만들어져 있어야 한다. 다음 절에서 nft set 문법을 더 자세히 설명한다.

add set [family] table set { type type ; [flags flags ;] [timeout timeout ;] [gc-interval gc-interval ;] [elements = { element[, ...] } ;] [size size ;] [policy policy ;] [auto-merge ;] }
{delete | list | flush} set [family] table set
list sets [family]
delete set [family] table handle handle
{add | delete} element [family] table set { element[, ...] }

집합은 사용자 정의 데이터 타입인 원소 컨테이너다. 사용자 지정 이름으로 유일하게 식별되며 테이블에 붙는다. 집합 생성 시점에 지정할 수 있는 플래그들로 동작을 조정할 수 있다.

add
지정한 테이블에 새 집합 추가. 집합 속성을 지정하는 방법에 대해선 아래의 집합 지정 표 참고.
delete
지정한 집합 삭제.
list
지정한 집합의 원소 표시.
flush
지정한 집합의 모든 원소 제거.
add element
지정한 집합에 쉽표 구분 목록의 원소들을 추가.
delete element
지정한 집합에서 쉼표 구분 목록의 원소들을 삭제.

표 8: 집합 지정

키워드 설명 타입
type 집합 원소의 데이터 타입 문자열: ipv4_addr, ipv6_addr, ether_addr, inet_proto, inet_service, mark
flags 집합 플래그 문자열: constant, dynamic, interval, timeout
timeout 집합에서 원소가 유지되는 시간. 집합이 패킷 경로(룰셋)로부터 추가되는 경우 필수. 문자열, 십진수에 단위 붙음. 단위: d, h, m, s
gc‑interval 가비지 컬렉션 간격. timeout이나 timeout 플래그가 활성일 때만 사용 가능. 문자열, 십진수에 단위 붙음. 단위: d, h, m, s
elements 집합에 담기는 원소들 집합 데이터 타입
size 집합의 최대 원소 수. 집합이 패킷 경로(룰셋)로부터 추가되는 경우 필수. 부호 없는 정수 (64비트)
policy 집합 정책 문자열: performance [기본], memory
auto‑merge 인접/중첩 집합 원소 자동 병합 (interval 집합에만)

add map [family] table map { type type [flags flags ;] [elements = { element[, ...] } ;] [size size ;] [policy policy ;] }
{delete | list | flush} map [family] table map
list maps [family]
{add | delete} element [family] table map { elements = { element[, ...] } ; }

맵은 입력으로 하는 어떤 특정 키에 따라 데이터를 저장한다. 사용자 지정 이름으로 유일하게 식별되며 테이블에 붙는다.

add
지정한 테이블에 새 맵 추가.
delete
지정한 맵 삭제.
list
지정한 맵의 원소 표시.
flush
지정한 맵의 모든 원소 제거.
add element
지정한 맵에 쉼표 구분 목록의 원소들을 추가.
delete element
지정한 맵에서 쉼표 구분 목록의 원소들을 삭제.

표 9: 맵 지정

키워드 설명 타입
type 맵 원소의 데이터 타입 문자열 ':' 문자열: ipv4_addr, ipv6_addr, ether_addr, inet_proto, inet_service, mark, counter, quota. counter과 quota는 키로 쓸 수 없음
flags 맵 플래그 문자열: constant, interval
elements 맵에 담기는 원소들 맵 데이터 타입
size 맵의 최대 원소 수 부호 없는 정수 (64비트)
policy 맵 정책 문자열: performance [기본], memory

플로테이블

{add | create} flowtable [family] table flowtable { hook hook priority priority ; devices = { device[, ...] } ; }
list flowtables [family]
{delete | list} flowtable {family] table flowtable
delete flowtable [family] table handle handle

플로테이블을 통해 소프트웨어에서 패킷 포워딩 속도를 높일 수 있다. 입력 인터페이스, 출발 및 목적 주소, 출발 및 목적 포트, 제3/4계층 프로토콜로 이뤄진 튜플을 통해 플로테이블 항목을 나타낸다. 각 항목에는 또한 패킷을 포워딩 하기 위한 목적 인터페이스와 (링크 계층 목적 주소를 갱신하기 위한) 게이트웨이 주소를 캐싱 한다. ttl 및 hoplimit 필드도 줄어든다. 그래서 플로테이블은 패킷이 전통적 포워딩 경로를 우회할 수 있는 또 다른 경로를 제공한다. 플로테이블은 prerouting 훅 전에 있는 ingress 훅에 위치한다. forward 체인에서 flow 식을 통해 오프로드 하고 싶은 흐름을 선택할 수 있다. 플로테이블은 주소 패밀리와 이름으로 식별된다. 주소 패밀리는 ip, ip6, inet 중 하나여야 한다. inet 주소 패밀리는 하이브리드 IPv4/IPv6 테이블을 만드는 데 쓰는 가상의 패밀리다. 패밀리를 지정하지 않으면 기본으로 ip를 쓴다.

priority는 부호 있는 정수나 (0을 나타내는) filter일 수 있다. 더하기와 빼기를 써서 상대 우선순위를 지정할 수 있다. 가령 filter + 55를 나타낸다.

add
지정한 패밀리에 지정한 이름으로 새 플로테이블 추가.
delete
지정한 플로테이블 삭제.
list
모든 플로테이블 나열.

상태 객체

{add | delete | list | reset} type [family] table object
delete type [family] table handle handle
list counters [family]
list quotas [family]

상태 객체는 테이블에 붙으며 유일한 이름으로 식별된다. 규칙들에서 상태 정보를 모은 것이며 규칙에서 참조하려면 "타입 이름" 키워드를 쓴다. 가령 "counter 이름"으로 쓴다.

add
지정한 테이블에 새 상태 객체 추가.
delete
지정한 객체 삭제.
list
객체가 담은 상태 정보 표시.
reset
상태 객체 나열 및 재설정.

ct helper

ct helper helper { type type protocol protocol ; [l3proto family ;] }

ct helper로 연결 추적 헬퍼를 정의하며, 그걸 ct helper set 문으로 사용할 수 있다. typeprotocol은 필수고 l3proto는 지정하지 않으면 테이블 패밀리로 정한다. 즉 inet 테이블에서는 커널에서 지원하면 ipv4 및 ipv6 헬퍼 백엔드를 모두 적재하려고 시도하게 된다.

표 10: conntrack 헬퍼 지정

키워드 설명 타입
type 헬퍼 타입 이름 따옴표 친 문자열 (예: "ftp")
protocol 헬퍼의 제4계층 프로토콜 문자열 (예: tcp)
l3proto 헬퍼의 제3계층 프로토콜 주소 패밀리 (예: ip)

ftp 헬퍼 정의하고 할당하기

iptables와 달리 conntrack 검색이 완료된 후에, 예를 들어 기본 훅 우선순위 0으로 헬퍼 할당을 수행해야 한다.

table inet myhelpers {
    ct helper ftp-standard {
        type "ftp" protocol tcp
    }
    chain prerouting {
        type filter hook prerouting priority 0;
        tcp dport 21 ct helper set "ftp-standard"
    }
}

ct timeout

ct timeout name { protocol protocol ; policy = { state: value [, ...] } ; [l3proto family ;] }

ct timeout으로 연결 추적 타임아웃 값을 변경한다. ct timeout set 문으로 타임아웃 정책을 할당한다. protocolpolicy는 필수고 l3proto는 지정하지 않으면 테이블 패밀리로 정한다.

표 11: conntrack 타임아웃 지정

키워드 설명 타입
protocol 타임아웃 객체의 제4계층 프로토콜 문자열 (예: tcp)
state 연결 상태 이름 문자열 (예: "established")
value 연결 상태의 타임아웃 값 부호 없는 정수
l3proto 타임아웃 객체의 제3계층 프로토콜 주소 패밀리 (예: ip)

ct 타임아웃 정책 정의하고 할당하기

table ip filter {
        ct timeout customtimeout {
                protocol tcp;
                l3proto ip
                policy = { established: 120, close: 20 }
        }

        chain output {
                type filter hook output priority filter; policy accept;
                ct timeout set "customtimeout"
        }
}

갱신된 타임아웃 정책 확인하기

% conntrack -E

다음처럼 나와야 한다.

[UPDATE] tcp      6 120 ESTABLISHED src=172.16.19.128 dst=172.16.19.1
sport=22 dport=41360 [UNREPLIED] src=172.16.19.1 dst=172.16.19.128
sport=41360 dport=22

ct expectation

ct expectation name { protocol protocol ; dport dport ; timeout timeout ; size size ; [l3proto family ;] }

ct expectation으로 연결 예상을 만든다. ct expectation set 문으로 예상을 할당한다. protocol, dport, timeout, size는 필수고 l3proto는 지정하지 않으면 테이블 패밀리로 정한다.

표 12: 연결 예상 지정

키워드 설명 타입
protocol 예상 객체의 제4계층 프로토콜 문자열 (예: tcp)
dport 예상 연결의 목적 포트 부호 없는 정수
timeout 예상의 타임아웃 값 부호 없는 정수
size 예상의 크기 값 부호 없는 정수
l3proto 예상 객체의 제3계층 프로토콜 주소 패밀리 (예: ip)

ct 예상 정책 정의하고 할당하기

table ip filter {
        ct expectation expect {
                protocol udp
                dport 9876
                timeout 2m
                size 8
                l3proto ip
        }

        chain input {
                type filter hook input priority filter; policy accept;
                ct expectation set "expect"
        }
}

counter

counter [packets bytes]

표 13: 카운터 지정

키워드 설명 타입
packets 시작 패킷 카운트 부호 없는 정수 (64비트)
bytes 시작 바이트 카운트 부호 없는 정수 (64비트)

quota

quota [over | until] [used]

표 14: 쿼터 지정

키워드 설명 타입
quota 쿼터 제한, 쿼터 이름으로 사용 인자 둘. 부호 없는 정수(64비트)와 문자열: bytes, kbytes, mbytes. 그 인자 앞에 "over"와 "until"이 옴.
used 쿼터의 시작 값 인자 둘. 부호 없는 정수(64비트)와 문자열: bytes, kbytes, mbytes

식은 값을 나타내는데, 네트워크 주소나 포트 번호 같은 상수일 수도 있고 룰셋을 평가하면서 패킷에서 수집한 데이터일 수도 있다. 이진식, 논리식, 관계식 등으로 식들을 결합해서 (검사를 위한) 복합식 내지 관계식을 만들 수 있다. NAT나 패킷 마킹 같은 특정 동작에 인자로 쓰기도 한다.

각 식에는 데이터 타입이 있고, 그에 따라 크기, 심볼 값의 파싱 및 표현 방법, 다른 식과의 타입 호환성이 결정된다.

describe 명령

describe expression | data type

describe 명령은 식의 종류와 그 데이터 타입에 대한 정보를 보여 준다. 데이터 타입을 줄 수도 있으며, 그 경우 nft는 그 타입에 대한 추가 정보를 표시한다.

describe 명령

$ nft describe tcp flags
payload expression, datatype tcp_flag (TCP flag) (basetype bitmask, integer), 8 bits

predefined symbolic constants:
fin                           0x01
syn                           0x02
rst                           0x04
psh                           0x08
ack                           0x10
urg                           0x20
ecn                           0x40
cwr                           0x80

데이터 타입

데이터 타입에 따라 크기, 심볼 값의 파싱 및 표현 방법, 식의 타입 호환성이 결정된다. 여러 가지 전역 데이터 타입이 있으며, 추가로 어떤 식들에서 그 식 종류에 한정된 데이터 타입을 추가로 정의한다. 대부분의 데이터 타입은 크기가 고정돼 있지만 일부는 크기가 동적일 수 있는데, 가령 문자열 타입이 그렇다.

어떤 타입에는 미리 정의된 심볼 상수들이 있다. nft describe 명령으로 그 상수들을 나열할 수 있다.

$ nft describe ct_state
datatype ct_state (conntrack state) (basetype bitmask, integer), 32 bits

pre-defined symbolic constants (in hexadecimal):
invalid                         0x00000001
new ...

하위 타입에서 다른 타입이 파생될 수도 있다. 예를 들어 IPv4 주소 타입은 정수 타입에서 파생된 것인데, IPv4 주소를 정수 값으로도 나타낼 수 있다는 뜻이다.

특정 맥락(집합 및 맵 정의)에서는 데이터 타입을 명시적으로 지정해야 한다. 타입마다 있는 이름을 거기 쓴다.

정수 타입

이름 키워드 크기 기반 타입
정수 integer 가변 -

정수 타입은 수 값에 쓴다. 10진수, 16진수, 8진수로 나타낼 수 있다. 정수 타입에는 정해진 크기가 없으며 쓰이는 식에 따라 그 크기가 결정된다.

비트마스크 타입

이름 키워드 크기 기반 타입
비트마스크 bitmask 가변 integer

비트마스크 타입(bitmask)은 비트마스크에 쓴다.

문자열 타입

이름 키워드 크기 기반 타입
문자열 string 가변 -

문자열 타입은 문자열에 쓴다. 문자열은 알파벳 문자(a-zA-Z)로 시작하고 0개 이상의 알파벳이나 숫자, /, -, _, . 문자가 온다. 추가로 큰괄호(")로 감싼 건 뭐든 문자열로 인식한다.

문자열 표시

# 인터페이스 이름
filter input iifname eth0

# 기이한 인터페이스 이름
filter input iifname "(eth0)"

링크 계층 주소 타입

이름 키워드 크기 기반 타입
링크 계층 주소 lladdr 가변 integer

링크 계층 주소 타입은 링크 계층 주소에 쓴다. 링크 계층 주소는 가변 개수의 16진수 숫자 두개 묶음을 콜론(:)으로 구분해서 나타낸다.

링크 계층 주소 표시

# 이더넷 목적 MAC 주소
filter input ether daddr 20:c9:d0:43:12:d9

IPv4 주소 타입

이름 키워드 크기 기반 타입
IPv4 주소 ipv4_addr 32비트 integer

IPv4 주소 타입은 IPv4 주소에 쓴다. 점 찍은 10진수, 점 찍은 16진수, 점 찍은 8진수, 10진수, 16진수, 8진수 표기, 또는 호스트 이름으로 주소를 나타낸다. 호스트 이름은 표준 시스템 리졸버를 이용해 해석한다.

IPv4 주소 표시

# 점 찍은 10진수 표기
filter output ip daddr 127.0.0.1

# 호스트 이름
filter output ip daddr localhost

IPv6 주소 타입

이름 키워드 크기 기반 타입
IPv6 주소 ipv6_addr 128비트 integer

IPv6 주소 타입은 IPv6 주소에 쓴다. 호스트 이름이나 콜론으로 구분된 16진수 하프워드들로 나타낸다. 포트 번호와 구별하기 위해 주소를 대괄호("[]")로 감쌀 수도 있다.

IPv6 주소 표시

# 축약된 루프백 주소
filter output ip6 daddr ::1

대괄호 표기법을 쓴 IPv6 주소 표시

# []가 없으면 포트 번호(22)가 ipv6 주소의 일부인 것으로
# 파싱 됨
ip6 nat prerouting tcp dport 2222 dnat to [1ce::d0]:22

불리언 타입

이름 키워드 크기 기반 타입
불리언 boolean 1비트 integer

불리언 타입은 편의를 위한 사용자 공간의 문법적 타입이다. (보통 암묵적인) 관계 식의 오른쪽에 쓰여서 왼쪽 식을 불리언 (일반적으로 존재 여부) 검사로 바꾼다.

표 15: 다음 키워드들은 자동으로 해당 값의 불리언 타입으로 결정된다.

키워드
exists 1
missing 0

표 16: 불리언 비교를 지원하는 식들

동작
fib 라우트 존재 확인.
exthdr IPv6 확장 헤더 존재 확인.
tcp option TCP 옵션 헤더 존재 확인.

불리언 지정

# 라우트 존재하면 일치
filter input fib daddr . iif oif exists

# IPv6 트래픽 중 단편화 안 된 패킷에 일치
filter input exthdr frag missing

# TCP 타임스탬프 옵션이 있으면 일치
filter input tcp option timestamp exists

ICMP 타입 타입

이름 키워드 크기 기반 타입
ICMP 타입 icmp_type 8비트 integer

ICMP 타입 타입은 ICMP 헤더의 type 필드를 간편하게 지정하는 데 쓴다.

표 17: ICMP 타입 지정 시 사용할 수 있는 키워드

키워드
echo-reply 0
destination-unreachable 3
source-quench 4
redirect 5
echo-request 8
router-advertisement 9
router-solicitation 10
time-exceeded 11
parameter-problem 12
timestamp-request 13
timestamp-reply 14
info-request 15
info-reply 16
address-mask-request 17
address-mask-reply 18

ICMP 타입 지정

# 핑 패킷 일치
filter output icmp type { echo-request, echo-reply }

ICMP 코드 타입

이름 키워드 크기 기반 타입
ICMP 코드 icmp_code 8비트 integer

ICMP 코드 타입은 ICMP 헤더의 code 필드를 간편하게 지정하는 데 쓴다.

표 18: ICMP 코드 지정 시 사용할 수 있는 키워드

키워드
net-unreachable 0
host-unreachable 1
prot-unreachable 2
port-unreachable 3
net-prohibited 9
host-prohibited 10
admin-prohibited 13

ICMPv6 타입 타입

이름 키워드 크기 기반 타입
ICMPv6 타입 icmpv6_type 8비트 integer

ICMPv6 타입 타입은 ICMPv6 헤더의 type 필드를 간편하게 지정하는 데 쓴다.

표 19: ICMPv6 타입 지정 시 사용할 수 있는 키워드

키워드
destination-unreachable 1
packet-too-big 2
time-exceeded 3
parameter-problem 4
echo-request 128
echo-reply 129
mld-listener-query 130
mld-listener-report 131
mld-listener-done 132
mld-listener-reduction 132
nd-router-solicit 133
nd-router-advert 134
nd-neighbor-solicit 135
nd-neighbor-advert 136
nd-redirect 137
router-renumbering 138
ind-neighbor-solicit 141
ind-neighbor-advert 142
mld2-listener-report 143

ICMPv6 타입 지정

# ICMPv6 핑 패킷 일치
filter output icmpv6 type { echo-request, echo-reply }

ICMPv6 코드 타입

이름 키워드 크기 기반 타입
ICMPv6 코드 icmpv6_code 8비트 integer

ICMPv6 코드 타입은 ICMPv6 헤더의 code 필드를 간편하게 지정하는 데 쓴다.

표 20: ICMPv6 코드 지정 시 사용할 수 있는 키워드

키워드
no-route 0
admin-prohibited 1
addr-unreachable 3
port-unreachable 4
policy-fail 5
reject-route 6

ICMPvX 코드 타입

이름 키워드 크기 기반 타입
ICMPvX 코드 icmpv6_type 8비트 integer

ICMPvX 코드 타입은 ICMP와 ICMPv6의 코드 타입에서 겹치는 값들을 추출한 것이며 inet 패밀리에서 쓰기 위한 것이다.

표 21: ICMPvX 코드 지정 시 사용할 수 있는 키워드

키워드
no-route 0
port-unreachable 1
host-unreachable 2
admin-prohibited 3

conntrack 타입

표 22: ct 식과 문에 쓰는 타입들

이름 키워드 크기 기반 타입
conntrack 상태 ct_state 4바이트 bitmask
conntrack 방향 ct_dir 8비트 integer
conntrack 상황 ct_status 4바이트 bitmask
conntrack 이벤트 비트 ct_event 4바이트 bitmask
conntrack 레이블 ct_label 128비트 bitmask

위의 타입들 각각에 대해 편의를 위한 키워드들이 있다.

표 23: conntrack 상태 (ct_state)

키워드
invalid 1
established 2
related 4
new 8
untracked 64

표 24: conntrack 방향 (ct_dir)

키워드
original 0
reply 1

표 25: conntrack 상황 (ct_status)

키워드
expected 1
seen-reply 2
assured 4
confirmed 8
snat 16
dnat 32
dying 512

표 26: conntrack 이벤트 비트 (ct_event)

키워드
new 1
related 2
destroy 4
reply 8
assured 16
protoinfo 32
helper 64
mark 128
seqadj 256
secmark 512
label 1024

conntrack 레이블 타입(ct_label)에 가능한 키워드들은 런타임에 /etc/connlabel.conf에서 읽어 들인다.

기본 식

가장 하위의 식이 기본 식이며 상수, 또는 패킷 페이로드나 메타 데이터, 상태 모듈에서 온 데이터 하나를 나타낸다.

meta 식

meta {length | nfproto | l4proto | protocol | priority}
[meta] {mark | iif | iifname | iiftype | oif | oifname | oiftype | skuid | skgid | nftrace | rtclassid | ibrname | obrname | pkttype | cpu | iifgroup | oifgroup | cgroup | random | ipsec | iifkind | oifkind | time | hour | day}

meta 식은 패킷과 연관된 메타 데이터를 나타내는 식이다.

meta 식에는 지정 meta 식과 비지정 meta 식 두 종류가 있다. 지정 meta 식에선 메타 키 앞에 meta 키워드가 필요하고 비지정 meta 식은 메타 키를 바로 쓰거나 지정 meta 식으로 지정할 수 있다. meta l4proto는 IPv4나 IPv6 패킷에 포함된 특정 전송 프로토콜을 맞춰 보는 데 유용하다. IPv6 패킷에 IPv6 확장 헤더가 있으면 그 역시 건너뛰게 된다.

meta iif, oif, iifname, oifname는 패킷이 도착한 인터페이스와 나갈 인터페이스를 맞춰 보는 데 쓴다.

iif와 oif는 인터페이스 번호로 맞춰 보는 반면 iifname과 oifname은 인터페이스 이름으로 맞춰 본다. 이 둘은 같지 않다. 가령 다음 규칙을 생각해 보면,

filter input meta iif "foo"

인터페이스 "foo"가 존재하는 경우에만 이 규칙을 추가할 수 있다. 또한 그 규칙은 인터페이스 "foo"의 이름이 "bar"로 바뀐 경우에도 계속 일치하게 된다.

그렇게 되는 이유는 내부적으로 인터페이스 번호를 쓰기 때문이다. tun/tap이나 다이얼업 인터페이스(예를 들어 ppp)처럼 동적으로 생성되는 인터페이스인 경우 iifname과 oifname을 쓰는 게 더 나을 수 있다.

그런 경우에 이름을 쓰면 규칙을 추가하기 위해 인터페이스가 꼭 존재할 필요가 없고, 인터페이스 이름이 바뀌면 일치하지 않게 됐다가 인터페이스가 삭제되고 같은 이름의 새 인터페이스가 생기면 다시 일치하게 된다.

표 27: meta 식 타입

키워드 설명 타입
length 바이트 단위 패킷 길이 integer (32비트)
nfproto 실제 훅 프로토콜 패밀리, inet 테이블에서만 유용 integer (32비트)
l4proto 제4계층 프로토콜, ipv6 확장 헤더 건너뜀 integer (8비트)
protocol EtherType 프로토콜 값 ether_type
priority TC 패킷 우선순위 tc_handle
mark 패킷 마크 mark
iif 입력 인터페이스 번호 iface_index
iifname 입력 인터페이스 이름 ifname
iiftype 입력 인터페이스 타입 iface_type
oif 출력 인터페이스 번호 iface_index
oifname 출력 인터페이스 이름 ifname
oiftype 출력 인터페이스 하드웨어 타입 iface_type
skuid 발신 소켓에 연계된 UID uid
skgid 발신 소켓에 연계된 GID gid
rtclassid 라우팅 realm realm
ibrname 입력 브리지 인터페이스 이름 ifname
obrname 출력 브리지 인터페이스 이름 ifname
pkttype 패킷 타입 pkt_type
cpu 패킷 처리 중인 cpu 번호 integer (32비트)
iifgroup 입력 장치 그룹 devgroup
oifgroup 출력 장치 그룹 devgroup
cgroup 제어 그룹 ID integer (32비트)
random 유사 난수 integer (32비트)
ipsec 불리언 boolean (1비트)
iifkind 입력 인터페이스 종류
oifkind 출력 인터페이스 종류
time 패킷을 수신한 절대 시간 integer (32비트) 또는 string
day 주 중 요일 integer (8비트) 또는 string
hour 하루 중 시간 string

표 28: meta 식 한정 타입

타입 설명
iface_index 인터페이스 번호 (32비트 수). 숫자로 또는 기존 인터페이스의 이름으로 지정 가능.
ifname 인터페이스 이름 (16바이트 문자열). 존재하지 않아도 됨.
iface_type 인터페이스 타입 (16비트 수).
uid 사용자 ID (32비트 수). 숫자로 또는 사용자 이름으로 지정 가능.
gid 그룹 ID (32비트 수). 숫자로 또는 그룹 이름으로 지정 가능.
realm 라우팅 realm (32비트 수). 숫자로 또는 /etc/iproute2/rt_realms에 정의된 심볼 이름으로 지정 가능.
devgroup_type 장치 그룹 (32비트 수). 숫자로 또는 /etc/iproute2/group에 정의된 심볼 이름으로 지정 가능.
pkt_type 패킷 종류: host (로컬 호스트 향함), broadcast (모두에게), multicast (그룹에게), other (다른 호스트 향함).
ifkind 인터페이스 종류 (16바이트 문자열). 존재하지 않아도 됨.
time 정수 또는 ISO 형식 날짜. 예를 들어 "2019-06-06 17:00". 시간과 초는 선택적이며 원하는 생략 가능. 생략 시 자정을 상정함. 즉 "2019-06-06", "2019-06-06 00:00", "2019-06-06 00:00:00"은 동등함. 정수를 주는 경우 유닉스 타임스탬프라고 상정함.
day 주 중 요일("Monday", "Tuesday", 등) 또는 0에서 6 사이 정수. 문자열 일치 여부에 대소문자를 구별하지 않으며 완전히 일치할 필요 없음. (가령 "Mon"이라고 하면 "Monday"에 일치함.) 정수를 주는 경우 0이 일요일이고 6이 토요일임.
hour 24시간 형식으로 시간을 나타내는 문자열. 초를 선택적으로 지정할 수 있음. 예를 들어 17:00과 17:00:00이 동등함.

meta 식 사용하기

# 지정 meta 식
filter output meta oif eth0

# 비지정 meta 식
filter output oif eth0

# 패킷이 ipsec 처리 대상이었음
raw prerouting meta ipsec exists accept

socket 식

socket {transparent | mark}

socket 식을 사용해 기존의 열린 TCP/UDP 소켓이나 패킷에 연계될 수 있는 소켓 속성을 탐색할 수 있다. 수립 상태이거나 0 아닌 주소에 (가능하면 로컬 아닌 주소에) 결속된 리스닝 소켓을 찾는다.

표 29: 사용 가능한 소켓 속성

이름 설명 타입
transparent 찾은 소켓의 IP_TRANSPARENT 소켓 옵션 값. 0 또는 1일 수 있음. boolean (1비트)
mark 소켓 마크(SOL_SOCKET, SO_MARK) 값. mark

소켓 식 사용하기

# 투명 소켓에 대응하는 패킷에 표시
table inet x {
    chain y {
        type filter hook prerouting priority -150; policy accept;
        socket transparent 1 mark set 0x00000001 accept
    }
}

# mark 값이 15인 소켓에 대응하는 패킷 추적
table inet x {
    chain y {
        type filter hook prerouting priority -150; policy accept;
        socket mark 0x0000000f nftrace set 1
    }
}

# 패킷 mark를 소켓 mark로 설정
table inet x {
    chain y {
        type filter hook prerouting priority -150; policy accept;
        tcp dport 8080 mark set socket mark
    }
}

osf 식

osf [ttl {loose | skip}] {name | version}

osf 식은 수동적 운영 체제 감식을 한다. 이 식은 SYN 비트가 설정된 패킷에서 가져온 몇 가지 데이터(윈도 크기, MSS, 옵션 및 순서, DF 등)를 비교한다.

표 30: 사용 가능한 osf 속성

이름 설명 타입
ttl 운영 체제를 판단하기 위해 패킷의 TTL 검사를 하기. string
version 패킷에서 OS 버전 검사 하기.
name 맞춰 볼 OS 시그너처 이름. pf.os 파일에 전체 시그너처들이 있음. 식에서 탐지할 수 없었던 OS 시그너처엔 "unknown" 사용. string

사용 가능한 ttl 값

TTL 속성을 주지 않으면 IP 헤더의 값과 핑거프린트 TTL 값이 같은지 비교한다. 일반적으로 LAN에서 잘 동작한다.

osf 식 사용하기

# TTL 비교 없이 "Linux" OS 계열 시그너처에 일치하는 패킷 허용하기
table inet x {
    chain y {
        type filter hook input priority 0; policy accept;
        osf ttl skip name "Linux"
    }
}

fib 식

fib {saddr | daddr | mark | iif | oif} [. ...] {oif | oifname | type}

fib 식은 fib(forwarding information base)를 조회해서 특정 주소가 사용하게 될 출력 인터페이스 번호 같은 정보를 얻는다. 입력은 fib 검색 함수 입력으로 쓸 요소들의 튜플이다.

표 31: fib 식 한정 타입

키워드 설명 타입
oif 출력 인터페이스 번호 integer (32비트)
oifname 출력 인터페이스 이름 string
type 주소 타입 fib_addrtype

모든 주소 타입들의 목록을 보려면 nft describe fib_addrtype.

fib 식 사용하기

# 역경로 없는 패킷 버리기
filter prerouting fib saddr . iif oif missing drop

이 예에서 saddr . iif는 출발 주소와 입력 인터페이스를 가지고 라우팅 정보를 검색한다. oif는 그 라우팅 정보에서 출력 인터페이스 번호를 뽑아낸다. 그 출발 주소/입력 인터페이스 조합에 대한 라우트를 찾지 못했으면 출력 인터페이스 번호가 0이다. 입력 키 중 일부로 입력 인터페이스를 지정한 경우 출력 인터페이스 번호는 언제나 입력 인터페이스 번호와 같거나 0이다. saddr oif만 준 경우에는 oif가 아무 인터페이스 번호 또는 0일 수 있다.

# 인터페이스에 설정 안 된 주소를 향한 패킷 버리기
filter prerouting fib daddr . iif type != { local, broadcast, multicast } drop

# 특정 '블랙홀' 테이블(0xdead, 적절한 ip rule 필요)에서 검색 수행하기
filter prerouting meta mark set 0xdead fib daddr . mark type vmap { blackhole : drop, prohibit : jump prohibited, unreachable : drop }

라우팅 식

rt [ip | ip6] {classid | nexthop | mtu | ipsec}

라우팅 식은 패킷에 연계된 라우팅 데이터를 가리킨다.

표 32: 라우팅 식 타입

키워드 설명 타입
classid 라우팅 realm realm
nexthop 라우팅 nexthop ipv4_addr/ipv6_addr
mtu 라우트의 TCP 최대 세그먼트 크기 integer (16비트)
ipsec ipsec 터널 또는 트랜스포트를 통한 라우트 boolean

표 33: 라우팅 식 한정 타입

타입 설명
realm 라우팅 realm (32비트 수). 숫자로 또는 /etc/iproute2/rt_realms에 정의된 심볼 이름으로 지정 가능.

라우팅 식 사용하기

# IP 패밀리와 무관한 rt 식
filter output rt classid 10
filter output rt ipsec missing

# IP 패밀리에 의존적인 rt 식
ip filter output rt nexthop 192.168.0.1
ip6 filter output rt nexthop fd00::1
inet filter output rt ip nexthop 192.168.0.1
inet filter output rt ip6 nexthop fd00::1

ipsec 식

ipsec {in | out} [ spnum NUM ] {reqid | spi}
ipsec {in | out} [ spnum NUM ] {ip | ip6} {saddr | daddr}

ipsec 식은 패킷에 연계된 ipsec 데이터를 가리킨다.

식에서 입력 또는 출력 방향 정책을 검사해야 하는 경우 in 또는 out 키워드를 써서 방향을 지정해야 한다. in 키워드는 prerouting, input, forward 훅에서 쓸 수 있다. out 키워드는 forward, output, postrouting 훅에 해당한다. 선택적인 spnum 키워드를 써서 체인 내의 특정 상태에 맞춰 볼 수 있으며 기본은 0이다.

표 34: ipsec 식 타입

키워드 설명 타입
reqid 요청 ID integer (32비트)
spi 보안 매개변수 색인 integer (32비트)
saddr 터널의 출발 주소 ipv4_addr/ipv6_addr
daddr 터널의 목적 주소 ipv4_addr/ipv6_addr

numgen 식

numgen {inc | random} mod NUM [ offset NUM ]

수 생성기를 만든다. incrandom 키워드가 동작 방식을 결정한다. inc 방식에서는 마지막 반환 값을 증가시킬 뿐이다. random 방식에선 새 난수를 반환한다. mod 키워드 뒤의 값은 반환되는 수가 도달할 수 없는 상한을 (모듈로 연산) 지정한다. 선택적인 offset를 통해 반환 값을 고정된 간격만큼 증가시킬 수 있다.

numgen의 일반적인 용도는 부하 분산이다.

numgen 식 사용하기

# 192.168.10.100과 192.168.20.200 중 하나로 라운드 로빈:
add rule nat prerouting dnat to numgen inc mod 2 map \
        { 0 : 192.168.10.100, 1 : 192.168.20.200 }

# 구간을 이용해 불균일하게 확률 기반 분산:
add rule nat prerouting dnat to numgen random mod 10 map \
        { 0-2 : 192.168.10.100, 3-9 : 192.168.20.200 }

페이로드 식

페이로드 식은 패킷 페이코드에서 온 데이터를 가리킨다.

이더넷 헤더 식

ether {daddr | saddr | type}

표 35: 이더넷 헤더 식 타입

키워드 설명 타입
daddr 목적 MAC 주소 ether_addr
saddr 출발 MAC 주소 ether_addr
type EtherType ether_type

VLAN 헤더 식

vlan {id | cfi | pcp | type}

표 36: VLAN 헤더 식

키워드 설명 타입
id VLAN ID (VID) integer (12비트)
cfi Canonical Format Indicator integer (1비트)
pcp Priority Code Point integer (3비트)
type EtherType ether_type

ARP 헤더 식

arp {htype | ptype | hlen | plen | operation | saddr { ip | ether } | daddr { ip | ether }}

표 37: ARP 헤더 식

키워드 설명 타입
htype ARP 하드웨어 타입 integer (16비트)
ptype EtherType ether_type
hlen 하드웨어 주소 길이 integer (8비트)
plen 프로토콜 주소 길이 integer (8비트)
operation 동작 arp_op
saddr ether 이더넷 송신자 주소 ether_addr
daddr ether 이더넷 대상 주소 ether_addr
saddr ip IPv4 송신자 주소 ipv4_addr
daddr ip IPv4 대상 주소 ipv4_addr

IPv4 헤더 식

ip {version | hdrlength | dscp | ecn | length | id | frag-off | ttl | protocol | checksum | saddr | daddr }

표 38: IPv4 헤더 식

키워드 설명 타입
version IP 헤더 버전 (4) integer (4비트)
hdrlength 옵션 포함 IP 헤더 길이 integer (4비트) FIXME 단위
dscp Differentiated Services Code Point dscp
ecn Explicit Congestion Notification ecn
length 패킷 총 길이 integer (16비트)
id IP ID integer (16비트)
frag-off 단편 오프셋 integer (16비트)
ttl Time to live integer (8비트)
protocol 상위 계층 프로토콜 inet_proto
checksum IP 헤더 체크섬 integer (16비트)
saddr 출발 주소 ipv4_addr
daddr 목적 주소 ipv4_addr

ICMP 헤더 식

icmp {type | code | checksum | id | sequence | gateway | mtu}

표 39: ICMP 헤더 식

키워드 설명 타입
type ICMP type 필드 icmp_type
code ICMP code 필드 integer (8비트)
checksum ICMP checksum 필드 integer (16비트)
id echo request/response의 ID integer (16비트)
sequence echo request/response의 일련 번호 integer (16비트)
gateway redirect의 게이트웨이 integer (32비트)
mtu 경로 MTU 탐색의 MTU integer (16비트)

IGMP 헤더 식

igmp {type | mrt | checksum | group}

이 식은 IGMP 헤더 필드들을 가리킨다. inet, bridge, netdev 패밀리에서 쓸 때는 IPv4에 대한 암묵적 의존성이 생기게 된다. IPv6 상의 IGMP 같은 특이한 경우에 일치하게 하려면 규칙에 따로 meta protocol ip6를 추가해 줘야 한다.

표 40: ICMP 헤더 식

키워드 설명 타입
type IGMP type 필드 igmp_type
mrt IGMP maximum response time 필드 integer (8비트)
checksum IGMP checksum 필드 integer (16비트)
group 그룹 주소 integer (32비트)

IPv6 헤더 식

ip6 {version | dscp | ecn | flowlabel | length | nexthdr | hoplimit | saddr | daddr}

이 식은 IPv6 헤더 필드들을 가리킨다. ip6 nexthdr 사용 시 조심해야 한다. 그 값은 다음 헤더를 가리킬 뿐이다. 즉 ip6 nexthdr tcp는 IPv6 패킷에 확장 헤더가 하나도 없는 경우에만 걸린다. 단편화 돼 있거나 가령 라우팅 확장 헤더를 담고 있는 패킷은 걸리지 않게 된다. 실제 전송 헤더를 확인하고 싶고 확장 헤더는 무시하고 싶다면 meta l4proto를 써 달라.

표 41: IPv6 헤더 식

키워드 설명 타입
version IP 헤더 버전 (6) integer (4비트)
dscp Differentiated Services Code Point dscp
ecn Explicit Congestion Notification ecn
flowlabel Flow label integer (20비트)
length 페이로드 길이 integer (16비트)
nexthdr nexthdr 프로토콜 inet_proto
hoplimit Hop limit integer (8비트)
saddr 출발 주소 ipv6_addr
daddr 목적 주소 ipv6_addr

ip6 헤더 식 사용하기

# 첫 번째 확장 헤더가 단편을 나타내면 일치
ip6 nexthdr ipv6-frag

ICMPv6 헤더 식

icmpv6 {type | code | checksum | parameter-problem | packet-too-big | id | sequence | max-delay}

이 식은 ICMPv6 헤더 필드들을 가리킨다. inet, bridge, netdev 패밀리에서 쓸 때는 IPv6에 대한 암묵적 의존성이 생기게 된다. IPv4 상의 ICMPv6 같은 특이한 경우에 일치하게 하려면 규칙에 따로 meta protocol ip를 추가해 줘야 한다.

표 42: ICMPv6 헤더 식

키워드 설명 타입
type ICMPv6 type 필드 icmpv6_type
code ICMPv6 code 필드 integer (8비트)
checksum ICMPv6 checksum 필드 integer (16비트)
parameter-problem 문제 포인터 integer (32비트)
packet-too-big 초과한 MTU integer (32비트)
id echo request/response의 ID integer (16비트)
sequence echo request/response의 일련 번호 integer (16비트)
max-delay MLD 질의 응답 최대 지연 integer (16비트)

TCP 헤더 식

tcp {sport | dport | sequence | ackseq | doff | reserved | flags | window | checksum | urgptr}

표 43: TCP 헤더 식

키워드 설명 타입
sport 출발 포트 inet_service
dport 목적 포트 inet_service
sequence 일련 번호 integer (32비트)
ackseq 확인 번호 integer (32비트)
doff 데이터 오프셋 integer (4비트) FIXME 단위
reserved 예비 영역 integer (4비트)
flags TCP 플래그 tcp_flag
window 윈도 integer (16비트)
checksum 체크섬 integer (16비트)
urgptr 긴급 포인터 integer (16비트)

UDP 헤더 식

udp {sport | dport | length | checksum}

표 44: UDP 헤더 식

키워드 설명 타입
sport 출발 포트 inet_service
dport 목적 포트 inet_service
length 패킷 총 길이 integer (16비트)
checksum 체크섬 integer (16비트)

UDP-Lite 헤더 식

udplite {sport | dport | checksum}

표 45: UDP-Lite 헤더 식

키워드 설명 타입
sport 출발 포트 inet_service
dport 목적 포트 inet_service
checksum 체크섬 integer (16비트)

SCTP 헤더 식

sctp {sport | dport | vtag | checksum}

표 46: SCTP 헤더 식

키워드 설명 타입
sport 출발 포트 inet_service
dport 목적 포트 inet_service
vtag 검증 태그 integer (32비트)
checksum 체크섬 integer (32비트)

DCCP 헤더 식

dccp {sport | dport}

표 47: DCCP 헤더 식

키워드 설명 타입
sport 출발 포트 inet_service
dport 목적 포트 inet_service

인증 헤더 식

ah {nexthdr | hdrlength | reserved | spi | sequence}

표 48: AH 헤더 식

키워드 설명 타입
nexthdr 다음 헤더 프로토콜 inet_proto
hdrlength AH 헤더 길이 integer (8비트)
reserved 예비 영역 integer (16비트)
spi 보안 매개변수 색인 integer (32비트)
sequence 일련 번호 integer (32비트)

보안 페이로드 캡슐화 헤더 식

esp {spi | sequence}

표 49: ESP 헤더 식

키워드 설명 타입
spi 보안 매개변수 색인 integer (32비트)
sequence 일련 번호 integer (32비트)

IPCOMP 헤더 식

comp {nexthdr | flags | cpi}

표 50: IPComp 헤더 식

키워드 설명 타입
nexthdr 다음 헤더 프로토콜 inet_proto
flags 플래그 bitmask
cpi 압축 매개변수 색인 integer (16비트)

비가공 페이로드 식

@base,offset,length

비가공 페이로드 식은 offset 번째 비트부터 length 개 비트를 읽어 온다. 0번째 비트는 제일 첫 비트를 가리킨다. C 프로그래밍 언어로는 최상위 비트, 즉 옥텟이라면 0x80에 해당한다. 아직 사람이 읽을 수 있는 템플릿 식이 없는 헤더에 맞춰 보는 데 유용하다. 참고로 nft에서 비가공 페이로드 식에 자동으로 의존 조건을 추가해 주지 않는다. 가령 프로토콜 번호 5인 전송 헤더의 프로토콜 필드에 맞춰 보고 싶다면 그 비가공 식 앞에 meta l4proto 5처럼 써서 다른 전송 헤더의 패킷들을 직접 제외해 줘야 한다.

표 51: 지원하는 페이로드 프로토콜 base

base 설명
ll 링크 계층. 예를 들어 이더넷 헤더
nh 네트워크 헤더. 예를 들어 IPv4나 IPv6
th 전송 헤더. 예를 들어 TCP

UDP와 TCP 모두의 목적 포트 확인하기

inet filter input meta l4proto {tcp, udp} @th,16,16 { 53, 80 }

위를 다음처럼 쓸 수도 있다.

inet filter input meta l4proto {tcp,udp} th dport { 53, 80 }

더 편리하긴 하지만 비가공 식 표기와 마찬가지로 어떤 의존 조건도 만들거나 확인하지 않는다. 포트 개념이 있는 헤더 종류들로만 검사를 한정하는 건 사용자의 책임이다. 그렇게 해 주지 않으면 가령 ESP 패킷의 SPI 필드를 포트로 잘못 해석해서 식과 무관한 패킷이 잘못 걸리게 된다.

ARP 패킷 목적 프로토콜 주소가 지정 주소와 일치하면 대상 하드웨어 주소 다시 쓰기

input meta iifname enp2s0 arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566 accept

확장 헤더 식

확장 헤더 식은 IPv6 확장 헤더, TCP 옵션, IPv4 옵션 같은 가변 크기 프로토콜 헤더의 데이터를 가리킨다.

nftables에서는 현재 IPv6 확장 헤더, TCP 옵션, IPv4 옵션 검사(찾기)를 지원한다.

hbh {nexthdr | hdrlength}
frag {nexthdr | frag-off | more-fragments | id}
rt {nexthdr | hdrlength | type | seg-left}
dst {nexthdr | hdrlength}
mh {nexthdr | hdrlength | checksum | type}
srh {flags | tag | sid | seg-left}
tcp option {eol | noop | maxseg | window | sack-permitted | sack | sack0 | sack1 | sack2 | sack3 | timestamp} tcp_option_field
ip option {lsrr | ra | rr | ssrr} ip_option_field

다음 문법은 식 오른편이 헤더 존재 여부만 확인하는 불리언 타입인 관계 식에서만 유효하다.

exthdr {hbh | frag | rt | dst | mh}
tcp option {eol | noop | maxseg | window | sack-permitted | sack | sack0 | sack1 | sack2 | sack3 | timestamp}
ip option {lsrr | ra | rr | ssrr}

표 52: IPv6 확장 헤더

키워드 설명
hbh Hop by Hop
rt Routing Header
frag Fragmentation Header
dst dst 옵션
mh Mobility Header
srh Segment Routing Header

표 53: TCP 옵션

키워드 설명 TCP 옵션 필드
eol 옵션 목록 끝 kind
noop 1 바이트 TCP no-op 옵션 kind
maxseg TCP 세그먼트 최대 크기 kind, length, size
window TCP 윈도 스케일링 kind, length, count
sack-permitted TCP SACK 허용 kind, length
sack TCP 선택적 확인 (0번 블록 별칭) kind, length, left, right
sack0 TCP 선택적 확인 (0번 블록) kind, length, left, right
sack1 TCP 선택적 확인 (1번 블록) kind, length, left, right
sack2 TCP 선택적 확인 (2번 블록) kind, length, left, right
sack3 TCP 선택적 확인 (3번 블록) kind, length, left, right
timestamp TCP 타임스탬프 kind, elngth, tsval, tsecr

표 54: IP 옵션

키워드 설명 IP 옵션 필드
lsrr Loose Source Route type, length, ptr, addr
ra Router Alert type, length, value
rr Record Route type, length, ptr, addr
ssrr Strict Source Route type, length, ptr, addr

TCP 옵션 찾기

filter input tcp option sack-permitted kind 1 counter

IPv6 exthdr 확인하기

ip6 filter input frag more-fragments 1 counter

IP 옵션 찾기

filter input ip option lsrr exists counter

conntrack 식

conntrack 식은 패킷과 연계된 연결 추적 항목의 메타 데이터를 가리킨다.

세 가지 conntrack 식이 있다. 어떤 conntrack 식에선 conntrack 키 앞에 흐름 방향이 필요하지만 다른 식은 방향과 무관할 수 있기 때문에 바로 쓸 수도 있다. packets, bytes, avgpkt 키워드는 방향과 함께 쓸 수도 있고 없이 쓸 수도 있다. 방향을 생략하면 original 방향과 reply 방향의 합을 내놓는다. zone도 마찬가진데, 방향을 주면 그 존 ID가 해당 방향에 결속돼 있는 경우에만 존이 일치한다.

ct {state | direction | status | mark | expiration | helper | label}
ct [original | reply] {l3proto | protocol | bytes | packets | avgpkt | zone}
ct {original | reply} {proto-src | proto-dst}
ct {original | reply} {ip | ip6} {saddr | daddr}

표 55: conntrack 식

키워드 설명 타입
state 연결의 상태 ct_state
direction 연결 기준 패킷 방향 ct_dir
status 연결의 상황 ct_status
mark 연결 마크 mark
expiration 연결 만료 시간 time
helper 연결에 연계된 헬퍼 string
label 연결 추적 레이블 비트 또는 nftables include 경로의 connlabel.conf에 정의된 심볼 이름 ct_label
l3proto 연결의 제3계층 프로토콜 nf_proto
saddr 해당 방향의 연결의 출발 주소 ipv4_addr/ipv6_addr
daddr 해당 방향의 연결의 목적 주소 ipv4_addr/ipv6_addr
protocol 해당 방향의 연결의 제4계층 프로토콜 inet_proto
proto-src 해당 방향의 제4계층 프로토콜 출발 주소 integer (16비트)
proto-dst 해당 방향의 제4계층 프로토콜 목적 주소 integer (16비트)
packets 해당 방향 또는 original과 reply 모두에서 지나간 패킷 수 integer (64비트)
bytes 지나간 바이트 수. packets 키워드 설명 참고 integer (64비트)
avgpkt 패킷당 평균 바이트. packets 키워드 설명 참고 integer (64비트)
zone conntrack 존 integer (16비트)

위에 나열된 conntrack 한정 타입들에 대한 설명을 위의 conntrack 타입 절에서 볼 수 있다.

서버로 동시에 향하는 연결 수 제한하기

filter input tcp dport 22 meter test { ip saddr ct count over 2 } reject

판정 문

페이로드 문

확장 헤더 문

로그 문

거절 문

카운터 문

conntrack 문

meta 문

제한 문

NAT 문

TPROXY 문

SYNPROXY 문

flow 문

queue 문

dup 문

fwd 문

set 문

map 문

vmap 문

추가 명령

monitor

오류 보고

종료 상태

SEE ALSO

libnftables(3), libnftables-json(5), iptables(8), ip6tables(8), arptables(8), ebtables(8), ip(8), tc(8)

공식 위키: https://wiki.nftables.org

AUTHORS

Patrick McHardy와 Pablo Neira Ayuso가 Netfilter 커뮤니티의 여러 다른 공헌자들과 함께 nftables를 작성했다.

Copyright © 2008-2014 Patrick McHardy kaber@trash.net Copyright © 2013-2018 Pablo Neira Ayuso pablo@netfilter.org

nftables is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation.

This documentation is licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 license, CC BY-SA 4.0 http://creativecommons.org/licenses/by-sa/4.0/.


12/06/2019 a8347553