WIP: Refactor DHCP

https://github.com/trema/pio/issues/60#issuecomment-39295599

で頂いたコメントで作成しました。
※`Dhcp::Message`と`Dhcp::Discover`がほぼ同じですので、rakeした時に失敗します。

頂いたコメントの意図を外していましたら、何卒ご指摘ください。
宜しくお願いします。

====
はじめから書き直すことにしました。（2014/06/19）
更新ごとにつらつらと書いていきます。
@yasuhito 何かありましたら追記をお願いします。
## 方針：
- 既存のコードやＡＰＩは流用する（可能な限り）
- 基本的なメッセージタイプはすべて網羅する
  - ACK
  - NACK
  - OFFER
  - DISCOVER
  - REQUEST
  - INFORM
  - RELEASE
- クラスの追加
  - RequestRelay
  - ReplyRelay
  - INFORM
  - RELEASE
  - NACK
  - OFFER
- メソッドの追加
  - Router
  - DNS
  - Hostname
  - Vendor Class Id
- 質問系メソッドの追加
  - boot_request?/ boot_reply?
  - ack?/offer?/nack?/discover?/request?/release?/decline?/inform?
- オプショナル・義務的フィールドの明確化
- RFC2131(http://srgia.com/docs/rfc2131j.html)   に準拠する（できるだけ）  
- RFC2132(http://tools.ietf.org/html/rfc2132)  に基づく値のチェック
  - もし与えられたオプション値がこれに従っていない場合はraise等する。（厳密化）
- 定数の排除（できるだけユーザに不用意に定数など見せたくない）
- 数値はすべてhex値に修正。

## Pio::Dhcp 新仕様
### クライアントメッセージオプション対応表

|Option|Discover|Request|Inform|Release|Decline|
|---------|---------|---------|---------|---------|---------| 
|destination_mac|TBD|TBD|TBD|TBD|TBD|
|source_mac|TBD|TBD|TBD|TBD|TBD|
|ether_type|TBD|TBD|TBD|TBD|TBD|
|ip_version|TBD|TBD|TBD|TBD|TBD|
|ip_header_length|TBD|TBD|TBD|TBD|TBD|
|ip_type_of_service|TBD|TBD|TBD|TBD|TBD|
|ip_total_length|TBD|TBD|TBD|TBD|TBD|
|ip_identifier|TBD|TBD|TBD|TBD|TBD|
|ip_flag|TBD|TBD|TBD|TBD|TBD|
|ip_fragment|TBD|TBD|TBD|TBD|TBD|
|ip_ttl|TBD|TBD|TBD|TBD|TBD|
|ip_protocol|TBD|TBD|TBD|TBD|TBD|
|ip_header_checksum|TBD|TBD|TBD|TBD|TBD|
|ip_source_address|TBD|TBD|TBD|TBD|TBD|
|ip_destination_address|TBD|TBD|TBD|TBD|TBD|
|udp_src_port|TBD|TBD|TBD|TBD|TBD|
|udp_dst_port|TBD|TBD|TBD|TBD|TBD|
|udp_length|TBD|TBD|TBD|TBD|TBD|
|udp_checksum|TBD|TBD|TBD|TBD|TBD|
|boot_message_type|TBD|TBD|TBD|TBD|TBD|
|message_type|TBD|TBD|TBD|TBD|TBD|
|hw_addr_type|TBD|TBD|TBD|TBD|TBD|
|hw_addr_len|TBD|TBD|TBD|TBD|TBD|
|hops|TBD|TBD|TBD|TBD|TBD|
|transaction_id|TBD|TBD|TBD|TBD|TBD|
|seconds|TBD|TBD|TBD|TBD|TBD|
|bootp_flags|TBD|TBD|TBD|TBD|TBD|
|client_ip_address|TBD|TBD|TBD|TBD|TBD|
|your_ip_address|TBD|TBD|TBD|TBD|TBD|
|next_server_ip_address|TBD|TBD|TBD|TBD|TBD|
|relay_agent_ip_address|TBD|TBD|TBD|TBD|TBD|
|client_mac_address|TBD|TBD|TBD|TBD|TBD|
|server_host_name|TBD|TBD|TBD|TBD|TBD|
|boot_file_name|TBD|TBD|TBD|TBD|TBD|
|server_identifier|TBD|TBD|TBD|TBD|TBD|
|renewal_time_value|TBD|TBD|TBD|TBD|TBD|
|rebinding_time_value|TBD|TBD|TBD|TBD|TBD|
|ip_address_lease_time|TBD|TBD|TBD|TBD|TBD|
|client_identifier|TBD|TBD|TBD|TBD|TBD|
|requested_ip_address|TBD|TBD|TBD|TBD|TBD|
|parameters_list|TBD|TBD|TBD|TBD|TBD|

### サーバメッセージオプション対応表

|Option|Offer|Ack|Nack|
|---------|---------|---------|---------|
|destination_mac|TBD|TBD|TBD|
|source_mac|TBD|TBD|TBD|
|ether_type|TBD|TBD|TBD|
|ip_version|TBD|TBD|TBD|
|ip_header_length|TBD|TBD|TBD|
|ip_type_of_service|TBD|TBD|TBD|
|ip_total_length|TBD|TBD|TBD|
|ip_identifier|TBD|TBD|TBD|
|ip_flag|TBD|TBD|TBD|
|ip_fragment|TBD|TBD|TBD|
|ip_ttl|TBD|TBD|TBD|
|ip_protocol|TBD|TBD|TBD|
|ip_header_checksum|TBD|TBD|TBD|
|ip_source_address|TBD|TBD|TBD|
|ip_destination_address|TBD|TBD|TBD|
|udp_src_port|TBD|TBD|TBD|
|udp_dst_port|TBD|TBD|TBD|
|udp_length|TBD|TBD|TBD|
|udp_checksum|TBD|TBD|TBD|
|boot_message_type|TBD|TBD|TBD|
|message_type|TBD|TBD|TBD|
|hw_addr_type|TBD|TBD|TBD|
|hw_addr_len|TBD|TBD|TBD|
|hops|TBD|TBD|TBD|
|transaction_id|TBD|TBD|TBD|
|seconds|TBD|TBD|TBD|
|bootp_flags|TBD|TBD|TBD|
|client_ip_address|TBD|TBD|TBD|
|your_ip_address|TBD|TBD|TBD|
|next_server_ip_address|TBD|TBD|TBD|
|relay_agent_ip_address|TBD|TBD|TBD|
|client_mac_address|TBD|TBD|TBD|
|server_host_name|TBD|TBD|TBD|
|boot_file_name|TBD|TBD|TBD|
|server_identifier|TBD|TBD|TBD|
|renewal_time_value|TBD|TBD|TBD|
|rebinding_time_value|TBD|TBD|TBD|
|ip_address_lease_time|TBD|TBD|TBD|
|subnet_mask|TBD|TBD|TBD|

### クライアントメッセージのブロードキャストとユニキャストの使用
- ユニキャスト：
  - DHCP サーバーのアドレスを知っている場合、クライアントは DHCPDISCOVER や DHCPREQUEST で IP ブロードキャストアドレスではなく、そのユニキャストアドレスを使用しても良い。
  - クライアントはサーバーに DHCPRELEASE メッセージをユニキャストする
  - 既知の DHCP サーバーに DHCPINFORM メッセージを送信する場合にも、クライアントはユニキャストを使用しても良い。

- ブロードキャスト：
  - DHCPサーバのアドレスを知らない限り、ＤＨＣＰクライアントは、DHCPDISCOVER、DHCPREQUEST、DHCPINRFORM の各メッセージをブロードキャストする
  - クライアントがサーバーによって提供された IP アドレスを使用を拒否するなら、クライアントは DHCPDECLINE メッセージをブロードキャストする。 

### サーバーメッセージの振る舞いについて

## 変更点：  

## TODO 
### 仕様の検討
- [ ] ノンデグレーション確認
- [ ] `.new`であたえられるオプションの種類の決定
- [ ] パーズした後に利用できるメソッドの種類の決定
- [ ] デフォルト値についての決定
### 既存の指摘点の対応について：  
- [ ] Delete `_hash' from *_hash methods (#60)
- [x] Make private methods private (#64)
- [ ] Rename `*_hash` methods to `*_tlv` (#63)
- [x] BootRequestOptions => BootRequest::Options (#53)
- [x] Remove the redundant `Dhcp` prefix from Dhcp::DhcpField (#52)
- [x] Refactor DHCP specs using rspec_given (#39)

## 質問事項
- message.rbとかで書いている、`def_delegators`が`packet_in`が提供するメソッドとかぶっています。必要ですか？
> > 得られる情報が同じものをPioで提供する必要があるかと考えてます。
> > たとえば、packet_in.macsaと@frame.source_macって得られる情報同じです。
> 
> たぶん心配しているのは、`Trema::PacketIn` と Pio でコードの重複が出ないかということだと思いますが、`Trema::PacketIn` の各メソッドは Pio の各クラスのメソッドを呼ぶだけという実装になるので、コードの重複は出ないはずです。
> 
> たとえば `packet_in.macsa` は実装上は Pio の `dhcp.macsa` を呼ぶだけなので、Pio の `#macsa` はどっちみち必要です。
> 
> 
> > なので、Trema::PacketInから得られないL7とかのペイロード情報を提供するに
> > しておけば、如何かと。
> 
> こちらも、もしそういうメソッドがあれば `PacketIn` から参照できるようにしようと思っています。

- テスト方針について：これまでジェネレータが出したフレームでテストを書くことがありましたが、今回は実際にスニファしたデータをベースにテストを作成したいと考えています。そのため、テストコードからはどのオプションがmandatoryかoptionalかわからなくなるかもしれませんが、その辺ドキュメントすればいいかなと思います。また、そういった場合、最小限・最大のオプションそれぞれのテストをどうしようか、言ったところはありますが、ご意見・ご指摘お願いします。
> テストコードの書きかたで mandatory/optional も表現できるので、コードと分離したドキュメンテーションはいらないはずです。たとえば Arp のテストでやっているように、
> 
>     mandatory なオプションを指定しなかったら例外が上がる
>     optional なやつを指定しなかったらデフォルトになる
> 
> と書けばよいわけで、こうしておけば rspec -fs の実行結果がオプションのドキュメントになります。RSpec > はテストコードを仕様 (spec) として読めるようにという目的のツールなので、こういう使いかたをしましょう。

- dhcpのtlvデータを読み込むためのメソッドを自動生成などして、行数をうまいこと減らしたいです
````ruby
      def message_type
        get_tlv_field(Dhcp::MESSAGE_TYPE_TLV)
      end

      def server_identifier
        get_tlv_field(Dhcp::SERVER_IDENTIFIER_TLV)
      end

      def client_identifier
        get_tlv_field(Dhcp::CLIENT_IDENTIFIER_TLV)
      end
````
これが今後要望などあると、追加するためのメソッドとともに増えてくるので、
行数と修正にかかる時間、デグレーションを減らしたい考えです。いい方法などあれば教えてください。