4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Intimate MergerAdvent Calendar 2024

Day 23

Pythonの標準ライブラリに学ぶIPv6備忘録

Last updated at Posted at 2024-12-22

はじめに

タイトルの後半部分を言いたかっただけです
こんにちは。株式会社インティメート・マージャーの十文字です。

IPv6の仕様って何度見ても覚えられないですよね。
ネットワークエンジニアやインフラエンジニアならまだしも、普段バックエンド開発をしている身としてはIPv6に触れる機会というのは少なく、毎回部分的に調べて満足してしまっています。

そこで今回は、IPv6の仕様を調べてまとめていこうと思ったのですが、私のような初学者にはRFCの仕様はとっつきづらかったため、今回はPythonの標準ライブラリであるipaddressのコードを読みながらIPv6について学びつつ、理解を深めていこうと思います。

ipaddress ライブラリ

今回はこの ipaddress ライブラリ内の IPv6Address というクラスを見ていくのですが、その前にライブラリ全体の構成を見てこいうと思います。

ipaddress ライブラリは以下のようになっており、リストだと分かりづらいのでクラス図も貼っておきます。

  • _IPAddressBase
  • _BaseAddress(_IPAddressBase)
  • _BaseNetwork(_IPAddressBase)
  • _BaseV4
  • IPv4Address(_BaseV4, _BaseAddress)
  • IPv4Interface(IPv4Address)
  • IPv4Network(_BaseV4, _BaseNetwork)
  • _BaseV6
  • IPv6Address(_BaseV6, _BaseAddress)
  • IPv6Interface(IPv6Address)
  • IPv6Network(_BaseV6, _BaseNetwork)

このままではまだ複雑なのでここでは Address 関連のもののみを見ていくことにします。
すると _BaseV6IPv6Address クラスを見れば大体IPv6の特徴が掴めそうということが分かります。

  • _IPAddressBase
    • _BaseAddress(_IPAddressBase)
  • _BaseV4
    • IPv4Address(_BaseV4, _BaseAddress)
  • _BaseV6
    • IPv6Address(_BaseV6, _BaseAddress)

IPv6Address

IPv4とIPv6の一番大きな違いとして使用可能なIPアドレスの数の違いがあります。

ipaddress.py
IPV4LENGTH = 32
IPV6LENGTH = 128

また、表現方法も異なるため、その辺の違いが _BaseV4_BaseV6 にそれぞれ実装されています。
(表現方法や省略方法についてはここでは割愛します)

ということでここからは IPv6Address を見ていくことにします。

IPv6Address プロパティ

IPv6Address には以下のプロパティが定義されています。

  • scope_id*
  • packed(バイナリ表現)
  • is_multicast(マルチキャストアドレス)
  • is_reserved(予約済みアドレス)
  • is_link_local*(リンクローカルアドレス)
  • is_site_local*, **(サイトローカルアドレス)
  • is_private(プライベートアドレス)
  • is_global(グローバルアドレス)
  • is_unspecified(未定義アドレス)
  • is_loopback(ループバックアドレス)
  • ipv4_mapped***(IPv4-mappedアドレス)
  • teredo*
  • sixtofour*

*: IPv4Address には無いプロパティ
**: 「サイトローカルアドレス」は廃止されています
***: IPv4Addressipv6_mapped がある

IPv6のアドレスアーキテクチャ関連

IPv6もIPv4同様、IPアドレスの先頭のビットパターンでIPアドレスの種類を区別します。
上記のプロパティを見たところ、is_ で始まるプロパティがそれらを区別するためのプロパティのようです。

