Připojení zařízení pomocí IPv6 a tunelu prakticky

V předchozím díle krátkého seriálu jsme si vysvětlili princip, na jehož základě lze zpřístupnit téměř libovolné zařízení na internetu pomocí IPv6 a tunelu. V této části si ukážeme prakticky, jakým způsobem je možné takový tunel vytvořit a jak přidělit svému zařízení za NATem veřejnou IPv6 adresu.

14.2.2013 00:00 | Petr Bravenec | přečteno 4647×

Při praktickém nastavování tunelu jsem se nechal inspirovat tímto krátkým návodem:

http://www.sixxs.net/wiki/L2tp

Pokud nezvolíte nějakou neobvyklou architekturu, mohli byste mít podle tohoto návodu nastavený základ pro tunel s IPv6 během chvilky. S trochou štěstí by vám mohl tunel už napoprvé fungovat alespoň na linkových adresách během půl hodiny (já jsem si celou úlohu trochu zkomplikoval procesorem ARM, ale už po dvou týdnech jsem na internetu našel fungující zdrojáky linuxového kernelu a mohl doplnit podporu pro l2tp a ppp).

Tunel jsem zkoušel vytvořit v distribuci Debian (na architektuře x86) a Gentoo (arm). Pro vytvoření tunelu s linkovými adresami potřebujete prakticky jen démony xl2tpd a pppd, pro nastavení veřejné IPv6 adresy pak můžete potřebovat i některý další program: radvd, dhcpd nebo, jako já, něco úplně jiného.

Nastavení serveru

Na serveru vytvořte konfigurační soubor /etc/xl2tpd/xl2tpd.conf:

[global]

[lns default]
name = mujserver
assign ip = no
require chap = yes
refuse pap = yes
require authentication = yes
unix authentication = no
ppp debug = no
pppoptfile = /etc/ppp/options.l2tpd

Zařízení se musí při vytváření tunelu autentikovat mechanismem chap. Heslo je uložené v souboru /etc/xl2tpd/l2tp-secrets:

# Secrets for authenticating l2tp tunnels
# us        them        secret
# my        oni         heslo
#
mujserver   zarizeni    heslo

Podobný soubor s hesly je umístěn i na zařízení, tam jsou však strany "my" a "oni" prohozené. Heslo můžete mít společné pro různá zařízení, není však problém vytvořit pro každé zařízení heslo vlastní.

V konfiguračním souboru můžete chtít změnit pouze parametr name - tento parametr je společným propojovatelem klientské části, serverové části a heslo.

Xl2tpd démon poslouchá na UDP portu 1701 (nezapomeňte na pravidla ve firewallu) a při příchozím požadavku na vytvoření tunelu od zařízení startuje pro toto zařízení démona pppd. Toho musíte nastavit taky. V konfiguraci xl2tpd je uvedeno, že pro pppd se má použít konfigurační soubor /etc/ppp/options.l2tpd:

refuse-eap
noccp
noauth
nodefaultroute
crtscts
idle 1800
mtu 1410
mru 1410
lock
connect-delay 5000
noip                    # zakáže IPv4
+ipv6                   # povolí IPv6
ipv6 ,                  # adresy pro IPv6 (nepřidělují se, zůstává prázdné)
ipv6cp-accept-local     # nastavit pouze linkové adresy

Po nastavení stačí na serveru spoustit démon xl2tpd. Jedinou podmínkou pro fungování démona je podpora pro l2tp a ppp v kernelu (obvykle není problém) a natažené moduly ipv6 a l2tp_ppp.

Nastavení zařízení

Na straně zařízení je nastavení podobně jednoduché. Konfigurační soubor /etc/xl2tpd/xl2tpd.conf:

[global]

[lac mujserver]
name = zarizeni
lns = mojebrana.hobrasoft.cz:1702
redial = yes
redial timeout = 15
require chap = yes
refuse pap = yes
require authentication = yes
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.client

