คุณเปิดทันเนลขึ้นมา แล้ว ping fe80::1 โดยใช้ทันเนลเป็นอินเทอร์เฟซต้นทาง แต่ไม่มีอะไรตอบกลับมา นี่ไม่ใช่กฎไฟร์วอลล์ที่คุณลืม ที่อยู่ link-local มีความหมายเฉพาะบนลิงก์เดียวที่มันถูกสร้างขึ้น และ VPN แบบกำหนดเส้นทางก็ไม่ใช่ลิงก์นั้น fe80::1 ของเราเตอร์ระยะไกลอยู่ในเซกเมนต์ LAN ระยะไกล คุณอยู่อีกหนึ่งฮอปบนเซกเมนต์อื่น และ IPv6 ไม่มีกลไกให้เราเตอร์ส่งต่อแพ็กเก็ต link-local เลย

คำนำหน้า fe80 อธิบายขอบเขต ไม่ใช่ช่วง

Link-local unicast คือ fe80::/10 และคุณสมบัติที่นิยามไม่ใช่ตัวเลขแต่คือขอบเขต ที่อยู่ใช้ได้บนลิงก์เดียวเท่านั้น และทุกอินเทอร์เฟซสร้างขึ้นจากตัวระบุของตัวเอง นี่คือเหตุผลที่เครื่องสองเครื่องที่ไม่เกี่ยวข้องกันบนเครือข่ายสองเครือข่ายที่ไม่เกี่ยวข้องกันตอบ fe80::1 ได้ทั้งคู่โดยไม่มีการชนกันแม้แต่น้อย คำนำหน้าจึงพูดเพียงว่า “บนลิงก์ใดลิงก์หนึ่ง” ส่วนลิงก์เองมาจากบริบท

บนโฮสต์ บริบทนั้นคือโซน หรือ scope ID ที่คุณต่อท้ายหลังเครื่องหมายเปอร์เซ็นต์: fe80::1%eth0 บน Linux, fe80::1%3 หรือ fe80::1%12 บน Windows และในเครื่องมือที่แสดงดัชนีอินเทอร์เฟซ เคอร์เนลจะแปลงที่อยู่พร้อมดัชนีอินเทอร์เฟซแล้วจึงส่ง ถ้าลบโซนออก ปลายทางจะกำกวม สแต็กจึงไม่มีทางเลือกเซกเมนต์

ทำไมทันเนลแบบกำหนดเส้นทางจึงส่งมันไม่ได้

VPN แบบกำหนดเส้นทางเป็นอินเทอร์เฟซแบบจุดต่อจุดที่พาที่อยู่ของตัวเอง โดยปกติเป็น /64 ของพื้นที่ IPv6 แบบ unique-local หรือส่วนตัวภายในทันเนล LAN ระยะไกลเป็นลิงก์แยกต่างหาก ซึ่งเข้าถึงได้โดยปลายทันเนลที่กำหนดเส้นทางและส่งต่อแทนคุณ ทั้งสองเป็นเซกเมนต์ที่ต่างกัน ที่อยู่จากอันหนึ่งจึงไม่ใช่ปลายทางที่ใช้ได้บนอีกอัน

กฎการส่งต่อระบุชัดในมาตรฐาน ไม่ใช่ความแปลกของไคลเอนต์คุณ: เราเตอร์ไม่ส่งต่อแพ็กเก็ตที่มีต้นทางหรือปลายทางเป็น link-local นี่คือเหตุผลที่แพ็กเก็ตตายแม้ตารางเส้นทางจะดูถูกต้อง ถ้าเพิ่มเส้นทางสำหรับ fe80::/64 ไปยังทันเนล สแต็กที่ทำตามมาตรฐานจะปฏิเสธหรือยอมรับแล้วทิ้งทุกแพ็กเก็ตที่ตรงกัน เพราะขอบเขตปลายทางบอกว่า “เฉพาะลิงก์นี้” และอินเทอร์เฟซขาออกไม่ใช่ลิงก์นั้น

