Об IPv6

2018-11-17

Тихо и незаметно наступает эпоха IPv6. Уже можно заполучить IPv6 дома или в офисе, по крайней мере, клиентам ЭР-Телекома. Уже подключают к интернетам только по IPv6, по крайней мере, некоторых клиентов нашего заказчика, где-то в Америке. Уже без проблем можно получить IPv6 адрес для любого сервера любого уровня виртуальности почти у любого хостера или облачного провайдера. Уже встречаются дешманские виртуалки только с IPv6, и с IPv4 через NAT, где проброшен десяток портов.

ipv6

В IPv4, как помните, адрес 32-битный. Лишь четыре миллиарда адресов. И они закончились ещё в 2011.

В IPv6 адрес уже 128 бит. Этого хватит всем. Всем землянам, по крайней мере. И миллионам их карманных устройств, у каждого.

IPv6 адреса записываются в виде шестнадцатеричных чисел. Восемь четырёхзначных чисел, разделённых двоеточием. Начальные нули в каждом числе можно упустить. Самую длинную группу из нулевых чисел тоже можно упустить. В результате адрес localhost выглядит как ::1.

$ ip addr show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

IPv6 адреса на интерфейсе, глядящем в Интернет, выглядят примерно так:

$ ip -6 addr show dev wlp2s0
2: wlp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 fd9e:ecc8:b68d::13f/128 scope global noprefixroute
       valid_lft forever preferred_lft forever
    inet6 2a02:ffff:ffff:12f0::13f/128 scope global dynamic noprefixroute
       valid_lft 85082sec preferred_lft 2282sec
    inet6 fd9e:ecc8:b68d:0:49ba:acb9:11b5:5adb/64 scope global temporary dynamic
       valid_lft 597201sec preferred_lft 78558sec
    inet6 fd9e:ecc8:b68d:0:fcd2:44e9:bb4d:d28b/64 scope global mngtmpaddr noprefixroute
       valid_lft forever preferred_lft forever
    inet6 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64 scope global temporary dynamic
       valid_lft 85081sec preferred_lft 2281sec
    inet6 2a02:ffff:ffff:12f0:c354:cdb3:9794:b0a/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 85081sec preferred_lft 2281sec
    inet6 fe80::ff0b:c674:5528:a3c8/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

Да. IPv6 адресов всегда больше одного.

Адрес, начинающийся с fe80::, который scope link, присутствует всегда. Просто, если у вас включен IPv6. Даже если вы никуда не подключены. Этот адрес назначается автоматически, и уникален для данного интерфейса.

Можно считать это неким аналогом адресов 169.254.0.0/16 в IPv4. Только в IPv4 адрес 169.254 выдаётся только если не удалось получить адрес другим способом, например, по DHCP. И на это иногда уходит несколько секунд при загрузке ОС.

А в IPv6 адрес fe80:: назначается сразу, локально. Он конструируется из MAC адреса. И, в результате, в IPv6 хост может сразу общаться с другими IPv6 хостами, правда, в рамках только локальной сети.

Этого вполне достаточно, чтобы запросить публичный маршрутизируемый адрес у ближайшего маршрутизатора. Таким образом, адреса получаются автоматически без привлечения других протоколов. Это называется Router Advertisement.

Адреса раздаются префиксами. Подсетями чудовищного размера с маской /64. Прикиньте, да, 1.8e+19 адресов на абонента.

В данном случае у нас виднеется два префикса.

$ sipcalc fd9e:ecc8:b68d:0:49ba:acb9:11b5:5adb/64
-[ipv6 : fd9e:ecc8:b68d:0:49ba:acb9:11b5:5adb/64] - 0

[IPV6 INFO]
Expanded Address        - fd9e:ecc8:b68d:0000:49ba:acb9:11b5:5adb
Compressed address      - fd9e:ecc8:b68d:0:49ba:acb9:11b5:5adb
Subnet prefix (masked)  - fd9e:ecc8:b68d:0:0:0:0:0/64
Address ID (masked)     - 0:0:0:0:49ba:acb9:11b5:5adb/64
Prefix address          - ffff:ffff:ffff:ffff:0:0:0:0
Prefix length           - 64
Address type            - Unassigned
Network range           - fd9e:ecc8:b68d:0000:0000:0000:0000:0000 -
                          fd9e:ecc8:b68d:0000:ffff:ffff:ffff:ffff

То, что начинается на fd9e:ecc8: — это "Unique Local Unicast" (ULA) адреса. Это аналог 192.168.0.0/16, или 10.xxx.xxx.xxx или 172.16.xxx.xxx. Это немаршрутизируемые адреса, применимые только в локальной сети. В данном случае эти адреса нам зачем-то выдал маршрутизатор на OpenWRT.

$ sipcalc 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64
-[ipv6 : 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64] - 0

