SONiCのACL

# はじめに

SONiCに興味を持たれてる方には釈迦に説法な感じもしますが一応説明すると、Access Control Listの頭文字を並べてACLです。パケットヘッダの指定フィールドが特定の値(たとえばEthernet Typeの値が0x0800、意味としてはIPv4パケット)を持つとき、そのパケットをdropしたり、指定のポートに転送したりするといったように、条件と実行内容を書き連ねたリストであり、またそのリスト通りに処理を実行する機能の名称としても使われます。

SONiCで設定されたACLは、ホワイトボックススイッチに搭載されているスイッチASICで処理されます。つまりハードウェアで処理されるのですが、ASICに実装されていない機能は当然ですが処理できません。下記で設定可能パラメータを列挙していますが、指定できるからと言ってすべての組み合わせがどのスイッチでも動作するとは限らないことに注意してください。

# SONiCのACL基本構造

テーブルを定義して、そのテーブルにルールを書き連ねます。

# テーブル

- テーブル名 (任意の文字列)
- テーブルタイプ (L3 or mirror)
- 対象ポート
- ステージ (ingress or egress)

対象ポートがテーブルに紐付いていますので、ポートごとに違うルールを適用したい場合はテーブルを分けます。

# ルール

- テーブル名 (定義されたテーブルの名前)
- ルール名 (任意の文字列)
- 優先順位 (0〜999999。値の大きいほうが優先、つまり先に実行される)
- 条件(match)
- 実行内容(action)

## 条件

組み合わせ(and)で指定できます。orの場合は別のルールを並べます。

<details><summary>ヘッダフィールド一覧</summary><div>

- `IN_PORTS`
- `OUT_PORTS`
- `SRC_IP`
- `DST_IP`
- `SRC_IPV6`
- `DST_IPV6`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `ETHER_TYPE`
- `IP_PROTOCOL`
- `NEXT_HEADER`
- `TCP_FLAGS`
- `IP_TYPE`
- `DSCP`
- `L4_SRC_PORT_RANGE`
- `L4_DST_PORT_RANGE`
- `TC`
- `ICMP_TYPE`
- `ICMP_CODE`
- `TUNNEL_VNI`
- `INNER_ETHER_TYPE`
- `INNER_IP_PROTOCOL`
- `INNER_L4_SRC_PORT`
- `INNER_L4_DST_PORT`

</div></details>

## 実行内容

現在４種類の実行内容(アクション)が定義されています。

- `FORWARD` (パケット転送)
- `DROP` (パケット破棄)
- `REDIRECT` (パケット転送先の変更)
- `DO_NOT_NAT` (NAT対象外とする)

ここでひとつ実ハードウェアの話を書いておきます。Broadcom Trident3は、ハードウェア的にあるいはSDKやSAI APIとして、ACLの`REDIRECT`アクションをサポートしています。しかし、SAIに対して「どんなアクションがあるか」queryするAPIを呼び出すとエラーが返ってきます(2020年12月時点)。

SONiCのACL実装では、事前のqueryに失敗すると最低限のアクションしか設定できず、他はエラーとして受け付けないようになっています。そのため、Trident3搭載スイッチ上で動かすSONiCでは`REDIRECT`アクションを使えません。どうしても`REDIRECT`が必要であれば、やや無理やりですがソースコード内の「最低限のアクション」のところに`REDIRECT`を書き足してビルドするという手順で対応できます。

# Schemaが異なる2つのJSON

## OpenConfig YANG model

- https://github.com/openconfig/public/blob/master/release/models/acl/openconfig-acl.yang

`"acl"`, `"acl-sets"`からはじまるJSONは、OpenConfig標準で定義されているACLのフォーマットです。`config acl`コマンドや`acl-loader`コマンドが受け付けるのはこちらのフォーマットです。

`config acl`コマンドは下記のように使います。

- `config acl add TABLE_NAME TABLE_TYPE` - テーブルの追加
- `config acl update ファイル` - ルールの更新
- `config acl remove TABLE_NAME` - テーブルの削除