การค้นหาเพื่อนบ้านตอกย้ำขอบเขตเดียวกัน โฮสต์แปลงฮอปถัดไปแบบ link-local ด้วยคำขอเพื่อนบ้านที่ส่งไปยัง multicast ขอบเขตลิงก์โดยมีขีดจำกัดฮอป 255 เราเตอร์จึงปฏิเสธสิ่งที่ถูกส่งต่อได้ การทำให้สิ่งนี้ทำงานข้ามทันเนลหมายถึงการพร็อกซีการค้นหาสำหรับเซกเมนต์ระยะไกล ซึ่งคือบริดจ์ Layer 2 ในหมวกอีกใบ

มีความล้มเหลวที่สองที่เงียบกว่า และมันจับคนที่ยังพยายามบังคับเส้นทาง: โซน โซนใดก็ตามที่คุณพิมพ์ในเครื่องจะระบุอินเทอร์เฟซของทันเนล ขณะที่ที่อยู่ที่คุณต้องการผูกกับอินเทอร์เฟซ LAN ระยะไกล ทั้งสองไม่เคยตรงกัน แพ็กเก็ตจึงถูกบังคับไปยังอินเทอร์เฟซที่ถูกต้องที่ปลายไกลไม่ได้

ความล้มเหลวหน้าตาเป็นอย่างไรในทางปฏิบัติ

เอาต์พุตของเครื่องมือเมื่อเกิดข้อผิดพลาดด้านขอบเขตอ่านเหมือนข้อผิดพลาดการกำหนดเส้นทางหรือไฟร์วอลล์ และนั่นคือเหตุผลที่ผู้คนใช้เวลาครึ่งวันผิดเลเยอร์

What you runWhat you seeWhat it means
ping fe80::1 with no zoneInvalid argument on Linux, or a transmit failure on WindowsThe stack cannot pick an interface for an ambiguous destination
ping -6 -I tun0 fe80::1Destination unreachable, or a silent timeoutThe address is not on the tunnel link, so there is nothing on-link to answer
ssh user@fe80::1 after the tunnel is upHangs, then no route to hostThe SYN never left the machine in a deliverable form; the server logs nothing
ip -6 neigh show dev tun0Empty tableNo neighbor entry can exist for a link-local host that is not on this link
fe80::/64 inside AllowedIPs or route-ipv6Config accepted, traffic still droppedThe tunnel carried the packet; the receiving stack refused to forward it

แถวที่สามเสียเวลามากที่สุด เพราะไคลเอนต์ ssh ที่ไม่มีเอาต์พุตดูเหมือนปัญหาเซอร์วิสที่กำลังฟังอยู่ ถ้าคุณมีทางเข้าใด ๆ ให้ตรวจฝั่งไกลและยืนยันว่า sshd ผูกกับทุกที่อยู่หรือเพียงที่เดียว

ใช้ที่อยู่ที่ถูกออกแบบมาให้กำหนดเส้นทางได้

สำหรับทุกสิ่งที่คุณต้องเข้าถึงผ่านทันเนล ให้ใช้ที่อยู่ที่เซอร์วิสได้รับมาเพื่อจุดประสงค์นั้นโดยเฉพาะ: ที่อยู่ unicast ทั่วโลก หรือ ที่อยู่ unique-local จาก fc00::/7 ซึ่งเป็นช่วงที่สงวนไว้สำหรับกรณีภายในนี้โดยเฉพาะ ยืนยันว่าตัวรับฟังผูกมันจริง และตรวจเส้นทางขากลับกับกฎไฟร์วอลล์ของโฮสต์

ดูโฮสต์ระยะไกลแทนการเดา ip -6 addr show แสดงที่อยู่ที่มีอยู่ และ ss -6 -ltn แสดงว่าเดมอนฟังอะไรอยู่ เซอร์วิสที่ผูกกับ fe80:: เท่านั้นเข้าถึงได้จากเซกเมนต์ของตัวเองเท่านั้น ซึ่งมักเป็นค่าดีฟอลต์ที่ไม่มีใครเลือกโดยเจตนาและเปลี่ยนได้เมื่อผู้ดูแลเห็นชอบ