[IPV6 INFO]
Expanded Address        - 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb
Compressed address      - 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb
Subnet prefix (masked)  - 2a02:ffff:ffff:12f0:0:0:0:0/64
Address ID (masked)     - 0:0:0:0:49ba:acb9:11b5:5adb/64
Prefix address          - ffff:ffff:ffff:ffff:0:0:0:0
Prefix length           - 64
Address type            - Aggregatable Global Unicast Addresses
Network range           - 2a02:ffff:ffff:12f0:0000:0000:0000:0000 -
                          2a02:ffff:ffff:12f0:ffff:ffff:ffff:ffff

А вот префикс 2a02:ffff:ffff:12f0::/64 — это уже настоящий публичный префикс, выданный провайдером.

И в этом настоящем префиксе у нас почему-то аж три адреса.

$ ip -6 addr show dev wlp2s0 | grep 2a02
    inet6 2a02:ffff:ffff:12f0::13f/128 scope global dynamic noprefixroute
    inet6 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64 scope global temporary dynamic
    inet6 2a02:ffff:ffff:12f0:c354:cdb3:9794:b0a/64 scope global dynamic mngtmpaddr noprefixroute

Адрес, помеченный mngtmpaddr, — это адрес, автоматически назначенный для данного префикса и данного MAC адреса. Точнее для EUI-64.

$ ipv6calc -i 2a02:ffff:ffff:12f0:c354:cdb3:9794:b0a/64
Address type: unicast, global-unicast, productive, iid, iid-global, iid-eui64
Interface identifier: c354:cdb3:9794:0b0a
EUI-64 identifier: c1:54:cd:b3:97:94:0b:0a
EUI-64 identifier is a global unique one

У этих адресов беда с безопасностью. Вторая половина IPv6 адреса у вашего компьютера будет всегда одной и той же, куда бы вы не перемещались, и какой бы IPv6 префикс вы не получали. Так можно отследить ваши перемещения.

Поэтому генерируется ещё второй адрес, помеченный как temporary. Здесь суффикс уже полностью случайный. Это называется "Privacy Extension" (RFC 3041).

$ ipv6calc -i 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64
Address type: unicast, global-unicast, productive, iid-random, iid, iid-local
Interface identifier: 49ba:acb9:11b5:5adb
Interface identifier is probably generated by privacy extension

Последний адрес выдан DHCPv6. На самом деле, DHCP для IPv6 не особо нужен, и так адреса нормально назначаются. Но иногда, вроде, он нужен. По крайней мере у ЭР-Телекома нужно заполучать префиксы через DHCPv6 внутри PPPoE соединения.

$ ipv6calc -i 2a02:ffff:ffff:12f0::13f/128
Address type: unicast, global-unicast, productive, iid, iid-local
Interface identifier: 0000:0000:0000:013f
Interface identifier is probably manual set

Так под каким адресом мы ходим в интернет? В таблице маршрутизации всё запутанно.

$ ip -6 route show dev wlp2s0
2a02:ffff:ffff:12f0::13f proto kernel metric 600 pref medium
2a02:ffff:ffff:12f0::/64 proto ra metric 600 pref medium
fd9e:ecc8:b68d::13f proto kernel metric 600 pref medium
fd9e:ecc8:b68d::/64 proto ra metric 600 pref medium
fd9e:ecc8:b68d::/48 via fe80::c66e:1fff:feb9:e41b proto ra metric 600 pref medium
fe80::/64 proto kernel metric 256 pref medium
fe80::/64 proto kernel metric 600 pref medium
default via fe80::c66e:1fff:feb9:e41b proto ra metric 600 pref medium

Тут видно, что адрес маршрутизатора (via) указан в виде локального адреса fe80::. Локальная сеть, оказывается, имеет большой префикс /48: fd9e:ecc8:b68d::/48. Адреса, полученные по DHCPv6, помечены как proto kernel. А адреса, полученные через Router Advertising, помечены как proto ra. И default маршрут тоже proto ra.

Выходит, что Router Advertising вроде как предпочтительнее DHCPv6. А temporary адрес, который случайный, должен быть предпочтительнее. И действительно, Яндекс Интернетометр говорит, что наш IPv6 адрес — 2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb. Собственно, остальные адреса и помечены как noprefixroute, то есть с ними не связаны маршруты.

Похоже, моя домашняя сеть — это какой-то IPv6 ад. На серваках всё проще.

# ip -6 addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
    inet6 2001:ffff:ffff:19c:5400:ff:fe54:a41/64 scope global mngtmpaddr dynamic
       valid_lft 2591971sec preferred_lft 604771sec
    inet6 fe80::5400:ff:fe54:a41/64 scope link
       valid_lft forever preferred_lft forever

Тут нет DHCPv6. Тут нет нужды в Privacy Extension, потому что серваки не ездят по планете. В результате у нас только два адреса: автоматический локальный fe80:: и настоящий публичный IPv6.

$ ip -6 route show dev eth0
2001:ffff:ffff:19c::/64  proto kernel  metric 256  expires 2591828sec
fe80::/64  proto kernel  metric 256
default via fe80::fc00:ff:fe54:a41  proto ra  metric 1024  expires 1628sec hoplimit 64

