昨年から趣味でgoflowdというソフトウェアを(進捗度合いが良くないですが)作っています。作ろうと思ったモチベーションは以下のとおりです。
- メンテしているsoftflowdのメンテが面倒になってきて、最近の言語で代替となるソフトウェアを作りたくなりました。 (元々softflowdはDamien MillerさんというOpenBSD comitterの人が作っていたソフトウェアです。以前、Patchを送っていたらContributor扱いにしてもらいました。 google codeが廃止になった際にに自分がgithubへとCloneしたら今や自分のgithubレポジトリがdebian packageの参照先になってしまいました。)
- softflowdの実装だと(-Tオプションで何パターンか選べますが)Felxible NetFlow相当のFlowKey選択ができません。そこでFlowKeyが自由に選択できる実装をRFC6728のyangを用いて作りたいと思いました。 yangをgoのソースコードに変換する方法として、例えばgobgpではopenconfigのyangをpyangのpluginで変換していますが最新のopenconfigのyangを変換できない状態になっていてpyang pluginを適切にメンテしないとyang側の変更についていけてないように見受けられます。 なのでpyangではなく、openconfigツールであるygotを試して見ようと思いました。
- NetFlow/IPFIXのSource/Destination ASの情報を得るために、gobgpと連携したくなりました。(これは全くまだできていませんが。)
以上の意気込みで作り始めましたが実際使ってみるとRFC6728とygotにそれぞれイマイチな点がありました。
RFC6728の問題
よくある5 tuppleのflowはSrc IP/Dst IP/Src Port/Dst Port/Protocolの5つで実際L4はTCP/UDP/ICMPが代表的なプロトコルなので、これを表そうとすると最低限以下の4パターンのIEを含めた4種類のテンプレートが必要になります。
- sourceIPv4Address, destinationIPv4Address, sourceTransportPort, destinationTransportPort, protocolIdentifier
- sourceIPv6Address, destinationIPv6Address, sourceTransportPort, destinationTransportPort, protocolIdentifier
- sourceIPv4Address, destinationIPv4Address, icmpTypeCodeIPv4, protocolIdentifier
- sourceIPv6Address, destinationIPv6Address, icmpTypeCodeIPv6, protocolIdentifier
RFC6728で表されるコンフィグの構造を一部抜き出すとOvservationPoint<->SeectionProcess<->Selectorの関係になっています。 上の4パターンを考慮した場合に観測したパケットがICMPかそれ以外でテンプレートを使いわけたいわけですが、パケットをFilterしようとすると、IPFIXのConfiguration ModelではFilterMatchを使う必要があります。 ところがFilterMatchはieとそのvalueしか指定できません。ICMPだけを指定したければie=4(protocolIdentifier)にしてvalue=1(ICMP)にすれば指定できます。 一方、それ以上のことができないので、ICMP以外という指定ができません。Notみたいな構文が欲しいわけです。 または、TCP/UDP/SCTP/DCCPなどのプロトコルを複数指定したいですが、valueはleafになっておりleaf-listになっていないため1つしか指定できません。 今のConfiguration Modelで表現するにはobservationPointにTCP用、UDP用、SCTP用、DCCP用、ICMP用のSelectionProcessを作れば実現できますが、単純なことをやりたい目的に対して大変設定が面倒です。
+-------------------------------+
| ObservationPoint |
+-------------------------------+
| name |
| observationPointId {readOnly} |
| observationDomainId | 0..*
| ifName[0..*] |-------------+
| ifIndex[0..*] | | 0..*
| entPhysicalName[0..*] | V
| entPhysicalIndex[0..*] | +------------------+
| direction = "both" | | SelectionProcess |
+-------------------------------+ +------------------+
+------------------+
| SelectionProcess |
+------------------+ 1..* +----------+
| name |<>------| Selector |
| | +----------+
| |
| | 0..* +--------------------------------+
| |<>------| SelectionSequence |
| | +--------------------------------+
| | | observationDomainId {readOnly} |
| | | selectionSequenceId {readOnly} |
| | +--------------------------------+
| |
| | 0..* 0..1 +-------+
| |----------->| Cache |
+------------------+ +-------+
+--------------------------------------+
| Selector |
+--------------------------------------+ 1 +-----------------+
| name |<>------+ SelectAll/ |
| packetsObserved {readOnly} | | SampCountBased/ |
| packetsDropped {readOnly} | | SampTimeBased/ |
| selectorDiscontinuityTime {readOnly} | | SampRandOutOfN/ |
| | | SampUniProb/ |
| | | FilterMatch/ |
| | | FilterHash/ |
+--------------------------------------+ +-----------------+
+---------------------------+
| FilterMatch |
+---------------------------+
| ieId/ieName |
| ieEnterpriseNumber = 0 |
| value |
+---------------------------+
解決手段としてはFilterMatchにvaluesとleaf-listを追加するかNotというbooleanを追加(augment)すればいいと思います。但しIPFIX WGはConcluded WGなのでRFCに対するbisを提案することはできませんので実装依存になります。
ygotの問題
問題というか単に設計思想ですが、生成されるgo構造体メンバが全てポインタです。 Configで何が設定されているかは分からないので値を参照しようと思うと毎回nilチェックが必要になります。 毎回nilチェックするのは効率が悪いのでgoflowdではconfig.goで実態の値を保持できる構造体を作ってそこにコンフィグされた値をコピーしています。 正直これは面倒でしょうがないです。