NATは意外と難しい

以下の議論はNATでもNAPTでも成り立つので、どちらの場合でも単にNATと書くことにします。

ViptelaのIPsecの認証方法はauthentication-typeというパラメータで指定することができます。例えばこんな感じです。

ah-sha1-hmac、sha1-hmacはほぼ自明だと思いますが、ah-no-idという見慣れない設定は何のためにあるのでしょう?

一般的にはNATが途中にあるとAHは機能しません。NATが書き換えるフィールドがAHで守られているからです。しかし、Viptelaのアーキテクチャでは、仮にNATが途中にあっても変換前と後のIPアドレス、ポート番号をシステムが把握しているため、NAT環境であってもAHを使用することができるようになっています。ただし、NATで書き換えられるのがIPアドレスとポート番号だけであれば良いのですが、一部のNATの中にはIPヘッダのIdentificationフィールド(以下、IDフィールド)を書き換えるものがあります。ah-no-idというパラメータはこのようなNATデバイスがある場合に使用するもので、AHによる検証をする際にIDフィールドを無視する、という意味になります。

世の中のNATにIDフィールドを書き換えるNATがいるのか最初は疑っていたのですが、実際このようなNAT実装は存在するようで、少なくともApple AirMac ExtremeやAirMac Expressはそのように動くようです。実際、手元にあったAirMac Expressで試してみました。

のようにパケットを出してみて、NATされる前と後のパケットを比較してみました(ちなみに-SオプションをつけてSYNフラグを付けているのは、NATの実装の多くはTCPのステートをみていて、SYNパケットがくる前にパケットが来てもトランスレーションせずに廃棄するからです)。

NAT変換前(AirPort Express):

NAT変換後(AirPort Express):

このように確かにIDフィールドが12345から14428に変換されています。

比較のためにVyOSのNATを使って同じように試験をしてみましょう。

NAT変換前(VyOS):

NAT変換前(VyOS):

こちらはNAT変換前も変換後もIDフィールドは12345のままです。

今まで私はIDフィールドはデータグラムがフラグメントされた際のリアセンブルに使用されるためのものなので、途中の機器で書き換えられるべきではない、と考えていました。したがって、このようにIDフィールドまで書き換えるNATはかなり特殊で、いわゆるキワモノ(変態)であると思っていたのですが、先日 RFC6864 “Updated Specification of the IPv4 ID Field” を見つけて読んでみると、この考えが変わりました。

このRFCではIPデータグラムをAtomicなものとNon-Atomicなものに分類して考えています。

Atomicなデータグラム
まだフラグメントされておらず、その後もフラグメントされることのない(フラグメントが禁じられている)データグラム
Non-Atomicなデータグラム
すでにフラグメントされているか、今後フラグメントされる可能性のあるデータグラム

歴史的にはIDフィールドはリアセンブル以外の目的(例えば重複検知、など)での使い方も考えられていたものの、このRFCでは改めてIDフィールドはリアセンブル以外の目的で使うことを禁止することを明確化し、それに伴いAtomicなデータグラムに対してはIDフィールドに関する要求条件を緩めています(AtomicなデータグラムにIDフィールドは不必要なため)。しかしながら、Non-Atomicなデータグラムに対しては依然RFC791で規定されている「MDL (Max Datagram Lifetime、典型的には2分)の間は同じIDを使い廻してはいけない」という要求をしています。

これはNATのようないわゆるミドルボックスにはかなりややこしい問題です。本RFCでも、

NATs/ASMs/rewriters present a particularly challenging situation for
fragmentation. Because they overwrite portions of the reassembly
tuple in both directions, they can destroy tuple uniqueness and
result in a reassembly hazard. Whenever IPv4 source address,
destination address, or protocol fields are modified, a
NAT/ASM/rewriter needs to ensure that the ID field is generated
appropriately, rather than simply copied from the incoming datagram.

Specifically:
>> Address-sharing or rewriting devices MUST ensure that the IPv4 ID
field of datagrams whose addresses or protocols are translated
comply with these requirements as if the datagram were sourced by
that device.

(拙訳)NAT/ASM (Address-Sharing Mechanism)/rewriterはフラグメンテーションに困難をもたらす。両方向においてリアセンブルするためのタプルの一部を書き換えるため、タプルのユニーク性を担保することができなくなり、リアセンブルができなくなるからである。IPv4送信元、送信先アドレスまたはプロトコルフィールドが書き換えられる場合は、NAT/ASM/rewriterは単に元のデータグラムのIDフィールドをコピーするのではなく、IDフィールドが正しく生成されることを保障しなければならない。

具体的には、Address-sharingまたはrewriterデバイスがIPv4データグラムのアドレスやプロトコル番号を書き換えた場合は、IDフィールドが、あたかもデータグラムがそのデバイスから出たかのごとく、これらの要求条件を満たすようにIDフィールドを使わなければいけない。

と書かれています。確かによく考えると、送信元、送信先アドレス、プロトコルのタプルでのIDフィールドのユニーク性を担保するためには、アドレスを書き換えるNATデバイスは、元のパケットのIDフィールドを単純にコピーするべきではなく、あたかも自分が出したパケットかのようにユニーク性を担保したIDを新たに払い出す必要があります。経験的には多くのNATデバイスはVyOS (多くのLinuxベースのもの同様でしょう)のように動くように思いますが、Apple AirMac Extreme/Expressの動作の方が正しい(というか素性がよろしい)ということになります。

Appleさん、今まで変態扱いしてごめんなさい。

Catch-22

本日、iPhoneのバッテリーがなくなってしまい、ソフトバンクショップに駆け込んだ。モバイル・バッテリーは持っていたものの、Lightningケーブルを忘れてしまったので充電することができなかったのだ。ショップで充電はしてくれたのだが、その間仕事ができないので、ソフトバンクショップで繋がる無線LANを探すも、残念ながら無料で繋がるものはなし。0001softbankというSSIDは繋がるものの、ソフトバンクのデバイスでないと使えないので、Mac/PCはダメ。

一方、SWS1dayというSSIDはソフトバンクのWi-Fiスポットで、ソフトバンク以外の端末でも一日467円で使えるようだ。

しかたない、何事も経験かと思い、これを使おうと試みてみたが、サインアップの過程でメールでPINが送られてくる模様。ネット接続できないからネット接続サービスに申し込もうとしているのに、申し込みにネット接続がいる。詰んだ orz

こんな顛末をオフラインで書いている間に充電されたiPhoneが戻ってきました。ちゃんちゃんw

ビッグエコー・ビジネスプランを試してみた