เมื่อเซอร์วิสผูกใหม่ไม่ได้จริง ๆ ให้ยกบริดจ์ขึ้นไปที่เลเยอร์แอปพลิเคชันแทนเลเยอร์เครือข่าย รีเลย์บนโฮสต์ระยะไกลที่ฟังบนที่อยู่ที่กำหนดเส้นทางได้และเชื่อมต่อไปยังที่อยู่ link-local แก้ปัญหาได้โดยไม่ต้องขยายเซกเมนต์ใด ๆ:

socat TCP6-LISTEN:8080,bind=[fd00:1:2::10] TCP6:[fe80::1%eth0]:80

ที่นี่โซนอยู่ฝั่งที่ถูกต้อง บนโฮสต์ที่มี eth0 บนลิงก์นั้นจริง ไคลเอนต์ของคุณคุยกับ fd00:1:2::10 ซึ่งกำหนดเส้นทางได้ตามปกติ และรีเลย์ทำฮอป link-local ในเครื่อง กลอุบายเดียวกันใช้ได้กับเว็บ UI ของอุปกรณ์หรือ BMC เก่าที่ประกาศเฉพาะที่อยู่ link-local

เมื่อบริดจ์เป็นคำตอบจริง ๆ และสิ่งทีต้องตรวจ

บางการติดตั้งต้องการเซกเมนต์ร่วมกันจริง ๆ: อุปกรณ์ที่พูดเฉพาะการค้นหา link-local อุปกรณ์ที่พึ่งพา multicast ห้องแล็บที่จำลอง LAN แบนราบ นี่เป็นปัญหา Layer 2 และต้องใช้คำตอบ Layer 2 เช่นอุปกรณ์ tap ที่บริดจ์ไปยังอินเทอร์เฟซระยะไกล หรือผลิตภัณฑ์ไคลเอนต์ที่มีโหมดบริดจ์ชัดเจน สิ่งนี้มีต้นทุนเป็นทราฟฟิก broadcast และ multicast ที่ข้ามทันเนลบวกการค้นหาเพื่อนบ้านที่ต้องทำงานตั้งแต่ต้นจนจบ

อย่าเข้าใกล้ปัญหานี้ด้วยการเพิ่ม fe80::/64 ลงในโปรไฟล์แบบกำหนดเส้นทาง นั่นสร้างสภาวะที่แย่ที่สุด: การกำหนดค่าดูเสร็จแล้ว ตารางเส้นทางแสดงรายการ และทุกแพ็กเก็ตยังคงถูกทิ้งที่ใดที่หนึ่งระหว่างสแตกสองอันที่ทำงานถูกต้องทั้งคู่

ตรวจสอบด้วยอินเทอร์เฟซไม่ใช่ ping ip -6 addr show tun0 บอกว่าลิงก์ของทันเนลสร้างที่อยู่ link-local ของตัวเองหรือไม่ และ ip -6 route get fe80::1%tun0 บอกว่าเคอร์เนลจะทำอะไรจริงกับปลายทางที่มีขอบเขต เมื่อเคอร์เนลบอกว่าปลายทางเข้าถึงไม่ได้บนอินเทอร์เฟซนั้น ไม่มีการตั้งค่าทันเนลใดเปลี่ยนได้ และถึงเวลาหยุดแก้ไขโปรไฟล์

ลำดับสั้น ๆ ที่ยุติคำถาม:

  • อ่านที่อยู่บนโฮสต์ระยะไกลและสังเกตว่ามันเป็น fe80:: หรือคำนำหน้าที่กำหนดเส้นทางได้
  • ถ้าเป็น fe80:: ให้ผูกเซอร์วิสใหม่ ทำรีเลย์บนโฮสต์นั้น หรือบริดจ์เซกเมนต์ ถ้ากำหนดเส้นทางได้ ให้แก้เส้นทาง ไฟร์วอลล์ หรือที่อยู่ผูก
  • ตรวจโซนใหม่ทุกครั้งที่ทดสอบซ้ำ เพราะคำสั่งที่คัดลอกมักคงอินเทอร์เฟซที่ผิดไว้

แหล่งข้อมูล: RFC 4291, สถาปัตยกรรมที่อยู่ IPv6.

บทความที่เกี่ยวข้อง