4 min read

IoT機器用にLANを分けた

うちは元々以下のようなネットワーク構成になっていた。

IX2215
├── 10.0.0.0/24
├── 10.0.1.0/24
└── 10.0.2.0/24
    └── L2SW (Unmanaged)
        ├── Raspberry Pi
        └── Wi-Fi AccessPoint (AP22)

それぞれの LAN は基幹ルーターの IX2215 によって VLAN で切られている。
なお VLAN で切っているのはわかりやすさのためだけであり、すべての機器はお互いに通信することができる。

しかし普段使いの機器とIoT機器が同じネットワークに属しているのはなんだか気持ち悪いため、IoT機器用の VLAN を切ってアクセス制御をすることにした。

要件

  • IoT LAN 10.0.10.0/24
  • 10.0.10.0/24 から他のネットワークには繋げない
  • 10.0.10.0/24 からインターネットには出られる
  • 他のネットワークから 10.0.10.0/24 に通信がきた場合、その戻りのために数秒間はそのホストに対してのアクセスを許可

VLAN作成

IoT機器はすべてが Wi-Fi 接続のため、AP が 10.0.2.0/2410.0.10.0/24 の SSID を放出しないとならない。
なのでタグ VLAN を使う。

まず現状のネットワークに IX2215 のインターフェース名を加えたものがこれ。

IX2215
├── 10.0.0.0/24 (GigaEthernet2:1.0)
├── 10.0.1.0/24 (GigaEthernet2:2.0)
└── 10.0.2.0/24 (GigaEthernet2:3.0)

見ての通りポートベース VLAN になっている。
そこにタグ VLAN を追加する。

ip dhcp profile dhcp10
  assignable-range 10.0.10.10 10.0.10.19
  subnet-mask 255.255.255.0
  default-gateway 10.0.10.1
  dns-server 10.0.10.1
  lease-time 86400

interface GigaEthernet2:3.1
  encapsulation dot1q 110 tpid 8100
  auto-connect
  ip address 10.0.10.1/24
  ip dhcp binding dhcp10
  no shutdown

たったこれだけ。
GigaEthernet2 の VLAN グループ 3 のタグ 1 個目
という感じで書けばいいみたい。
この時点でもうここまでできた。

IX2215
├── 10.0.0.0/24  (GigaEthernet2:1.0) (Untagged)
├── 10.0.1.0/24  (GigaEthernet2:2.0) (Untagged)
├── 10.0.2.0/24  (GigaEthernet2:3.0) (Untagged)
└── 10.0.10.0/24 (GigaEthernet2:3.1) (Tagged 110)

あとは AP でタグ 110 の SSID を作るだけでOK

アクセス制御

必要な箇所のみ抜粋

ip access-list acc-fr-lan0-2 permit ip src 10.0.0.0/24 dest any
ip access-list acc-fr-lan0-2 permit ip src 10.0.1.0/24 dest any
ip access-list acc-fr-lan0-2 permit ip src 10.0.2.0/24 dest any

ip access-list acc-in-lan0-2 permit ip src any dest 10.0.0.0/24
ip access-list acc-in-lan0-2 permit ip src any dest 10.0.1.0/24
ip access-list acc-in-lan0-2 permit ip src any dest 10.0.2.0/24

ip access-list iot-in deny ip src any dest 10.0.0.0/24
ip access-list iot-in deny ip src any dest 10.0.1.0/24
ip access-list iot-in deny ip src any dest 10.0.2.0/24

ip access-list permit-all permit ip src any dest any
ip access-list permit-dhcp permit udp src any sport eq 68 dest any dport eq 67

ip access-list dynamic dyn-iot-out access acc-fr-lan0-2 in acc-in-lan0-2

interface GigaEthernet2:3.1
  ip filter permit-dhcp 1 in
  ip filter iot-in 10 in
  ip filter permit-all 100 in
  ip filter dyn-iot-out 10 out
  ip filter permit-all 100 out

