Calico Kubernetes the hard way v2 How packet goes from pod

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску

Calico: как пакет выходит из POD

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

Упрощенная схема

На схеме не показаны интерфейсы которые не описаны в этом документе, только те что касаются пересылки пакетов из POD в хостовую систему

+----------------------------------------+
| Host:                                  |
|    +---------------------+             |
|    | POD                 |             |
|    |                     |             |
|    |  10.244.235.132/32  |             |
|    +---eth0--------------+             |
|         |                              |
|         |                              | 
|    calieede18e00ae                     |
|     На этом интерфейсе НЕТ ip адреса   |
+----------------------------------------+

"Странности" внутри PODa

Внутри POD таблица маршрутизации выглядит достаточно необычно:

route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

При этом на первый взгляд выглядит так как-будто работать такая схема не должна
Адрес 169.254.1.1 это вообще Link-Local address
На интерфейсе установлен адрес с маской /32, в качестве шлюза указан адрес доступный непосредственно через интерфейс

ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.235.132  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::68a6:d0ff:fe94:1d10  prefixlen 64  scopeid 0x20<link>
        ether 6a:a6:d0:94:1d:10  txqueuelen 0  (Ethernet)
        RX packets 5  bytes 446 (446.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 17  bytes 1286 (1.2 KB)
        TX errors 0  dropped 1 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0



Сам интерфейс внутри POD представляет из себя обычный для контейнеров veth, второй конец которой находится за пределами сетевого пространства имен POD,
для того что бы найти второй конец виртуального линка можно определить peer_ifindex

ethtool -S eth0
NIC statistics:
     peer_ifindex: 254
     rx_queue_0_xdp_packets: 0
     rx_queue_0_xdp_bytes: 0
     rx_queue_0_xdp_drops: 0

В примере выше peer_ifindex: 254 это и есть индекс искомого интерфейса (на хосте, за пределами POD), который можно увидеть командой ip link show

ip -d link show

254 это и есть найденный peer_ifindex

...
254: calieede18e00ae@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default 
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 13 promiscuity 0 minmtu 68 maxmtu 65535 
    veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 
    RX: bytes  packets  errors  dropped overrun mcast   
    237561215  1243131  0       0       0       0       
    TX: bytes  packets  errors  dropped carrier collsns 
    236237144  1273779  0       0       0       0   

Как это работает (отправка пакета из POD)

Для того что бы PODмог отправить пакет, он проделывает следующие шаги:

  • просматривает таблицу маршрутизации
  • так как в таблице всего одна запись, шлюз, то все пакеты будут отправляться на шлюз
  • для того что бы отправить пакет на шлюз необходимо знать МАК-адрес шлюза, соответственно необходима запись в arp-таблице
  • для ее создания будет создан arp-запрос

Но, так как (со стороны хоста, за пределами контейнера) на ответной части интерфейса eth0, интерфейсе calieede18e00ae нет никакого ip адреса, возникает вопрос.


Откуда берется arp-ответ?

Linux proxy_arp

Короткий ответ на вопрос - это "proxy arp".
Для того что бы в первом приближении понять что механизм proxy_arp включен, можно проверить sysctl:

sysctl -a | grep calieede18e00ae | grep -v ipv6

Вывод сокращен:

...
net.ipv4.conf.calieede18e00ae.proxy_arp = 1
net.ipv4.conf.calieede18e00ae.proxy_arp_pvlan = 0
net.ipv4.neigh.calieede18e00ae.proxy_delay = 0
...

Из вывода видно (net.ipv4.conf.calieede18e00ae.proxy_arp = 1), что для интерфейса механизм proxy_arp включен (значение ключа установлено в 1)
Далее рассмотрим его работу более подробно.

Proxy ARP: 2 варианта

В целом в Linux есть 2 реализации proxy_arp, которые работают немного по разному

  1. proxy_arp, который используется Calico
  2. proxy_arp_pvlan, который реализует несколько другую логику ответов на arp-запросы (о нем ниже)


proxy_arp

Описание в документации достаточно скудное:

proxy_arp - BOOLEAN
	Do proxy arp.
	proxy_arp for the interface will be enabled if at least one of
	conf/{all,interface}/proxy_arp is set to TRUE,
	it will be disabled otherwise

Информации не много. Из других источников, удалось выяснить, что при включении этой опции ядро будет отвечать на arp-запросы только если они требуют переадресации.
Попробую пояснить на примерах.

Пример

Допустим есть интерфейс eth0 на котором включен proxy_arp и на этом интерфейсе настроен IP адрес 192.168.0.1/24.
Согласно маске /24, это означает что все адреса от 192.168.0.0 до 192.168.0.255 доступны непосредственно через этот интерфейс.

Ядро будет отвечать МАК-адресом интерфейса eth0 на все ARP-запросы, полученные на интерфейсе eth0,
если IP aдрес в запросе, для которого запрашивается МАК адрес
доступен НЕ через этот интерфейс, другими словами любой адрес в ARP, Request who-has не из диапазона 192.168.0.0-192.168.0.255


  • ARP, Request who-has 10.20.30.40 - будет обработан и отправлен ответ с мак-адресом интерфейса eth0,так как адрес в запросе 10.20.30.40 не из диапазона 192.168.0.0-192.168.0.255 (при условии что существует маршрут к 10.20.30.40, в том числе даже если это default
  • ARP, Request who-has 192.168.0.100 - будет проигнорирован так как адрес 192.168.0.100 из 192.168.0.0-192.168.0.255

Это поведение вполне можно считать логичным - по-умолчанию ядро предполагает, что хост с адресом 192.168.0.100, так как он находится в сети доступной через eth0, сам может ответить и "помогать" с таким запросом необходимости нет.


В нашем случае с Calico этот механизм вполне работает, и вот почему

  • Ядро получает arp-запрос ARP, Request who-has 169.254.1.1 на интерфейсе calieede18e00ae
  • Адрес 169.254.1.1 с точки зрения ядра доступен через маршрут по-умолчанию (а не через интерфейс calieede18e00ae)
  • Условия соблюдены, значит ответ на запрос будет отправлен.


Отмечу отдельно, что в такой конфигурации можно было бы обойтись даже без шлюза, указав со стороны POD маршрут по-умолчанию в интерфейс eth0
Это несомненно работает, но влечет за собой перерасход памяти, так как вместо одной записи для шлюза в таблицу arp будут попадать записи для каждого IP адреса с которым POD будет устанавливать соединение

proxy_arp_pvlan

Второй доступный в Linux механизм это proxy_arp_pvlan
Мне при первом прочтении документация показалась совершенно недостаточной

proxy_arp_pvlan - BOOLEAN
	Private VLAN proxy arp.
	Basically allow proxy arp replies back to the same interface
	(from which the ARP request/solicitation was received).

	This is done to support (ethernet) switch features, like RFC
	3069, where the individual ports are NOT allowed to
	communicate with each other, but they are allowed to talk to
	the upstream router.  As described in RFC 3069, it is possible
	to allow these hosts to communicate through the upstream
	router by proxy_arp'ing. Don't need to be used together with
	proxy_arp.

	This technology is known by different names:
	  In RFC 3069 it is called VLAN Aggregation.
	  Cisco and Allied Telesyn call it Private VLAN.
	  Hewlett-Packard call it Source-Port filtering or port-isolation.
	  Ericsson call it MAC-Forced Forwarding (RFC Draft).

Тут суть проблемы в том, что существуют сети, которые хотя и выглядят внешне как ethernet таковыми являются не в полной мере

  • Радио (wifi) - представим себе ситуацию, что есть сеть из трех устройств, и максимальное расстояние на которых они могут взаимодействовать это 100м. Сеть состоит из точки доступа (базовой станции) и 2 клиентов

    |(((((((((((      )))))) | ((((((          ))))))) |
    |                       /|\                        |
+---+-------+           +--------------+           +---+-------+
| Client 1  |           | Base Station |           | Client 2  |
+-----------+           +--------------+           +-----------+
 192.168.0.2/24             192.168.0.1/24                 192.168.0.3/24
    0M -----------------------100M ----------------200M

Из грубой схемы видно, что хотя каждый из клиентов сможет передавать данные на базовую станцию и получать от нее ответы, связаться друг с другом напрямую клиенты не могут из-за того что находятся далеко друг от друга.
при этом типичная ситуация что все клиенты находятся в одном диапазоне адресов. Таким образом возникает проблема передачи данных от Client1 к Client 2:

    • Прямая связь невозможна из за расстояния
    • База даже при включенном proxy_arp будет "думать" что клиенты должны и без нее знать маки друг-друга


  • Сети с изоляцией портов. Эта технология позволяет специально запретить всякое прохождение пакетов между портами коммутатора, указанными как клиентские и разрешить прохождение только от порта роутера к клиенту и от клиента к порту роутера. У разных производителей оборудования технология может называться по-разному.


Для решения этих проблем и был добавлен механизм proxy_arp_pvlan, который разрешает ответы proxy_arp даже если они получены с того же интерфейса где ожидаемо находится адрес, который запрашивался

В документации написано Basically allow proxy arp replies back to the same interface (from which the ARP request/solicitation was received)., что меня сбивает с толку и наводит на мысли о том что есть случаи когда ответ отправляется не с того интерфейса, с которого был запрос. Это НЕ ТАК. Речь идет о том интерфейсе, через который доступен адрес, чей мак запрашивается.

  • В случае если к базовой станции из примера выше приходит запрос ARP, Request who-has 192.168.0.3 от клиента 1, базовая станция сделает следующие шаги
    • Проверит через какой интерфейс получен запрос (для простоты считаем что это wifi0)
    • Проверит через какой интерфейс доступен запрошенный адрес 192.168.0.3, это тоже wifi0

В зависимости от настройки proxy_arp_pvlan будет принято решение

  • если proxy_arp_pvlan = 1 ответ будет отправлен, так как базовая станция знает что не каждый клиент может получить мак другого клиента
  • если proxy_arp_pvlan = 0 запрос будет проигнорирован, так как базовая станция думает что клиенты могут сами обменяться информацией о мак-адресах


При этом настройка proxy_arp_pvlan никак не влияет на работу proxy_arp - для запросов адресов которые доступны через другие интерфейсы, отличные от того через который поступил запрос, будет обработан или игнорироваться в зависимости от значения proxy_arp

Для Cisco

  • ip proxy-arp - аналог proxy_arp
  • ip local-proxy-arp - аналог proxy_arp_pvlan

Подведение итога

Для закрепления еще раз повторю путь пакета из POD до хост-системы

  • Внутри POD3 POD хочет отправить пакет
  • Внутри POD3 POD просматривает свою таблицу маршрутизации и находит что у него единственный маршрут через шлюз
  • Внутри POD3 Шлюз доступен через интерфейс eth0
  • Внутри POD3 формируется и отправляется ARP запрос
  • На ноде Arp запрос получен на стороне ноды, и обработан за счет механизма proxy_arp
  • Внутри POD3 POD получает ответ на свой запрос, добавляет запись в свою arp таблицу
  • Внутри POD3 POD отправляет пакет, где в МАК-адрес получателя это МАК-адрес интерфейса на стороне ноды, изученный их ARP-ответа
  • На ноде Нода получает пакет, и так как ма-адрес получателя это ее МАК то пакет не отбрасывается а обрабатывается далее согласно правилам маршрутизации/файрволла

Ссылки