Konfigurace xl2tpd na straně zařízení je podobná konfiguraci na serveru. Pravděpodobně budete chtít změnit jméno sekce "lac" - sem uveďte jméno tunelu na serveru - zde "mujserver", a parametr "name". Důležité je nastavení parametru "lns" - zde se očekává buď IPv4 adresa nebo dns jméno, uvést můžete i číslo portu (port 1702 používám kvůli jistému adminovi v zahraničí, který na požadavek "povolte ven port 1701 protokolem UDP" povolil vše ostatní, kromě portu 1701 - ten zakázal. Než shánět tlumočníka, abych adminovi sdělil svůj názor a vysvětlil význam požadavku, bylo jednodušší doplnit na serveru jedno pravidlo do iptables a změnit port).

Velmi podobný je i soubor s hesly /etc/xl2tpd/l2tp-secrets:

# Secrets for authenticating l2tp tunnels
# us        them        secret
# my        oni         heslo
#
zarizeni    mujserver   heslo

Konfigurace pppd je umístěná v souboru /etc/ppp/options.l2tpd.client a je prakticky shodná s konfigurací na serveru:

refuse-eap
noccp
noauth
nodefaultroute
crtscts
idle 1800
mtu 1410
mru 1410
lock
connect-delay 5000
noip                    # zakáže IPv4
+ipv6                   # povolí IPv6
ipv6 ,                  # adresy pro IPv6 (nepřidělují se, zůstává prázdné)
ipv6cp-accept-local     # nastavit pouze linkové adresy

Jakmile máte nakonfigurovaný a nastartovaný xl2tpd démon na obou koncích, na serveru i na zařízení, můžete na zařízení vyzkoušet nastartovat tunel:

echo "c mujserver" > /var/run/xl2tpd/l2tp-control

S trochou štěstí by se už nyní mohl tunel rozběhnout, ověřit jej můžete příkazem ifconfig nebo ip. Ve výpisu by mělo figurovat síťové rozhraní ppp0 s linkovou IPv6 adresou:

# ifconfig
ppp0      Link encap:Point-to-Point Protokol  
          inet6-adr: fe80::1923:481c:d7f8:68dd/10 Rozsah:Linka
          AKTIVOVÁNO POINTOPOINT BĚŽÍ NEARP MULTICAST  MTU:1410  Metrika:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
          kolizí:0 délka odchozí fronty:3 
          RX bytes:720 (720.0 B)  TX bytes:618 (618.0 B)

# ip addr
109: ppp0:  mtu 1410 qdisc pfifo_fast state UNKNOWN qlen 3
    link/ppp 
    inet6 fe80::1923:481c:d7f8:68dd/10 scope link 
       valid_lft forever preferred_lft forever

Máte-li adresu nastavenou, můžete svůj nový tunel vyzkoušet. Nezapomeňte, že linkové adresy lze použít pouze na síťovém rozhraní ppp0 a příkazu ping musíte parametrem -I sdělit, které rozhraní má použít. Adresu protějšku můžete zjistit na druhé straně tunelu:

# ping6 -I ppp0 fe80::fdc8:d2e6:eb72:ac51
PING fe80::fdc8:d2e6:eb72:ac51(fe80::fdc8:d2e6:eb72:ac51) from fe80::1923:481c:d7f8:68dd ppp0: 56 data bytes
64 bytes from fe80::fdc8:d2e6:eb72:ac51: icmp_seq=1 ttl=64 time=108 ms
64 bytes from fe80::fdc8:d2e6:eb72:ac51: icmp_seq=2 ttl=64 time=102 ms
64 bytes from fe80::fdc8:d2e6:eb72:ac51: icmp_seq=3 ttl=64 time=86.0 ms

Nyní by měl mezi serverem a zařízením fungovat veškerý síťový provoz, můžete se zkusit připojit například přes ssh, opět nezapomeňte uvést jméno síťového rozhraní:

