Network Fundamental ข้ามเครือข่าย
การเลือกเส้นทาง — เกตเวย์จัดการต่ออย่างไร
สิบเก้าบทบนเว็บนี้พูดถึงเส้นทาง แต่ไม่มีบทไหนบอกว่าเราเตอร์ตัดสินใจอย่างไร บทนี้ตอบด้วยกฎข้อเดียวที่วัดได้จากเครื่องตัวเอง
· หมวด 4 · ข้ามเครือข่าย · 9 นาที
บทความ IP จบหัวข้อการเลือกทางออกด้วยประโยคที่ฟังดูสวยงาม
"และเพราะทุกเครื่องบนเส้นทางคิดแบบเดียวกัน แพ็กเก็ตจึงเดินทางไปได้ทั่วโลก โดยไม่มีใครสักคนรู้เส้นทางทั้งเส้น"
แล้วจบตรงนั้น ส่วนบทความ VLAN ก็ชี้กลับมาว่าการข้ามวง "เป็นงานของเราเตอร์เสมอ" และ IP เองก็บอกแค่ว่า "ส่งให้เกตเวย์แล้วให้มัน จัดการต่อ"
ลูกศรชี้กันเป็นวงแล้วตกพื้น ไม่มีบทไหนบอกว่าเกตเวย์จัดการต่ออย่างไร
บทนี้ตอบคำถามนั้น และคำตอบสั้นกว่าที่คนคิดมาก — มีกฎข้อเดียว ซึ่งวัดได้จาก เครื่องที่คุณนั่งอยู่ตอนนี้
ถ้ายังไม่เคยดู เริ่มตรงนี้
เครื่องทุกเครื่องมีตารางเส้นทางของตัวเอง ไม่ใช่แค่เราเตอร์
$ netstat -rn -f inet
ของเครื่องที่ใช้เขียนบทความนี้ ย่อเหลือแก่น
Destination Gateway Netif
default 192.168.1.1 en0
127 127.0.0.1 lo0
192.168.1 link#12 en0
192.168.1.130 (link layer) en0
สี่บรรทัดนี้คือทั้งหมดที่เครื่องรู้เกี่ยวกับโลก และคำสั่งถัดไปคือสิ่งที่ทำให้ บทความนี้พิสูจน์ตัวเองได้
$ route -n get 8.8.8.8
มันบอกว่า เคอร์เนลเลือกบรรทัดไหน สำหรับปลายทางที่ถาม
กฎข้อเดียว — prefix ที่ยาวที่สุดชนะ
RFC 1812 เดือนมิถุนายน 1995 ชื่อ Requirements for IP Version 4 Routers เขียนกฎนี้ไว้เป็นประโยคเดียว
"Routers must use the most specific matching route (the longest matching network prefix) when forwarding traffic."
ผมถามเครื่องด้วยปลายทางสามแบบที่ตั้งใจให้ตรงคนละบรรทัด
destination matched entry next hop
192.168.1.130 192.168.1.130 ส่งตรง
192.168.1.200 192.168.1.0 ส่งตรง
8.8.8.8 default 192.168.1.1
สามปลายทาง สามบรรทัด และเรียงตามความยาวของ prefix พอดี — /32 ชนะ /24 และ /24 ชนะ /0
เอกสารยังยกตัวอย่างเดียวกันไว้ด้วย ถ้าปลายทางคือ 10.144.2.5 และตารางมี 10.144.2.0/24, 10.144.0.0/16 และ 10.0.0.0/8 กฎนี้เก็บเฉพาะอันแรก "because its prefix length is longest"
กฎนี้ไม่มีข้อยกเว้น และไม่มีการโหวต เราเตอร์ทุกตัวบนโลกใช้กฎเดียวกันนี้ ซึ่งเป็นเหตุผลที่แพ็กเก็ตเดินทางถึงปลายทางได้โดยไม่มีใครวางแผนเส้นทางทั้งเส้น
เส้นทางในตารางมาจากไหน
มีสามทางเท่านั้น
หนึ่ง connected — เสียบสายแล้วตั้งเลขไอพีบนอินเทอร์เฟซ เส้นทางไปวงนั้น โผล่ขึ้นมาเอง ในตารางข้างบนคือบรรทัด 192.168.1 ที่ gateway เป็น link#12 แทนที่จะเป็นเลขไอพี — นั่นแปลว่า "ไม่ต้องส่งให้ใคร ส่งออกทางนี้ได้เลย"
สอง static — คนพิมพ์เข้าไปเอง เหมาะกับเส้นทางที่ไม่เปลี่ยน และเส้นทาง ค่าเริ่มต้นในบ้านเกือบทั้งหมดก็เป็นแบบนี้ ข้อเสียคือมันไม่รู้ว่าปลายทางล่ม มันจะชี้ไปที่เดิมตลอด
สาม dynamic — เราเตอร์คุยกันเองแล้วแลกเปลี่ยนว่าใครไปถึงอะไรได้บ้าง ข้อดีคือเมื่อเส้นทางหนึ่งขาด มันหาทางใหม่ได้เอง ข้อเสียคือต้องเชื่อสิ่งที่ เพื่อนบ้านบอก ซึ่งเป็นหัวข้อที่บทถัดไปต้องพูดถึง
เส้นทางค่าเริ่มต้น คือการยอมรับว่าไม่รู้
บรรทัด default คือ 0.0.0.0/0 ซึ่งเป็น prefix ที่สั้นที่สุดเท่าที่เป็นไปได้ มันจึงตรงกับทุกปลายทางในจักรวาล และแพ้ทุกบรรทัดที่เจาะจงกว่า
ความหมายจริงของมันคือ "ปลายทางนี้ฉันไม่รู้จัก ส่งให้คนที่น่าจะรู้มากกว่าฉัน"
และนั่นคือสิ่งที่ทำให้ตารางเส้นทางในบ้านมีแค่สี่บรรทัด ทั้งที่อินเทอร์เน็ตมี ปลายทางเป็นพันล้าน — เพราะเราเตอร์ที่บ้านไม่ต้องรู้อะไรเลย นอกจากว่าจะโยนของ ที่ไม่รู้จักไปให้ใคร
ที่น่าสนใจคือตารางของเครื่องที่ใช้เขียนบทความนี้มี เส้นทางค่าเริ่มต้นสองเส้น
default 192.168.1.1 en0
default link#20 bridge100
สองบรรทัดที่ prefix ยาวเท่ากัน กฎ longest match จึงตัดสินไม่ได้ ต้องใช้เกณฑ์ อื่นต่อ เช่นลำดับความสำคัญของอินเทอร์เฟซ ซึ่งเป็นจุดที่ระบบปฏิบัติการแต่ละตัว ทำไม่เหมือนกัน และเป็นสาเหตุของอาการ "บางทีออกเน็ตได้ บางทีไม่ได้" ที่หายาก มากบนเครื่องที่มีหลายอินเทอร์เฟซ
ข้ามวงจริง ๆ ต้องทำอะไรบ้าง
บทความ VLAN บอกว่าต้องมีอุปกรณ์ที่มีขาอยู่ในทั้งสองวง แต่ไม่ได้บอกว่ามันทำ อะไรตอนแพ็กเก็ตผ่าน
มีสองรูปแบบที่ใช้กันจริง
router on a stick — เราเตอร์ต่อกับสวิตช์ด้วยสายเส้นเดียวที่เป็นทรังก์ แล้วสร้างอินเทอร์เฟซย่อยหนึ่งอันต่อหนึ่ง VLAN ทราฟฟิกข้าม VLAN จึงวิ่งขึ้นไป หาเราเตอร์แล้ววิ่งกลับลงมาบนสายเส้นเดิม — ซึ่งแปลว่าสายเส้นนั้นรับภาระสองเท่า
switched virtual interface — สวิตช์ชั้นสามมีอินเทอร์เฟซเสมือนอยู่ข้างใน ตัวมันเอง ทราฟฟิกจึงไม่ต้องออกไปไหน เร็วกว่ามากและเป็นวิธีที่ใช้กันเป็นหลัก ในปัจจุบัน
ทั้งสองแบบทำงานเหมือนกันทุกประการในสายตาของแพ็กเก็ต ต่างกันแค่ว่ากล่องไหน ทำหน้าที่นั้น
สิ่งที่เปลี่ยนจริงตอนส่งต่อ
นี่คือส่วนที่คนมักเข้าใจผิด — เราเตอร์ไม่ได้เปลี่ยนที่อยู่ IP
ผมสร้างหัว IP ขึ้นมาแล้วจำลองการผ่านเราเตอร์สามตัว
hop TTL IP checksum src IP dst IP
0 64 0xa2a9 192.0.2.10 198.51.100.7
1 63 0xa3a9 192.0.2.10 198.51.100.7
2 62 0xa4a9 192.0.2.10 198.51.100.7
3 61 0xa5a9 192.0.2.10 198.51.100.7
ที่อยู่ IP ไม่ขยับเลยสักไบต์ สิ่งที่เปลี่ยนคือ TTL ลดลงหนึ่ง และ checksum ของหัว IP ต้องคิดใหม่ตาม
สังเกตว่า checksum เพิ่มขึ้น 0x0100 พอดีทุกครั้ง ซึ่งไม่ใช่ความบังเอิญ — มันคือเหตุผลที่สูตรลัดในบทความ การตรวจจับข้อผิดพลาด มีอยู่ตั้งแต่แรก เพราะเราเตอร์ต้องแก้ checksum ทุกฮอปและอ่านทั้งแพ็กเก็ตใหม่ ไม่ไหว
ส่วนที่เปลี่ยนทั้งคู่ทุกฮอปคือ ที่อยู่ MAC — ต้นทางกลายเป็นของเราเตอร์ที่ กำลังส่ง ปลายทางกลายเป็นของอุปกรณ์ถัดไป
นั่นคือเหตุผลที่ต้องมี ARP ทำงานทุกฮอป และเป็นเหตุผลที่ traceroute เห็นชื่อเราเตอร์ทีละตัวได้
เครื่องที่ใช้เขียนบทความนี้ก็เป็นเราเตอร์
คำว่าเราเตอร์ไม่ใช่ชนิดของอุปกรณ์ แต่เป็นบทบาท เครื่องไหนก็ทำได้ถ้าเปิดสวิตช์ ตัวเดียว
$ sysctl net.inet.ip.forwarding
net.inet.ip.forwarding: 1
ค่าเป็น 1 แปลว่าแล็ปท็อปเครื่องนี้กำลังส่งต่อแพ็กเก็ตให้คนอื่นอยู่ตอนนี้ ซึ่งผมไม่ได้ตั้งใจเปิดเอง มันถูกเปิดโดยฟีเจอร์ที่สร้างอินเทอร์เฟซ bridge100 ขึ้นมา
ความต่างระหว่างเครื่องธรรมดากับเราเตอร์จึงมีแค่ค่านี้ค่าเดียว — ค่าเป็น 0 แปลว่าแพ็กเก็ตที่ไม่ได้ส่งถึงเราจะถูกทิ้ง ค่าเป็น 1 แปลว่ามันจะถูกส่งต่อ
เมื่อมันโกหก
"เราเตอร์รู้เส้นทางทั้งเส้น" ไม่รู้เลย มันรู้แค่ว่าฮอปถัดไปคือใคร ประโยค ที่บทความ IP ทิ้งไว้จึงเป็นจริงทุกคำ
"เส้นทางในตารางคือเส้นทางที่ใช้จริง" ใช้จริงเฉพาะบรรทัดที่ชนะ ซึ่งอาจไม่ใช่ บรรทัดที่คุณกำลังอ่านอยู่ ให้ถามด้วย route -n get แทนการเดาจากตาราง
"เพิ่มเส้นทางแล้วทราฟฟิกจะไปทางใหม่" ไปก็ต่อเมื่อ prefix ยาวกว่าบรรทัดเดิม การเพิ่มเส้นทางที่กว้างเท่ากันไม่ได้ทำอะไรเลย
"เราเตอร์เปลี่ยนที่อยู่ IP" ไม่เปลี่ยน สิ่งที่เปลี่ยนที่อยู่ IP คือ NAT ซึ่งเป็นคนละเรื่องและเป็นสิ่งที่เพิ่มเข้ามาทีหลัง
"ping ได้แปลว่าเส้นทางถูก" ping พิสูจน์ว่ามีเส้นทางไปและกลับ ไม่ได้พิสูจน์ ว่าเป็นเส้นทางที่คุณตั้งใจ และเส้นทางขากลับอาจไปคนละทางกับขาไปได้
ตัวอย่างจริงจากงานจริง
กรณีที่ 1 — ทราฟฟิกไปผิดทาง
สถานการณ์ ตั้งเส้นทางใหม่แล้ว แต่ทราฟฟิกยังไปทางเดิม
คำสั่ง ถามเคอร์เนลตรง ๆ แทนที่จะอ่านตารางแล้วเดา
$ route -n get 10.20.30.40
อ่านอย่างไร ดูบรรทัด destination ที่มันตอบกลับมา นั่นคือบรรทัดที่ชนะจริง ถ้าไม่ใช่บรรทัดที่คุณเพิ่งเพิ่ม แปลว่ามีบรรทัดที่ prefix ยาวกว่าอยู่ก่อนแล้ว การเพิ่มเส้นทางที่กว้างเท่ากันหรือกว้างกว่าไม่มีทางชนะได้เลย
สิ่งที่ยังไม่ได้พิสูจน์ คำสั่งนี้บอกการตัดสินใจของเครื่องนี้เท่านั้น ไม่ได้ บอกว่าเราเตอร์ตัวถัดไปจะตัดสินใจอย่างไร และไม่ได้บอกเรื่องเส้นทางขากลับซึ่ง เป็นคนละชุด
กรณีที่ 2 — ไปถึงแต่กลับไม่ได้
สถานการณ์ ส่งออกไปได้ แต่ปลายทางตอบกลับไม่ถึง
อ่านอย่างไร เส้นทางเป็นเรื่องทิศทางเดียว การที่เรามีเส้นทางไปหาเขา ไม่ได้ แปลว่าเขามีเส้นทางกลับมาหาเรา อาการนี้พบบ่อยที่สุดตอนเพิ่มวงใหม่แล้วลืมบอก เราเตอร์อีกฝั่ง วิธีตรวจคือดูจากฝั่งปลายทางว่ามีเส้นทางกลับมาที่วงของเราไหม ไม่ใช่ตรวจจากฝั่งเรา
สิ่งที่ยังไม่ได้พิสูจน์ อาการเดียวกันเกิดจากไฟร์วอลล์ที่ปล่อยขาออกแต่บล็อก ขากลับได้ด้วย ตามที่บทความ ไฟร์วอลล์ อธิบาย ต้องแยก สองอย่างนี้ด้วยการดูตัวนับที่อุปกรณ์
กรณีที่ 3 — เครื่องมีหลายอินเทอร์เฟซแล้วสับสน
สถานการณ์ เครื่องต่อทั้งสายและ Wi-Fi หรือมี VPN อยู่ ทราฟฟิกออกทางที่ ไม่คาดคิด
อ่านอย่างไร ดูว่ามีเส้นทางค่าเริ่มต้นกี่เส้น อย่างเครื่องที่ใช้เขียน บทความนี้มีสองเส้น เมื่อ prefix ยาวเท่ากัน กฎ longest match ตัดสินไม่ได้ ต้องไปดูเกณฑ์รองซึ่งแต่ละระบบปฏิบัติการทำไม่เหมือนกัน อาการที่ได้คือ "บางทีได้ บางทีไม่ได้" ซึ่งหาสาเหตุยากมากถ้าไม่รู้ว่าต้องมองตรงนี้
สิ่งที่ยังไม่ได้พิสูจน์ การรู้ว่ามีหลายเส้นทางค่าเริ่มต้นบอกว่ามีความ กำกวม ไม่ได้บอกว่าระบบจะเลือกอันไหน ต้องทดสอบด้วย route -n get กับปลายทาง จริงที่สนใจ
เมื่อการเลือกเส้นทางในบ้านเราไม่พอ
ทุกอย่างในบทนี้อธิบายว่าเราเตอร์ใช้ตารางอย่างไร แต่ยังไม่ได้ตอบว่าตาราง ของอินเทอร์เน็ตทั้งใบมาจากไหน
เส้นทางค่าเริ่มต้นที่บ้านโยนของไปให้ผู้ให้บริการ ผู้ให้บริการโยนต่อไปอีก และ สุดท้ายมีใครสักคนที่ต้องรู้จริง ๆ ว่า prefix นั้นอยู่ที่ไหนในโลก
คนคนนั้นรู้เพราะมีคนอื่นบอก และไม่มีอะไรพิสูจน์ว่าคนที่บอกพูดความจริง — ซึ่งเป็นรูปแบบเดียวกับที่ ARP, DHCP และ STP เจอมาแล้วทั้งสามบท
นั่นคือ BGP และเป็นเรื่องของบทถัดไป
อ้างอิง
มาตรฐาน
- RFC 1812 มิถุนายน 1995 Requirements for IP Version 4 Routers — ประโยคเรื่อง longest match ที่ยกมาอ้าง และอัลกอริทึมการเลือกเส้นทางพร้อมตัวอย่างในเอกสาร
วัดจากเครื่องที่ใช้เขียน
netstat -rn -f inetตารางเส้นทางจริง รวมทั้งเส้นทางค่าเริ่มต้นสองเส้นroute -n getการทดลองสามปลายทางที่ตรงคนละบรรทัดsysctl net.inet.ip.forwardingค่าที่ทำให้เครื่องธรรมดากลายเป็นเราเตอร์
คำนวณเอง
- ตาราง TTL กับ checksum ของหัว IP ทีละฮอป คำนวณจากรูปแบบหัว IP โดยตรง ที่อยู่ที่ใช้เป็นช่วงเอกสารตาม RFC 5737