เส้นทางเริ่มต้น IPv6 หายไปหลังจากเปิดใช้การส่งต่อบน Linux
การเปิดใช้การส่งต่อ IPv6 ทำให้เคอร์เนลเพิกเฉยต่อ router advertisements บน uplink และเส้นทางเริ่มต้นที่เรียนรู้มาหมดอายุ แก้ไขด้วย accept_ra
ผมตั้ง net.ipv6.conf.all.forwarding=1 บนเกตเวย์ Debian เพื่อให้ส่งต่อทราฟฟิกของผู้ใช้ VPN ได้ และประมาณครึ่งชั่วโมงต่อมาเครื่องก็ไม่มีเส้นทางเริ่มต้น IPv6 เหลืออยู่ เราเตอร์ต้นน้ำยังทำงานอยู่ เพื่อนบ้าน link-local ยังตอบสนอง และ uplink ยังคงถือแอดเดรสโกลบอลของตน สิ่งที่หยุดไปคือความเต็มใจของเคอร์เนลที่จะรับ router advertisements บนอินเทอร์เฟซนั้นต่อไป เส้นทางไม่ได้หายตอนที่ผมพิมพ์ sysctl แต่หายตอนที่อายุของเส้นทางเริ่มต้นซึ่งเรียนรู้จาก RA หมดลงและไม่มีอะไรต่ออายุให้
เมื่อมองแวบแรกไม่มีอะไรดูเสียหาย การส่งต่อทำงานได้ ผู้ใช้ยังผ่านไปได้ตราบที่เส้นทางเก่ายังอยู่ในแคช และความล้มเหลวมาช้ากว่าการเปลี่ยนแปลงที่ก่อให้เกิดมัน ความล่าช้านั้นเองคือเหตุที่ทำให้โทษไปตกที่ต้นน้ำหรือกฎไฟร์วอลล์ที่ไม่เคยเกี่ยวข้องเลย
อ่านตารางเส้นทางก่อนแตะ sysctl ใด ๆ
เริ่มด้วย ip -6 route show default และอ่านแอตทริบิวต์ ไม่ใช่แค่เกตเวย์ เส้นทางเริ่มต้นที่เรียนรู้จาก RA จะมีเครื่องหมาย proto ra มีค่าความชอบ และแสดงการนับถอยหลัง expires เส้นทางที่คุณเพิ่มเองด้วยมือไม่มีทั้งสองอย่าง และความต่างนั้นบอกว่ากลไกใดเป็นเจ้าของเส้นทาง
$ ip -6 route show default
default via fe80::1 dev eth0 proto ra metric 1024 expires 1432sec hoplimit 64 pref mediumจากนั้นดูแอดเดรส: ip -6 addr show dev eth0 แอดเดรสโกลบอลที่มีอายุแบบ preferred และ valid ซึ่งวัดเป็นชั่วโมงจะอายุยืนกว่าเส้นทางเริ่มต้นที่อายุเราเตอร์วัดเป็นนาที การมีแอดเดรสแต่ไม่มีเส้นทางเริ่มต้นคือลายเซ็นของปัญหา advertisement หากแอดเดรสหายไปด้วย สิ่งที่ต้องตามก่อนคือลิงก์ ข้อมูล prefix หรือการตรวจจับแอดเดรสซ้ำ
ทำไมการส่งต่อจึงปิดการประมวลผล RA
RA คือแพ็กเก็ต ICMPv6 ชนิด 134 ส่งจากต้นทาง link-local โดยมีขีดจำกัดฮอป 255 ก่อนที่เคอร์เนลจะใช้มัน จะตรวจ accept_ra ของอินเทอร์เฟซก่อน ค่าเริ่มต้นคือ 1 และ 1 ไม่ได้หมายถึงใช่ มันหมายถึงรับเฉพาะในขณะที่โฮสต์นี้ไม่ได้ส่งต่อ เมื่อตั้ง forwarding=1 เงื่อนไขนั้นก็ไม่เป็นจริง advertisement จึงถูกทิ้งก่อนที่จะพิจารณา prefix หรือเราเตอร์เริ่มต้นใด ๆ ในนั้น
มีสามค่า และมีเพียงค่าที่สามที่รอดจากการส่งต่อ:
accept_ra | forwarding=0 | forwarding=1 |
|---|---|---|
| 0 | ignored | ignored |
| 1 (default) | accepted | ignored |
| 2 | accepted | accepted |
แฟล็กอีกสองตัวเป็นตัวกำหนดว่าจะติดตั้งอะไรจริง ๆ accept_ra_defrtr ควบคุมว่าเราเตอร์เริ่มต้นใน advertisement จะกลายเป็นเส้นทางหรือไม่ และ accept_ra_pinfo ควบคุมว่าจะใช้ข้อมูล prefix สำหรับการกำหนดค่าแอดเดรสหรือไม่ การปิด accept_ra_defrtr เป็นวิธีปกติในการเก็บแอดเดรส SLAAC ไว้บนโฮสต์ที่ส่งต่อโดยไม่ต้องเรียนรู้เส้นทางเริ่มต้น นี่เป็นคันโยกอีกตัวที่ต่างจาก accept_ra และเป็นตัวที่ใช้เมื่อต้องการแอดเดรสแต่ไม่ต้องการเส้นทาง เอกสารอ้างอิง IP sysctl ของเคอร์เนล ระบุทั้งสามค่า
ยืนยันด้วยสามคำสั่ง
อย่างแรก อ่านค่าที่ใช้งานจริงแทนไฟล์ที่คุณคิดว่าถูกโหลดอยู่:
sysctl net.ipv6.conf.eth0.forwarding net.ipv6.conf.all.forwarding \
net.ipv6.conf.eth0.accept_ra net.ipv6.conf.eth0.accept_ra_defrtrอย่างที่สอง พิสูจน์ว่า advertisement ยังมาถึง การกรองด้วยไบต์ชนิด ICMPv6 ก็เพียงพอ และช่วยให้ไม่ต้องอ่านกำแพงของทราฟฟิกการค้นหาเพื่อนบ้าน:
tcpdump -ni eth0 'icmp6 and ip6[40] == 134'หากไม่มีอะไรปรากฏภายในหลายนาที accept_ra ไม่ใช่ปัญหาของคุณ ต้นน้ำหยุดประกาศ มีบางอย่างบนเส้นทางกรอง ICMPv6 หรืออินเทอร์เฟซนั้นไม่ได้อยู่ในเซกเมนต์ที่คุณคิดจริง ๆ อย่างที่สาม ขอ advertisement เองแล้วอ่านฟิลด์ของมัน:
rdisc6 eth0rdisc6 อยู่ในแพ็กเกจ ndisc6 บน Debian และ Ubuntu ในเอาต์พุต ให้ดูอายุเราเตอร์และค่าความชอบก่อนจะเปลี่ยนอย่างอื่น advertisement ที่มีอายุเราเตอร์ 0 คือคำสั่งให้ถอดเราเตอร์เริ่มต้นออก และไม่มีค่า accept_ra ใดเปลี่ยนมันเป็นเส้นทางที่ใช้งานได้ นี่เป็นวิธีจับ CPE ต้นน้ำในโหมดบริดจ์ที่แจก prefix แต่ไม่เคยเสนอตัวเองเป็นเราเตอร์ด้วย
ทำให้ uplink เป็นอินเทอร์เฟซเดียวที่ผ่อนปรน
หากต้นน้ำควรเป็นเราเตอร์เริ่มต้น ให้รับ advertisement ที่นั่นแม้มีการส่งต่อ และไม่รับที่อื่น:
# /etc/sysctl.d/60-ipv6-router.conf
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.eth0.accept_ra = 2
net.ipv6.conf.eth0.accept_ra_defrtr = 1การจำกัดขอบเขตสำคัญเพราะการส่งต่อ IPv6 บน Linux เป็นสวิตช์โกลบอลตัวเดียว ไม่ใช่ตัวละหนึ่งลิงก์ เขียน 1 ลงใน net.ipv6.conf.wg0.forwarding แล้วอ่าน net.ipv6.conf.all.forwarding ค่าโกลบอลจะพลิกและทุกอินเทอร์เฟซเริ่มส่งต่อ คุณเป็นเราเตอร์บนอุโมงค์และเป็นโฮสต์ธรรมดาบน LAN พร้อมกันไม่ได้ สิ่งที่จำกัดได้คือการยอมรับ ให้คง accept_ra=2 ไว้บน uplink และปล่อยอินเทอร์เฟซอุโมงค์กับ LAN ไว้ที่ 1 หรือ 0 เพื่อให้ advertisement ที่หลงมาทางอุโมงค์ไม่สามารถติดตั้งเส้นทางเริ่มต้นที่ชี้เข้าไปข้างในได้
sysctl -w ชั่วคราวจะไม่รอดการบูตครั้งถัดไป และทั้ง systemd-networkd และ NetworkManager ต่างเขียนค่าเหล่านี้จากคอนฟิกของตัวเอง ใน networkd ให้ตั้ง IPv6Forwarding=yes และ IPv6AcceptRA=yes ในไฟล์ .network ของ uplink networkd ระบุว่า IPv6Forwarding เป็นค่าโกลบอลด้วยเหตุผลเดียวกันฝั่งเคอร์เนล ใน NetworkManager นั้น ipv6.method=auto บน uplink เป็นตัวขับค่า accept_ra ดังนั้นให้อ่าน sysctl ใหม่หลัง nmcli connection up แทนที่จะเชื่อการตรวจครั้งแรก
ทางเลือกแบบสแตติก ip -6 route add default via fe80::1 dev eth0 ใช้ได้จริง และเป็นคำตอบที่สมเหตุสมผลเมื่อเราเตอร์ต้นน้ำมีแอดเดรส link-local ที่เสถียร จงชัดเจนว่าคุณแลกอะไรไป เส้นทางเริ่มต้นแบบสแตติกไม่เคยถูกต่ออายุและไม่เคยถูกถอน จึงยังชี้ไปยังเพื่อนบ้านที่ตายแล้วหลังเปลี่ยนต้นน้ำ เส้นทางจาก RA ล้มเหลวอย่างปลอดภัย ส่วนแบบสแตติกล้มเหลวอย่างเงียบ ๆ
ทดสอบแพ็กเก็ตที่ถูกส่งต่อ ไม่ใช่ของเกตเวย์เอง
ping ของเกตเวย์เองอาจสำเร็จบนเส้นทางที่ไม่มีไคลเอนต์ใดใช้ได้ และอาจสำเร็จเพราะเกตเวย์เป็นต้นทางด้วย ทดสอบจากไคลเอนต์ที่เชื่อมต่ออยู่ แล้วถามเกตเวย์ว่าจะทำอะไรกับแอดเดรสของไคลเอนต์นั้น:
# on the VPN client
ping -6 -c 3 2001:4860:4860::8888
# on the gateway, using a client address
ip -6 route get 2001:4860:4860::8888 from fd42:20::2จากนั้นรอหนึ่งรอบอายุเราเตอร์แล้วดูอีกครั้ง สถานะคงที่ที่ต้องการคือเส้นทางเริ่มต้นที่ทำเครื่องหมาย proto ra ซึ่งค่า expires รีเซ็ตอยู่ตลอด บวกกับ all.forwarding=1 หาก expires แค่นับถอยหลัง แสดงว่า advertisement ยังถูกทิ้งที่ไหนสักแห่ง และตัวการปกติคือตัวจัดการเครือข่ายที่เขียนทับ accept_ra หลังจาก sysctl ตอนบูตทำงานไปแล้ว
ก่อนจะปิดเรื่องนี้ ให้ยืนยันสี่อย่าง: เส้นทางเริ่มต้นเป็น proto ra และกำลังต่ออายุ accept_ra=2 มีผลเฉพาะ uplink การส่งต่อยังเป็น 1 หลังรีบูตและหลังรีสตาร์ตตัวจัดการ และไคลเอนต์ไม่ใช่เกตเวย์เป็นผู้ทำ ping IPv6 สำเร็จ หากไคลเอนต์ยังล้มเหลวทั้งที่เส้นทางปกติ ผู้ต้องสงสัยรายต่อไปคือเส้นทางขากลับหรือไฟร์วอลล์ต้นน้ำ ไม่ใช่ advertisement