ZTP (Zero Touch Provisioning)でACL URLを指定されていた場合、このフォーマットのJSONをURLから取得し、起動時に自動的に適用させることができます。

なお、キーワードが上記の説明と若干違っていたりします。YANG Modelの定義を参照してください。

<details><summary>例</summary><div>

`sonic-ssh-only` や `Sonic-SNMP_ACL` がテーブル名です。ルールにはシーケンス番号を振りますが、名前はありません。

```
{
  "acl": {
    "acl-sets": {
      "acl-set": {
        "sonic-ssh-only": {
          "acl-entries": {
            "acl-entry": {
              "1": {
                "config": {
                  "sequence-id": 1
                },
                "actions": {
                  "config": {
                    "forwarding-action": "ACCEPT"
                  }
                },
                "ip": {
                  "config": {
                    "protocol": "IP_TCP",
                    "source-ip-address": "192.168.0.0/18"
                  }
                },
                "transport": {
                  "config": {
                    "destination-port": "22"
                  }
                }
              },
              "2": {
                "config": {
                  "sequence-id": 2
                },
                "actions": {
                  "config": {
                    "forwarding-action": "ACCEPT"
                  }
                },
                "ip": {
                  "config": {
                    "protocol": "IP_TCP",
                    "source-ip-address": "192.168.192.0/18"
                  }
                },
                "transport": {
                  "config": {
                    "destination-port": "22"
                  }
                }
              }
            }
          },
       "Sonic-SNMP_ACL": {
          "acl-entries": {
            "acl-entry": {
              "1": {
                "config": {
                  "sequence-id": 1
                },
                "actions": {
                  "config": {
                    "forwarding-action": "ACCEPT"
                  }
                },
                "ip": {
                  "config": {
                    "protocol": "IP_UDP",
                    "source-ip-address": "192.168.0.0/18"
                  }
                }
              }
            }
          },
          "config": {
            "name": "Sonic-SNMP_ACL"
          }
        }
      }
    }
  }
}
```
</div></details>


## `config_db.json`

- https://github.com/Azure/SONiC/blob/master/doc/mgmt/SONiC_YANG_Model_Guidelines.md

`config acl`コマンドや`acl-loader`コマンドを使って流し込んだ結果としてConfig DBに書き込まれる内容です。設定後に `show runningconfiguration all` で参照できます。`config save`できますし、あらかじめ`/etc/sonic/config_db.json`に書いておくこともできます。

記述する際の注意点ですが、値としてシンボリックネームが使えません。たとえば`"IP_PROTOCOL": "TCP"`は無効です。`"IP_PROTOCOL": "6"`とキジュする必要があります。ご注意ください。

<details><summary>例</summary><div>

```
{
  "ACL_TABLE": {
    "TestTable": {
      "ports": {
        "Ethernet0"
        "Ethernet4",
        "Ethernet8"
      },
      "type": "l3",
      "stage": "ingress"
    }
  },
  "ACL_RULE": {
    "TestTable|Rule1": {
      "PRIORITY": "10",
      "SRC_IP": "192.168.0.1",
      "PACKET_ACTION": "REDIRECT:Ethernet0"
    },
    "TestTable|DefaultRule": {
      "PRIORITY": "1",
      "ETHER_TYPE": "0x0800",
      "PACKET_ACTION": "DROP"
    }
  }
}
```
</div><details>


## どちらを使うべき?

オペレーションや標準に沿うことを考えるとOpenConfigのほうがつぶしが利きます。SONiC依存ではありませんので、他のプラットフォームでも同じ記述を使い回せます。

ただし、機能面から考えると、`config_db.json`を使わざるを得ないケースもあるかと思います。一例を挙げると、実装との間に下記のような相違があります。

- OpenConfigには(SONiCでは指定可能な)redirect actionが定義されていない。
- OpenConfigにはL2タイプがある(が、SONiCでは実装がない)

## おわりに

YAMAHAのルータなどでCLIを使ってフィルタルールを設定するのと比較すると、どうしても記述が冗長になってしまうのはしかたのないところだと思います。