OS X 10.11のDNS64らしき動きをするDNSサーバを作る

OS X 10.11 El Capitanのインターネット共有機能を使うと,IPv6インターネット接続性が無い環境であってもIPv6 only networkを作ることができる,とAppleは言っている.どういう仕組みなのか少し調べてみたところ,AAAA filterに似た仕組みをDNS64に組み込んでいるらしい.

具体的には,あるドメイン名に対するAAAAクエリがDNS64サーバに来た際,AレコードをNAT64形式に変換してAAAAレコードに詰め直したものを返す,という動作をするようである.通常のDNS64ではAAAAレコードが実在すればそれを返すが,こちらはAAAAレコードが実在してもそれを無視してAレコードを参照する,という違いがある.

この仕組みにより,ルータとなるMacIPv6接続性を持っていなくても,インターネット共有機能を利用している端末に対してIPv6接続性を提供できる.

これと同様の環境をMac以外でも構築できないかと思い,まずはOS XのDNS64と同じような挙動をするDNSサーバを作ってみた.

Code

AAAAクエリが来たら問答無用でそれを握り潰してAクエリを投げ,応答をNAT64形式のアドレスに変換した上でAAAAレコードに詰めて返す. 実際の名前解決廻りの処理にはPythonのTwistedに含まれるNamesを使用している.起動部分のベースはTwistedのサンプルコードから.