ヴィプテラ・ジャパンにはオフィスがない。最近は電源を取れるカフェも多くなってきたので、カフェを仕事場がわりに使うことが多いのだが、電話会議をしたり、二人で共同作業をしたり、という時にはどうしてもカフェでは制約が多い。そこで、先日ビッグエコーとNTTコムがタイアップして始めた「ビジネスプラン」というのを利用してみた。

夜は賑わうカラオケボックスも、昼間の時間帯はどこも利用率が低い状況だ。そのような時間帯の利用率をどのようにあげるかはカラオケボックス業界の大きな課題だが、ビジネスユースに使おうというのはなかなか面白い試みだと思う。

平日19時まで、60分600円、もしくはフリータイムで1,500円を選択できる。この料金には1ソフトドリンクが含まれているので、かなりリーズナブルな値段設定と言えると思う。ビジネスプランはこれに加えて、電源タップ、HDMIケーブル(両側HDMIのケーブルに加え、片側ミニHDMIなケーブルも用意されているようだ)、ホワイトボードなどを借りることができ、店舗によっては無線LANが使えるところもある。使う部屋に依存するとは思うが、私が試した時は無線LANを使って8.8.8.8にpingした際のRTTは概ね30ms〜50ms程度であった。素晴らしいとも言えないが、普通に使うなら困ることはないだろう。

ふだんはカラオケの映像が流れている画面にPCの画面が映し出されているのはかなりシュールな絵図ではある。

BigEcho Business Plan

ビッグエコーのビジネスプランを使ってみた

少々困ったのはTVへのHDMIケーブルの接続。カラオケボックスのTVは壁に据え付けられていることが多いので、TVの背面や側面を自由に見ることができない。HDMIをどこに挿したらいいのか手探りで探す必要があった。

