DHCPで静的経路の配布 (Classless Static Routes)

DHCPでClassless Static Routes option (`option 121`)を使って静的経路情報をDHCPクライアントに配布すると，Router option (`option 3`)が無視されてしまいハマったのでその対策の備忘録．

この記事の環境

* DHCP クライアント: Ubuntu 16.04
* DHCP サーバ: CISCO IOS 15.6.3M2(ED) (CISCO1921), isc-dhcpd, dnsmasq

# そもそもClassless Static Routes option (`option 121`)とは
DHCPサーバからDHCPクライアントに対し，静的ルートを配布できるDHCPの機能．デフォルトゲートウェイのルータに静的経路が入っていれば通常問題ないが，たまにこれがあると便利な時がある．自分が経験したのは下記2つのケース．

* ケース1: VPNのトンネル終端点がデフォルトゲートウェイとは違うIPアドレスかつ同じサブネットで動作している場合．
* ケース2: マルチホーム環境下で，通常のトラヒックはeth0でいいけど，一部の宛先だけ，eth1から特定のゲートウェイを通して通信したい場合．

ちなみに，普通のブロードバンドルータでは見かけないが，本来DHCPはデフォルトゲートウェイ(Router option)やDNS(Name Server option)だけでなく，NTPサーバ(NTP Servers option)やドメイン名(Domain Name option)等かなり沢山の項目を配布できる．ちゃんと設定すると結構便利だったりする．オプションの一覧は，[@ITのDHCPオプション一覧](http://www.atmarkit.co.jp/fnetwork/netcom/netcom06/option.html)や[IANAのDHCPのページ](https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options)を参照されたい．

# クライアントの挙動
[RFC 3442](https://tools.ietf.org/html/rfc3442)によれば，Classless Static Routes optionとRouter optionが同時に配布された場合，Router optionを無視することと明記されている．

>   If the DHCP server returns both a Classless Static Routes option and
>   a Router option, the DHCP client MUST ignore the Router option.

しかし，Windows 7やMacOSX El CapitanではRouter optionは無視されない．Ubuntu 16.04では正しく無視された．

追記．AndroidもUbuntu 16.04同様にRouter optionを無視するようです[^1]．

[^1]: Wifi connects but will not transmit data when DHCP info contains CLASSLESS STATIC ROUTE Information [36920068] - Visible to Public - Issue Tracker https://issuetracker.google.com/issues/36920068

## Ubuntu 16.04の挙動をRFCに反した挙動にさせるには
`/sbin/dhclient-script`あたりのファイルを改変すれば良いと思われる．`$new_rfc3442_classless_static_routes`があれば，デフォルトゲートウェイ追加の処理がスキップされるようになっている．

## isc-dhcp-clientでの実装
また，`/etc/dhclient/dhclient-exit-hooks.d/rfc3442-classless-routes`にClassless Static Routes optionを処理する実装が書かれている．とても単純な動作になっているので読んでみると面白い．

## 古いWindows
Windows 7では`option 121`でClassless Static Routes optionが適用されるが，古いWindowsでは，[`option 249`](https://msdn.microsoft.com/en-us/library/cc227282.aspx)での指定が必要．今の御時世で249が必要になることはほとんどないと思われる．

# サーバの設定
Classless Static Routes optionでデフォルトゲートウェイを配布する方法も含めて設定を例示する．
前述の通り，クライアントでは，Classless Static Routes option指定時にRouter optionによるデフォルトゲートウェイの指定が無視されてしまうため，Classless Static Routes optionでデフォルトゲートウェイを指定する．

## isc-dhcpd
`dhcpd.conf`に下記の様に記載．サブネットマスクを先頭において，サブネットを表現する． `192.168.30.0/24`のゲートウェイが`192.168.1.1`のときは，`24, 192,168,30, 192,168,1,1`となる．サブネットマスクの値に応じてオクテット単位で省略する．

```
option rfc3442-classless-static-routes code 121 = array of integer 8;
option rfc3442-classless-static-routes 24, 192,168,30, 192,168,1,254, 0, 192,168,1,1;
```

## dnsmasq
`dnsmasq.conf`に下記の様に記載．こっちはわかりやすい．

```
dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
```

## CISCO IOS
`0.0.0.0/0 192.168.1.1`と`192.168.30.0/24 192.168.1.254`を設定する例．サブネットマスクを先頭に持ってくる書き方はdhcpd.confと同様．更に，オクテット単位で，hexに変換してコンフィグを投入する．ip表記も可能だが，0.0.0.0を受け付けないため，デフォルトゲートウェイが投入できない．

```
ip dhcp pool SAMPLE
  network 192.168.1.0 255.255.255.0
  dns-server 192.168.1.1 8.8.8.8
!  option 121 ip 0.0.0.0 192.168.1.1 24.192.168.30 192.168.1.254
!  ip指定だとデフォルトゲートウェイが設定できない
  option 121 hex 00:c0:a8:01:01:18:c0:a8:1e:c0:a8:01:fe
```

`00:c0:a8:01:01`が`0 192.168.1.1`に相当し，`18:c0:a8:1e:c0:a8:01:fe`が`24 192.168.30 192.168.1.254`に相当している．変換が面倒なときは，[hexroute](http://www.xrx.ca/hexroute.htm)という便利なツールを使えば良い．ただし，こちらも`0.0.0.0/0`を処理できない…


# 参考
* https://askubuntu.com/questions/767002/dhcp-connection-does-not-set-default-gateway-automatically
* https://packages.ubuntu.com/xenial/isc-dhcp-client
* http://www.hits-net.com/blog/?p=2963
* http://www.atmarkit.co.jp/fnetwork/netcom/netcom06/option.html
* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options
* https://tools.ietf.org/html/rfc3442