NPF(NetBSD Packet Filter)のパケットフィルタルールを見てみる

[NetBSD Advent Calendar 2022](https://qiita.com/advent-calendar/2022/netbsd) 2日目の記事です。今年もNetBSDの記事を増やして行きましょう！
今日は[npf(7)](https://man.netbsd.org/npf.7)におけるフィルタルールの記法を紹介しようと思います。

## NPFのフィルタルール

NPFに限らず、ファイアウォールを設定する際には何らかの記法でフィルタルールを設定します。FreeBSDの[ipf(5)](https://www.freebsd.org/cgi/man.cgi?query=ipf&sektion=5&format=html)では以下のような記述を用います。ipfではフィルタルールとルールのグルーピングを一行で指定する形になっています。

```
# ICMPパケットをブロックする例
block in log quick proto icmp all group 100
```

同等のフィルタルールをNPFに置き換えると以下のようになります。一行でルールを表現するのはipfと同じなのですが、フィルタルールをグルーピングしたうえでルールを記述できる記法になっています。このように
、NPFは他のファイアウォール実装におけるフィルタルールと比べてわかりやすいルール記述が可能となっています。

```
group "icmp rule" {
  block in on $ext_if proto icmp all apply "log"
}
```

また、ルールのグルーピング以外にも、変数やインタフェースからIPv4/v6アドレスの取得、IPアドレスのリスト(特定のポート疎通を許可する等で使用)といった、便利な記法(文法)がサポートされています。

## NPFのフィルタルール文法

NPFのフィルタルールの文法を見てみます。文法の全てを紹介しているわけではありませんが、通常設定するフィルタルールを記述する場合は、ここで紹介する内容で十分かと思います。

### 変数

NPFのフィルタルールでは変数を使用可能で、先頭に `$` をつけると変数として扱われます。変数に代入可能な値は以下となっており、 `$var2 = { 10.0.0.1, 10.0.0.2 }` のように値をリストとして代入することも
可能です。

 * IPアドレス
 * ネットワーク
 * ポート
 * インタフェース

```
$var1 = 10.0.0.1
$var2 = { 10.0.0.1, 10.0.0.2 }
```

### インタフェース

インタフェースに降られているIPアドレスの取得も文法でサポートされています。 `inet4()` , `inet6()` でそれぞれIPv4,IPv6アドレスが取得できます。また、 `ext_addrs()` を使用すると、IPv4,IPv6両方のアド
レスを取得できます。

ちなみに、 `inet4()` 等はインタフェースに紐づくIPアドレスを返す拡張関数として実装されているとのことです。

```
$ext_if = "wm0"  # LANの外側のインタフェース

$ext_v4 = inet4($ext_if)  # IPv4アドレスを取得する
$ext_v6 = inet6($ext_if)  # IPv6アドレスを取得する

$ext_addrs = ifaddrs($ext_if)  # IPv4/IPv6両方のアドレスを取得する
```

### グループ

NPFでは「グループ」という単位でフィルタルールを定義します。 `default` グループは最初から存在しているグループになっており、パケットがどのグループにもマッチしなかった場合は、この `default` グループ
のルールがマッチングされます。
そのため、 `default` グループでは不要なパケットを全てブロックする設定にしておき、他のグループで疎通させたいパケットのルールを記述する、という使い方が良さそうです。

グループは複数定義することも可能なうえ、入れ子の形でも定義できます。このあたりの記法を活用すると、フィルタルールのメンテナンスが楽になりそうです。

```
$ext_if = "wm0"  # LANの外側のインタフェース
$int_if = "wm1"  # LANの内側のインタフェース

# グループに名前をつけることも可能。
# このグループには"icmp rule"という名前が付けられている
group "icmp rule" {
  # LANの外側からのICMPパケットは全てブロックする
  block in  on $ext_if proto icmp all apply "log"
  block out on $ext_if proto icmp all apply "log"

  # LANの内側から出てゆくICMPパケットは許可する
  pass in  on $int_if proto icmp all
  pass out on $int_if proto icmp all
  pass stateful out all
}

group default {
  # defaultグループのルール
}
```

また、グループにインタフェースを紐づけておくことも可能です。以下の例ではLANの外側のインタフェースに対するsshアクセスをブロックする設定です。`group "external" on $ext_if` という記述で、"external"
グループのフィルタルールがLANの外側のインタフェースにのみ適用される形になります。

```
$ext_if = "wm0"  # LANの外側のインタフェース

group "external" on $ext_if {
  block in on $ext_if from any to $ext_v4 port 22 apply "log"
}
```

### ルール

パケットフィルタルールを指定する、一番肝心な文法です。ルールの例はこれまでにいくつか挙げていますので、ここではルールのシンタックスを示します。

ルールのマッチング動作としては、複数ルールが存在した場合は最初に一致したルールが適用されます。NPFの挙動としては、フィルタルールを一通り当てはめたのち、一致するルールを適用しますが、 `final` キー
ワードを指定することで、そのルールにマッチした時点でパケットを処理させることも可能です。基本的にはルールにマッチした時点でパケットを処理したいはず(？)なので、 `final` 指定は頻繁に使用するような気
がします。
また、 `proto` キーワードを指定することで、任意のプロトコル(TCP,ICMP等)をフィルタすることも可能です。


```
 group           = "group" ( "default" | group-opts ) "{" rule-list "}"
 group-opts      = name-string [ "in" | "out" ] [ "on" interface ]
 rule-list       = [ rule new-line ] rule-list

 npf-filter      = [ "family" family-opt ] [ proto ] ( "all" | filt-opts )
 static-rule     = ( "block" [ block-opts ] | "pass" )
                   [ "stateful" | "stateful-ends" ]
                   [ "in" | "out" ] [ "final" ] [ "on" interface ]
                   ( npf-filter | "pcap-filter" pcap-filter-expr )
                   [ "apply" proc-name ]

 dynamic-ruleset = "ruleset" group-opts
 rule            = static-rule | dynamic-ruleset
```

### アドレス変換(NAT)

アドレス変換、いわゆるNAT(Network Address Translation)です。 `->` で外に出てゆくパケット(outbound)のアドレス変換、 `<-` で外から入ってくるパケット(inbound)のアドレス変換を行います。以下の例ではLANの内側( `192.168.0.0/24` のネットワーク)からLANの外側に出てゆくパケットのアドレス変換ルールを設定しています。

また、 `<->` でoutbound/inboundのアドレス変換ルールも設定可能です(ちょっと具体的なユースケースが思いつかない...)。

```
$ext_if = "wm0"  # LANの外側のインタフェース
$int_if = "wm1"  # LANの内側のインタフェース
```

## まとめ

NPFにおける、パケットフィルタルールの文法を紹介しました。ここで紹介した文法を用いることで、最低限のフィルタルールを記述できるようになるかと思います。