Nefunkční připojení k IPv4 lookbacku v IPv6 síti

AI_ADDRCONFIG flag

IPv6 only síť již používáme na všechny naše produkty a musíme říct, že aktualizovaný software je na IPv6 only nasazení celkem dobře připraven a nenastává velmi často, že bychom řešili nějakou zapeklitou situaci.

Tentokrát jsme se ale setkali se situací, kde jsme se nakonec museli podívat do zdrojových kódů jedné knihovny, abychom našli zdroj našich starostí.

Popis prostředí

Přestože většinu serverů nasazujeme jako IPv6 only, tak na serverech je stále dostupný IPv4 loopback a vypnutí IPv4 nelze udělat tak jednoduše jako vypnutí IPv6. Síťová konfigurace takového serveru pak vypadá následovně:

root@test:~# ip a
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
48: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:62:e8:9f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 2001:db8:0:0:216:3eff:fe62:e89f/64 scope global mngtmpaddr dynamic
       valid_lft 86103sec preferred_lft 14103sec
    inet6 fe80::216:3eff:fe62:e89f/64 scope link
       valid_lft forever preferred_lft forever

Server tedy má jednu globální IPv6 adresu na rozhraní ens3 a na loopbacku jak IPv4, tak IPv6 adresu.

Na server jsme nainstalovali memcached server a protože se jednalo o testovací server, tak jsme jej nechali v defaultní konfigurací. Naslouchal tedy jen na IPv4 loopacku - čili na adrese 127.0.0.1.

root@test:~# ss -at 'sport = 11211'
State       Recv-Q Send-Q         Local Address:Port         Peer Address:Port
LISTEN      0      128            127.0.0.1:11211            *:*

Nelze se připojit k memcached

Testovaná aplikace měla v konfiguraci nastaveno, aby se připojovala k adrese 127.0.0.1:11211 po TCP. Jenže připojení k memcached se nezdařilo. Dokonce ani strace neukazoval žádné známky toho, že by se vůbec o nějaký connect() syscall pokoušel.

Po několika pokusech najít problém pomocí telnet, strace, ss a dalších nástrojů, jsme zkusili vše překonfigurovat na IPv6 loopback - čili adresu [::1]:11211, kde se připojení bez problému podařilo.

Řekněme, že v tento moment je problém vyřešen. Pro nás je ale důležité nejen najít řešení, ale i vědět proč k té situaci vůbec došlo. A tak jsme se pustili do dalšího studia vzniklé situace.

Vytáhli jsme si tedy z aplikace kus kódu, který se připojuje k memcached a vytvořili jsme si jednoduchý test case, na kterém jsme problém dále simulovali. Aplikace je napsaná v perlu a testovací kód tedy vypadá následovně:

#!/usr/bin/perl

use Cache::Memcached::Fast;

my $memcached = Cache::Memcached::Fast->new({ 'servers' => [ '127.0.0.1:11211' ] });

my $ismemcached = $memcached->set('ismemcached','1');
unless ($ismemcached) {
    warn "\nMemcached is not responding\n";
}

Tento krátký prototyp nás dovedl k tomu, že bude problém v knihovně Cache::Memcached::Fast, kterou aplikace používá. Rychle jsme si také vyzkoušeli, že velmi podobná knihovna Cache::Memcached tímto problémem netrpí.

Problém způsobil flag AI_ADDRCONFIG

Autor knihovny se snažil dostát názvu knihovny všemi možnými prostředky a tak nastavil u funkce getaddrinfo, která se stará o překlad adres, flag AI_ADDRCONFIG.

Popis AI_ADDRCONFIG flagu:

If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4 addresses are returned in the list pointed to by res only if the local system has at least one IPv4 address configured, and IPv6 addresses are returned only if the local system has at least one IPv6 address configured.  The loopback address is not considered for this case as valid as a configured address.  This flag is useful on, for example, IPv4-only systems, to ensure that getaddrinfo() does not return IPv6 socket addresses that would always fail in connect(2) or bind(2).

Zjednodušeně řečeno tento flag zajistí, že getaddrinfo vrátí adresy z rodiny IP protokolů jen takové, které jsou na daném systému nakonfigurované. V našem případě tedy fungoval pouze v IPv6 světě a IPv4 loopback pro něj vůbec neexistoval, protože na serveru nebyla vyjma loopbacku žádná IPv4 adresa.

Závěr

Použití IPv4 loopbacku v IPv6 only síti je zcela legitimní a věřím, že IPv4 svět s námi bude ještě několik generací. Popravdě nás tato situace docela překvapila, protože nebyla podobná žádnému jinému běžnému problému, se kterými se setkáváme. Celé řešení však netrvalo dlouho a to hlavně díky otevřenému zdrojovému kódu, ve kterém jsme byli schopni velmi rychle najít příčinu naší situace.

Pevně věříme, že autor knihovny měl s daným flagem dobrý záměr a vyřešil mu nějakou jinou problémovou situaci. I tak jsme ale alespoň vytvořili issue, ve kterém celou situaci můžeme prodiskutovat a případně zaslat úpravu kódu. Z naší zkušenosti ale víme, že u běžných změn trvá velmi dlouho než se případné změny dostanou do upstreamu a pak do distribucí. V tomto případě jsme tedy upravili náš config management, aby se aplikace k memcached připojovala pomocí IPv6 loopbacku.