ipaddressモジュールの紹介
*************************

author:
   Peter Moody

author:
   Nick Coghlan


概要
^^^^

このドキュメントは "ipaddress" モジュールへのやさしい入門を提供するの
が目的です。これは主にまだIPネットワーキングの用語に詳しくないユーザー
を対象としていますが、"ipaddress" がどのようにIPネットワークアドレス処
理の概念を表現しているかをざっと見るのにも役立つかもしれません。


アドレス/ネットワーク/インターフェースオブジェクトの作成
========================================================

"ipaddress" はIPアドレスを検査したり操作したりするためのモジュールであ
るため、まずはオブジェクトを作成したほうが良いでしょう。文字列および整
数を使用して "ipaddress" を使用してオブジェクトを作成できます。


IPバージョンについての注意
--------------------------

IPアドレッシングに詳しくない読者にとっては、インターネットプロトコルが
現在バージョン4プロトコルからバージョン6へと移行している途中であること
を知っておくのが重要です。この移行はプロトコルのバージョン4が世界中の
需要に対応するために十分なアドレスを提供できないことが大きな理由で起こ
っており、特にインターネットへ直接接続する機器が増えている状況を踏まえ
たものです。

上述した2つのプロトコルバージョンの違いの詳細を説明するのはこの入門ド
キュメントの対象範囲を超えていますが、読者は少なくともこれらの2つのバ
ージョンが存在することと、どちらかのバージョンを強制的に使用することが
必要になることもあるということを認識する必要があります。


IPホストアドレス
----------------

アドレスは多くの場合「ホストアドレス」と呼ばれ、IPアドレッシングを扱う
場合の最も基本的な単位です。アドレスを作成する単純な方法は
"ipaddress.ip_address()" ファクトリー関数を使用することで、この方法で
は渡された値をもとにIPv4かIPv6アドレスのどちらかを自動的に決定します:

>>> ipaddress.ip_address('192.0.2.1')
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address('2001:DB8::1')
IPv6Address('2001:db8::1')

アドレスは整数から直接作成することも可能です。32ビットに収まる値はIPv4
アドレスであるとみなされます:

   >>> ipaddress.ip_address(3221225985)
   IPv4Address('192.0.2.1')
   >>> ipaddress.ip_address(42540766411282592856903984951653826561)
   IPv6Address('2001:db8::1')

IPv4またはIPv6アドレスの使用を強制するには、適切なクラスを直接呼び出す
必要があります。これは特にIPv6アドレスを小さな整数で作成する場合に役立
ちます:

   >>> ipaddress.ip_address(1)
   IPv4Address('0.0.0.1')
   >>> ipaddress.IPv4Address(1)
   IPv4Address('0.0.0.1')
   >>> ipaddress.IPv6Address(1)
   IPv6Address('::1')


ネットワークを定義する
----------------------

ホストアドレスは通常IPネットワークへ統合され、 "ipaddress" はネットワ
ーク定義の作成、検査、操作の手段を提供します。IPネットワークオブジェク
トはネットワークの一部であるホストアドレスの範囲を定義する文字列から生
成されます。この情報の最も単純な形式は「ネットワークアドレス/ネットワ
ークプレフィックス」のペアであり、プレフィックスは先頭のビット数として
定義され、アドレスがネットワークの一部であるかどうか、それらのビットに
期待される値がネットワークアドレスに定義されているかを判断するために比
較されます。

アドレスについては、正しいIPバージョンを自動的に判断するファクトリー関
数が提供されます:

   >>> ipaddress.ip_network('192.0.2.0/24')
   IPv4Network('192.0.2.0/24')
   >>> ipaddress.ip_network('2001:db8::0/96')
   IPv6Network('2001:db8::/96')

ネットワークオブジェクトにはホストビットを設定することができません。こ
れについての実践的な効果は "192.0.2.1/24" がネットワークを表さないとい
うことです。次の項でより詳しく説明しますが、ネットワーク内IP表記は一般
的に特定のネットワーク上のネットワークインターフェースを表すのに使用さ
れるため、このような定義はインターフェースオブジェクトと呼ばれます。