# ssh 'fe80::fdc8:d2e6:eb72:ac51%ppp0'
The authenticity of host 'fe80::fdc8:d2e6:eb72:ac51%ppp0 (fe80::fdc8:d2e6:eb72:ac51%ppp0)' can't be established.
RSA key fingerprint is 68:7d:e6:2b:e4:e9:5c:a2:a9:a7:d4:90:4b:83:6b:06.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'fe80::fdc8:d2e6:eb72:ac51%ppp0' (RSA) to the list of known hosts.
Password:
server ~ # 

Zprovoznění IPv6 protokolu na linkových adresách je základ, bez kterého nemá smysl pokračovat dál. Máte-li s nastavením tunelu problém, zkuste hledat v logu /var/log/daemon.log, pomoci vám může i utilita tcpdump.

Startování tunelu

Pro automatické startování tunelu musíte do startovacích skriptů spouštěných při startu systému zahrnout i xl2tpd. Tím však nastartujete pouze démona, tunel se nevytvoří. Ten se vytváří až při zápisu příkazu "c mujserver" do souboru /var/run/xl2tpd/l2tp-control. Teoreticky by mělo stačit vytvořit tunel jednorázově po startu xl2tpd. Prakticky to nestačí - xl2tpd v distribuci Debian po nějakém čase neúspěšnou snahu na nastartování tunelu vzdá a vy tak můžete zůstat bez připojení, dokud někdo zařízení schované za NATem nerestartuje (parametr max redial nefunguje, jak by měl). Vhodnější a jednodušší, než vytvářet vlastní startovací skript pro vytvoření tunelu, je jediný řádek v crontab:

*/5 * * * * echo "c mujserver" > /var/run/xl2tpd/l2tp-control

Přizabijete tak dvě mouchy jednou ranou: vytváření tunelu při startu systému a restart tunelu při déletrvajících problémech s IPv4.

Nastavení veřejné IPv6 adresy

Zprovoznění linkové adresy je jen část úkolu. Kvůli point-to-point komunikaci nemělo smysl nastavovat IPv6 tunel, v původním zadání šlo o zpřístupnění zařízení v lokální síti pomocí veřejné IPv6 adresy.

Máte-li v internetu jediné zařízení, které chcete takto vybavit veřejnou IPv6 adresou, je nejjednodušší uvést adresy přímo do konfiguračního souboru pro pppd:

# Původní řádek zakomentovat, nebo smazat
# ipv6 ,                  # adresy pro IPv6 (nepřidělují se, zůstává prázdné)
ipv6 2001:db8:3000:1::1, 2001:db8:3000:1::2

Máte-li zařízení více, je možné použít pro přidělování adres démona radvd. V tomto případě je ale potřeba vytvořit konfiguraci pro každé síťové rozhraní v souboru /etc/radvd.conf podobnou této:

interface ppp0 {
        AdvSendAdvert on;
        MinRtrAdvInterval 3;
        MaxRtrAdvInterval 10;
        AdvHomeAgentFlag off;
        prefix  2001:db8:3000:1::/64  {
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
                };
};

Potíž může být v tom, že při přidělování adres pomocí radvd můžete dostat pokaždé jinou IPv6 adresu. Neovlivníte ani síťové rozhraní, nad kterým se tunel vytváří, takže vaše zařízení může dokonce pokaždé spadnout i do jiné sítě. Pokud vám to nevadí, může být radvd nejjednodušším řešením (nezkoušel jsem).

Výše zmiňovaný informační pramen zmiňuje pro nastavení IP adres také program quagga a routovací protokol ospf6.