A DNS64 resolver with AAAA filter-like behavior (s ...

Example

上で挙げたDNSサーバが127.0.0.1:10053で動いているとする.

$ dig @127.0.0.1 -p 10053 www.facebook.com. A +short
star.c10r.facebook.com.
173.252.120.6
$ dig @127.0.0.1 -p 10053 www.facebook.com. AAAA +short
star.c10r.facebook.com.
64:ff9b::adfc:7806

普通のDNSサーバの応答と比べると、実在するAAAAレコードが無視されていることが分かる.

$ dig @8.8.8.8 www.facebook.com. A +short
star.c10r.facebook.com.
31.13.79.246
$ dig @8.8.8.8 www.facebook.com. AAAA +short
star.c10r.facebook.com.
2a03:2880:f00d:1:face:b00c:0:1

あとはTAYGA等でNAT64を行えば,簡易的なテスト用環境として使えるレベルにはなりそう.

Norikraでそこそこ手軽にNetFlow解析

去年のJANOGで別の方が発表された内容とダダ被りではあるものの,折角なのでメモ程度に書いておく.

概要

  • データセンタネットワークやバックボーンネットワークの運用をやっていると,インターネットに出入りするトラフィックの内訳 (どのISP向けのトラフィックが多いのか,どの回線にトラフィックが乗っているか,等) を見たいことがよくある
  • NetFlowをNorikraで解析し,その結果をGrowthForecastに流し込むと,トラフィックの内訳をそこそこ手軽に見ることができる
  • 定常的なモニタリング用途以外に,突発的なトラブルシューティングにも使えていろいろ便利

背景

自前でAS (Autonomous System) を運用している事業者では,トラフィックのコントロールの観点から,「どのAS向けのトラフィックがどのくらいあるのか」「このASとはどの回線を使って通信しているか」といった情報を知りたいケースがよくある (と思う). このような場面では,NetFlowに代表されるトラフィック統計情報を収集する機能が用いられる.NetFlowを使うと,ある機器を通過した通信フローに関する情報 (src/dst IP, src/dst ASなど) が取得できるので,これらの情報を解析すればトラフィックの内訳を知ることができる.

NetFlow解析を行う場合,一般的には次のような選択肢がある.

回線ごとのトラフィック集計を行いたいケースや複数ASを運用しているようなケースでは,柔軟性の観点から自前での構築を選びたくなる.が,一から全てやるのは結構大変なので,解析はNorikraに,結果のグラフ化はGlowthForecastにやってもらうことにする.

実現方法

構成はNorikra+Fluentd+GrowthForecast.NetFlowコレクタはfluent-plugin-netflowを使用する.

f:id:yunazuno:20150331024123p:plain

Fluentdの設定はこんな感じ.fluent-plugin-forest使ってるので、AS数や回線数によってはメモリの消費に注意.

## <- NetFlow
<source>
  type netflow
  tag  netflow.flow

  bind 0.0.0.0
  port 2055
</source>

## -> Norikra
<match netflow.*>
  type     norikra
  norikra  127.0.0.1:26571

  remove_tag_prefix  netflow
  target_map_tag     true
</match>

## <- Norikra
<source>
  type    norikra
  norikra 127.0.0.1:26571
  <fetch>
    method     sweep
    tag_prefix norikra.query
    tag field  _gf_key
    interval   1m
  </fetch>
</source>

## -> GrowthForecast
<match norikra.query.**>
  type forest
  subtype growthforecast
  remove_prefix norikra.query

  <template>
    gfapi_url http://127.0.0.1:5125/api/
    graph_path netflow/as${tag_parts[0]}/${tag_parts[1]}_bps_${tag_parts[2]}
    name_keys traffic_bps
  </template>
</match>

この構成でNorikraに下のようなクエリを登録した後NetFlowを流し込めば,ひとまずAS毎の集計結果がNorikraで出力されるはず (ただしこの時点では全ての回線の合計値が出力される).

select
    src_as, dst_as,
    (SUM(in_bytes * sampling_interval) * 8) / 60 as traffic_bps,
from
    flow.win:time_batch(60 sec, 0L)
group by
    src_as, dst_as

次は回線毎の集計.SNMP ifIndexと回線の対応関係やフローの向きのチェックを全てEPLで書くのは辛いので,簡単なUDFを作った.

github.com

norikra-udf-netflowでは,NetFlowのデータを扱い易い形式に変換するためのいくつかの関数を定義している.内容は下の表のような感じ.変換に必要な情報はインストール前にあらかじめdefinition.yamlに書いておく必要がある.

Function Description
NFDirection(src_as, dst_as) フローの向き ("in" or "out")
NFOppositeASN(src_as, dst_as, ipv4_src_addr, ipv4_dst_addr) 対向のAS番号
NFRouter(host_ipaddr) NetFlowの生成元のルータの名称
NFCareer(host_ipaddr, ifindex_in, ifindex_out, flow_direction) フローが通過した回線の名称

これらの関数を使って回線毎の集計をやる場合,こんな感じのクエリになる.

Query name: flow_aggregator, Group: LOOPBACK(aggregated_flow)

select
    NFOppositeASN(src_as, dst_as, ipv4_src_addr, ipv4_dst_addr) as opposite_as,
    NFCareer(host, input_snmp, output_snmp, NFDirection(src_as, dst_as)) as career,
    (SUM(in_bytes * sampling_interval) * 8) / 60 as _traffic_bps,
    NFDirection(src_as, dst_as) as flow_direction
from
    flow.win:time_batch(60 sec, 0L)
group by
    OppositeASN(src_as, dst_as, ipv4_src_addr, ipv4_dst_addr),
    NWPoPCareer(host,(code) input_snmp, output_snmp, FlowDirection(src_as, dst_as)),
    FlowDirection(src_as, dst_as)

Query name: traffic_as_per_career

select
    opposite_as,
    career,
    case LAST(_traffic_bps) when null
        then 0
        else LAST(_traffic_bps)
    end as traffic_bps,
    (opposite_as || "." || career || "." || flow_direction) as _gf_key
from
    aggregated_flow.win:time_batch(60 sec, 0L)
group by
    opposite_as,
    career,
    (opposite_as || "." || career || "." || flow_direction)

一度LOOPBACKで集計済みデータを別target(上の例だとaggregated_flow)に流しているのは,後で突発的に解析用のクエリを投入する時にデータを再利用しやすくするため.NorikraのクエリでFluentdのタグを作ってる点がちょっとアレ. ここまで全て上手く動いていれば,GrowthForecastでグラフが作られているはず.

これまでの運用状況など

Norikra

去年末に投入後,約3ヶ月稼働中.単純な時間平均で 10000 event/sec (=10000 flow/sec) 弱程度のeventを流し込んでいる.JVMが数回突然死したり,CPUコアが多い環境で起動しないトラブルに遭遇した*1以外は概ね安定している.現在heapに30GB弱割り当てていて,GCのタイミングで結構な時間止まっているので何とかしたい.

Flow collector

fluent-plugin-netflowが手元の環境だと1プロセスあたり8000 flow/sec 近辺でsocket bufferが埋まる速度に追い付けなくなった.順当に行けばfluent-plugin-multiprosessでポートを分けて負荷を分散させる場面だと思われる.が,これをやるとルータによってNetFlowの送り先が変わってしまうので後々大変そう,ということになり*2nfcollectという超簡易的なNetFlow collectorを書いて凌いだ.

github.com

nfcollectはフローの到着順を保持しない代わりにそこそこのスループットが出るようになっていて,flow-genで試したところ同じ環境で30000 flow/secぐらいまでは何事も無く捌いてくれた.こちらを使った場合でもある時点でfluent-plugin-netflowと同じ問題が発生することに変わりは無いものの,現状30000 flow/secを越えることをはまず無さそうということで一旦目を瞑っている.

その他諸々

当初は単純な集計用途だけを想定していたが,使い始めてから暫くすると,トラブルシューティング的な用途にも使えそうなことが分かった.例えば本来存在するはずの通信が見えない時や,特定ISPから特定アドレス帯への通信が上手くいかないといった時に,その問題に合わせたクエリをその場で書いてNorikraに投入してしばらく待てば,パケットキャプチャより手軽に調査ができる*3.ちょっとしたクエリのサンプル集のようなものを事前に書いておけば,大抵の人は問題無く扱えると思う.

まとめ

NetFlow解析,割と難しいもの扱いされているという噂を耳にしますが,やってみると色々便利なのでまずは試してみると良いと思います.

*1:単純にエラーメッセージちゃんと読んでなかったのが原因.現在はNorikra側でも対策が行われている.

*2:BGPルータの設定変更,あまりやりたい仕事ではないので

*3:ただし対象の通信が平常時からある程度の量流れている場合に限る

モダンなcipher suiteに対応していないHTTPSクライアントを搭載したロードバランサが存在する問題について

実は一般的に知られている問題なのかもしれないものの,最近知ったのでメモ.

概要

  • 一部のロードバランサアプライアンスにおいて,搭載されているHTTPSクライアントが所謂"モダンな"暗号スイートに対応していないものが存在する
  • このため,特定条件下において意図せずヘルスチェックに失敗し,場合によってはVIPがdownする
  • 現時点では直ぐに影響が出る訳では無いものの,気に留めておかないと近い将来痛い目を見そう

背景

DSR構成のロードバランサでSSL(TLS)を使用する場合,SSLの終端はロードバランサではなくリアルサーバの仕事になる*1

この環境下においてリアルサーバの死活監視にLayer 7方式を使用するパターンを考える.このパターンでは,ロードバランサからリアルサーバにSSLで接続し,HTTP GETないしHEADリクエストを投げて意図したレスポンスが返ってくれば,リアルサーバは正常動作しているとみなされる*2

f:id:yunazuno:20150227225611p:plain

問題となるケース

リアルサーバ上で稼働しているWebサーバ(Apache, Nginx等)において,セキュリティの要件上Mozillaが言うところの所謂"モダンな"cipher suiteのみ使用可能にする (=RC4や3DESに依存したCipher suiteを使用不可にする) ケースを考える.

この時,ロードバランサがヘルスチェックに使用するHTTPS Clientが先に述べたモダンなcipher suiteに非対応だと,リアルサーバとのHandshakeに失敗してしまう.その結果,実際は正常動作しているリアルサーバが死んでいると誤認識されてしまい,意図せずリアルサーバないしVIPがdownする.

どう対応するか

ぱっと思い付く対応策はこんな感じ:

  1. Layer 7ヘルスチェックを諦めてLayer 4ないしLayer 3でのヘルスチェックに切り替える
  2. 要件を曲げてCipher suiteの制約を緩める
  3. ロードバランサ側をモダンなCipher suiteに対応させる
    • ベンダに機能追加してもらう,対応OSにバージョンアップする,対応LBに入れ替える,など

商用サービスでモダンもの限定にするケースはまだレアだとは思うものの,気に留めておく必要がありそう.特に,現在使用しているCipher suiteがある時点から安全でなくなる可能性を考慮すると,上述したケースでどういう対応を取るか,ぐらいは考えておいたほうが良さそう.

また,今回のHTTPS Client側cipher suiteの問題に限らず,ロードバランサでSSLを終端した場合のCipher suiteやTLSバージョンの対応状況等,ロードバランサ+SSLには諸々のハマりどころが存在するようなので,運用している人は頑張りましょう.

*1:一般的なプロキシ構成下では,SSLはロードバランサで終端する

*2:プロキシ構成下では,リアルサーバへの接続にSSLは不要

第3回「さくら石狩DC見学ツアー」に行ってきたレポート

気付けば石狩から帰ってきて1週間も経ってしまったものの、まだなんとか記憶に残っているうちに「さくら石狩DC見学ツアー」に参加してきたレポートを。

今回のツアーの応募理由

「他所のデータセンターの中がどうなっているか見てみたい」というのがツアーに応募した最大の理由。 DC内・DC間ネットワーク(完全オンプレ)の面倒を見る仕事を今年の4月に始めてから約8ヶ月経った今、DC内での機器設置や配線をやったりDC調達の話を聞き齧ったりしたことで、DC絡みの諸々をまずは一通り眺めたような状態ではあった。が、それはあくまでこれまで見てきたDCに関してのことで、それらが一般的に見てどうなのかとか、他にどういう考え方・方法があるのか、といった点がここ最近の疑問だった。データセンターその他の低いレイヤのインフラ技術については中々知見が公開されないこともあって他者との比較が難しく、割と悶々としていたような状況だった。

そんな中で、はてブに上がってきていた募集の記事を見かけて応募したら運良く当選した、という次第。普通、DCの中を見せてくれる組織はなかなか無いので、今回当選して本当にラッキーだった。

全体を通しての感想

石狩DCの第一印象

石狩DCの建屋。「あれ、思ってたより小さい・・・?」というのが、バスでの到着時の最初の感想。実際はラック数(500ラック/棟)を考えれば妥当、というかむしろ少し大きめぐらいのサイズだが、何故か超広い土地に超デカい建物がドカンと建っている、みたいなものを勝手に想像してしまっていたので拍子抜けだった・・・。

(写真は特に断りが無い限りはてなさんにご提供。)

石狩DC全景 (後ろから)

後から冷静に考えてみると、地方のDCであればわざわざ建物を大きく作る必要が無いのだなぁ、と。都心のDCだと土地が狭いが故、縦に建物を広げるしかないので、最初にデカい建物をドカンと建てる方法になる。一方地方であれば横に広げることができるので、小さい単位で徐々に改善しつつ作ればよい、ということで納得。この「小さい単位で色々試しつつ改善する」というのは、今回の見学を通して随所で感じられた。1号棟->2号棟と順に見て廻ると、空調方式や電源まわりの構成であったり建物自体の設計であったりの改善がはっきりと分かるのは面白かった。さくらの方も仰っていたような気がするが、このあたりは自前DC+自前サービス向けだからこそできることで、借り物のDCであったりハウジング向けだったりすると中々難しそう。

DC内部の諸々

DC内部について。配線の処理とか機器の選定とか惹かれる箇所が多々あった。が、一番惹かれたのはサーバルーム入口の引き戸タイプの自動ドア!

自動ドア!段差無し!

DCで作業したことがある人であればご存知かと思うが、普通のサーバルームの入口は観音開きになっていて、かつ消化用ガスが漏れるのを防ぐために床部分が盛り上がっている。ここを荷物を載せた台車で乗り越えるのは割と神経を使う作業で、滑り落ち防止のベルトを掛け忘れて台車からサーバが飛び立ったりSFPが舞い落ちたりと、場合によっては割と笑えないことになる。この自動ドアは2号棟から導入したとのことだったが、現場の作業者の声がきちんとフィードバックされている感があって非常に好印象だった。これも一気にドカンと建てるのではなく小さく改善しつつ作ることの強みだと思う。

休憩スペース。過去に見てきたDCの休憩スペースはもっとこう、殺伐とした感じだったのでなんとなく落ち着かなかったものの、冷静に考えてこれが正常なような。

殺伐としていない休憩スペース

地理的な距離や立地

東京23区内から石狩DCまで約4時間弱?といったところ。もっと時間が掛かるかと思っていたが、意外と近かった。DCの近所には海底ケーブルの陸揚げ局があってそこから直にケーブル引けたり、新しく発電所が建つとのこと。DCの立地としてはかなり良さそう。

何の変哲も無い陸揚げ局

さくら田中社長のこと

噂には聞いていたが、さくらの田中社長が技術について嬉々として話す姿は衝撃的だった。仮想化環境でのIO云々の話をしたかと思えばサーバラックの熱処理の話をしてみたりと、ただひたすら凄い、の一言に尽きる。また、「DCがどうこうではなく、あくまで提供しているサービスの中身で勝負する。サービスに直結しないノウハウの部分は公開する。」といった旨の事を仰っていたのが非常に印象に残っている。特に前半部分については、インフラの特に低いレイヤの部分をやっているとつい忘れがちなように思う。

嬉々として喋る田中社長

その他諸々

新千歳に到着して早々、沿岸バスの洗礼を受ける。

車体に描かれていた3人の中ではこの子がよい

お昼ごはん。初日分、蓋を開けたところの写真も撮るつもりが開けた瞬間にそのことを忘れて食べ始めてしまったらしく、蓋付きの写真しか残っていなかった。もちろん非常に美味。

1日目のおひるごはん

流石に2日目ともなると冷静で、写真がちゃんと残っていた。

懇親会でLTしたらMackerel Tシャツを頂いた。はてなさんありがとうございます!(これだけ自分で撮影)

はてなさんありがとうございます

ツアー前日も1日中DCに籠って作業していた*1ら、そのダメージで初日夜は体力が残っておらず、懇親会2次会に参加しそびれた。逃した蟹は大きい。

おわりに

今回石狩DCツアーに参加して、当初の目的が果たせたのはもちろん、思っていた以上に色々なものを見たり聞いたりすることができた。見せてもらいっぱなしなのもアレなので、今後何らかの形で還元できればなぁ、といったことを考えている。

もし今後同じようなDC見学ツアーが開催されるのであれば、普段データセンターに関わっている人であっても、普段やっていることを客観的に振り返るという意味で参加すると良いと思う。普段DCに出入りしている人であっても、ベンダの保守やってるような人で無い限り複数の組織のサーバルームに出入りする機会は殆ど無いだろうし、そういう人こそ参加してみると意外な発見があるような気がする。 もちろん、データセンター見たことも入ったことも無い、という人も是非一度は見ておくべきだということは言うまでもない。

最後に、今回このような機会を作っていただいたさくらインターネットはてなの皆様、ご案内頂いた石狩開発さん、JTBさん、本当にありがとうございました!

*1:障害対応とも言う

VyOS+fluent-plugin-netflowでNetFlowを取得する

NetFlowまわりの諸々を触りたいとき,VyOSだとハードウェアを別途用意しなくても手軽に色々試せて便利.

VyOSの設定

PlixerManageEngineの記事を見つつ,下の設定さえあればとりあえず動く.Timeout値まわりは環境に合わせて要調整.バージョンはVyOS 1.0.4.

set system flow-accounting netflow version 5
set system flow-accounting interface eth0
set system flow-accounting netflow server 192.168.56.1 port 2055

# system flow-accounting netflow timeout以下は必要に応じて設定
set system flow-accounting netflow timeout expiry-interval 60

なお,BGPのOrigin ASやnext-hopも併せて取得したい場合,/opt/vyatta/bin/sudo-users/quagga_gen_as_network.plあたりを実行してやる必要があるように見える (未調査).

現在のフロー情報はshow flow-accountingで確認できる.

vyos@router1:~$ show flow-accounting
flow-accounting for [eth0]
Src Addr        Dst Addr        Sport Dport Proto    Packets      Bytes   Flows
172.16.0.2      10.1.1.100      0     0      icmp        106       8904       1

Total entries: 1
Total flows  : 1
Total pkts   : 106
Total bytes  : 8,904

NetFlowコレクタで確認

ここでは fluent-plugin-netflow をNetFlowコレクタとして使用する.ひとまず動作確認として,受け取ったNetFlowエントリをそのままstdoutに出力させる.

<source>
    type netflow
    tag netflow.event
    
    bind 0.0.0.0
    port 2055
</source>

<match netflow.*>
    type stdout
</match>

このconfigでFluentdを起動し,VyOS側でフローの収集対象にしたインタフェース(上の例ではeth0)に適当なトラフィックを流せば,fluentd側でフロー情報が収集できていることが確認できる.

collector$ fluentd
(snip)
2014-10-14 22:22:22 +0900 netflow.event: {"version":"5","flow_seq_num":"0","engine_type":"0","engine_id":"0","sampling_algorithm":"0","sampling_interval":"0","flow_records":"1","ipv4_src_addr":"172.16.0.2","ipv4_dst_addr":"10.1.1.100","ipv4_next_hop":"0.0.0.0","input_snmp":"2","output_snmp":"0","in_pkts":"92","in_bytes":"7728","first_switched":"2014-10-14T13:21:44.789Z","last_switched":"2014-10-14T13:22:02.789Z","l4_src_port":"0","l4_dst_port":"0","tcp_flags":"0","protocol":"1","src_tos":"0","src_as":"0","dst_as":"0","src_mask":"0","dst_mask":"0","host":"192.168.56.90"}

手軽に試せて便利なので,もう少し色々と触ってみたい.

Client Subnet in DNS Requests (draft-vandergaast-edns-client-subnet) を読んだ

先日googleとakamaiは仲良し?というエントリを読んだとき,恐らくEDNS Client Subnet Optionだろうとは思った*1ものの,EDNS Client Subnet Optionの動作をきちんと把握してはいなかったので,この機会にdraftを読んでみた.

概要

  • DNS権威サーバの中には,クエリ送信元のDNSキャッシュサーバのIPアドレスによって応答を変えるものが存在する
  • クライアントとキャッシュサーバのネットワーク的な距離が離れている場合,権威サーバは適切でない応答を返す恐れがある
  • キャッシュサーバから権威サーバへのクエリ内にクライアントのIPアドレスの一部を含めることで,権威サーバがより適切な応答を返すことを実現する
  • 利用にはキャッシュサーバと権威サーバの対応が必要 (クライアントの対応は不要)

問題: Public DNSと"不適切"な応答の発生

CDN事業者やWebサービス事業者が運用しているDNS権威サーバの中には,問い合わせ元のDNSキャッシュサーバのIPアドレスの応じて応答を変えるものが存在する.例えば,日本のISP Aのキャッシュサーバからの問い合わせに対しては日本に設置されたエッジサーバのIPアドレスを返せば,クライアントからエッジサーバへのレイテンシは抑えられると考えらえる.

ここで問題となるのは,クライアントとキャッシュサーバのネットワーク的な距離が離れている場合に,適切でない応答が発生することである.例えば,日本のISP A内のクライアントが台湾のキャッシュサーバを利用していた場合,権威サーバは台湾に設置されたエッジサーバのIPアドレスを返してしまう.これにより,クライアントにとってより適切なエッジサーバが存在するにも関わらず適切でないサーバに接続してしまい,結果としてパフォーマンス上の問題が発生する.

以前であればISPのキャッシュサーバを利用することが殆どであったため問題は発生しなかった*2.しかし,最近はGoogle Public DNSをはじめとした公開DNSキャッシュサーバの普及によって,クライアントとDNSキャッシュサーバの距離が離れているケースが生じるようになった。その結果、上述したような問題が顕著化するようになった.

解決策: クライアントのIPアドレスをクエリに埋め込む

この問題を解決するために考えらえたのが、キャッシュサーバから権威サーバへのクエリ内にクライアントのIPアドレスを埋め込むことで,権威サーバが適切な応答を返せるようにする,というもの.

EDNS Client Subnet Optionに対応したキャッシュサーバは,クライアントのIPアドレスの一部 (ADDRESS) とプレフィクス長 (SOURCE NETMASK; 標準ではIPv4で/24*3 ) をOPT RRに埋め込んで権威サーバに送信する.

f:id:yunazuno:20140817180611p:plain

これを受け取ったEDNS Client Subnet Option対応権威サーバは,クライアントのIPアドレスに応じた応答を返す.このとき,適切な応答を生成するのに必要だったプレフィクス長 (SCOPE NETMASK) を含める.たとえば,キャッシュサーバからSOURCE NETMASKが24のIPアドレスが与えられたとして,権威サーバは/22の情報しか使用しなかった場合,SCOPE NETMASKは22となる.

f:id:yunazuno:20140817180615p:plain

キャッシュサーバはクライアントに対して応答を返すと共に,権威サーバからの応答を自身のキャッシュに格納する.このときのキーは (FAMILY, ADDRESS, SCOPE NETMASK) の組となる.

ちなみに,EDNS Client Subnet Optionに対応していない権威サーバにオプション付きのクエリを投げたとしても,単純に未対応のオプションとして無視されるだけである.

実際に試してみる

Client Subnet Option付きクエリを投げる機能を追加するpatchを当てたdigで実験.ns1-4.google.comはClient Subnet Option対応権威サーバとして謳われているので,今回はこれを使用.

まずは適当な日本国内のIPアドレス帯でクエリを投げる.

$ dig @ns1.google.com. www.google.com. +norecurse +client=122.102.128.0/24

; <<>> DiG 9.9.3 <<>> @ns1.google.com. www.google.com. +norecurse +client=122.102.128.0/24
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42644
;; flags: qr aa; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 122.102.128.0/24/21
;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         300     IN      A       173.194.126.209
www.google.com.         300     IN      A       173.194.126.210
www.google.com.         300     IN      A       173.194.126.211
www.google.com.         300     IN      A       173.194.126.212
www.google.com.         300     IN      A       173.194.126.208

;; Query time: 44 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Sun Aug 17 15:46:53 JST 2014
;; MSG SIZE  rcvd: 134

続いてシンガポールの適当なIPアドレス帯で同様に.

$ ./bin/dig/dig @ns1.google.com. www.google.com. +norecurse +client=118.189.0.0/24

; <<>> DiG 9.9.3 <<>> @ns1.google.com. www.google.com. +norecurse +client=118.189.0.0/24
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57557
;; flags: qr aa; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 118.189.0.0/24/20
;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         300     IN      A       74.125.130.104
www.google.com.         300     IN      A       74.125.130.105
www.google.com.         300     IN      A       74.125.130.147
www.google.com.         300     IN      A       74.125.130.103
www.google.com.         300     IN      A       74.125.130.106
www.google.com.         300     IN      A       74.125.130.99

;; Query time: 52 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Sun Aug 17 15:48:30 JST 2014
;; MSG SIZE  rcvd: 150

最後にイギリスの適当なIPアドレス帯で.

$ ./bin/dig/dig @ns1.google.com. www.google.com. +norecurse +client=212.140.0.0/16

; <<>> DiG 9.9.3 <<>> @ns1.google.com. www.google.com. +norecurse +client=212.140.0.0/16
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47653
;; flags: qr aa; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 212.140.0.0/16/18
;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         300     IN      A       74.125.230.112
www.google.com.         300     IN      A       74.125.230.115
www.google.com.         300     IN      A       74.125.230.114
www.google.com.         300     IN      A       74.125.230.113
www.google.com.         300     IN      A       74.125.230.116

;; Query time: 42 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Sun Aug 17 15:49:24 JST 2014
;; MSG SIZE  rcvd: 133

各クエリで得られたIPアドレス宛に東京からpingを打ってみたところ,下のような結果になった.

  • 173.194.126.209 (東京): 12ms
  • 74.125.130.104 (シンガポール): 80ms
  • 74.125.230.112 (イギリス): 230ms

東京-シンガポール,東京-イギリス間の一般的なRTTに近い値であることから,それぞれのクエリにおいてクライアントに近いサーバのアドレスが返されていると考えられる.

まとめ

EDNS Client Subnet Optionのdraftを読み,おおまかな動作の流れをまとめてみた.

現時点での対応状況として,キャッシュサーバ側ではGoogle Public DNSやOpenDNSが,権威サーバ側(コンテンツ提供側)ではAkamaiやCloudFrontが対応しているようである.逆に言うと,普通のキャッシュサーバや権威サーバが対応するメリットは無いので,特に気にする必要は無い.

ただし,Public DNSに限らずISPDNSキャッシュサーバが対応するとメリットが出るような場合もある.たとえば,この資料ではインドネシアのように国土が分散している場合におけるClient Subnetのメリットが述べれらている.また権威サーバ側でも,海外など広い範囲でサービスを提供していて,かつ海外にもPOP (Point of Presence) を設置しているような事業者の場合,対応することでクライアントのレイテンシ改善を実現できるかもしれない.

何にせよ,現状はパブリックなサーバ側実装が無い状態なので,まずはオープンかつ信頼できる実装が出てくると良いのかなぁ,と感じた*4

参考

*1:件のエントリにも同様の指摘のコメントが何件が付いている

*2:広いエリアを1箇所に設置したDNSキャッシュサーバでカバーするような運用を行っていたISPを除く

*3:ちなみにIPv6については未定(!)

*4:DNSサーバ自前実装は避けたいのが正直なところで、特にClient Subnetに関してはキャッシュ管理辛そう&セキュリティ的な地雷が埋まってそうなので尚更

grepcidr: IPアドレスをサブネットで一括grep

あるファイルに含まれるIPアドレスgrepしたいとき,/32なり/128なりのIPアドレスそのものではなく,サブネットでgrepできると便利な場面が多々ある.それらしき物が無いか探したところ,grepcidrというツールがあったのでメモ.

導入

単純にソースコードをダウンロードして make & make install するだけ.

$ wget http://www.pc-tools.net/files/unix/grepcidr-2.0.tar.gz
$ tar xvf grepcidr-2.0.tar.gz
$ cd grepcidr-2.0
$ make
# make install

使い方

たとえば下のようなファイルがあるとする.

$ cat ipaddr.txt
192.0.2.0/24
192.0.2.0/25
192.0.2.128/25
192.0.2.254/32

2001:db8::/32
2001:db8:a:1::/64
2001:db8:a:2::/64
2001:db8:a:1::1/128

これが色々なpatternでgrepできる.

$ grepcidr 192.0.2.0/24 ipaddr.txt
192.0.2.0/24
192.0.2.0/25
192.0.2.128/25
192.0.2.254/32
$ grepcidr 192.0.2.1/25 ipaddr.txt
192.0.2.0/24
192.0.2.0/25
$ grepcidr 192.0.2.128/25 ipaddr.txt
192.0.2.128/25
192.0.2.254/32
$ grepcidr 2001:db8:a::/48 ipaddr.txt
2001:db8:a:1::/64
2001:db8:a:2::/64
2001:db8:a:1::1/128

かなり便利.