IPv6 link-local fungerar inte över VPN: varför fe80 stannar vid länken
En fe80:: adress har bara betydelse på en enda länk. Se varför en routad VPN inte kan bära den, hur felet ser ut och vilken adress du bör använda i stället.
Du får upp tunneln, pingar fe80::1 med tunneln som källgränssnitt och får ingenting tillbaka. Det är inte en brandväggsregel du glömt. En link-local-adress har bara betydelse på den enda länk där den bildades, och en routad VPN är inte den länken. fe80::1 hos fjärrroutern ligger på fjärr-LAN-segmentet; du sitter ett hopp bort på ett annat segment, och IPv6 har ingen mekanism för att en router vidarebefordrar link-local-paket över huvud taget.
fe80-prefixet beskriver en omfattning, inte ett intervall
Link-local unicast är fe80::/10, och den avgörande egenskapen är inte talet utan omfattningen. Adressen är giltig bara på en länk, och varje gränssnitt bildar en från sin egen identifierare, därför kan två orelaterade maskiner på två orelaterade nätverk båda svara på fe80::1 utan minsta konflikt. Prefixet säger i praktiken ”på någon länk”; själva länken kommer från sammanhanget.
På en värd är det sammanhanget zonen, eller scope ID, som du lägger till efter ett procenttecken: fe80::1%eth0 på Linux, fe80::1%3 eller fe80::1%12 på Windows och i verktyg som visar gränssnittsindex. Kärnan löser adressen tillsammans med gränssnittsindexet och skickar sedan. Tar du bort zonen blir målet tvetydigt, så stacken har inget sätt att välja ett segment.
Varför en routad tunnel inte kan leverera den
En routad VPN är ett punkt-till-punkt-gränssnitt som bär sina egna adresser, vanligen ett /64 av unique-local eller privat IPv6-rymd inne i tunneln. Fjärr-LAN:et är en separat länk, nådd av tunnelns ändpunkt som routar och vidarebefordrar å dina vägnar. Det är två olika segment, så en adress från det ena är inte ett giltigt mål på det andra.
Vidarebefordringsregeln står uttryckligen i standarden och är ingen egenhet hos din klient: routrar vidarebefordrar inte paket med link-local källa eller mål. Därför dör paketet även när routingtabellen ser korrekt ut. Lägg till en rutt för fe80::/64 mot tunneln och en standardenlig stack antingen avvisar den eller accepterar den och släpper varje matchande paket: målets omfattning säger ”bara den här länken”, och det utgående gränssnittet är inte den länken.
Grannupptäckt förstärker samma gräns. Värdar löser link-local nästa hopp med en granneförfrågan som skickas till multicast med länkomfattning och hoppgräns 255, så routrar kan avvisa allt som vidarebefordras. Att få det att fungera över en tunnel skulle innebära att proxy:a upptäckten för ett fjärrsegment, vilket är Layer 2-bryggning i en annan hatt.
Det finns ett tystare andra fel, och det fångar människor som ändå försöker tvinga fram rutten: zonen. Vilken zon du än skriver lokalt namnger den tunnelgränssnittet, medan adressen du vill ha är bunden till fjärr-LAN-gränssnittet. De två matchar aldrig, så paketet kan inte styras till rätt gränssnitt i andra änden.
Hur felet ser ut i praktiken
Verktygsutdata för ett omfattningsfel läses som ett routing- eller brandväggsfel, och det är precis därför människor tillbringar en eftermiddag i fel lager.
| What you run | What you see | What it means |
|---|---|---|
| ping fe80::1 with no zone | Invalid argument on Linux, or a transmit failure on Windows | The stack cannot pick an interface for an ambiguous destination |
| ping -6 -I tun0 fe80::1 | Destination unreachable, or a silent timeout | The address is not on the tunnel link, so there is nothing on-link to answer |
| ssh user@fe80::1 after the tunnel is up | Hangs, then no route to host | The SYN never left the machine in a deliverable form; the server logs nothing |
| ip -6 neigh show dev tun0 | Empty table | No neighbor entry can exist for a link-local host that is not on this link |
| fe80::/64 inside AllowedIPs or route-ipv6 | Config accepted, traffic still dropped | The tunnel carried the packet; the receiving stack refused to forward it |
Den tredje raden slösar mest tid, eftersom en ssh-klient utan utdata ser ut som ett problem med en lyssnande tjänst. Om du har något sätt in, kontrollera fjärrsidan och bekräfta om sshd är bunden till alla adresser eller bara en.
Använd en adress som var avsedd att routas
För allt du måste nå genom tunneln, använd adressen tjänsten fick just för detta ändamål: en global unicast-adress, eller en unique-local-adress från fc00::/7, intervallet som är reserverat just för detta interna fall. Bekräfta att lyssnaren verkligen binder den, och kontrollera returvägen och varje brandväggsregel på värden.
Titta på fjärrvärden i stället för att gissa. ip -6 addr show listar adresserna som finns, och ss -6 -ltn listar vad daemonen lyssnar på. En tjänst som bara är bunden till fe80:: går bara att nå från sitt eget segment, vilket vanligen är en standard som ingen valde medvetet och som kan ändras med administratörens samtycke.
När tjänsten verkligen inte kan bindas om, flytta upp bryggan till applikationslagret i stället för nätverkslagret. En relay på fjärrvärden som lyssnar på en routbar adress och ansluter till den link-local adressen löser problemet utan att utöka något segment:
socat TCP6-LISTEN:8080,bind=[fd00:1:2::10] TCP6:[fe80::1%eth0]:80Här sitter zonen på rätt sida, på värden som verkligen har eth0 på den länken. Din klient talar med fd00:1:2::10, som routas normalt, och relayen gör link-local-hoppet lokalt. Samma knep fungerar för ett web-gränssnitt på en apparat eller en gammal BMC som bara annonserar en link-local-adress.
När bryggning verkligen är svaret, och vad du bör kontrollera
Vissa installationer behöver verkligen ett delat segment: en enhet som bara talar link-local-upptäckt, en apparat som är beroende av multicast, ett labb som återskapar ett platt LAN. Det är ett Layer 2-problem och det kräver ett Layer 2-svar, som en tap-enhet bryggad till fjärrgränssnittet eller en klientprodukt med ett uttryckligt bryggläge. Det kostar dig broadcast- och multicasttrafik som korsar tunneln plus grannupptäckt som måste fungera från ände till ände.
Närma dig inte detta genom att lägga till fe80::/64 i en routad profil. Det ger det sämsta tillståndet: konfigurationen ser färdig ut, routingtabellen visar en post, och varje paket släpps fortfarande någonstans mellan två stackar som båda beter sig korrekt.
Verifiera med gränssnittet i stället för med ping. ip -6 addr show tun0 berättar om tunnellänken bildade en egen link-local-adress, och ip -6 route get fe80::1%tun0 berättar vad kärnan faktiskt skulle göra med ett mål med omfattning. När kärnan säger att målet är onåbart på det gränssnittet ändrar ingen tunnelinställning det, och det är dags att sluta redigera profilen.
En kort sekvens som avgör frågan:
- Läs adressen på fjärrvärden och notera om den är fe80:: eller ett routbart prefix.
- Om den är fe80::, bind om tjänsten, relay på den värden eller brygga segmentet; om den är routbar, rätta rutten, brandväggen eller bind-adressen.
- Kontrollera zonen varje gång du testar om, eftersom ett kopierat kommando vanligen behåller fel gränssnitt.
Källor: RFC 4291, IPv6-adressarkitektur.