SONiCのCoPP

# CoPP?
コントロールプレーンポリシング。ざっくりいうと、パケットの種類を特定して、ASICで処理するかCPUに処理を任せるかなどを決めるというものです。CoPPの設定により、通常のトラフィックをハードウェアで転送しつつ、スイッチASICで扱うことが難しい複雑なプロトコル(e.g. BGP)をCPUで処理させることができます。

# SONiCのCoPP
`config_db.json`に何も設定していなくても、いくつかの設定が入るようになっています。また、`config_db.json`に記述を書き加えることで、CPUで処理したいパケットを(設定可能な範囲で)自由に決めることができます。なお、SONiCのCoPPはSAIを使って制御している関係上、forwarding portに対して機能するものであって、management portには効力がありません。

## CoPPデフォルト設定

設定未記述の場合、下記のような設定が入ります。

```shell
$ grep COPP /var/log/swss/swss.rec
2019-12-16.15:05:40.584815|COPP_TABLE:trap.group.arp|SET|cbs:600|cir:600|meter_type:packets|mode:sr_tcm|queue:4|red_action:drop|trap_action:copy|trap_ids:arp_req,arp_resp,neigh_discovery|trap_priority:4
2019-12-16.15:05:40.584829|COPP_TABLE:default|SET|cbs:600|cir:600|meter_type:packets|mode:sr_tcm|queue:0|red_action:drop
2019-12-16.15:05:40.584838|COPP_TABLE:trap.group.bgp.lacp|SET|queue:4|trap_action:trap|trap_ids:bgp,bgpv6,lacp|trap_priority:4
2019-12-16.15:05:40.584847|COPP_TABLE:trap.group.lldp.dhcp.dhcpv6.udld|SET|queue:4|trap_action:trap|trap_ids:lldp,dhcp,dhcpv6,udld|trap_priority:4
2019-12-16.15:05:40.584856|COPP_TABLE:trap.group.ip2me|SET|cbs:600|cir:600|meter_type:packets|mode:sr_tcm|queue:1|red_action:drop|trap_action:trap|trap_ids:ip2me|trap_priority:1
```

上記を読み解くと、trap priorityの大きい順に

- それぞれ、red判定されたパケットはすべてdrop
- arp req, arp reply, neighbor discoveryをcopyしてtrap
- bgp, bgpv6, lacpをtrap
- lldp, dhcp, dhcpv6, udldをtrap
- ip2me(自宛パケット)をtrap

これはswss dockerの中にある[`/etc/swss/config.d/00-copp-config.json`](https://github.com/Azure/sonic-swss/blob/master/swssconfig/sample/00-copp.config.json)の記述と合致しています。

また、[`config_db.json`形式の記述](https://github.com/Azure/sonic-swss/blob/master/doc/Configuration.md#copp_table)もできます。

## CoPP設定
### trap id
CoPPの設定でパケットの種類と特定するために使われるのがtrap idです。現時点でSONiCが扱えるtrap idは下記となっています。

- stp
- lacp
- eapol
- lldp
- pvrst
- igmp_query
- igmp_leave
- igmp_v1_report
- igmp_v2_report
- igmp_v3_report
- sample_packet
- switch_cust_range
- arp_req
- arp_resp
- dhcp
- ospf
- pim
- vrrp
- bgp
- dhcpv6
- ospfv6
- vrrpv6
- bgpv6
- neigh_discovery
- mld_v1_v2
- mld_v1_report
- mld_v1_done
- mld_v2_report
- ip2me
- ssh
- snmp
- router_custom_range
- l3_mtu_error
- ttl_error
- udld
- bfd
- bfdv6

これらは[SAI](https://www.opencompute.org/wiki/Networking/SAI)のhostifにてtrapがサポートされているものですが、SAIで定義されているすべてではありません。またキーワードとしてはSONiCのソースコード内に記述されています。それぞれのtrap idについて、無指定時の挙動がforwardかdropかなどが決められています([`saihostif.h`内のコメント](https://github.com/opencomputeproject/SAI/blob/v1.5/inc/saihostif.h#L162)を参照)。custom rangeというキーワードがありますが、定数としてそれ以降の値はベンダなどによる拡張を許すというもののようです。

### レート制限
通常はスイッチASICによりハードウェア転送されるパケットをCPUに流し込むことになるので、無制限に流し込むと過負荷になることが容易に想像できます。そのためCoPPでは転送レートに上限を設ける設定が可能となっています。レート制限のためのモードは下記のいずれかです。

- srtcm (single rate three color meter) - [RFC2697](https://tools.ietf.org/html/rfc2697)
- trtcm (two rate three color meter) - [RFC2698](https://tools.ietf.org/html/rfc2698)
- storm

three color meterは単純な上限レート設定ではなく、パケットの流量に応じて個々のパケットに3種類(green, yellow, red)の色付けを施すというものです。これにより慢性的な高レート転送には制限をかけつつバーストトラフィックを処理できるといったレート制限が可能となります。パラメータがいくつか指定できますが、詳細はそれぞれのRFCを参照してください。

### 取りうるアクション
[`saiswitch.h`](https://github.com/opencomputeproject/SAI/blob/v1.5/inc/saiswitch.h#L68)のコメントにて解説されています。

- drop - data planeでパケットを破棄
- forward - data planeでパケットを転送
- copy - パケットをCPUにコピー (コピー元パケットには影響を与えない)
- copy_cancel - CPUへのパケットコピーをキャンセル
- trap - copy+dropの組み合わせ。元パケットをCPUに転送しdata planeで破棄。
- log - copy+forwardの組み合わせ。
- deny - copy_cancel+dropの組み合わせ。
- transit - copy_cancel+forwardの組み合わせ。

## CoPP設定例
役に立たない例ですが、「自宛パケットをすべてdropする」という設定が下記になります。標準で用意されている設定の`trap_action`を変更し`drop`としただけです。

```json
{
"COPP_TABLE": {
     "trap.group.ip2me": {
         "cbs": "600",
         "cir": "600",
	 "meter_type": "packets",
	 "mode": "sr_tcm",
	 "queue": "1",
	 "red_action": "drop",
	 "trap_action": "drop",
	 "trap_ids": "ip2me",
	 "trap_priority": "1"
      }
    }
}
```

# CoPPでCPUに送られるパケットはどうなる?
CoPPでtrapされたパケットは、SAIによって作成され、Linuxから見えるnetdevであるEthernet0, Ethernet4, ...という名前のネットワークインタフェースで受信したということになります。結果としてPC上のLinuxでのパケット処理と同様にソフトウェアで処理することになります。

逆にソフトウェアによってネットワークインタフェースにパケットを送信すると、SAIのhostifの機能によってパケットがforwarding portから外部に向かって送信されます。

# 参考リンク
- [`/etc/swss/config.d/00-copp-config.json`](https://github.com/Azure/sonic-swss/blob/master/swssconfig/sample/00-copp.config.json)
- [Configuration.md#copp_table](https://github.com/Azure/sonic-swss/blob/master/doc/Configuration.md#copp_table)