Jinou cestou může být přidělování adres pomocí dhcp (na IPv6 nemám zkušenosti), kde však může být problém naopak s tím, že dostanete pokaždé stejnou IPv6 adresu - mně to třeba vadí z toho důvodu, že potřebuji mít adresu nastavitelnou jednoduše v zařízení. Sám jsem proto zvolil třetí, nejméně vhodnou cestu: zařízení se dohodne na IPv6 adrese se serverem pomocí jednoduchého skriptu, který se spouští po úšpěšném vytvoření tunelu.

Skript /etc/ppp/ipv6-up

Jakmile je tunel vytvořený a ppp nastaví linkové adresy pro spojení zařízení se serverem, máte možnost do celého procesu vstoupit pomocí skriptu /etc/ppp/ipv6-up. Ten je volán z programu pppd a umožňuje vám provádět s celou linkou další různá kouzla. V distribucích už bývají obsažené různé skripty, které mění například /etc/resolv.conf, sambu, poštovní server (v každé distribuci najdete něco jiného). Skript lze s úspěchem použít i pro nastavení IPv6 adres na obou stranách tunelu (jak na serveru, tak na zařízení). Jako typické využití skriptu bych viděl nastavení firewallu.

Skript se spouští na obou stranách tunelu. Skript je volaný s několika užitečnými parametry (podle pořadí):

Všimněte si, že skript dostane všechny potřebné informace pro komunikaci s druhou stranou. Ve skriptu na zařízení se můžete připojit na server přes nově vytvořený tunel a požádat si například o IP adresu pomocí HTTP protokolu (na druhé straně musí poslouchat webový server):

wget http://$4%1/dej-mi-adresu?HOSTNAME=`hostname`

Na straně serveru pak můžete skript využít pro nastavení firewallu, například takto:

ip6tables -A FORWARD -i $1 -j ACCEPT
ip6tables -A FORWARD -o $1 -j ACCEPT

Podobně, jako je při vytvoření tunelu volaný skript /etc/ppp/ipv6-up, při ukončení tunelu se volá skript /etc/ppp/ipv6-down. Sem lze umístit například uklízení v iptables na straně serveru, aby se vám ve firewallu nehromadila zbytečná pravidla:

ip6tables -D FORWARD -i $1 -j ACCEPT
ip6tables -D FORWARD -o $1 -j ACCEPT

U zařízení pro sběr dat z fotovoltaických elektráren jsem skripty využil jak pro nastavení veřejných IPv6 adres a firewallu, tak i pro další konfiguraci zařízení, aby se zprovoznění vlastního zařízení omezilo na zapojení do sítě, nastavení hostname a případně nastavení IPv4 adresy (pro případ, že je nutné zařízení provozovat v síti bez DHCP).

Netcat6

Pro předávání parametrů jsem využil utilitu netcat6. Netcat je velmi univerzální nástroj pro komunikaci po síti - dokaže se připojit protokolem UDP či TCP na libovolnou IP adresu s libovolným číslem portu (pak jej můžete používat místo telnetu), nebo dokáže poslouchat na síti, čekat na příchozí spojení a síťový provoz propojit s vaším vlastním skriptem. Pomocí nástroje netcat tak můžete vytvořit v shellu jednoduchý síťový server.

"Originál" netcat nedovede pracovat v síti IPv6. V Gentoo jsem objevil čtyři různé verze programu netcat, ale z těch vyzkoušených pro mé účely vyhovoval pouze netcat6.

Pro skripty v ppp je dále výhodou, že u programu netcat lze velmi přesně specifikovat, na jakém síťovém rozhraní, na kterém portu a jakým protokolem má netcat poslouchat, nastavit lze i různé timeouty. Díky tomu je možné netcat nastavit tak, aby se vaše zařízení mohla domlouvat pouze přes tunel a pouze v krátkém časovém okamžiku těsně po vytvoření tunelu. Nemusíte se proto příliš trápit se zabezpečením (nikdo jiný se s vámi bavit nemůže), ani s uklízením (po vypršení timeoutu netcat automaticky skončí).

