Posted & filed under Valencià.

Afegir suport per a IPv6 m'ha ocupat la major part d'aquesta setmana i una mica de l'anterior, però ja està fet i ja puc dir que el servidor LwIP del Hurd suporta IPv6 :). Concretament, això és el que hi ha implementat:

  • L'assignació automàtica d'adreces link-local.
  • La configuració automàtica d'adreces amb SLAAC.
  • L'assignació manual d'adreces.
  • El protocol MLD.

Activar IPv6 en LwIP és cosa fàcil, n'hi ha prou amb establir algunes macros. I la creació d'adreces link-local també és molt senzilla, només cal cridar una funció d'LwIP que fa servir EUI-64 per generar l'identificador d'interfície i afegir-lo al prefix fe80::/64. De fet, és un procés que es pot fer localment sense necessitat de connectivitat. Tanmateix, la pila ofereix la possibilitat d'emprar DAD per comprovar si l'adreça està ja agafada. No és normal que ocórrega perquè l'identificador d'interfície es basa en l'adreça d'enllaç del dispositiu i aquesta hauria de ser única, però ho he deixat activat per si de cas.

Pel que fa a la configuració automàtica amb SLAAC, de nou LwIP ho posa fàcil. Només cal establir una macro i activar un marcador a la interfície que es desitja auto-configurar. Ara bé, dels tres modes en què permet treballar SLAAC, només n'he provat realment un: el de "nomes SLAAC", on la porta d'enllaç ofereix el prefix de xarxa, la seua adreça link-local i el servidor DNS, mentre que el node ha de calcular la seua id d'interfície amb EUI-64 o aleatòriament si el prefix no és 64. Hi ha un altre mode anomenat "SLAAC amb DHCPv6 sense estat" on la porta d'enllaç ofereix la seua adreça link-local i el prefix de xarxa, però s'espera que el node contacte amb el servidor DHCPv6 per obtindre l'adreça del servidor DNS; i finalment un tercer anomenat "SLAAC amb DHCPv6 amb estat" on la porta d'enllaç només ofereix la seua adreça link-local i s'espera que el node contacte amb el servidor DHCPv6 per rebre la seua adreça completa, amb prefix inclòs, i també el servidor DNS. Aquests darrers dos modes no els he arribat a provar però fent una ullada a les altres macros de configuració d'LwIP, em fa l'efecte que també estan suportats. Una altra línia per al meu TODO.

Els altres dos elements de la llista, com era d'esperar, s'activen de la mateixa manera: amb macros i cridant les funcions pertinents. No obstant això, vaig estar uns dies encaixat perquè no podia fer ping a qualsevol altra adreça que no fóra la link-local. El fet és que tot semblava correcte, podia comprovar amb el depurador que les adreces s'assignaven correctament a les seues interfícies, però per alguna raó no contestaven als pings. Després d'un temps fent proves, vaig veure que les trames del ping ni tan sols arribaven a la pila, mentre que la resta sí que ho feien. Aquest error em va fer aprendre un punt bàsic de IPv6, i és que la multidifusió Ethernet és un requisit fonamental per a què funcione.

Us explicaré ràpidament què estava passant. Una part fonamental d'IPv6 és el protocol ND que funciona sobre ICMPv6 i que s'usa en el protocol DAD abans esmentat per a determinar si una adreça està ja ocupada. També l'usa el protocol SLAAC per obtindre l'adreça de xarxa i és el protocol amb el qual s'obté l'adreça d'enllaç a partir d'una adreça de xarxa. És a dir, substitueix ARP. El problema és que aquest protocol envia missatges adreçats a adreces de multidifusió Ethernet però no sempre a la mateixa adreça, sinó que l'adreça d'enllaç de destinació depèn de l'adreça de xarxa de destinació. Com hem vist a l'article de la Viquipèdia que us he enllaçat més amunt, IPv6 utilitza adreces MAC de destinació que comencen per 33:33 i acaben pels dos darrers hextets de l'adreça IPv6 de destinació. Per exemple, per a enviar un missatge a ff02::2, l'adreça d'enllaç serà 33:33:00:00:00:02. Doncs bé, el comportament per defecte de la majoria de controladors de targetes de xarxa és permetre les trames adreçades a algunes adreces comuns com 33:33:00:00:00:01, per a ff02::1, però no deixar passar les altres trames amb adreça 33:33:X que no estiguen en una llista interna gestionada pel sistema operatiu. L'objectiu és aturar les trames que ja se sap que no van adreçades al nostre node i així estalviar a la pila la feina d'haver de fer aquest filtratge.