С маршрутами тоже всё просто и понятно.

Сетевые приложения, как правило, предпочитают IPv6 сокеты. Серверный IPv6 сокет способен принимать подключения как по IPv6, так и по IPv4, не пугайтесь.

# ss -lnpt
State   Recv-Q Send-Q  Local Address:Port  Peer Address:Port
LISTEN  0      128         127.0.0.1:6379             *:*     users:(("redis-server",pid=571,fd=4))
LISTEN  0      128                 *:80               *:*     users:(("nginx",pid=598,fd=6),("nginx",pid=596,fd=6))
LISTEN  0      128                 *:22               *:*     users:(("sshd",pid=555,fd=3))
LISTEN  0      128                 *:443              *:*     users:(("nginx",pid=598,fd=8),("nginx",pid=596,fd=8))
LISTEN  0      128                :::8080            :::*     users:(("java",pid=516,fd=39))
LISTEN  0      128                :::80              :::*     users:(("nginx",pid=598,fd=7),("nginx",pid=596,fd=7))
LISTEN  0      128                :::22              :::*     users:(("sshd",pid=555,fd=4))
LISTEN  0      128                :::443             :::*     users:(("nginx",pid=598,fd=9),("nginx",pid=596,fd=9))

А вот для Nginx нужно явно указать слушание обоих протоколов.

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    listen [::]:80 default_server;
    listen [::]:443 ssl default_server;

    #...

В URL IPv6 адрес положено брать в квадратные скобки: http://[2a00:1450:4011:808::1001].

Для IPv6 адресов в DNS предусмотрен специальный тип записи AAAA.

$ dig +noall +question +answer aaaa google.com
;google.com.                    IN      AAAA
google.com.             299     IN      AAAA    2a00:1450:4011:808::1008

У самих DNS серверов, включая публичные DNS гугла, тоже есть IPv6 адреса.

$ dig +noall +question +answer +stats @2001:4860:4860::8888 aaaa google.com
;google.com.                    IN      AAAA
google.com.             271     IN      AAAA    2a00:1450:4011:80b::1002
;; Query time: 48 msec
;; SERVER: 2001:4860:4860::8888#53(2001:4860:4860::8888)
;; WHEN: Sat Nov 17 19:38:19 +06 2018
;; MSG SIZE  rcvd: 67
$ dig +noall +question +answer +stats @2001:4860:4860::8844 aaaa google.com
;google.com.                    IN      AAAA
google.com.             299     IN      AAAA    2a00:1450:4011:80e::1009
;; Query time: 52 msec
;; SERVER: 2001:4860:4860::8844#53(2001:4860:4860::8844)
;; WHEN: Sat Nov 17 19:39:00 +06 2018
;; MSG SIZE  rcvd: 67
$ dig +noall +question +answer +stats @2606:4700:4700::1111 aaaa google.com
;google.com.                    IN      AAAA
google.com.             188     IN      AAAA    2a00:1450:4011:804::1004
;; Query time: 35 msec
;; SERVER: 2606:4700:4700::1111#53(2606:4700:4700::1111)
;; WHEN: Sat Nov 17 19:40:06 +06 2018
;; MSG SIZE  rcvd: 67
$ dig +noall +question +answer +stats @2606:4700:4700::1001 aaaa google.com
;google.com.                    IN      AAAA
google.com.             157     IN      AAAA    2a00:1450:4011:804::1004
;; Query time: 35 msec
;; SERVER: 2606:4700:4700::1001#53(2606:4700:4700::1001)
;; WHEN: Sat Nov 17 19:40:37 +06 2018
;; MSG SIZE  rcvd: 67

IPv6 вполне поддерживается в соответствующих типах данных PostgreSQL.

postgres=# select network('2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64');
         network
--------------------------
 2a02:ffff:ffff:12f0::/64
(1 строка)

postgres=# select inet '2a02:ffff:ffff:12f0:49ba:acb9:11b5:5adb/64' && cidr '2a02:ffff:ffff:12f0::/64';
 ?column?
----------
 t
(1 строка)

Вроде как существует возможность, имея только IPv6 адрес, ходить в IPv4 сети. Чтобы занатить IPv6 адреса в IPv4, есть NAT64. Чтобы резолвить то, что резолвится только в IPv4, в IPv6 адреса, есть DNS64.

Доля мирового IPv6 трафика уже доходит до 25%. Так что пора, пора приобщаться к новому Интернету. Пока не поздно.

ipv6 adoption

К сожалению, ЭР-Телеком не выдаёт статические IPv6 префиксы. То есть IPv6 вы попробовать сможете, но вот поднять у себя сервер без DynDNS не выйдет. Ну тоже неплохо.

И не забывайте, что IPv6 — это настоящий адрес. Безо всякого NAT. Так что настраивайте файерволы на маршрутизаторах, чтобы ваши телефоны в вайфае не похакали. В OpenWRT по дефолту всё норм, входящие соединения запрещены, получается как за NAT.