デフォルトでは、ホストビットセットの指定でネットワークオブジェクトを作
成しようとすると "ValueError" が発生する結果となります。
"strict=False" フラグをコンストラクタに渡すと、追加分のビットをゼロと
して強制的に処理するように要求できます:

   >>> ipaddress.ip_network('192.0.2.1/24')
   Traceback (most recent call last):
      ...
   ValueError: 192.0.2.1/24 has host bits set
   >>> ipaddress.ip_network('192.0.2.1/24', strict=False)
   IPv4Network('192.0.2.0/24')

文字列形式では劇的に柔軟性を提供できる一方、ネットワークはちょうどホス
トアドレスのように整数で定義することもできます。この場合、ネットワーク
は整数により特定できる単一のアドレスのみを含むものとして処理され、ネッ
トワークプレフィックスはネットワークアドレスの全体を含みます:

   >>> ipaddress.ip_network(3221225984)
   IPv4Network('192.0.2.0/32')
   >>> ipaddress.ip_network(42540766411282592856903984951653826560)
   IPv6Network('2001:db8::/128')

アドレスについては、ファクトリー関数を呼ぶ代わりにクラスコンストラクタ
を直接呼ぶことで、作成対象のネットワークを特定の種類に強制することがで
きます。


ホストインタフェース
--------------------

直前に述べたように、特定のネットワーク上のアドレスを示したい場合には、
アドレスもネットワーククラスも十分ではありません。 "192.0.2.1/24" のよ
うな表記は「 "192.0.2.0/24" のネットワーク上の "192.0.2.1" のホスト」
の短縮形として通常ネットワークエンジニアやファイアウォールやルータ用の
ツールを書く人が使用し、 "ipaddress" はそれに応じて特定のネットワーク
とアドレスを関連づけるハイブリッドクラス集を提供します。アドレス部分が
ネットワークアドレスであることに制限されないことを除き、作成用のインタ
フェースはネットワーク定義用のインタフェースと同一です。

>>> ipaddress.ip_interface('192.0.2.1/24')
IPv4Interface('192.0.2.1/24')
>>> ipaddress.ip_interface('2001:db8::1/96')
IPv6Interface('2001:db8::1/96')

整数入力は(ネットワークと同様に)受け入れられ、適切なコンストラクタを直
接呼び出すことで特定のIPバージョンの使用を強制できます。


アドレス/ネットワーク/インタフェースオブジェクト
================================================

さて、IPv(4|6)(アドレス|ネットワーク|インタフェース)オブジェクトをわざ
わざ作成したので、多分それについて情報がほしいことでしょう。
"ipaddress" はこの作業を簡単で直観的にできるようにします。

IPバージョンを抽出する:

   >>> addr4 = ipaddress.ip_address('192.0.2.1')
   >>> addr6 = ipaddress.ip_address('2001:db8::1')
   >>> addr6.version
   6
   >>> addr4.version
   4

インタフェースからネットワークを取得する:

   >>> host4 = ipaddress.ip_interface('192.0.2.1/24')
   >>> host4.network
   IPv4Network('192.0.2.0/24')
   >>> host6 = ipaddress.ip_interface('2001:db8::1/96')
   >>> host6.network
   IPv6Network('2001:db8::/96')

個別なアドレスがいくつネットワークに存在するかを調べる:

   >>> net4 = ipaddress.ip_network('192.0.2.0/24')
   >>> net4.num_addresses
   256
   >>> net6 = ipaddress.ip_network('2001:db8::0/96')
   >>> net6.num_addresses
   4294967296

「使用可能」なネットワーク上のアドレスを順番にたどる:

   >>> net4 = ipaddress.ip_network('192.0.2.0/24')
   >>> for x in net4.hosts():
   ...     print(x)  
   192.0.2.1
   192.0.2.2
   192.0.2.3
   192.0.2.4
   ...
   192.0.2.252
   192.0.2.253
   192.0.2.254

ネットマスクを取得(例: ネットワークプレフィックスに応じてビットを設定)
したりホストマスク(ネットマスクの一部ではない任意のビット)を取得する:

>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.netmask
IPv4Address('255.255.255.0')
>>> net4.hostmask
IPv4Address('0.0.0.255')
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.netmask
IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
>>> net6.hostmask
IPv6Address('::ffff:ffff')

