無線ルータなどにはEthernetSwitch機能が搭載されている物が多くあります。FreeBSD 10.0Rでetherswitchというドライバコードが入りました。このコードはRealtek,IC Plus,Atheros(Qualcomm)などのEthernetSwitchチップをサポート対象としています。
大昔のEthernetSwitchは単純にL2の振り分けをおこなうだけでしたが、1998年に802.1Q(VLAN)がIEEEで規格化され2002年くらいから製品のチップが出回り始め、最近のEthernetSwitchはほとんどこの機能が入っています。
無線ルータのSOC(主にmips系)に内蔵または接続されたEthernetSwitchのVLAN機能とFreeBSDのvlan(4)インターフェースを使うとそれぞれのポートを別々のインターフェースのように使い分ける事ができます。
etherswitchは当初ZRouter.orgというFreeBSDを使ったルーター用ファームウエアイメージのビルド環境の一部として開発が始まったようなのですが、何らかの意見の違いがあったのか、ZRouterのrayの実装とは別になってしまっていました。当初はサポート範囲が違っていたのですが、etherswitch のサポート範囲も増え、ZRouterもetherswitchを使うように変更しました。
CPUからEthernetSwitchの制御はMDIOやSMI(I2C)といわれるバスを利用します。etherswitchはMDIOやSMIをサポートしていています。
etherswitchはドライバコードとetherswitchcfgというコマンドで構成されています。ドライバはチップへ設定をおこなうようなインターフェースを持ち、コマンドはユーザが設定をおこなうために用意されています。
etherswitchのドライバはetherswitch本体とそれぞれのチップに対応したコードで構成されています。対応のコードの中にはukswitchというものがあり、チップの対応コードが無いが、MDIOでSWITCHのPHYが見えているケースに利用しています。構成はhintsに書きます。
etherswitchのRealtekのRTL8366RBのサポートはTP-Link TL-WR1043NDをターゲットにしていたようです。このルータは日本国内では手に入りませんが、バッファローのWZR-HP-G301NHなどが、RTL8366RBを使っているようです。
ハードオフで手に入れたWZR-HP-G300NHも同じ物なのかと考えて試してみたところ全く動きませんでした。よくよく調べてみるとWZR-HP-G300NHはEthernetSwitchにRTL8366RBを使った物とRTL8366SRを使った物があるようです。手元にあった物は初期形のRTL8366SRのタイプのものでした。RTL8366SRとRTL8366RBはレジスタのアドレスの構成が違っているようでした。
RTL8366SRのSOCとの接続はSMI(I2C)でおこなわれていて、SOC側はGPIOを利用しています。
アドレスなどが定義されているヘッダーファイルをRTL8366SR用に用意してレジスタを書き換えて試したところChip IDは拾えるようになったのですが、処理が全く出来ない状態でした。I2Cではスレーブ側(RTL8366SR)からのACKが返ってくるのですが、これが動いていないようです。Chip IDが正しく拾えているのでその処理を見ていたところ、ワークアラウンドらしきコードがあり、同じ処理を通常処理にも入れたところなんとなく動くようになりました。
RTL8366RBとRTL8366SRの内部レジスタのアドレスは微妙な違いがあり、なぜこのような違いがあるチップを作るのか不思議な感じがします。
デバッグは以下のような手順になりました。
VLANではタグを付けて振り分けをおこなうのですが、この機能はまだ確認できていないのですが、タグ無しでL2のEthernetSwitchとしては動作しました。
# etherswitchcfg
etherswitch0: VLAN mode: DOT1Q
port0:
pvid: 1
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port1:
pvid: 1
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port2:
pvid: 1
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port3:
pvid: 1
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port4:
pvid: 0
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port5:
pvid: 1
flags=1<CPUPORT>
media: Ethernet 1000baseT <full-duplex,rxpause,txpause>
status: active
vlangroup0:
vlan: 1
members 0,1,2,3,5
vlangroup1:
vlan: 2
members none
この設定でport5にはsoc内蔵のarge0が接続されており、port0に接続された外部のホストの間でpingが通るようになりました。
ちなみにZRouterのコマンドでは上記のようなダンプ機能はなく原始的なレジスタのダンプになるようです。
RTL8366SR用の修正はレビューを出してheadに入れてもらいました。
元々のRTL8366サポートコードに問題があって、SOCが1ポートEtherの場合は問題ないのですが、WZR-HP-G300NHのようにSOCが2ポートEtherの場合はport4はPhyとして使われてSwitchには含まれないので、リストに出すのは不味いです。もう一つ問題があり、mdioproxyのような機能がないためPhy相当で接続されているarge1のメディア認識機能が使えません。
よくよく確認してみたらAtherosのチップのコードにはこの既にこの問題につての実装があったので、修正してみました。レビュー
修正して確認して気がついたのですが、SRなチップはSWitch側のSOCとの接続は1000BaseTでいけるのですがRBの方はなぜか100BaseTXでないとだめです。おそらく私がいじる前からそうだったのだと思うのですが、これは特に必要も無いので、当面放置のつもりです。
後日追記
etherswitchではいくつかデバイスをサポートしていますが、実装レベルがあまり整っていないようです。RTL8366RBはTagが使えますが、他でTagが使えるデバイスは今のところ確認できてません。RTL8366RBがリファレンスで他は必要な人が直したり作ったりしていくのが良いのかもしれません。
MIIバスを使って制御するAR7240で試したところ、以下のような構成になっているようだ。miibusを置き換えているようなのだが、ちょっと複雑だ。また現時点ではhintsに依存したドライバになっていて、fdtへ移行するのがちょっと難儀に思える。
# devinfo -r
nexus0
apb0
Hardware IRQs:
4
uart0
APB memory window:
0x18020003-0x18020022
APB IRQ:
0x3
gpio0
APB memory window:
0x18040000-0x18040fff
APB IRQ:
0x2
gpiobus0
gpioled0
gpioled1
gpioled2
gpioc0
arge0
Hardware IRQs:
2
Memory addresses:
0x19000000-0x19000fff
miiproxy0
miibus4
ukphy4
arge1
Hardware IRQs:
3
Memory addresses:
0x1a000000-0x1a000fff
argemdio0
Memory addresses:
0x19000000-0x19000fff
mdio0
mdioproxy0
arswitch0
miibus0
ukphy0
miibus1
ukphy1
miibus2
ukphy2
miibus3
ukphy3
mdio1
mdioproxy1
etherswitch0
spi0
Memory addresses:
0x1f000000-0x1fffffff
spibus0
mx25l0
ar71xx_wdog0
clock0
Hardware IRQs:
5
#
etherswitchcfgでみるとこんな感じ。
# ./etherswitchcfg -v
etherswitch0: Atheros AR7240 Ethernet Switch (ver 0 rev 0) with 5 ports and 16 V
LAN groups
etherswitch0: VLAN capabilities=16<PORT,DOT1Q,QinQ>
etherswitch0: VLAN mode: none
port0:
flags=1<CPUPORT>
media: Ethernet 1000baseT <full-duplex>
status: active
port1:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port2:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port3:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port4:
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
AR7240は以下のような構成のようだ。StefanBethkeさんのページにある図でMDIO masterの接続が間違っているので修正してみた。
hintsの設定が悪いのか今のところarge0が使えてない。。。 hints調整して使えるようになりました。
AR7240の後継と思われるAR9340は内蔵Switchにつながれているmdioがarge1の方に変更されています。これはarge0の方はGMIIとして外に出してGigaなPhyやSwitchに接続する事ができるようにした事にともなう変更だと考えられます。
よくよく調べてみると、上の構成はAR7420だけでAR7241以降はarge1にmdioがあるようです。
AR7242にAR8316を接続したモデルがあり、arge1の内蔵Switchの方はまったく使わずarge0だけで使うようになっていました。
arge0を内蔵SwitchのPhy4で使う場合の構成は下記になります。
AR9341も同様なのだが、このSOCはETH_CFGというレジスタで、phy4とphy0を交換できる。SW_PHY_ADDR_SWAPとSW_PHY_SWAPという設定がある。
ZRouterにしか無かったRT3050のサポートは2016/5にheadのetherswitchに入ったようだ。
IP175Cを使ったRT1310なルータがあったのでドライバを書いて試してみたが認識はされるもののすぐに固まる状態であった。etherswitchはmiiバスでスイッチの状態をポーリングしているようなのだが、相性が悪いのかもしれない。
RTL8366RBが入ったWZR-HP-G301NHが手に入ったのでheadのソースを試したところ、CPUポートのOUTがなぜか捨てられる状態でした。またゴミパケットが何故か全ポートに出力されています。TL-WR1043NDで動作確認したようなので、なんらかの違いが影響しているのかもしれません。
21:31:12.779503 00:00:00:00:00:00 (oui Ethernet) > Broadcast, RRCP-0x03 query
RTL8366RBの問題は二つあって、ハードのコンフィグレーションかU-Bootの処理が影響しているのか、WZR-HP-G301NHではkernelが起動してドライバの処理で、RTL8366RBをハードウエアリセットするとRTL8366RBが暴走してしまっているようです。とりあえずソフトウエアリセットするオプションを入れてみました。またRTL8366RBのCPUポートがなぜか100BaseTになっているのでarge0を100にhintsを書いておくと使えるようになりました。
etherswitchはおそらくOpenFlowにつないで使えるようにする事を考えているのかもしれません。OpenFlowのSwitchにするためのスタックを誰か作りませんか?とりあえずmrubyからetherswitch叩くためのmrbgemsプロト作ってみました。
OpenFlowのコントローラにTremaというものがあってRubyで使えるようです。コントローラがrubyでSwitchがmrubyってちょっと面白くないですか?
ちなみにOpenWRT方面にはRTL8366RBをOpenFlowのSwitchにするofsoftswitchという実装があるようなのですが、いろいろライブラリの依存がありFreeBSDにもってくるのはちょっと難しそうです。
RBとSRをレジスタを確認してオートプローブした方がいいという意見があったので、重い腰を上げて修正してレビュー出してみました。
DOT1Q VLANの使い方
RTL8366RBの初期値は以下のような内容になります。
# ./etherswitchcfg -v
etherswitch0: Realtek RTL8366RB with 6 ports and 16 VLAN groups
etherswitch0: VLAN capabilities=4<DOT1Q>
etherswitch0: VLAN mode: DOT1Q
port0:
pvid: 2
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port1:
pvid: 1
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port2:
pvid: 1
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port3:
pvid: 1
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port4:
pvid: 1
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port5:
pvid: 1
flags=1<CPUPORT>
media: Ethernet 100baseTX <full-duplex,rxpause,txpause>
status: active
vlangroup0:
vlan: 1
members 1,2,3,4,5
vlangroup1:
vlan: 2
members 0,5t
ポート1,2,3,4とCPUポート(5)が普通に接続され、0とCPUポート(5t)がタグ付きで接続されています。
ポート4はarge1に接続されているはずなのですが、おそらく何らかのおまじないが必要で、今のところ使えません。
# ifconfig vlan2 create vlan 2 vlandev arge0
# ifconfig vlan2 inet 10.10.10.111
# ifconfig -a
arge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=8<VLAN_MTU>
ether 62:73:64:00:00:00
inet 10.0.1.13 netmask 0xffffff00 broadcast 10.0.1.255
media: Ethernet 100baseTX <full-duplex>
status: active
arge1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=8<VLAN_MTU>
ether 62:73:64:00:00:00
media: Ethernet 1000baseT <full-duplex>
status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
inet 127.0.0.1 netmask 0xff000000
groups: lo
vlan2: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
ether 62:73:64:00:00:00
inet 10.10.10.111 netmask 0xff000000 broadcast 10.255.255.255
media: Ethernet 100baseTX <full-duplex>
status: active
vlan: 2 vlanpcp: 0 parent interface: arge0
groups: vlan
これでポート0は他のポートとは別のネットワークとなります。
ポート1を0と同じネットワークに移すには以下のようにします。
# etherswitchcfg vlangroup0 members 2,3,4,5
# etherswitchcfg vlangroup1 members 0,1,5t
# etherswitchcfg port1 pvid 2
WZR-HP-AG300HはAR8316が入っているが、構成は以下のようになっている。
# etherswitchcfg -v
etherswitch0: Atheros AR8316 Ethernet Switch (ver 1 rev 1) with 5 ports and 16 V
LAN groups
etherswitch0: VLAN capabilities=16<PORT,DOT1Q,QinQ>
etherswitch0: VLAN mode: none
port0:
flags=1<CPUPORT>
media: Ethernet 1000baseT <full-duplex>
status: active
port1:
flags=0<>
media: Ethernet autoselect (1000baseT <full-duplex,master>)
status: active
port2:
flags=0<>
media: Ethernet autoselect (1000baseT <full-duplex,master>)
status: active
port3:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port4:
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
tag/untagはPortに設定するタイプ(RT3050,ADM6996,KS8995MAなど)とVLANのTableに設定するタイプ(RTL8366など)のチップがあります。設定方法が違います。
portに設定するタイプ
# etherswitchcfg port0 addtag
# etherswitchcfg port1 striptag
設定を外す時は-addtagや-striptagとします。
VLAN Tableに設定するタイプ
# etherswitchcfg vlangroup0 member 0,2,5t
PORT VLANの使い方
# etherswitchcfg -v
etherswitch0: Atheros AR7240 Ethernet Switch (ver 0 rev 0) with 5 ports and 16 V
LAN groups
etherswitch0: VLAN capabilities=16<PORT,DOT1Q,QinQ>
etherswitch0: VLAN mode: none
port0:
flags=1<CPUPORT>
media: Ethernet 1000baseT <full-duplex>
status: active
port1:
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port2:
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port3:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port4:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
# etherswitchcfg config vlan_mode PORT
etherswitch0: VLAN mode: PORT
# etherswitchcfg -v
etherswitch0: Atheros AR7240 Ethernet Switch (ver 0 rev 0) with 5 ports and 16 V
LAN groups
etherswitch0: VLAN capabilities=16<PORT,DOT1Q,QinQ>
etherswitch0: VLAN mode: PORT
port0:
flags=1<CPUPORT>
media: Ethernet 1000baseT <full-duplex>
status: active
port1:
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port2:
flags=0<>
media: Ethernet autoselect (100baseTX <full-duplex>)
status: active
port3:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
port4:
flags=0<>
media: Ethernet autoselect (none)
status: no carrier
vlangroup0:
port: 0
members 0,1,2,3,4
vlangroup1:
port: 1
members 0,1,2,3,4
vlangroup2:
port: 2
members 0,1,2,3,4
vlangroup3:
port: 3
members 0,1,2,3,4
vlangroup4:
port: 4
members 0,1,2,3,4
vlangroupは通信できるポートを示しています。
# etherswitchcfg vlangroup1 members 0,1
vlangroup1:
port: 1
members 0,1
これで、Port1はCPUポート(Port0)とだけ通信ができるようになります。
QinQ VLANの使い方
AR7240のドライバではサポートがあるがまだ確認していません。
いろいろ
RT3050/3052は内蔵の100MのSwitchがあるのですが、EthernetをGigaにするためにRTL8366を外付けしている製品があります。構成はこのようになります。(IOData WN-G300DGR)
このケースでは内蔵のスイッチはIN/OUT用に使いPhyポートは使いません。Switchが二つあるので/dev/etherswitch0とetherswitch1が表れます。実際にはetherswitch0は機能してなくてRTL8366のetherswitch1が実際のSwitchになります。
RT2880にRTL8366SRが付いた珍し物もありました。(Planex MZK-W300NAG)
IOData WN-AG450DGRというRT3883とRT3052を使った機種は以下のような構成になっていました。オリジナルのfirmwareはVLAN(.1Q)でWANポートとLANポートを分けていると思われます。