DHCP の dest は制限してしまうと取得できなかった。
おそらくどこに対しての DHCP をくれという明示的なリクエストではないから?(不明)

これで要件を満たす動作をする。
インターネットに出ることができ、他の LAN からアクセスが飛んできた際にはそのホストにだけ戻り通信を一定時間許可される。

検証

10.0.10.50 から

  • ✅ DHCP 取得
  • ❌ ping 10.0.0.1
  • ❌ ping 10.0.1.1
  • ❌ ping 10.0.2.1
  • ✅ ping 10.0.10.1
  • ❌ ping 10.0.2.10
  • ✅ ping 8.8.8.8
  • ✅ Web 閲覧

✅ ping 10.0.2.10 から 10.0.10.50

10.0.2.10 から ping 継続中 10.0.10.50 にて

  • ❌ ping 10.0.0.1
  • ❌ ping 10.0.1.1
  • ❌ ping 10.0.2.1
  • ✅ ping 10.0.10.1
  • ✅ ping 10.0.2.10
  • ✅ ping 8.8.8.8
  • ✅ Web 閲覧

上記 ping が終了してから30秒後、ping 開始前と同じ状態に戻る

OK!
なお、30秒という待機時間も変更可能だが今回はとりあえずしなかった。

Chromecast問題

ここまででIoT機器用の VLAN 作成とアクセス制御はできた。
しかしいざ機器を繋いでみるとわかる。
GoogleHome や Chromecast に異セグメントのスマホからキャストできなくなっている。

interface GigaEthernet2:3.1
  ip directed-broadcast

一応こうすることで異セグメントの PC の GoogleChrome からはキャストできるが、スマホからは依然として無理。

いろいろ調べてみるとこれらの機器は mDNS によってその位置を特定しているらしい。
そして mDNS はセグメントを跨がないため同一のネットワークでないと機能しない。
一般に言われている「同じ Wi-Fi に繋げ」というのはこういうことだったらしい。

そこでまた調べてみると mDNS Reflector というものがあり、異なるネットワークにも mDNS を伝播させることが可能なようだった。
それを内蔵したルーターや L2SW もあるようだが IX2215 にそんな機能はなく、L2SW も Unmanaged なのでもちろんない。
ここでやっと一番上のネットワーク構成にあったラズパイが活躍する。

このリフレクターにはいろいろな種類のソフトウェアがあるのだが、今回はラズパイに最初から入っていて稼働もしている Avahi を使った。

まずはラズパイにインターフェースを追加する。

$ sudo nmcli connection add type vlan con-name vlan110 dev eth0 id 110
$ # vlan110 `nmcli connection show` で確認できる名前
$ # eth0 物理インターフェース名
$ # 110 VLAN ID

これだけで追加することができ、勝手に DHCP から IP を取得してくれる。
一応 $ ip a などで確認する。

Avahi のコンフィグは以下のような感じ。(抜粋)

$ cat /etc/avahi/avahi-daemon.conf
[server]
use-ipv4=yes
use-ipv6=no
allow-interfaces=eth0,eth0.110
#deny-interfaces=eth1

[reflector]
enable-reflector=yes
#reflect-ipv=no
#reflect-filters=_airplay._tcp.local,_raop._tcp.local

Avahi の再起動をする。

$ sudo systemctl restart avahi-daemon.service

特にポートの開放などは必要なく、すぐにスマホからキャストできるようになった。

一件落着。

おわり

一番苦労したのはアクセス制御まわりだった。
初めてまともに ip filter を使ったと思う。

だけどそのおかげでホームなネットワークからIoT機器を完全に分離させることができてとても嬉しい。
それに Chromecast などをどのように認識しているのか、異セグメントではほんとうにいけないのかなどの知見も得られた。

すべての作業が完了するのに三日間かかったが、それ以上に得られたもののほうが大きい。
良い休日、そして良いネットワーク構成になったと思う。
今後も定期的にネットワークの整理やお掃除をしていけたらいいな。