SONiC ACLのカスタマイズ

# はじめに

SONiCのACL(Access Control List)機能は、基本的にスイッチASICに設定を投入してハードウェア処理されます。ACLテーブルを作成し、テーブルごとに対象ポートを指定し、テーブルに設定されたルールを適用します。


テーブルにはタイプが指定できます。[公式ドキュメント](https://github.com/sonic-net/SONiC/blob/master/doc/acl/acl.md)によるとタイプとして `l3` と `mirror` が指定できると説明があるのですが、テーブルタイプは他にもあり、また自分で独自のタイプを用意することもできます。これらのカスタマイズについて解説します。

# 既存のACLテーブルタイプ

それぞれ、条件(matches)とアクション(actions)が定義されています。

- `L3`
- `L3V6`
- `MIRROR`
- `MIRRORV6`
- `MIRROR_DSCP`
- `PFCWD`
- `CTRLPLANE`
- `DTEL_FLOW_WATCHLIST`
- `MCLAG`
- `DROP`

以下、具体的にどのような条件とアクションが設定可能かを並べます。説明がないテーブルタイプの多くは`L3`タイプのサブセットであるため、通常は`L3`を使えば事足りるようです。なお、ASICによっては受け付けない条件やアクションがありますので、動作するかどうか実際に確認してからご利用ください。

## L3

L3ヘッダの指定条件にマッチしたパケットに対してアクションを実行します。

```json
{
  "ACL_TABLE": {
    "tab": {
      "type": "l3",
      "stage": "ingress",
      "ports": [ "Ethernet0", "Ethernet4" ]
    }
  }
  "ACL_RULE": {
    "tab|rule1": {
      "PRIORITY": "10",
      "SRC_IP": "192.168.0.0/24",
      "PACKET_ACTION": "DROP"
    }
  }
}
```

### matches

- `ETHER_TYPE`
- `VLAN_ID`
- `IP_TYPE`
  - `ANY`
  - `IP`
  - `NON_IP`
  - `IPV4ANY`
  - `NON_IPV4`
  - `IPV6ANY`
  - `NON_IPV6`
  - `ARP`
  - `ARP_REQUEST`
  - `ARP_REPLY`
- `SRC_IP`
- `DST_IP`
- `ICMP_TYPE`
- `ICMP_CODE`
- `IP_PROTOCOL`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `TCP_FLAGS`

### actions

- `PACKET_ACTION`
  - `FORWARD`
  - `DROP`
  - `REDIRECT`
- `REDIRECT_ACTION`

## L3V6

IPv6パケット向けのテーブルタイプです。

### matches

- `VLAN_ID`
- `IP_TYPE`
- `SRC_IPV6`
- `DST_IPV6`
- `ICMPV6_TYPE`
- `ICMPV6_CODE`
- `NEXT_HEADER`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `TCP_FLAGS`

### actions

- `PACKET_ACTION`
  - `FORWARD`
  - `DROP`
  - `REDIRECT`
- `REDIRECT_ACTION`

## MIRROR

`MIRROR`タイプは少し特殊で、IPv6を`MIRROR`タイプでIPv4と同じテーブルで扱うか、`MIRRORV6`タイプを使いIPv4と分けて使うかがASICによって違います。ここでは分けて使う想定で書きますが、同じテーブルで扱えるASICであれば、後述の`MIRRORV6`の条件を`MIRROR`テーブルのルールとして設定します。

- `MIRROR`と`MIRRORV6`で分けるプラットフォームの例
  - Mellanox
  - Marvell
  - Broadcom DNX (Jerico系)
- `MIRROR`でIPv6条件を書くプラットフォームの例
  - Broadcom Trident
  - Broadcom Tomahawk
  - Barefoot

### matches

- `ETHER_TYPE`
- `VLAN_ID`
- `IP_TYPE`
- `SRC_IP`
- `DST_IP`
- `ICMP_TYPE`
- `ICMP_CODE`
- `IP_PROTOCOL`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `TCP_FLAGS`
- `IN_PORTS`
- `DSCP`
- `L4_DST_PORT_RANGE`

### actions

- `MIRROR_INGRESS`
- `MIRROR_EGRESS`

## MIRRORV6

`MIRROR`にIPv6ルールを書かず、分離するタイプのASIC向けです。

### matches

- `VLAN_ID`
- `IP_TYPE`
- `SRC_IPV6`
- `DST_IPV6`
- `ICMPV6_TYPE`
- `ICMPV6_CODE`
- `NEXT_HEADER`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `TCP_FLAGS`
- `DSCP`
- `L4_DST_PORT_RANGE`

### actions

- `MIRROR_INGRESS`
- `MIRROR_EGRESS`

## MIRROR_DSCP

DSCPを見て特定パケットのみをミラーするタイプです。

### matches

- `DSCP`

### actions

- `MIRROR_INGRESS`
- `MIRROR_EGRESS`

## PFCWD

### matches

- `TC`

### actions

- `PACKET_ACTION`
  - `FORWARD`
  - `DROP`
  - `REDIRECT`

## CTRLPLANE

`CTRLPLANE`テーブルタイプは特殊で、これはASICではなくCPUで処理されます。テーブルにはサービスを指定します。サービスは下記のいずれか。

- `NTP`
- `SNMP`
- `SSH`
- `EXTERNAL_CLIENT`
- `ANY`

設定に従って`iptables`あるいは`ipv6tables`を呼び出します。(`src/sonic-host-services/scripts/caclmgrd`)

## DTEL_FLOW_WATCHLIST

Dataplane TELemetry略してDTELはINT(Inbound Network Telemetry)と同じ機能を指します。テレメトリ用というだけあって、アクションが特殊です。

### matches

- `ETHER_TYPE`
- `SRC_IP`
- `DST_IP`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `IP_PROTOCOL`
- `TUNNEL_VNI`
- `INNER_ETHER_TYPE`
- `INNER_SRC_IP`
- `INNER_DST_IP`
- `VLAN_ID`
- `ACL_IP_TYPE`
- `TCP_FLAGS`
- `DSCP`
- `SRC_IPV6`
- `DST_IPV6`
- `ICMP_TYPE`
- `ICMP_CODE`
- `ICMPV6_TYPE`
- `ICMPV6_CODE`
- `NEXT_HEADER`

### actions

- `FLOW_OP`
  - `NOP`
  - `POSTCARD`
  - `INT`
  - `IOAM`
- `INT_SESSION`
- `DROP_RESPOT_ENABLE`
- `TAIL_DROP_REPORT_ENABLE`
- `REPORT_ALL_PACKETS`
- `FLOW_SAMPLE_PERCENT`

## MCLAG

### matches

- `ETHER_TYPE`
- `VLAN_ID`
- `IP_TYPE`
- `SRC_IP`
- `DST_IP`
- `ICMP_TYPE`
- `ICMP_CODE`
- `IP_PROTOCOL`
- `L4_SRC_PORT`
- `L4_DST_PORT`
- `TCP_FLAGS`
- `OUT_PORTS`
- `L4_DST_PORT_RANGE`

### actions

- `PACKET_ACTION`
  - `FORWARD`
  - `DROP`
  - `REDIRECT`

## DROP

### matches

- `TC`

### actions

- `PACKET_ACTION`
  - `FORWARD`
  - `DROP`
  - `REDIRECT`

# テーブルタイプのカスタマイズ

CLIからは設定できませんが、config DBに書くことでカスタムテーブルタイプを定義できます。

```json
{
  "ACL_TABLE_TYPE|name": {
    "MATCHES": [
      条件1,
      条件2,
        :
    ],
    "ACTIONS": [
      アクション1,
      アクション2,
          :
    ],
    "BIND_POINTS": [
       "PORT",
       "PORTCHANNEL",
         :
    ]
  }
}
```

定義したタイプは`sudo config acl add table テーブル名 タイプ`のタイプ部分に指定できます。

## `MATCHES`

ここに並べられた条件が、ルールとして設定可能となります。記述できるものは下記のうちのいずれかです。SAIのヘッダ(`saiacl.h`)に書かれているものすべてではありません。ここにない条件を使いたい場合、ソースコード(`src/sonic-swss/orchagent/aclorch.cpp`と同ディレクトリの`aclorch.h`)の変更とイメージのビルドが必要です。ASICによっては受け付けない条件がある点にもご注意ください。

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

## `ACTIONS`

`MATCHES`と同様に、ここに書かれたアクションが設定可能となります。設定可能アクションは下記になります。これらについても`saiacl.h`に書かれている全てではなく、増やす場合はソースコードの変更とイメージのビルドが必要です。また、ASICによって受け付けないアクションがある点にも注意です。

- L3関連
  - `PACKET_ACTION`
  - `REDIRECT_ACTION`
  - `DO_NO_NAT_ACTION`
- MIRROR関連
  - `MIRROR_INGRESS_ACTION`
  - `MIRROR_EGRESS_ACTION`
- DTEL関連
  - `FLOW_OP`
  - `INT_SESSION`
  - `DROP_REPORT_ENABLE`
  - `TAIL_DROP_REPORT_ENABLE`
  - `FLOW_SAMPLE_PACKET`
  - `REPORT_ALL_PACKETS`
- その他
  - `COUNTER`

## BIND_POINTS

ACLの適用場所の指定です。`PORT`あるいは`PORTCHANNEL`、もしくはその両方が指定できます。SAIではその他`VLAN`, `ROUTER_INTERFACE`, `SWITCH`が指定可能なようですが、実際にASICに指定可能なのかは未確認です。VLANは`VLAN_ID`の条件を指定できればそれで代替できそうです。

# カスタマイズ例

## VXLAN

`TUNNEL_VNI`という条件でマッチできるテーブルを作成してみます。

```json
{
  "ACL_TABLE_TYPE": {
    "vxlan": {
      "MATCHES": [
        "TUNNEL_VNI"
      ],
      "ACTIONS": [
        "PACKET_ACTION"
      ],
      "BIND_POINTS": [
        "PORT"
      ]
    }
  },
  "ACL_TABLE": {
    "vxlan_drop": {
      "type": "vxlan",
      "stage": "ingress",
      "ports": [ "Ethernet0", "Ethernet4" ]
    }
  },
  "ACL_RULE": {
    "vxlan_drop|testrule1": {
      "TUNNEL_VNI": "1000",
      "PACKET_ACTION": "DROP"
    }
  }
}
```

## アクションを追加した場合

アクションを追加したイメージでは、下記のようにカスタムテーブルを定義して従来では設定できないACLルールを追加することができます。できるはずです。

```json
{
  "ACL_TABLE_TYPE": {
    "NAT": {
      "MATCHES": [
        "IN_PORTS",
        "OUTER_VLAN_ID",
        "SRC_IP",
        "DST_IP"
      ],
      "ACTIONS": {
        "SET_SRC_IP_ACTION",
        "SET_DST_IP_ACTION"
      ],
      "BIND_POINTS": [
        "PORT"
      ]
    }
  },
  "ACL_TABLE": {
    DNAT": {
      "type": "NAT",
      "stage": "ingress"
    },
    "SNAT": {
      "type": "NAT",
      "stage": "ingress"
    }
  }
  "ACL_RULE": {
    "DNAT|rule1": {
      "DST_IP": "192.168.0.1",
      "SET_DST_IP_ACTION": "10.0.0.1"
    },
    "SNAT|rule1": {
      "SRC_IP": "10.0.0.1",
      "SET_SRC_IP_ACTION": "192.168.0.1"
    }
  }
}
```

## 設定できないテーブルタイプを作ろうとするとどうなる?

エラーが出ます。が、ログに記録されるだけなので `show logging`で確認してください。なお、このエラーでSONiCの各機能が停止することはないようです。

例: 追加しようとしたテーブルたいぷ定義

```json
    "ACL_TABLE_TYPE": {
        "NAT": {
            "MATCHES": [
                "IN_PORTS",
                "VLAN_ID",
                "SRC_IP",
                "DST_IP"
            ],
            "ACTIONS": [
                "SET_SRC_IP_ACTION",
                "SET_DST_IP_ACTION"
            ],
            "BIND_POINTS": [
                "PORT"
            ]
        }
    },
```

エラー (`show logging`からエラー箇所を抜粋)

```
Aug  7 17:49:28.084646 sonic ERR swss#orchagent: :- parseAclTableTypeActions: Unknown action SET_SRC_IP_ACTION
Aug  7 17:49:28.084646 sonic ERR swss#orchagent: :- doAclTableTypeTask: Failed to parse ACL table type configuration NAT
```

# ACLの動作状況を確認する

`show acl`コマンドでは設定参照しかできません。

`aclshow`コマンドを使いましょう。

```shell-session
$ aclshow --help
usage: aclshow [-h] [-a] [-c] [-r RULES] [-t TABLES] [-v] [-vv]

Display SONiC switch Acl Rules and Counters

optional arguments:
  -h, --help            show this help message and exit
  -a, --all             Show all ACL counters
  -c, --clear           Clear ACL counters statistics
  -r RULES, --rules RULES
                        action by specific rules list: Rule1_Name,Rule2_Name
  -t TABLES, --tables TABLES
                        action by specific tables list: Table1_Name,Table2_Name
  -v, --version         show program's version number and exit
  -vv, --verbose        Verbose output
```

# SONiC-VSでは?

`caclmgrd` は`iptables`を設定しますが、これはコントロールプレーン専用です。また、SONiC-VSのSAI実装である`libsaivs`のACL実装(`src/sonic-sairedis/vslib/sai_vs_acl.cpp`)ではなにも実装されていません。よって、残念ながらSONiCのACL設定は基本的にSONiC-VSでは利用できません。

# おわりに

SAIのヘッダ(`saiacl.h`)を見ると、`SAI_ACTION_TYPE_SET_VRF`といった興味深いアクションがあったりするのですが、動くか動かないかは各ASICに用意されたSAI実装次第です(Broadcom Trident3では未実装でした)。複数種類のハードウェアを試せれば条件やアクションの○×表を書けるかもしれません。