Ukážu vám nyní svoje skripty pro nastavení linky (očesané na nezbytné minimum). Berte je, prosím, s malou rezervou. Chcete-li si situaci maximálně zjednodušit, můžete ve svých skriptech nastavit IPv6 adresy natvrdo (nemáte-li zařízení více a hodláte spoléhat na to, že váš ISP vaše IPv6 adresy nezmění).

Skript /etc/ppp/ipv6-up na straně serveru

#!/bin/sh

# Pracovní skript v /tmp adresáři
# Spouští se z programu netcat, jakmile protistrana naváže spojení
SCRIPT=/tmp/ipv6-up.$$.sh

# Přidat pravidlo do ip6tables
ip6tables -A FORWARD -i $1 -j ACCEPT
ip6tables -A FORWARD -o $1 -j ACCEPT

# Zde se vytváří skript pro netcat
cat < $SCRIPT
#!/bin/sh
IFACE=$1
LOCALIP=$4
REMOTEIP=$5

# do proměnné X je načteno hostname protistrany
read X
PUBLICIP=\$( cat /root/seznam-zarizeni | awk -v X="\$X" '\$1 == X { print \$2; }' )

if [ "\$PUBLICIP" = "" ]; then
    PUBLICIP="2001:db8:3000::1"
    fi
LOCALPUBLICIP="\${PUBLICIP}1"
REMOTEPUBLICIP="\${PUBLICIP}2"

/sbin/ip addr add "\$LOCALPUBLICIP" dev $1
/sbin/ip -6 route add "\$PUBLICIP/64" dev $1

echo "\$REMOTEPUBLICIP"
echo 
echo 
!

chmod 700 $SCRIPT

# Spustí se netcat a poslouchá na portu 1371 na spojení protistrany
# Jakmile je spojení uskutečněno, spustí se skript pro komunikaci
# s protistranou (vyvořený výše). Skript očekává hostname a podle
# hostname v jednoduchém seznamu vyhledá adresu a pošle ji protistraně
nc6 --timeout 15 --idle-timeout 3 -6 --exec $SCRIPT --listen --port 1371 --address "$4%$1"

rm -f $SCRIPT

Skript /etc/ppp/ipv6-up na straně zařízení

#!/bin/sh

sleep 1

# Parametry spojení zjištuji podle hostname
export HOSTNAME=$(hostname)

# Do X se uloží odpověď serveru (IPv6 adresa)
X=$(echo $HOSTNAME | nc6 --timeout 3 --idle-timeout 3 -6 "$5%$1"  1371)
NET=$(echo $X | sed 's/::.*/::/')

/sbin/ip addr add $X dev $1
/sbin/ip -6 route add ${NET}/64 dev $1
/sbin/ip -6 route replace default via ${NET}1 dev $1

Součástí uvedeného řešení je i tabulka s parametry. V tomto případě je tabulka velmi jednoduchá. Skript ji očekává v /root/seznam-zarizeni. V tabulce je pro každé zařízení jeden řádek s hostname zařízení a adresou sítě:

zarizeni1 2001:db8:3000:1::
zarizeni2 2001:db8:3000:2::
zarizeni3 2001:db8:3000:3::

Odkazy

Závěr

Dnešní technologie dokáže levně a snadno řešit množství úloh, které dříve spadaly do kategorie scifi. Kde včera sbíral data několik dní člověk se zápisníkem, stačí dnes jednoduché, jednoúčelové zařízení postavené na bázi PC nebo jednodeskového ARM počítače. Kupodivu je to jiná technologie - IPv4 a NAT, která efektivnímu využívání brání.

Řešení představené v článku ukazuje jednu z cest, jak využít alespoň oklikou množství adres, které nabízí IPv6. Nepochybuji o tom, že by se dalo vymyslet lepší řešení, zvláště pro přidělování a předávání veřejných IPv6 adres. Budu rád, pokud mě na taková řešení upozorníte.

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1962