アドレスを展開したり圧縮したりする:

   >>> addr6.exploded
   '2001:0db8:0000:0000:0000:0000:0000:0001'
   >>> addr6.compressed
   '2001:db8::1'
   >>> net6.exploded
   '2001:0db8:0000:0000:0000:0000:0000:0000/96'
   >>> net6.compressed
   '2001:db8::/96'

IPv4は展開や圧縮には対応していませんが、それでも関連したオブジェクトが
適切なプロパティを提供し、バージョンに対して中立なコードがIPv4アドレス
を正しく処理する一方でIPv6向けには最も簡潔または最も詳細な形式が使用さ
れることを保証します。


アドレスのリストとしてのネットワーク
====================================

ネットワークをリストとして扱うと便利な場合があります。これは、ネットワ
ークを次のようにインデックス付けすることが可能なことを意味します:

   >>> net4[1]
   IPv4Address('192.0.2.1')
   >>> net4[-1]
   IPv4Address('192.0.2.255')
   >>> net6[1]
   IPv6Address('2001:db8::1')
   >>> net6[-1]
   IPv6Address('2001:db8::ffff:ffff')

以下のようなリストのメンバーシップの判定用文法が使用可能なように、ネッ
トワークオブジェクト自身が適応します:

   if address in network:
       # do something

ネットワークプレフィックスに基づき、所属しているかどうかの判定が効果的
にできます:

   >>> addr4 = ipaddress.ip_address('192.0.2.1')
   >>> addr4 in ipaddress.ip_network('192.0.2.0/24')
   True
   >>> addr4 in ipaddress.ip_network('192.0.3.0/24')
   False


比較
====

"ipaddress" はオブジェクトの比較をするための単純で、かつ願わくば直観的
な方法を意味が通じるように提供します:

   >>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
   True

異なるバージョンや異なるタイプのオブジェクトを比較しようとすると
"TypeError" 例外が発生します。


他のモジュールでIPアドレスを使用する
====================================

IPアドレスを使用する他のモジュール("socket" など)は通常このモジュール
からオブジェクトを直接受け付けません。直接渡すのではなく、他のモジュー
ルが受け付ける整数や文字列に強制的に変換する必要があります:

   >>> addr4 = ipaddress.ip_address('192.0.2.1')
   >>> str(addr4)
   '192.0.2.1'
   >>> int(addr4)
   3221225985


インスタンスの生成に失敗した場合にさらに詳細を取得する
======================================================

上述したバージョンに関知しないファクトリー関数を使用してアドレス/ネッ
トワーク/インタフェースオブジェクトを作成する場合、渡された値がそのタ
イプのオブジェクトとして認識されなかったと単純に通知する汎用的なメッセ
ージとともに、どのエラーでも "ValueError" として報告されます。特定のエ
ラーが提供されないのは、拒否された理由をより詳細に提供するために、値が
IPv4またはIPv6である *はず* かどうかを知る必要があるためです。

追加の詳細を得ると役立つ場合のユースケースに対応するため、個別のクラス
のコンストラクタは実際に "ValueError" のサブクラスである
"ipaddress.AddressValueError" と "ipaddress.NetmaskValueError" を発生
させて具体的に定義のどの部分のパースに失敗したのかを示します。

クラスコンストラクタを直接使用する場合にはこれらのエラーメッセージはさ
らに非常に詳細になっています。例えば:

   >>> ipaddress.ip_address("192.168.0.256")
   Traceback (most recent call last):
     ...
   ValueError: '192.168.0.256' does not appear to be an IPv4 or IPv6 address
   >>> ipaddress.IPv4Address("192.168.0.256")
   Traceback (most recent call last):
     ...
   ipaddress.AddressValueError: Octet 256 (> 255) not permitted in '192.168.0.256'

   >>> ipaddress.ip_network("192.168.0.1/64")
   Traceback (most recent call last):
     ...
   ValueError: '192.168.0.1/64' does not appear to be an IPv4 or IPv6 network
   >>> ipaddress.IPv4Network("192.168.0.1/64")
   Traceback (most recent call last):
     ...
   ipaddress.NetmaskValueError: '64' is not a valid netmask

しかし、モジュールについて固有の上記例外のどちらも "ValueError" を親ク
ラスとして持ち、エラーの特定のタイプに興味がない場合でも、以下のように
コードを記述できます:

   try:
       network = ipaddress.IPv4Network(address)
   except ValueError:
       print('address/netmask is invalid for IPv4:', address)