Quan es tracta de resoldre adreces, el protocol ND envia les trames a l'anomenada adreça de node sol·licitat. Això vol dir que si l'adreça que està intentant resoldre és per exemple, la fc00:124::178, s'enviaran les trames a l'adreça de xarxa ff02::1:ff00:178 i per tant a l'adreça d'enllaç multidifusió 33:33:ff:00:01:78. Vegeu la diferència amb ARP, on les trames simplement s'envien a ff:ff:ff:ff:ff:ff. Ara es veu perquè no podien funcionar els pings, perquè ni tan sols s'arribava a poder resoldre l'adreça d'enllaç. Ara per ara, el Hurd no permet gestionar la llista d'adreces multidifusió Ethernet permeses en cada targeta de xarxa, de manera que ho he solucionant senzillament acceptant-les totes i deixant que siga la pila qui realitze el filtratge.

Amb tot, em quedaven un parell d'incògnites que no encertava a endevinar. La primera, per què LwIP no em permetia especificar la longitud del prefix en el moment d'afegir una nova adreça IP, i la resposta és que de moment LwIP només suporta adreces amb prefix /64. La segona incògnita és per què a LwIP no hi ha manera d'establir una porta d'enllaç IPv6 per defecte. Li vaig traslladar aquesta pregunta a David van Moolenbroek, la persona que ha portat LwIP al Minix, i paga la pena compartir la resposta perquè posa l'accent sobre una diferència important entre IPv4 i IPv6 que jo desconeixia:

Aquesta és una àrea on IPv4 i IPv6 són completament diferents. Com he dit abans, en l'autèntic model RFC d'IPv6, hi ha la distinció conceptual entre "nodes" i "encaminadors". Segons aquest model IPv6 autèntic, un node mai no encamina paquets, i s'espera que es configure a sí mateix basant-se en els anuncis que arriben des dels encaminadors de la seua xarxa local.

Els encaminadors són portes d'enllaç. O més concretament: no tots els encaminadors són portes d'enllaç, però un encaminador pot anunciar que és una porta d'enllaç, i això fa que esdevinga un "default router" (veure RFC 4861 per a conéixer tots els detalls). En qualsevol cas, *només* els encaminadors poden ser portes d'enllaç, perquè un no-encaminador és per definició un node i un node mai encamina paquets. A més, es requereix que tots els encaminadors en funcionament envien anuncis d'encaminador. Si combinem tot açò, obtenim la conclusió que *si* hi ha una porta d'enllaç (això és, un "default router") a la xarxa, lwIP s'adonarà de la seua presència pels seus anuncis d'encaminador, de manera que mai caldrà configurar l'adreça de la porta d'enllaç. I per tant, no hi ha una manera (manual) de fer-ho.

La principal "víctima" d'aquest model és el desig d'un node d'implementar una regla d'encaminament del tipus "Per a un rang X d'adreces, usar la porta d'enllaç Y". Els encaminadors IPv6 segueixen un concepte de tot-o-res aquí: o són capaços d'encaminar a totes les possibles destinacions (possiblement reenviant els paquets a un altre encaminador local) o no són una porta d'enllaç en absolut. Per tant, tots els "default router" són vàlids per a tots els rangs d'adreces. Això fa que el model autèntic IPv6 d'alguna manera no encaixe amb l'enfocament més tradicional d'IPv4 per a les taules d'encaminament, si més no per als nodes.

Però encara queda un aspecte per tractar, i és com gestionar amb el Hurd una pila Dual-Stack, que és a la vegada IPv4 i IPv6. El problema és el següent: al Hurd l'espai de noms dels servidors és el sistema de fitxers, de manera que tots els translators estan associats a un fitxer en concret i els programes d'usuari utilitzen la ruta del fitxer per a trobar el servidor. Tanmateix, per a la pila de xarxa no hi ha un fitxer sinó dos: /servers/socket/2 per a la pila IPv4 i /servers/socket/26 per a la pila IPv6. Això ens suggereix que hauríem de tindre dues instàncies de la pila executant-se simultàniament on cada una gestionara una família de protocols diferent. El que ocorre és que el món real necessitem que un socket de la família IPv6 servisca per escoltar els missatges entrants per qualsevol de les possibles adreces, i això inclou també les adreces IPv4. De fet, hi ha programes que es posen a escoltar explícitament en les dues famílies. Per aquesta raó, el que féiem amb pfinet i el que fem ara també amb el servidor LwIP és executar-lo per a una família determinada associat a un fitxer determinat, per exemple IPv4 en /servers/socket/2 i després fer que ell mateix s'instal·le en un altre fitxer per a l'altra família, per exemple /servers/socket/26 per a IPv6, de manera que hi haja una única instància de la pila per a gestionar les dues famílies.

Un cop tenim una pila dual-stack com LwIP i una manera d'associar el nostre servidor a dos noms diferents, només ens queda trobar la manera de distingir quin dels dos noms és el que el programa d'usuari ha emprat per a contactar amb LwIP. De nou, la solució és copiar a pfinet i crear dues classes de libports, una per a cada família, i configurar libtrivfs per tal que assigne a cada nou protid la classe pertinent segons si l'RPC ha entrat per un nom o per l'altre. L'única operació que necessita saber amb quina família treballa és socket-create, la resta d'operacions treballen de manera transparent sense importar si el socket és de la família AF_INET o AF_INET6.