以下に実装の一部を載せます。
(IPv4-mappedアドレスの際の実装は省きます)

  • is_multicast(マルチキャストアドレス)
    •   @property
        def is_multicast(self):
            """Test if the address is reserved for multicast use.
      
            Returns:
                A boolean, True if the address is a multicast address.
                See RFC 2373 2.7 for details.
      
            """
            return self in IPv6Network('ff00::/8')
      
  • is_reserved(予約済みアドレス)
    •   @property
        def is_reserved(self):
            """Test if the address is otherwise IETF reserved.
      
            Returns:
                A boolean, True if the address is within one of the
                reserved IPv6 Network ranges.
      
            """
            return any(self in x for x in [
                IPv6Network('::/8'), IPv6Network('100::/8'),
                IPv6Network('200::/7'), IPv6Network('400::/6'),
                IPv6Network('800::/5'), IPv6Network('1000::/4'),
                IPv6Network('4000::/3'), IPv6Network('6000::/3'),
                IPv6Network('8000::/3'), IPv6Network('A000::/3'),
                IPv6Network('C000::/3'), IPv6Network('E000::/4'),
                IPv6Network('F000::/5'), IPv6Network('F800::/6'),
                IPv6Network('FE00::/9'),
            ])
      
  • is_link_local(リンクローカルアドレス)
    •   @property
        def is_link_local(self):
            """Test if the address is reserved for link-local.
      
            Returns:
                A boolean, True if the address is reserved per RFC 4291.
      
            """
            return self in IPv6Network('fe80::/10')
      
  • is_site_local(サイトローカルアドレス)
    •   @property
        def is_site_local(self):
            """Test if the address is reserved for site-local.
      
            Note that the site-local address space has been deprecated by RFC 3879.
            Use is_private to test if this address is in the space of unique local
            addresses as defined by RFC 4193.
      
            Returns:
                A boolean, True if the address is reserved per RFC 3513 2.5.6.
      
            """
            return self in IPv6Network('fec0::/10')
      
  • is_private(プライベートアドレス)
    •   @property
        @functools.lru_cache()
        def is_private(self):
            """``True`` if the address is defined as not globally reachable by
            iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
            (for IPv6) with the following exceptions:
      
            * ``is_private`` is ``False`` for ``100.64.0.0/10``
            * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
                semantics of the underlying IPv4 addresses and the following condition holds
                (see :attr:`IPv6Address.ipv4_mapped`)::
      
                    address.is_private == address.ipv4_mapped.is_private
      
            ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
            IPv4 range where they are both ``False``.
            """
            # Not globally reachable address blocks listed on
            # https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
            _private_networks = [
                IPv6Network('::1/128'),
                IPv6Network('::/128'),
                IPv6Network('::ffff:0:0/96'),
                IPv6Network('64:ff9b:1::/48'),
                IPv6Network('100::/64'),
                IPv6Network('2001::/23'),
                IPv6Network('2001:db8::/32'),
                # IANA says N/A, let's consider it not globally reachable to be safe
                IPv6Network('2002::/16'),
                IPv6Network('fc00::/7'),
                IPv6Network('fe80::/10'),
            ]
            _private_networks_exceptions = [
                IPv6Network('2001:1::1/128'),
                IPv6Network('2001:1::2/128'),
                IPv6Network('2001:3::/32'),
                IPv6Network('2001:4:112::/48'),
                IPv6Network('2001:20::/28'),
                IPv6Network('2001:30::/28'),
            ]
            return (
                any(self in net for net in _private_networks)
                and all(self not in net for net in _private_networks_exceptions)
            )
      
  • is_global(グローバルアドレス)
    •   @property
        def is_global(self):
            """``True`` if the address is defined as globally reachable by
            iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
            (for IPv6) with the following exception:
      
            For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
            semantics of the underlying IPv4 addresses and the following condition holds
            (see :attr:`IPv6Address.ipv4_mapped`)::
      
                address.is_global == address.ipv4_mapped.is_global
      
            ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
            IPv4 range where they are both ``False``.
            """
            return not self.is_private
      
  • is_unspecified(未定義アドレス)
    •   @property
        def is_unspecified(self):
            """Test if the address is unspecified.
      
            Returns:
                A boolean, True if this is the unspecified address as defined in
                RFC 2373 2.5.2.
      
            """
            return self._ip == 0
      
  • is_loopback(ループバックアドレス)
    •   @property
        def is_loopback(self):
            """Test if the address is a loopback address.
      
            Returns:
                A boolean, True if the address is a loopback address as defined in
                RFC 2373 2.5.3.
      
            """
            return self._ip == 1
      

一番気になったのは is_private プロパティです。
IPv6の文脈では、ローカルやグローバルという言葉はありますが、プライベートという言葉を使っている資料をほとんど見ないので見慣れないのですが、どうやらグローバルから到達出来ないアドレスのことを言っているようで、そのようなIPネットワークがいくつか定義されているようです。

そしてもう一つ、is_site_local プロパティですが、こちらはコメントにもある通り廃止されている(セキュリティ上の問題があった)ようで、調べてみると新たに「ユニークローカルアドレス」というアドレスに置き換わっており、_private_networks 内の IPv6Network('fc00::/7') として定義されています。

その他

  • scope_id*
    •   @property
        def scope_id(self):
            """Identifier of a particular zone of the address's scope.
      
            See RFC 4007 for details.
      
            Returns:
                A string identifying the zone of the address if specified, else None.
      
            """
            return self._scope_id
      
  • teredo*
    •   @property
        def teredo(self):
            """Tuple of embedded teredo IPs.
      
            Returns:
                Tuple of the (server, client) IPs or None if the address
                doesn't appear to be a teredo address (doesn't start with
                2001::/32)
      
            """
            if (self._ip >> 96) != 0x20010000:
                return None
            return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
                    IPv4Address(~self._ip & 0xFFFFFFFF))
      
  • sixtofour*
    •   @property
        def sixtofour(self):
            """Return the IPv4 6to4 embedded address.
      
            Returns:
                The IPv4 6to4-embedded address if present or None if the
                address doesn't appear to contain a 6to4 embedded address.
      
            """
            if (self._ip >> 112) != 0x2002:
                return None
            return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)
      

こちらもそれぞれあまりピンとこず、とくに「Teredo」という単語は初めて目にしました。
これも調べてみたところ、どうやらTeredoトンネリングという、経路上にIPv6を理解しない通信機器が存在していてもIPv4を用いて通信を行うことを可能にするためのIPv6移行技術に利用されるものでした。

「sixtofour」に関してはTeredo同様、6to4トンネリングというIPv6移行技術の一つで、IPv6パケットをIPv4パケットのペイロードとしてカプセル化するためのものでした。

そして「スコープID」は RFC4007 に定義されているようで、こちらはリンクローカルアドレスの後ろにインターフェース名(ID)を fe80::1%eth0 のように付与して利用する際に用いられるもので、どのインターフェースからパケットが送出されているか分かるようになっています。

まとめ

ということで、今回はPythonの標準ライブラリである ipaddress の実装からIPv6に関する技術を見ていきました。
RFCの仕様ほど深い内容にはそこまで触れることは出来ませんでしたが、知識の浅い自分にとってはちょうどいい内容でした。

アドベントカレンダーも残ることあと2日ですが、残りの記事もぜひ楽しんでいってください。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?