ビジネスプランで借りて、仕事に行き詰まったら気晴らしに歌えるのかって? どうやらそれは店舗によるようだ。特に制約なしという店舗とビジネスプランでは歌えないという店舗があった(歌えないと言われた店舗もかなり近くでガンガン歌っていたので、ビジネスプランと通常利用の部屋をきっちり分けている(例えばフロアで分ける、など)はしてない模様。したがってビジネスプランで使っているにもかかわらず、周りからは絶叫ソングが聴こえてくることもあるが、これが気にならないのであればビッグエコーのビジネスプランはかなりお得で快適な環境を与えてくれると思う。

このビジネスプラン、まだ使える店舗は東京と神奈川の一部の店舗に限られているが、なかなか快適な環境なのでノマドワーカーの方は一度試されてみてはいかが?

OVN (Open Virtual Network) の紹介

OVN(Open Virtual Network)は、L2、L3、ACLなどの仮想ネットワーキング機能を提供するオープンソースソフトウェアです。OVNは、仮想スイッチとして非常に広く使われているOpen vSwitch(OVS)と連携して動作します。開発もOVSと同じコミュニティで行われていおり、OVSのサブプロジェクト的な位置付けになっています。OVSと同様、ソースコードは完全にオープン、議論はパブリックなメーリングリスト及びIRCを使って進められています。VMware、RedHatなどが中心となって開発を進めていますが、誰でも開発に参加することができます。

現在OVNがターゲットにしているプラットフォームはLinux系ハイパーバイザ(KVM、Xen)やコンテナなどです。DPDKにも対応します。今のところESXiのサポートの予定はありません。なお、OVSはHyper-Vに移植されていますので、OVNのHyper-Vサポートも将来的にはあるかもしれません。

OVN自体はCMS(Cloud Management System)中立で、OpenStack、DockerやMesosなどとの連携を視野に入れています。その中でも特にOpenStackとの連携はOVNにとって大きな意味を持ち、OpenStackとのより良いインテグレーションはOVN開発の大きな動機の一つになっています。

OVNの目指すところはOVSと同様で、ハイパーバイザ1000台規模の大規模環境をサポートし、かつ、商用利用に耐える品質を持ったものを提供する、というものです。

以下に、OVNの基本アーキテクチャを以下に示します。

OVN Architecture

OVNアーキテクチャ

OVNには主に二つのコンポーネント(プロセス)があります。一つはovn-northd、もう一つはovn-controllerです。ovn-northdはその名前からも分かる通り、CMSに対して “northbound” (北向き)なインターフェースを提供しています。現状、ovn-northdはOVNのデプロイメントに1つだけ存在しますが、将来的にはこの部分は冗長化、スケールアウト化していく予定です。

OVNのアーキテクチャでとてもユニークなのは、CMSとのインテーグレーションポイントがデータベース(DB)である、という点です。この部分にRESTfulなAPIを期待される方も多いかと思いますが、現状ovn-northdはCMSとDBを使って連携をするようになっています。

ovn-northdは2つのデータベースを使います。一つは “Northbound DB” で、こちらは仮想ネットワークの「あるべき」状態(desired state)を保持します。具体的には論理スイッチ、論理ポート、論理ルータ、論理ルータポート、ACLなどに関する情報が保持されます。ここには一切物理的な情報は含まれません。ovn-northdから見てnorth側にいる実体(典型的にはCMS)はこのNorhbound DBにデータを書き込むことでovn-northdと連携します。ovn-northdが扱うもう一つのDBは “Southbound DB” で、こちらは物理、論理、バインディングなどのランタイム情報を保持します。このDBにはChassis、Datapath Binding、Encapsulation、Port Binding、Logical Flowといった情報が含まれます。ovn-northdの主な役割は、Northbound DBをSouthbound DBに変換し、Logical Flowを生成することです。

OVNを構成するもう一つのプロセスはovn-controllerというもので、こちらは各ホストにインストールされる分散されたローカル コントローラです。ovn-controllerはSouthbound DBから情報を読み取り、しかるべき設定を各ホスト上のOVS(ovsdb-server, ovs-vswitchd)に対して行います。また、ホストやVIFが追加された場合には、それらの情報をSouthbound DBへ書き込みます。Logical Flow情報をPhysical Flow情報に変換してOVSに設定を投入するのもovn-controllerの役目です。

ちなみに現在、OVN用のDBとしてOVSDBを使っています。本質的にはDBは何でも良いのですが、OVSにはOVSDBがすでに用意されていて、開発者側もその特性をよく理解しているので、手っ取り早くOVSDBを使っています。

OVNが提供する機能はL2、L3、ACL (Security Group)の3つです。

OVNのL2機能は論理スイッチ機能を提供します。具体的には、各ホスト間でL2 over L3なオーバーレーネットワークを自動的に作成してくれます。OVNがデフォルトで使うカプセル化技術はGeneveです。メタデータのサポート、マルチパスとの親和性、ハードウェアアクセラレーションの必要性を考慮すると、Geneveが最も妥当な選択でしょう。ただし、Geneveのハードウェアアクセラレーションに対応したNICはまださほど多くはないので、Geneveに対応していないNICで高い性能が必要な場合はSTTを使うこともできます。また、通常HW-VTEPはGeneveやSTTには対応していないので、HW-VTEPと通信する場合はVXLANを使います。

OVNのL3の機能はいわゆる分散論理ルーティング機能を提供します。OVNのL3機能はどこか特定のホストで動くわけではなく、すべてのホストが自律的にL3機能を実行します。OVNが現在サポートしているL3構成はとてもシンプルなもので、ルータの下に接続されている論理スイッチ間のルーティング及びDefault Gatewayへのルーティングに限られます。今のところ、Default Gateway以外のStatic Routeを設定することはできません。シンプルですが、OpenStack NeutronのL3基本機能を実現するのには十分です。NATは近々サポートされる予定です。

従来、OpenStack NeutronのOVS Pluginでは、Linux Bridge上のtapインターフェース(vnet)にiptablesを使ってACLをかけることによってSecurity Groupを実現していました。OVSのBridgeとLinux Bridgeが混在しているので、いささか複雑なアーキテクチャだったと言えます。

under-the-hood-scenario-1-ovs-compute

従来のOpenStack Neutron OVS pluginのアーキテクチャ(http://docs.ocselected.org/openstack-manuals/kilo/networking-guide/content/figures/6/a/a/common/figures/under-the-hood-scenario-1-ovs-compute.png より引用)

しかしOVS 2.4からOVSがconntrackと連携できるようになったので、iptablesに頼ることなくOVSでステートフルなACLをネイティブに実現できるようになりました。OVNはこのOVSとconntrackの連携機能を使ってACLを実現しています。

conntrack連携機能はOVSの機能なので、OVNなしでも利用はできますが、OVNからはconntrackを意識することなく、論理的なACLを設定するだけでそれがOVSのconntrack機能に自動的にコンパイルされて設定されますので大変便利です。

次回以降で、L2、L3、Security Groupの機能をもう少し深堀りしていこうと思います。

NetFlow on Open vSwitch

Open vSwitch (OVS)はかなり以前(2009年頃から)NetFlowをサポートしています。OVSでNetFlowをenableするには以下のようなコマンドを使います:

また、NetFlowをdisableにするには次のようにすれば良いです。

NetFlowには幾つかのバージョンがあります。その中でV5とV9が最もよく使われいるバージョンだと思います。OVSがサポートしているのはNetFlow V5のみです。NetFlow V9は現時点ではサポートしていません(OVSはNetFlow V9の直接の後継にあたるIPFIXをすでにサポートしているので、OVSがNetFlow V9をサポートするようになることはまずないと思われます)。

以下に、NetFlow V5パケットのヘッダーとフローレコード(1 NetFlowパケットに最大30個まで入ります)を示します。

NetFlow V5 header format NetFlow V5 header format

NetFlow V5 flow record format NetFlow V5 flow record format

NetFlow V5はIPv6のフローレコードを扱うことができません。IPv6トラフィックをモニターしたい場合は、sFlowかIPFIXを使ってください。

一般的なルータ/スイッチでのNetFlow実装と比較して、OVSのNetFlowの実装にはいくつかユニークな点がありますので、それらについて以下で述べます。

多くのNetFlow対応ルータ/スイッチではいわゆる「サンプリング」をサポートしてます。サンプリングとは、全パケットを処理するのではなく、一部だけを処理対象とするものです(サンプリング手法はいくつかありますが、このBlogの趣旨からはずれるのでここでは詳しくは述べません)。一方、OVSのNetFlowはサンプリングを行いません。OVSでサンプリングを使ったフロー処理をしたい場合はsFlowかIPFIXを使う必要があります。

OVSのNetFlowがサンプリングをしない、という点とも若干関連しますが、NetFlowフローレコード中にある「バイト数(dOctets)」と「パケット数(dPkts)」が32bitのフィールドなので、巨大なフロー(elephantフローなどと呼ばれることもあります)があると、これらのフィールドは比較的容易に溢れてしまいます。OVS内部では64bit値でバイト数、パケット数を数えていますので、これらの数が32bitでは収まらないフローがあった場合には、1つのフローの情報を複数のフローレコードに分けてエクスポートするようになっており、できるかぎり正確な値をコレクターに伝えるように努力をしています。

典型的なルータ/スイッチのNetFlow設定では、グローバルな設定(例えばエクスポート先の指定、など)に加え、インターフェース毎にNetFlow処理のenable/disalbe設定がある場合が多いと思います。一方、OVSではインターフェース毎の設定ではなく、ブリッジ毎にNetFlowの設定を行う形になっています。

多くのルータ/スイッチベースのNeflowエクスポータは、NetFlowパケットのソースIPアドレスを明示的に設定することができます(この際loopbackアドレスを使うのが一般的でしょう)。一方、OVSにはこのような設定はありません。OVSのNetFlowパケットのソースIPアドレスはホストOSのIPスタックによって決定さます。通常、このアドレスはパケットが送出されるインターフェースに紐付いているIPアドレスになります。NetFlow V5にはsFlowの「agent address」のような概念がないため、コレクターはNetFlowパケットのソースIPアドレスでエクスポータを区別するのが一般的です。OVSではNetFlowパケットのソースIPアドレスを明示的に設定することができませんので、なにかしらの理由でNetFlowパケットを送出するインターフェースが変わった場合は、NetFlowパケットのソースIPアドレスも変わる可能性があることに留意をしておきましょう。

ドキュメントにははっきりとは書かれていませんが、OVSはNetFlowエクスポータを複数指定するすることができるようになっています。これによりコレクタの冗長構成を実現することができます。設定は以下のとおりです:

通常フローベースのネットワーク管理を行う場合、フローレコードに含まれるインターフェースのIn/Outの番号がとても重要な意味を持ちます。なぜなら、このインターフェース番号の情報を使って、興味関心のあるトラフィックのフィルタをすることが多いからです。商用コレクターの多くは洗練されたフィルタ機能を持っていることが多いです。ルータ/スイッチのNetFlowエクスポータの場合は、このインターフェース番号にはSNMPのIfIndexが使われます。一方、OVSのNetFlowではOpenFlowのポート番号が使われます。この番号はovs-ofctlコマンドで確認することができます。

この例ではeth1のOpenFlowポート番号が1であることを示しています。幾つかのインターフェース番号は特定の意味を持っています。ホスト自身が持つインターフェース(上の例で”LOCAL”と示されているインターフェース)のインターフェース番号は65534になります。また、パケットがブロードキャスト/マルチキャストの場合にはOutのインターフェース番号は65535になります。一般的なルータ/スイッチではこれはどちらの場合もインターフェース番号は0になることが多いと思います。

あれ、最近OVSにはIfIndexが追加されたんじゃ、と思われる方もおられるかもしれません。ご指摘の通りで、最近OVSの”Interface”テーブルにIfIndexが追加されています。ではNetFlowでもこのIfIndexを使うべきではと考えるのはごもっともですが、実はそんなに簡単な話ではありません。例えば、OVSによって作られるトンネルインターフェース(GRE, VXLAN, STTなど)はIfIndexを持っていませんので、単純にNetFlowのフローレコードでIfIndexを使おうとすると、これらのインターフェースに流れるトラフィックの情報を伝えられなくなってしまいます。

NetFlow V5のヘッダには「Engine ID」と「Engine Type」というフィールドがあります。デフォルトでこれらのフィールドがどのような値になるかはデータパスの種類によって異なります。OVSがnetdevを使ったユーザスペースで動いている場合、Engine ID、Engine Typeはおのおのデータパス名のハッシュから得られた値の上位8bit、下位8bitになります。一方、Linux Netlinkを使ったkernelデータパスの場合は、Engine ID、Engine TypeともにデータパスのIfIndexが使われます。OVSのデータパスのIfIndexは以下のコマンドで確認することができます:

もちろんこららのデフォルト値は明示的に設定することも可能です。例えば以下のお通り:

上のコマンドは最初にNetFlowの設定を行うのと同時にEngine TypeとEngine IDを設定をする場合の例ですが、NetFlowの設定をしたあとに、関連パラメータを変更をすることも可能です。

典型的なEngine TypeとEngine IDのユースケースは、物理的には一つだが論理的には複数あるエクスポータを区別するというものだと思います。Cisco 6500のケースなどがいい例です。Cisco 6500はMSFCとPFCがそれぞれ独立したNetFlowエンジンを持っているので、これらを区別したい場合にEngine Type、Engine IDを使う場合があります。OVSの場合はには、複数のブリッジから出力されるNetFlowフロレコードを区別したいケースなどで使用することができます。先に述べた通り、OVSでは、NetFlowパケットのソースIPアドレスはホストの標準的なIPスタックにによって決められます(そしてこれは、必ずしもNetFlowがenableにされたブリッジインターフェスのIPアドレスと同じものにはなりません)。したがって、NetFlowパケットのソースIPアドレスを使ってどのブリッジからエクスポートされたフローレコードなのかを特定することができません。しかし、Engine TypeやEngine IDにそれぞれ個別な値を設定することによって、どのブリッジからのフローレコードなのかを識別することができるようになります。とはいうものの、Engine Type/Engine IDをみて論理的なエクスポータを識別することができるコレクターは私の知る限りそう多くはないと思います。

Engine IDに関してはもう一つの使い道があります。既に述べたように、OVSはNetFlowフローレコードのIn/Outのインターフェース番号としてOpenFlowのポート番号を使用します。OpenFlowのポート番号はブリッジごとにユニークな値です。したがって、複数のブリッジでは同じOpenFlowポート番号が使われる可能性があります。このままではコレクターはそれらのインターフェースを区別することができません。このような場合にはadd_to_interfaceという設定をtrueにしてやることで解決することができます。

このパラメータがtrueの場合、In/Outのインターフェース番号の上位7bitがEngine IDの下位7bitで置き換えられるようになります。ブリッジごとに異なるEngine IDを振るようにすれば、ブリッジ間のインターフェース番号の衝突を避けることができるようになります。

一般的なルータ/スイッチベースのNetFlowエクスポータと同様、OVSのNetFlowにもactiveおよびinactiveなタイムアウトの概念があります。activeタイムアウトは以下のようなコマンドで設定できます(秒で指定)。

明示的に設定されたない場合はactiveタイムアウトは600秒になります。また、activeタイムアウトに-1が指定された場合はactiveタイムアウトはしないようになります。

OVSのNetFlowにもinactiveタイムアウト機構が備わっていますが、明示的にこれを設定することはできません。OVSが管理しているフローの情報がinactiveタイムアウトでdatapathから削除された際にNetFlowのフローレコードもエクスポートされるようになっています。このinactiveタイムアウトは動的であり、さまざまな要素(OVSのバージョン、CPUやメモリの使用状況、など)によって決まります。このタイムアウト時間は最近のOVSでは通常1〜2秒と短めになっています。これは一般的なルータ/スイッチベースのNetFlowエクスポータのデフォルトのinactiveタイムアウト時間(通常15秒)と比べるとかなり短い時間となっています。

ICMPのフローがエクスポートされる際の扱いですが、OVSは一般的なルータ/スイッチと同様な動きをします。すなわち、フローレコードのソースポート番号に “ICMP Type * 256 + Code” の値を入れ、デスティネーションポート番号には0を入れる、というものです。

フローレコード中のNextHop、ソース/デスティネーションAS番号およびnetmaskは0になります。これはOVSが「スイッチ」であることを考えると妥当な動きと言えます。

ここまでで述べてきたように、OVSでNetFlowを使うにあたってはいくつかのポイントについて留意をしておく必要があります。sFlowやIPFIXと比べた時のNetFlowの強みの一つは、NetFlowをサポートしたオープンソースや商用のコレクタが豊富にあることだと思います。どのようなコレクタを選択してもまず間違いなくNetFlow V5はサポートされているはずです。ぜひ、OVSのNetFlow機能を試してみてください。きっとあなたのネットワークにいままでにない可視性を提供してくれることと思います。

ネットワーク仮想化とNSXに関する本が出ます

いままでなにか形になったものを残したいとずっと思っていましたが、少しだけ目標が叶いました。このたび、ヴイエムウェアの同僚たち数名と「詳解VMware NSX ネットワーク仮想化の基礎と応用」という書籍を出す事になりました。

詳解VMware NSX

詳解VMware NSX

翻訳本ではなく完全書き下ろしです。500ページを越えるボリュームなので、ちょっとお値段も張ってしまい申し訳ありませんが、もしご興味があれば買ってみてください。

章立ては以下のようになっています。

  • Chapter 01 技術背景と定義
  • Chapter 02 標準化とメリット
  • Chapter 03 既存ネットワークの課題
  • Chapter 04 ネットワーク仮想化のAPI
  • Chapter 05 NSXの技術解説
  • Chapter 06 OpenStackとNeutron
  • Chapter 07 SDDCとNSX

この中で私は「Chapter 05 NSXの技術解説」の前半部分といくつかのコラムを書かせていただいています。いままでも本の執筆や雑誌等への寄稿はそれなりにやっていましたが、名前が陽に出る形のものは今回がはじめてで、ちょっと感慨深いものがあります。

この本を出したいと思ったきっかけは、われわれの日々の活動の中にありました。ネットワークの仮想化というのはまだ新しい概念で、世の中で十分に浸透しているものではありません。したがってお客様の中にはネットワーク仮想化について十分に理解されていなかったり、場合によっては勘違いされていたりということがままありました。しかし、このような状況も無理はありません。いままでほとんど情報が世に出ていなかったのですから。そこで、われわれがネットワーク仮想化とNSXに関する本を書いて、このような状況を変えていかねば、と思った次第です。

この本を出すのは簡単ではありませんでした。構想からほぼ1年。途中頓挫しそうになったこともありましたが、なんとかやり遂げる事ができました。また、世界に先駆けて日本でこのような本を出せたのも意義深いことであると思っています。これもひとえに他の著者さん(水本さん、田中さん、横井さん、高田さん、小椋さん)が日々の業務の傍ら頑張ってくれたおかげです。特に、執筆者としてだけではなく、本プロジェクトをリードもしてくれた田中さんの、ともするとさぼりがちな筆者一同への激励と “ケツ叩き”(笑)のおかげで、なんとかvForum Tokyo 2014というヴイエムウェア(株)のイベントまでに出版をするという目標が達せられました。また、我々の不慣れな文章の編集に連日の徹夜でおつき合いくださった株式会社Heculaの丸山弘詩様と株式会社インプレスジャパン畑中二四様にも感謝をしたいと思います。

本を出すのが我々のゴールではありません。この本によってみなさんのネットワークの仮想化についての理解が少しでも深まり、NSXをより身近なものに感じていただけるようになれば幸いです。

Geneve on Open vSwitch

先日のBlogでGeneveという新しいEncapsulation方式について紹介をしましたが、さっそくOpen vSwitch(OVS)に実装されmasterにマージされましたので試してみました。今回はGeneveを検証する事が目的ですので、KVMなどは使わずに、単純に異なるホストにある2つのOVS BridgeをGeneveトンネルで繋いでみることにします。VMware FusionでUbuntu 14.04を2つ(host-1、host-2)動かし、それぞれgithubから取ってきた最新のOVSをインストールしました。各仮想マシンにはNATによるEthernetインターフェースを1つ設定し、DHCPでアドレスをとる事にします。以下の例ではそれぞれのホストで192.168.203.151と192.168.203.149というIPアドレスがDHCPで取れた場合の例です。Bridgeを2つ(br0、br1)作成し、br0を物理ネットワークと接続するブリッジとし、br1の間にGeneveのトンネルを張る事にします。

Geneve Test with Open vSwitch Geneve Test with Open vSwitch

host-1, host-2でのOVSの設定は以下の通りです。

このような設定をすれば、host-1とhost-2のbr1間のpingが通るようになり、物理ネットワーク側にパケットが出る際にはGenveでトンネルされます。

Wiresharkでパケットを見てみましょう。Wiresharkにも2014/06/16にGeneveのdissectorが追加されていますので、最新のWiresharkをビルドすればGeneveパケットの中身を見る事ができます。

Geneve Frame by Wireshark

Geneve Frame by Wireshark

Geneveが使うポート番号は6081/udpです(このポート番号は2014/03/27にIANAに登録されています)。また、今回のように単純にOVSのBridge同士を繋ぐだけであれば特にVNIは必要はありませんので、明示的にVNIの値を指定していません。その場合はVNIの値は0になります。VNIの値を明示的に設定したい場合は、

というようにoptionsでkeyというパラメータで指定すればOKです。その際のパケットは以下のようになります(10進数の5000は16進数では0x1388)。

Geneve Frame with VNI 5000 by Wireshark

Geneve Frame with VNI 5000 by Wireshark

GeneveはEthernetフレームだけではなく、他のタイプのフレームもトンネルできるようになっています。そのためにProtocol Typeというフィールドが用意されています。今回の例ではEthernetフレームをトンネルしていますので、Protocol Typeの値はTransparent Ethernet Bridgingを示す0x6558が指定されています。

現時点の実装では、Geneveの特徴であるGeneve Optionsを指定する事はまだできません。追ってサポートが加えられると思います。

Geneveの真価が発揮されるのは、Geneveのフレームフォーマットを理解してTSOをすることができるNICが出現した場合です。そのようなNICは現時点ではまだ世の中には出てないですが、少なくともソフトウェア的(OVS的)にはGeneve Readyになりました。現在はまだmasterブランチにしかGeneveのコードが入っていなかったのでgithubの最新masterブランチから取ったコードをビルドして試しましたが、OVSのバージョン2.2がリリースされれば、今回試したコードも含まれてくることになるので、簡単に試す事ができるようになるはずです。

Geneve Encapsulation

L2 over L3のencapsulationとしては、GRE, NVGRE, VXLAN, STTなどが良く知られているものですが、新たなencapsulationとして “Geneve” というものがIETFに提案されています(draft-gross-geneve-00.txt)。ドラフトにはWorking Group名は入っておらずまだ個人名になっていますが、現在nvo3 (Network Virtualization Overlays) Worging Groupで議論がされています。ドラフトを書いているのはVMware, Microsoft、RedHat、Intelの人たちです。

現時点でも十分にいろいろな種類のL2 over L3 encapsulation方式があり、それぞれ実装も多数あるのに、なぜさらに新しいencapsulation方式を作る必要があるのでしょう? まず、前提として各種L2 over L3 encapsulationは、多少の差こそあれ、本質的にはL2フレームをL3データグラムで包んでやる事が主目的であり、それを実現するために幾つもの方式があるのは、作る側(ベンダー)にとっても使う側(ユーザ)にとっても嬉しい事ではないであろう、ということがあります。

その上で、Geneveが目指すのは、主に以下のの2点となります。

  • 拡張性を備える事
  • オフロード機能を有効に活用できる事

NVGRE, VXLAN, STTヘッダはいずれも固定長で自由に拡張する事ができないため、今後新たに必要となるさまざまな要件に対応するのが困難です。そのような新たな要件として1つ想定されるのが “metadata” のサポートです。ここでいうmetadataとは、パケット自体ではなくそれに付随するさまざまなcontext情報の事を意味します。例えばパケットがどのようなサービスを経由すべきか(いわゆるservice chaining)や、そのパケットが誰が出したどのようなアプリケーションのパケットであるのか、などと言った情報がmetadataにあたります。NVGRE, VXLANはいずれも原則仮想ネットワーク識別子しか扱えず、このようなメタな情報を伝えるのが困難です。STTには64bit長の “Context ID” というフィールドがあるので、ここを利用する事でNVGREやVXLANよりは多くの情報を伝える事ができますが、64bitという固定長フィールドですので伝えられる情報量には限りがあります。

そこでGeneveでは、encapsulationに拡張性を持たせられるように、固定フィールドに加え、自由に伝える情報を拡張する事が出来る可変長のOptionフィールドが定義されています。このフィールドはいわゆるTLV (Type/Length/Value)形式を取っており、自由に拡張をすることができます。また、Typeの前にはOption Classというフィールドがあり、Typeの名前空間を定義できます。ここに(IANAから割り当てられた)ベンダーIDを入れる事でベンダー固有のTypeを自由に定義・追加できるように設計されています。

Geneve class= Geneve Options

Geneveヘッダには2種類のフラグ(O、C)が定義されいます。OフラグはOAMフレームを表し、CフラグはCriticalフレームを表しています。GeneveのOAMはEnd-to-EndのOAMを想定しており、途中の経由するノードがこのフラグを見てなにかしらの解釈してはしてはいけない事になっています。また、GeneveのEndノードはOフラグが立ったパケットを優先的に処理する事が期待されています。一方、Cフラグが立っているオプションを受け取ったEndノードはそれを解釈しなければなりません。逆に言うと、Cフラグが立っていなければ、それを受け取ったノードはそれを無視しても良い、ということになります。

VXLAN、NVGREと同様、Geneveでも仮想ネットワークを識別するための識別子(VXLANではVNI、NVGREではVSID)は24bit長となっています。また、VXLANと同様、GeneveでもUDPでトランスポートするよう定められており、UDPソースポート番号を適切に使うこととにより、ファブリックのようなマルチパスネットワークの帯域を最大限有効に使えるようにになっています。

2つ目のGeneveのDesign Goalのである「オフロード機能を有効に使えるようにする」という点はL2 over L3 encapsulationする際に高い性能を実現する上でとても重要です。各種オフロード機能の中でも特にSegmentation Offload機能は性能向上において非常に有効に機能します。Segmentation Offload機能は、最近の多くのNICで実装されている機能の一つで、CPUの負荷を減らしつつ高いスループットを実現する事のできる仕組みです。具体的には、オペレーティングシステムからNICに対して非常に大きなパケットの送信を依頼しするようにします。そのような大きなパケットは実際にはネットワーク上に出す事は出来ませんので(MTUの制限)、パケットを小さく分断(segmentation)してネットワーク上に出してやる必要があるのですが、通常はこの処理はソフトウェア(オペレーティングシステム)が行います。Segmenation Offload機能を持つNICの場合は、このsegmenation処理をオペレーティングシステムに代わってNICがハードウェアで処理をしてくれます。このような機能は一般的にはLarge Segmenation Offload機能と呼ばれており、特にTCPパケットにに対して行う場合TSO (TCP Segmenation Offload)機能と呼ばれています。LSO/TSOが機能すればホストのCPUサイクルを使わずに済みますので、ホストのCPUに負荷をかけず高いスループットを実現する事ができます。実際STTではこのNICのTSO機能を上手く利用できるように工夫されていて、TCPでEncapsulationをするようになっています。

Encapsulationの拡張性と性能はトレードオフの側面があります。Encapsulationに拡張性を持たせるために、ヘッダにOptionフィールドを儲けて自由に情報を追加できるようにすると、NICからするとどこからデータが始まるのかが分からずにSegmentation Offloadを行うのが困難になるからです。そこで、GeneveヘッダにはOptionの長さがはっきり(一つ一つのOptionをパースしなくとも)とわかるように、明示的にOption長を入れるようになっています。このフィールドの値を見ればNICはすぐにデータがどこから始まるのか判断する事ができます。Geneveではこのフィールドは6bit長ありますが、実際のOptionの長さはこのフィールドの値の4の倍数の長さになるので、最大64*4=256バイトのOptionを扱う事ができる、ということになります。

STTはTSO機能を持った既存のNICでH/Wアクセラレーション効果を享受できますが、Geneveではどうでしょう? 上述したように、Geneveでencapsulationされたパケットに対してNICのSegmentaion Offload機能を利用するためには、GeneveのOption長フィールドを意識する必要があり、Segmentationする際にGeneveのヘッダ(Optionも含む)を全てのSegmentにコピーしてやる必要もありますので、NIC自体がGeneve対応(Geneve-aware)になっている必要があります。現在はこのようなNICは存在しませんが、Geveneではこのような実装をハードウェアで行いやすいように慎重に設計されているのが特徴になっています。今後GeneveのOptionがいかに増えようとも、Geveve自体の仕様を変える必要はなく、また、Geneve-awareなNICのSegmentaion Offload機能になんら影響を与えずにOptionを自由に追加していくことができます。Geneveでは拡張性と性能を両立させる事ができるわけです。

一方、Geneveに反対をしている人たちもいます。そのような人たちの主張はおおかた以下のよなものです。

  • Geneveで実現しようとしている事は、既存のencapsulation(L2TPv3のstatic tunnelingやVXLAN)の拡張で実現可能なので、新たなencapsulationは要らない。
  • metadataをencapsulationにbindするべきではない。
  • VNIが24bitでは不十分。最低でも32bitは欲しい。

一般的にはL2TPはシグナリングを含みますが、L2TPのシグナリングを使わずに両端で静的に必要な設定をする事でL2TPを使う事も可能です。これをL2TP static tunnelingといい、この場合は純粋なencapsulation (Pseudo Wire)方式ということになります。L2TPv3自体は2005年にRFCになっていますので、十分に枯れた仕様であり実装も十分にあります。例えばCisco IOSでも実装されていますし、Linuxでもip l2tpコマンドで利用できます。

L2TPv3のデータパケットのencapsulationには実質上Session IDとCookieしかありません(Tフラグは常に0、Verは3である必要があります)。

L2TPv3 Data Header

L2TPv3 encapsulationではSession IDがVXLAN/GeneveのVNIに相当する事になり、ここは32bit長となっています。

このパケットフォーマットを変えないままL2TPv3 static tunnelにGeneveのような拡張性を持たせる事も可能ですが、その場合は両端で(合意できる)設定をして、それをSeesion IDとして表す事になります(この点を考慮すると、Session IDを純粋にVNIとして使える訳ではない、ということになります)。この暗黙の合意はEnd Point同士でしか分かりませんので、途中のノードがそれを解釈することはできません。この「途中のノード」にはNICも含まれることに注意をすべきでしょう。

一方、Segmentation Offload機能を使えるか、という点については、現状のL2TPv3の仕様的には難しいでしょう。L2TPv3 encapsulationではCookieの長さもパケットを見ただけではわかりません(極端な話、その有無さえもわかりません)。これらはL2TPのシグナリングの過程(static tunnelの場合は両端の静的設定)で決められます。どこからデータが始まっているのかをNICなどに伝える事ができないためSegmentation Offloadを実現するのは困難です。ただしL2TPv3の前身のプロトコルであるL2TPv2には “Offset” というフラグとフィールドが定義されていて、これでデータがどこから始まるのかをヘッダー部分に入れる事ができるようになっています。L2TPv3 encapsulationにはこのOffsetフィールドは(RFC的には)定義されていませんが、L2TPv2時代の名残りでL2TPv3でもこのOffsetフィールドを使う事ができる場合が多いようです。

上記のような観点からは、VXLANもL2TPv3 encapsulationとあまり大きな違いはありません。

VXLAN Header

標準で規定されている以外の情報(現状はVNIしか伝える事しか出来ない)を伝えようとすると、現状のReservedとなっているフラグやフィールドを使って拡張をするか、パケットのフォーマットを大幅に変更をする事になるでしょう。例えば、現状のVXLANヘッダにはencpasulationするフレームがどのようなフレームなのかを示すフィールドがなくencapsulationの対象となるフレームはEthernetのみですが、VXLANでEthernetフレーム以外をencapsulationするようにするための拡張が提案されています(draft-quinn-vxlan-gpe)。この拡張はまさにReservedなフラグとフィールドを使ってVXLANを拡張しています。Encapsulation対象のフレームの種類を表すだけならなんとかReservedのフィールドでおさまるので、VXLANのパケットフォーマットを変更せずに済みますが、これ以外の拡張をしたくなったらもはや使えるフィールドはないので、フレームフォーマットの変更を余儀なくされるでしょう。

現状のVXLANヘッダは8バイトで固定なので、Segmentation Offloadはし易い仕様になっていますが、このフレームフォーマットを超えた拡張情報を扱いたくなった場合には、Segmentation Offloadするのは困難になってくるでしょう。

一方、metadataをencapsulationにbindすべきではない、という意見はそれなりに的を得ているように思います。metadataはencapsulationに対して直交しているべきで、どのようなencapsulation(VXLAN, NVGRE, L2TPv3, etc.)でも使えるべきだ、と考えるのは自然です。実際metadataを一般的な枠組みとしてどのように扱うかについては、IETFのsfc(Service Function Chaining)Working Groupというnvo3 WGとは別のWGで議論されています。ただ、だからといってGeneveがmetadataにbindされている、という指摘をするのはいささか勇み足でしょう。Geneveはmetadataを格納できるような仕組み(フレームワーク)を提供するだけで、そこにmetadataを入れる事を強要しませんし、metadataをどのように表現するかも規定していません。あくまで、Optionフィールドの使い方の一つとして考えられるのがmetadataの格納である、というだけです。Service Function Chaining方式とmetadataの表現方法が標準化されればそれに従えばよく、それまでの間はGeneveのOptionフィールドを使ってmetadataを運べるようそのような枠組みを用意しておく、というのは非常に合理的だと思います。Optionフィールドはmetadata以外の目的にも使えるわけですし。

VNIが24bitでは足りない、という意見ですが、24bitでも1600万個のネットワークを表現できるので、これで足りないという主張は私はあまりピント来ません。このような主張をしている人たちは、このVNIを純粋にネットワークの識別子としてだけ使うのではなく、そこに何かしらのsemanticsやmetadata的なものを含めようとすると24bitでは足らなくなくなる、という事を懸念しているように思います。GeneveではOptionフィールドでmetadataなどのcontextを表現できますので、VNIは純粋に仮想ネットワーク識別子として使えます。であれば24bitあれば十分なのではないでしょうか?

結局のところ、Geneveの存在意義があるかないか(既存のencapsulationで十分ではないか)という議論は、encapsulation方法としてmetadata以外に想定される拡張機能が必要か否か、という点に行き着くのではないかと思います。もし、metadata以外に想定される拡張機能が必要ないのであれば、sfc Working Groupの標準化を待ち、既存のencapsulation(あるいはその若干の修正形)を使えば良いでしょう。Geneveが提供してくれる他の機能(OAMの識別や、Ethernet以外のフレームを扱える枠組み)は他のencpsulation方式もフレームフォーマットを大きく変更することなく対応できるでしょうし、そのような拡張もすでに提案されています。逆に、今後metadata以外での拡張の必要性が見込まれるのであれば、新たに必要となるOptionが増えてもNICのSegmenation Offload機能を利用できるGeneveは非常に理にかなったencpasulationであると言えます。

Geneveは拡張性を備えつつもVXLAN/NVGREのようにハードウェアで処理しやすいように(特にNICでのSegmentation Offloadが機能するように)設計された、STTとVXLAN/NVGREの両方のエッセンスを汲んだ新たなencpasulationです。この新しいencpasulation方式が市民権を得られるかどうかは、Geneveをサポートしたハードウェアやソフトウェアがどれくらい早く市場に現れてそれが受け入れられるか、という点にかかっていると思います。

私がNiciraに入ったわけ

私は2011年の5月に(当時の)Niciraに入りました。その当時、私は「Fivefront」という、以前Ascendで仕事をしていた時の同僚と起こした会社をやっていました。Fivefrontでは主にNetFlow / sFlowのコレクタ製品の販売に携わっていましたが、新しい商材も探すのも私のミッションの一つでした。あるとき米国の知人(彼もAscend時代の同僚でした)、彼が務めていた会社が日本に進出したいので一度話を聞いてくれないか、と相談があり、我々としても新しい商材を探していたので、一度話を聞かせて欲しいと返事をしました。彼の来日の日取りも決まり来日目前のところで、突然彼の会社が別の会社に買収されてしまったのです。彼はその買収元の会社で働く気はないとのことで、その会社を辞めてしまったため、来日の計画もパー、事業提携の話も白紙になりました。実はその彼が移った先の会社がNiciraだったのです。

彼が移った先の会社がどんなところか興味があったので、Webで調べてみたりしたのですが、当時のNiciraはまだステルスモードだったため、ホームページを見てもほとんど何も分かりません。でも、なんとなく面白そうな事をやっていそうな気配があったので、その彼に「前の会社の話はともかく、移った先の会社の話を聞かせて欲しい」と頼むと、ちょうどNiciraも日本に進出する計画があり、日本にも来る予定があるとの事だったので、その時に話を聞かせてもらう約束をしました。その前に少し勉強ておきたいので何か資料を送って貰えないかと頼んだところ「ステルスモード中なので資料は何も出せない。代わりにこの論文を読んでおけ!」と送られてきたのが、Martin Casadoらが書いた論文2本でした(笑)。そのうちの一本がこれです。

その論文を読んだところ、当時の私には100%理解はできかったものの、非常にドキドキしたの事を深く記憶しています。「ひょっとしたらネットワークが変わるかもしれない。もし変わるとしたら、こういうところから変わるはずだ!」と感じたのでした。後日、彼ともう一人エンジニアのボスが来日した際に話を聞きましたが、この直感は確信に近いものに変わりました。帰り際に「日本に進出するにあたりエンジニアを捜している。今回の来日でも何人かと面接をした。誰かいいエンジニアを知っていたら紹介してくれ」と頼まれました。心が揺れました。この会社に入ってこれからのネットワークを変える仕事に携わりたい、と思ったからでした。一方で、Fivefrontは本当に小さな会社だったので、仮に一人社員を欠くだけで会社に大きな影響を与える事は避けられません。ましてや会社を起業した一人として、一社員とはまったく違う次元の責任を負っています。一生懸命働いて小さな会社を支えている他の同僚の方々に対して大変申し訳なく思う気持ちはありました。しかし最終的には「僕をそのエンジニア候補の一人に加えてもらえないか?」とNicira側に申し出る決断をしました。

その後、Niciraの上層部の人たちから電話面接を受けました。その時の一人はMartinでした。あまり技術的な話をした記憶はありません。そもそも当時の私は仮想化に関する知識はほぼゼロでした。Hypervisorに触った事もありません。OpenFlowという言葉を知ってても、それがどんなものかも知りません。Open vSwitch?、何それ?状態です(笑)。当時、このへんの事についてアンテナを伸ばしていた人はNiciraがどんな会社で何をやろうとしているか、などなんとなく知っていたに違いありません。一方、私はド素人です。にもかかわらず、なぜか私は合格し(これはいまでも不思議でなりません)、晴れてNiciraで日本第一号の社員となりました。

入社して最初の週にたまたまSales/SEが一同に本社(カリフォルニア州パロアルト)に集まる機会があり、私もそれに出席しました。ただ、皆が何を話しているかさっぱり分かりません。新手のプロトコルかと思った4文字英語が実は客の名前だったりして、まったく話が噛み合いません(爆)。また、その日初めてNVP(NSXの前身)に触りました。「ほら、これ見てやってみろ」と、”Getting Started Guide” というドキュキュメントを渡されて、NVPの設定を初めて行いました。実はその時に初めてHypervisorというものに触りました。フムフム、Hypervisorって、こんなふうになってるのか。。ただ、ここをこうしてこう設定しろ、ってGetting Started Guideに書いてあるのですが、何のためにそれをしているのかさっぱりわかりません。そんな状態ですので、思ったように動かなかった場合の切り分けできようはずもありません。もうもなかば泣きながら設定をしました。当然予定されていた時間内に終わらず、居残り作業です。もう一人いた同時期入社のSEと夜遅くまで頑張りました。

そんな感じで始まったNiciraでの日々ですが、ステルスモードが明けてからあれよあれよという間にVMwareに買収され、NVPがNSXとして進化し、今日に至っています。その間、沢山の苦労もありました。長かったようでもあり短かったようでもあり。でも、今、自分がここにいれて本当に良かったと思っています。

万里の長城に行ってみた

慕田峪長城入口

慕田峪長城入口

日航の北京発羽田着便は夕方発なので、それまでの時間を利用して万里の長城に行ってみる事にした。もちろん初体験。

万里の長城は全長3000キロ近くあるので、色々なエントリーポイントがあるわけだが、今回は北京からも近く、景色も良いとされる慕田峪長城に行ってみる事にした。北京市内からは車で1.5時間くらい。ホテルを朝の7時に出発して、現地に着いたのは8時半頃であった。

入り口から徒歩で上がる事もできるようだが、今回はケーブルカーである程度上まで登ってから万里の長城を楽しむ事にした(あとの事を考えると、この選択は正しかったと思われるw)。ケーブルカーを降りて右に行くコースと左に行くコースがあったが、今回は左のコースを選択した。朝早かった事もあり、客数は少ない。

Relatively easy slope

最初は比較的なだらかな道のり。

最初は比較的なだらかな坂道である。慕田峪長城は万里の長城の中でも比較的よく整備がされているそうで、石畳と階段がしっかりとしており歩き易い。しかし、徐々にきつい坂が続くようになり、場所によっては角度が60度くらいの階段もあり(こうなると階段と言うよりもむしろ梯子に近い)かなりしんどい。

Steep slope.

厳しい階段。

ポイントポイントに砦のようなものがある。ここからの眺めは抜群である。また、吹く風が心地よい。

一応最後の砦まで行ってもその先に進む事はできるようだ。ただし、その先には整備が一切されていないので、滑落の危険等があるため進まないほうが賢明、ということで、そこで引き返してくる事にした。良く言う事であるが、登りよりも下りのほうがつらい。登りで足が疲労しているせいもあるのだろうが、下りは踏ん張らなければいけないくて、それがかなりこたえる。フラフラしながら無事に下山。

Steep step. Almost like a ladder.

急な階段。というよりほとんど梯子w。

ともかく圧巻であった。よくもまあ、こんな事をやろうと思い、そしてそれをやってのけてしまったもんだと感心してしまった。普通に考えたら国の廻り3000キロも壁てくくってしまおうなんて、馬鹿げててとても実行には移せないと思う。それをやってのけてしまうところが中国の凄いところだ。

帰りにケーブルカー降り場のトイレに同行したもう一人のエンジニア(彼は台湾人)が入ったが、彼がしばらくして居心地悪そうに出てきた。トイレットペイパーがなかったとのこと。なんとか持っていたパンフレットなどを使って対処をしてきたとのこと。海外からの観光客も多い施設なはずなのに、このへんにも中国らしさを感じてしまった(笑)

Unmaintained wall

最終の砦の先にも長城は続いているが、整備はされておらず、こんな感じの状態。危険なのでこれより先には行かず。