บทความ NAT ยกประโยคจาก RFC 1631 ปี 1994 มาไว้ว่าทางออก ระยะสั้นคือ CIDR ส่วนทางออกระยะยาวคือโพรโทคอลใหม่ที่มีเลขที่อยู่ยาวกว่าเดิม
หน้านี้คือโพรโทคอลนั้น
และคำที่ต้องทิ้งตั้งแต่ต้นคือคำว่า "เลขยาวขึ้น" เพราะเลขที่ยาวขึ้นเป็นแค่หัวข้อ ข่าว สิ่งที่เปลี่ยนจริงคือของสี่อย่างที่ถูกลบออกไป — เราเตอร์เลิกแบ่ง แพ็กเก็ต checksum ในหัวถูกลบ broadcast หายไปทั้งหมด และ ARP ย้ายไปอยู่ใน ICMP
ทุกการลบมีผลตามมา และผลตามมานั่นแหละคือเนื้อหาของหน้านี้
เลขที่ยาวขึ้น และขนาดที่วาดไม่ได้
2^32 = 4,294,967,296
2^128 = 340,282,366,920,938,463,463,374,607,431,768,211,456
บทความ NAT คำนวณไว้แล้วว่าเลข IPv4 หารด้วยประชากรโลกได้คนละ 0.52 เลข ซึ่งอธิบาย ว่าทำไมถึงไม่มีทางพอตั้งแต่ต้น
อัตราส่วนระหว่างสองบรรทัดข้างบนคือ 2 ยกกำลัง 96 และวิธีที่ทำให้เห็นภาพได้จริงคือ ลองแปลงเป็นระยะทาง ถ้าพื้นที่ทั้งหมดของ IPv4 กว้างหนึ่งมิลลิเมตร พื้นที่ของ IPv6 จะกว้างราวแปดพันสี่ร้อยล้านปีแสง
ตัวเลขนี้ตรวจได้เอง เอา 2^96 คูณหนึ่งมิลลิเมตร แล้วหารด้วยระยะทางหนึ่งปีแสง
แต่ขนาดไม่ใช่ประเด็นที่ยากที่สุดของหน้านี้ ส่วนที่เหลือคือของที่หายไป
ถ้ายังไม่เคยดู เริ่มตรงนี้
เครื่องที่กำลังอ่านหน้านี้อยู่มีที่อยู่ IPv6 อยู่แล้วสี่รายการบนอินเทอร์เฟซเดียว
$ ifconfig en0 | grep inet6
inet6 fe80::1a2b:3c4d:5e6f:7a8b%en0 prefixlen 64 secured scopeid 0xc
inet6 2001:db8::a41:2f77:81c3:5e02 prefixlen 64 autoconf secured
inet6 2001:db8::5d19:c8ae:3b40:9f61 prefixlen 64 autoconf temporary
inet6 2001:db8::5 prefixlen 64 dynamic
สี่รายการบนการ์ดใบเดียวคือเรื่องปกติ ไม่ใช่ความผิดพลาด และแต่ละรายการมี หน้าที่ต่างกันชัดเจน ซึ่งจะอธิบายทีละอันเมื่อถึงหัวข้อของมัน
สังเกตสามอย่างไว้ก่อน ทุกรายการเป็น prefixlen 64 เท่ากันหมด ตัวแรกขึ้นต้น ด้วย fe80:: และมี %en0 ต่อท้าย และคำว่า temporary กับ secured เป็นคนละ อย่างกัน
เขียนที่อยู่ให้ถูก
128 บิตเขียนเป็นเลขฐานสิบหกแปดชุด ชุดละสี่หลัก คั่นด้วยทวิภาค แล้วย่อได้สองแบบ
2001:0db8:0000:0000:0000:ff00:0042:8329 เขียนเต็ม
2001:db8:0:0:0:ff00:42:8329 ตัดศูนย์นำหน้าทิ้ง
2001:db8::ff00:42:8329 ย่อชุดศูนย์
RFC 4291 ระบุกฎที่สำคัญที่สุดไว้สั้น ๆ ว่า "The '::' can only appear once in an address"
เหตุผลอยู่ในคณิตศาสตร์ ไม่ใช่ในความสวยงาม ถ้าใช้สองครั้งในที่อยู่เดียว จะไม่มีทาง รู้ว่าแต่ละครั้งแทนศูนย์กี่ชุด เพราะโจทย์กลายเป็นสมการที่มีคำตอบหลายชุด
RFC 5952 เพิ่มกฎอีกชั้นเพื่อให้ที่อยู่เดียวเขียนได้แบบเดียว ซึ่งจำเป็นเพราะระบบ จำนวนมากเปรียบเทียบที่อยู่ด้วยการเทียบสายอักขระ
- "Leading zeros MUST be suppressed" ตัดศูนย์นำหน้าทิ้งเสมอ
- "The longest run of consecutive 16-bit 0 fields MUST be shortened" ถ้ายาวเท่ากันให้ย่อชุดแรก
- "MUST NOT be used to shorten just one 16-bit 0 field" ห้ามใช้
::กับศูนย์ชุดเดียว - ตัวอักษร
aถึงf"MUST be represented in lowercase"
ข้อสุดท้ายดูจุกจิกจนกว่าจะเจอระบบที่เก็บที่อยู่เป็นข้อความแล้วเทียบตรง ๆ 2001:DB8::1 กับ 2001:db8::1 เป็นที่อยู่เดียวกัน แต่เป็นคนละสายอักขระ
หัวใหญ่ขึ้นเท่าตัว แต่เราเตอร์ทำงานน้อยลง
หัว IPv6 ยาว 40 ไบต์ เทียบกับหัว IPv4 ที่ต่ำสุด 20 ไบต์ ดูเผิน ๆ เหมือนแย่ลง แต่ตัวเลขนั้นหลอก
bits field
4 version
8 traffic class
20 flow label
16 payload length
8 next header
8 hop limit
128 source address
128 destination address
---
320 bits = 40 bytes
32 ไบต์จาก 40 ไบต์นั้นคือที่อยู่ ส่วนที่เหลือมีแค่แปดไบต์ เทียบกับ IPv4 ที่ ใช้ 12 ไบต์ทำงานเดียวกัน หัวยาวขึ้นเพราะที่อยู่ยาวขึ้น ไม่ได้ยาวขึ้นเพราะซับซ้อน
และ 40 คือค่าคงที่ ไม่ใช่ค่าต่ำสุด หัว IPv4 มีความยาวแปรผันได้ตั้งแต่ 20 ถึง 60 ไบต์ จึงต้องมีฟิลด์ IHL ไว้บอกความยาว และเราเตอร์ต้องอ่านฟิลด์นั้นก่อนถึงจะรู้ ว่าข้อมูลเริ่มตรงไหน ส่วน IPv6 ตัดขั้นตอนนั้นทิ้งทั้งหมด
สิ่งที่ถูกลบทิ้ง
IPv4 field what happened in IPv6
IHL gone, the header is always 40
total length became payload length, header excluded
identification moved to a fragment extension header
flags + frag offset moved to a fragment extension header
header checksum deleted outright
options moved to extension headers
checksum ในหัวคือรายการที่กล้าที่สุดในตารางนี้ IPv4 คำนวณ checksum ของหัวใหม่ ที่ทุกเราเตอร์ เพราะทุกเราเตอร์แก้ TTL ทุกครั้ง งานนั้นหายไปหมด
เหตุผลที่ลบได้คือมันซ้ำซ้อน ชั้นล่างมี FCS ของ อีเทอร์เน็ต ตรวจอยู่แล้ว ส่วนชั้นบนมี checksum ของ TCP กับ UDP ตรวจอยู่แล้ว การตรวจอีกชั้นตรงกลางจึงจ่ายแพงกว่าที่ได้
แต่การลบนี้มีเงื่อนไขติดมาด้วย RFC 8200 ระบุไว้ว่า "the default behavior when UDP packets are originated by an IPv6 node is that the UDP checksum is not optional" — UDP เคยปิด checksum ได้บน IPv4 บน IPv6 ปิดไม่ได้ เพราะไม่เหลือ อะไรตรวจเลยถ้าปิด
extension header — ตัวเลือกที่ย้ายออกไปข้างนอก
ฟิลด์ next header แปดบิตทำหน้าที่เดียวกับ protocol ของ IPv4 คือบอกว่าถัดจาก นี้คืออะไร แต่คำตอบของมันมีสองแบบ อาจเป็นโพรโทคอลชั้นบนอย่าง TCP หรืออาจเป็น extension header อีกใบ ซึ่งข้างในก็มี next header ของตัวเองอีก
ผลคือหัวกลายเป็นโซ่ ยาวเท่าไรก็ได้ และของที่ IPv4 ยัดไว้ใน options กับฟิลด์ fragment ย้ายมาอยู่ที่นี่ทั้งหมด
ข้อดีคือเราเตอร์ที่ไม่สนใจไม่ต้องอ่าน อ่านแค่ 40 ไบต์แรกก็เราต์ต่อได้ ต่างจาก IPv4 ที่ options อยู่ในหัวหลัก จึงต้องแยกให้ออกก่อนทุกครั้ง
ข้อเสียคือของจริงไม่เป็นอย่างที่ออกแบบ อุปกรณ์กลางทางจำนวนมากทิ้งแพ็กเก็ตที่มี extension header เพราะแยกไม่ออกว่าข้างในคืออะไร กลไกที่ออกแบบมาให้ยืดหยุ่นจึงเป็น กลไกที่ใช้จริงได้น้อยที่สุดในทั้งโพรโทคอล
เราเตอร์เลิกแบ่งแพ็กเก็ต
RFC 8200 เขียนไว้ตรง ๆ ว่า "fragmentation in IPv6 is performed only by source nodes, not by routers along a packet's delivery path"
นี่คือการเปลี่ยนความรับผิดชอบ ไม่ใช่การตัดความสามารถ IPv4 ให้เราเตอร์กลางทาง แบ่งแพ็กเก็ตที่ใหญ่เกินได้ ซึ่งสะดวกกับต้นทาง แต่แพงมากกับเราเตอร์ เพราะต้องเก็บ สถานะ คำนวณ และสร้างหัวใหม่ทุกชิ้น
IPv6 ย้ายงานนั้นไปให้ต้นทางทั้งหมด เราเตอร์ที่เจอแพ็กเก็ตใหญ่เกินมีทางเลือกเดียว คือ ทิ้ง แล้วส่ง ICMPv6 Packet Too Big กลับไปบอก
1280 คือพื้นขั้นต่ำ
RFC 8200 กำหนดพื้นไว้ว่า "IPv6 requires that every link in the Internet have an MTU of 1280 octets or greater" และแนะนำต่อว่า "it is recommended that they be configured with an MTU of 1500 octets or greater"
1280 พื้นที่ทุกลิงก์ต้องรองรับ ไม่มีข้อยกเว้น
1500 ค่าที่มาตรฐานแนะนำให้ตั้ง
เลข 1280 มีไว้เพื่อให้ต้นทางที่ไม่อยากทำ Path MTU Discovery มีค่าปลอดภัยให้ใช้ ส่งที่ 1280 แล้วไม่ต้องรออะไรเลย
และการพึ่ง ICMP แบบนี้คือจุดเปราะที่สุดของทั้งเรื่อง RFC 8201 เตือนไว้เองว่า ถ้ากรอง Packet Too Big ทิ้ง ต้นทางจะไม่มีวันรู้ค่าจริง อาการที่ได้คือ "the TCP three-way handshake completes correctly but the connection hangs when data is transferred"
จับมือสำเร็จ แล้วค้างตอนส่งข้อมูล นั่นคือลายเซ็นของเรื่องนี้โดยเฉพาะ
ไม่มี broadcast อีกต่อไป
RFC 4291 เขียนไว้ประโยคเดียวจบ — "There are no broadcast addresses in IPv6, their function being superseded by multicast addresses"
ประโยคนี้ฟังดูเหมือนการจัดระเบียบชื่อ แต่ผลของมันลึกกว่านั้นมาก
broadcast บังคับให้ทุกเครื่องบนสายต้องรับเฟรมขึ้นไปให้ซีพียูอ่าน แล้วเกือบ ทุกเครื่องพบว่าไม่ใช่ของตัวเอง งานนั้นเสียเปล่าทั้งหมด และยิ่งเครือข่ายใหญ่ยิ่ง เสียมาก ซึ่งเป็นเรื่องที่บทความ VLAN กับ Spanning Tree พูดถึงจากคนละมุม
multicast ให้เครื่องเลือกได้ว่าจะฟังกลุ่มไหน การ์ดเครือข่ายกรองทิ้งเองในฮาร์ดแวร์ ก่อนถึงซีพียู
หนึ่งอินเทอร์เฟซมีหลายที่อยู่ และนั่นคือเรื่องปกติ
ใน IPv4 การมีที่อยู่หลายอันบนการ์ดใบเดียวเป็นการตั้งค่าพิเศษ ใน IPv6 มันคือ ค่าตั้งต้น
RFC 4291 ระบุรายการที่อยู่ที่เครื่องหนึ่ง ต้อง รู้จักไว้ ซึ่งรวมถึง link-local ของทุกอินเทอร์เฟซ ที่อยู่ loopback กลุ่ม all-nodes multicast และ กลุ่ม solicited-node ของทุกที่อยู่ยูนิคาสต์ที่ตัวเองมี
ข้อสุดท้ายตรวจได้บนเครื่องนี้ ที่อยู่ยูนิคาสต์บน en0 มีสี่รายการ และเมื่อ คำนวณกลุ่ม solicited-node ของทั้งสี่ตามสูตร แล้วไปเทียบกับรายการกลุ่มที่เครื่อง เข้าร่วมอยู่จริง ตรงกันครบทั้งสี่รายการ
ที่อยู่ fe80:: เป็นอันที่แปลกที่สุดสำหรับคนที่มาจาก IPv4 มันมีเสมอ มันเกิดก่อน ทุกอย่าง และมันไม่ต้องมีใครแจก การเราต์เกือบทั้งหมดในเครือข่ายจริงชี้ไปที่ ที่อยู่ link-local ไม่ใช่ที่อยู่ global ซึ่งจะเห็นในตัวอย่างจริงข้างล่าง
ส่วน %en0 ที่ต่อท้ายเรียกว่า zone index และมันจำเป็นเพราะ fe80::1 บนการ์ด คนละใบเป็นคนละเครื่องกัน ที่อยู่ link-local ไม่ซ้ำกันทั้งโลก มันไม่ซ้ำกันแค่ บนสายเส้นนั้น
ARP ย้ายเข้าไปอยู่ใน ICMP
RFC 4861 อธิบายตัวเองไว้ชัดว่า Neighbor Discovery "corresponds to a combination of the IPv4 protocols Address Resolution Protocol [ARP], ICMP Router Discovery [RDISC], and ICMP Redirect"
สามอย่างที่เคยแยกกันรวมเป็นอันเดียว และทั้งหมดวิ่งบน ICMPv6
133 Router Solicitation
134 Router Advertisement
135 Neighbor Solicitation
136 Neighbor Advertisement
137 Redirect
ความเปลี่ยนแปลงที่ลึกกว่าการรวมคือ ARP ไม่ได้อยู่บน IP มันอยู่ข้าง ๆ IP มันเป็นโพรโทคอลของตัวเองที่มี EtherType ของตัวเอง ส่วน NDP เป็น ICMPv6 ซึ่งอยู่ บน IPv6
ผลคือ NDP ได้ทุกอย่างที่ IPv6 มีมาฟรี ทั้งความสามารถในการเข้ารหัส การตรวจสอบ และกลไกความปลอดภัยของชั้น IP
solicited-node multicast
แทนที่จะถามทั้งสาย NDP ถามไปที่กลุ่มที่คำนวณมาจากที่อยู่ที่กำลังหา
unicast 2001:db8::a41:2f77:81c3:5e02
low 24 bits c3 5e 02
solicited-node ff02::1:ffc3:5e02
RFC 4291 ให้วิธีสร้างไว้ว่าเอา 24 บิตท้ายของที่อยู่ยูนิคาสต์ ไปต่อท้ายคำนำหน้า FF02:0:0:0:0:1:FF00::/104
RFC 4861 อธิบายผลไว้เป็นตัวเลข "Address resolution multicasts are 'spread' over 16 million (2^24) multicast addresses, greatly reducing address-resolution- related interrupts on nodes other than the target"
สิบหกล้านกลุ่มแปลว่าโอกาสที่เครื่องอื่นจะถูกรบกวนโดยเปล่าประโยชน์เหลือน้อยมาก และการกรองเกิดที่การ์ดเครือข่าย ไม่ใช่ที่ซีพียู
hop limit 255 เป็นเครื่องมือความปลอดภัย
RFC 4861 ใช้เทคนิคที่ฉลาดมากและอธิบายไว้ว่า "By setting the Hop Limit to 255, Neighbor Discovery is immune to off-link senders"
กลไกคือ NDP ส่งด้วย hop limit เท่ากับ 255 ซึ่งเป็นค่าสูงสุดที่ฟิลด์แปดบิตเก็บได้ ผู้รับตรวจว่าค่าที่ได้รับยังเป็น 255 อยู่หรือไม่
ถ้ามีเราเตอร์คั่นกลางแม้แต่ตัวเดียว ค่าจะลดลงเหลือ 254 เพราะทุกตัวที่ส่งต่อ ต้องลดหนึ่ง การเห็น 255 จึงพิสูจน์ได้ว่าผู้ส่งอยู่บนสายเดียวกันจริง
นี่คือการใช้ฟิลด์ที่มีอยู่แล้วทำงานที่มันไม่ได้ถูกออกแบบมาทำ โดยไม่ต้องเพิ่ม อะไรเลย
เครื่องหาที่อยู่เองได้
RFC 4862 บรรยายเป้าหมายของ SLAAC ไว้ว่ามัน "requires no manual configuration of hosts, minimal (if any) configuration of routers, and no additional servers"
ประโยคสุดท้ายคือของจริง ไม่ต้องมีเซิร์ฟเวอร์ ต่างจาก DHCP ที่ต้องมีใครสักคน คอยแจกและคอยจำว่าแจกอะไรไปแล้วบ้าง
ลำดับมีสี่ขั้น
หนึ่ง อินเทอร์เฟซขึ้นมาแล้วสร้างที่อยู่ link-local เองทันที จาก fe80:: บวก interface ID ขั้นนี้ไม่ต้องคุยกับใครเลย
สอง ทำ DAD ก่อนใช้ คือส่ง Neighbor Solicitation ถามหาที่อยู่ที่ตัวเองกำลัง จะใช้ ถ้าไม่มีใครตอบแปลว่าว่าง RFC ระบุว่าถ้ามีคนตอบ ที่อยู่นั้น "MUST NOT be assigned to an interface"
สาม ส่ง Router Solicitation ออกไป เราเตอร์ตอบ Router Advertisement กลับมา พร้อม prefix
สี่ เอา prefix มาบวก interface ID เดิม ได้ที่อยู่ global แล้วทำ DAD ซ้ำ
ทำไมต้อง /64
RFC 4291 เขียนไว้ตรง ๆ ว่า "Interface IDs are required to be 64 bits long"
เลข 64 ไม่ได้มาจากการคำนวณว่าต้องการกี่เครื่อง มันมาจากการที่ครึ่งหลังของที่อยู่ ถูกจองไว้ให้เครื่องสร้างเอง ถ้าแบ่งซับเน็ตเล็กกว่า /64 กลไก SLAAC ทำงานไม่ได้ เพราะไม่เหลือที่ให้ interface ID อยู่
นี่คือความต่างที่ใหญ่ที่สุดจาก ซับเน็ต ของ IPv4 ที่การ แบ่งเป็นเรื่องของการประหยัดเลข ใน IPv6 การแบ่งไม่ใช่เรื่องประหยัด หนึ่งซับเน็ต คือ /64 เสมอ ต่อให้มีเครื่องเดียวในนั้นก็ตาม
จำนวนที่อยู่ในหนึ่ง /64 คือ 2^64 ซึ่งมากกว่าที่อยู่ทั้งหมดของ IPv4 ทั้งโลก สี่พันล้านเท่า
interface ID กับปัญหาความเป็นส่วนตัว
วิธีแรกที่ใช้สร้าง interface ID คือแปลงมาจาก MAC address โดยตรง ซึ่งใช้ได้และ รับประกันว่าไม่ซ้ำ แต่มันแปลว่าเลขครึ่งหลังของที่อยู่ติดตัวเครื่องไปทุกที่
RFC 8981 อธิบายปัญหาไว้ว่า "Anytime a fixed identifier is used in multiple contexts, it becomes possible to correlate seemingly unrelated activity using this identifier"
พาเครื่องไปต่อเน็ตที่ร้านกาแฟ prefix เปลี่ยน แต่ครึ่งหลังเหมือนเดิม ใครที่เห็น ทราฟฟิกทั้งสองที่จึงรู้ว่าเป็นเครื่องเดียวกัน
ทางแก้มีสองชั้น และเครื่องนี้ใช้ทั้งสองชั้นพร้อมกัน
ชั้นแรกคือที่อยู่ถาวรที่ไม่ได้มาจาก MAC สร้างจากฟังก์ชันสุ่มเทียมที่รับ prefix เข้าไปด้วย ผลคือคงที่ในเครือข่ายเดียว แต่เปลี่ยนเมื่อย้ายเครือข่าย บรรทัดที่มีคำว่า secured คืออันนี้
ชั้นที่สองคือที่อยู่ชั่วคราวที่เปลี่ยนเองเรื่อย ๆ RFC 8981 ระบุค่าตั้งต้นของ อายุที่ต้องการไว้ที่หนึ่งวัน บรรทัดที่มีคำว่า temporary คืออันนี้ และเครื่องนี้ ยืนยันว่าเปิดใช้อยู่จริง
$ sysctl net.inet6.ip6.use_tempaddr net.inet6.ip6.prefer_tempaddr
net.inet6.ip6.use_tempaddr: 1
net.inet6.ip6.prefer_tempaddr: 1
prefer_tempaddr เป็น 1 แปลว่าเวลาเปิดการเชื่อมต่อออกไป เครื่องเลือกใช้ที่อยู่ ชั่วคราว ส่วนที่อยู่ถาวรเก็บไว้ให้คนอื่นติดต่อเข้ามา ซึ่งตรงกับที่ RFC เขียนว่า "Temporary addresses are typically employed for initiating outgoing sessions"
ICMPv6 ปิดไม่ได้
คนที่มาจาก IPv4 ติดนิสัยว่า ICMP เป็นของเสริม บล็อกทิ้งได้ ไม่มีอะไรพัง
บน IPv6 นิสัยนั้นทำให้เครือข่ายใช้ไม่ได้ RFC 4890 เขียนไว้ตรง ๆ ว่า ICMPv6 "cannot be treated as an auxiliary function with packets that can be dropped in most cases without damaging the functionality of the network"
เหตุผลอ่านย้อนจากหัวข้อก่อนหน้าได้หมด — การหาเพื่อนบ้านคือ ICMPv6 การหาเราเตอร์ คือ ICMPv6 การตั้งที่อยู่คือ ICMPv6 และการหาขนาดแพ็กเก็ตที่ส่งได้ก็คือ ICMPv6
RFC 4890 ให้รายการที่ห้ามทิ้งไว้
1 Destination Unreachable
2 Packet Too Big
3 Time Exceeded code 0
4 Parameter Problem code 1 และ 2
128 Echo Request
129 Echo Reply
และเตือนผลของการทิ้งไว้เป็นรายข้อ เรื่อง Packet Too Big มันเขียนว่า "Effectively, parts of the Internet will become inaccessible"
สังเกตว่า Echo Request กับ Echo Reply อยู่ในรายการห้ามทิ้งด้วย ping บน IPv6 จึงไม่ใช่ของเล่นที่ปิดได้ตามใจเหมือนบน IPv4
การอยู่ร่วมกันกับ IPv4
สองโพรโทคอลนี้คุยกันตรง ๆ ไม่ได้เลย ที่อยู่คนละขนาด หัวคนละแบบ ไม่มีทางที่ แพ็กเก็ต IPv6 จะไปถึงเครื่องที่มีแต่ IPv4 ได้โดยไม่มีใครแปลให้
วิธีที่ใช้กันจริงมีสองแบบ
ดูอัลสแตก คือให้เครื่องมีทั้งสองอย่างพร้อมกัน DNS ตอบทั้ง A และ AAAA แล้ว เครื่องเลือกเอง วิธีนี้ง่ายที่สุดและแพงที่สุด เพราะต้องดูแลสองเครือข่ายและสอง ชุดกฎไฟร์วอลล์ตลอดไป
NAT64 กับ DNS64 สำหรับเครื่องที่มี IPv6 อย่างเดียว DNS64 สังเคราะห์คำตอบ AAAA ขึ้นมาจากคำตอบ A ที่มีอยู่ แล้ว NAT64 แปลแพ็กเก็ตจริงที่ชายแดน
RFC 6146 ระบุขอบเขตไว้ว่ามัน "allows IPv6-only clients to contact IPv4 servers" ซึ่งคำที่ต้องอ่านให้ดีคือ clients กับ servers ทิศทางนี้ทางเดียว ฝั่ง IPv4 จะเป็นคนเริ่มติดต่อเข้ามาไม่ได้ ถ้าไม่ตั้งค่าเพิ่ม
ข้อสังเกตที่น่าสนใจคือ NAT64 คือ NAT อีกแบบหนึ่ง สิ่งที่ IPv6 ตั้งใจจะกำจัด กลับมาเป็นสะพานที่พาไปหา IPv6 เอง
เมื่อมันโกหก
"IPv6 ปลอดภัยกว่าเพราะสแกนไม่ไหว" จริงครึ่งเดียว การสแกน /64 ทั้งช่วงเป็นไป ไม่ได้จริง แต่ผู้โจมตีไม่ได้สแกน เขาอ่านจาก DNS จากล็อก จากใบรับรอง และจาก ทราฟฟิกที่เครื่องส่งออกมาเอง ความยากของการสแกนไม่ใช่ความปลอดภัย
"ไม่มี NAT แล้วเครื่องข้างในจะโดนตรง ๆ" สับสนระหว่างการเราต์ได้กับการเข้าถึงได้ ที่อยู่ที่เราต์ได้ไม่ได้แปลว่าเปิดรับทุกอย่าง สิ่งที่กันคือไฟร์วอลล์ ซึ่งใน IPv4 งานนั้นก็เป็นของไฟร์วอลล์อยู่แล้ว NAT แค่บังเอิญกันให้ด้วยเพราะไม่รู้จะส่งต่อ ให้ใคร
"เปิด IPv6 แล้วต้องเลิกใช้ IPv4" ไม่ใช่ ดูอัลสแตกคือสภาพปกติของเครือข่าย เกือบทั้งหมดในโลกวันนี้ และจะเป็นแบบนี้ไปอีกนาน
"ที่อยู่หลายอันบนการ์ดใบเดียวคือของที่ตั้งค่าผิด" ไม่ใช่ นั่นคือค่าตั้งต้น และการพยายามบังคับให้เหลืออันเดียวคือการต่อสู้กับการออกแบบ
"IPv6 ทำงานอยู่ เพราะเปิดเว็บได้" ไม่ได้พิสูจน์อะไรเลย เบราว์เซอร์สมัยใหม่ ลองทั้งสองทางพร้อมกันแล้วใช้ทางที่ตอบก่อน ถ้าเส้นทาง IPv6 พัง มันจะถอยไปใช้ IPv4 เงียบ ๆ โดยไม่บอกใคร เว็บยังเปิดได้ และไม่มีใครรู้ว่ามีอะไรเสีย
ตัวอย่างจริงจากงานจริง
กรณีที่ 1 — จับมือสำเร็จ แล้วค้างทันทีที่เริ่มโหลดข้อมูล
บางเว็บเปิดได้ปกติ บางเว็บค้างที่หน้าขาว ทั้งที่ปิงได้ และเว็บที่ค้างเป็นเว็บ เดิมทุกครั้ง
อ่านอย่างไร อาการนี้แคบกว่าที่คิดมาก ปิงผ่านแปลว่าเส้นทางถึงกัน จับมือสำเร็จ แปลว่าแพ็กเก็ตเล็กผ่านได้ ค้างตอนโหลดแปลว่าแพ็กเก็ตใหญ่ไม่ผ่าน
ทดสอบด้วยการค่อย ๆ เพิ่มขนาด
$ ping6 -c 2 -s 1200 2001:db8::1
2 packets transmitted, 2 packets received
$ ping6 -c 2 -s 1400 2001:db8::1
2 packets transmitted, 0 packets received
1200 ผ่าน 1400 ไม่ผ่าน และไม่มี Packet Too Big กลับมาเลย ถ้ากลไกทำงานปกติ จะต้องมี ICMPv6 type 2 ตอบกลับมาบอกขนาด การเงียบคือหลักฐานว่ามีใครกรองทิ้ง
ทางแก้ระยะสั้นคือลด MTU ที่ปลายทางฝั่งเรา ทางแก้ที่ถูกคือหาว่าใครกรองแล้วเลิกกรอง
สิ่งที่ยังไม่ได้พิสูจน์ เรารู้ว่า Packet Too Big ไม่กลับมา แต่ยังไม่รู้ว่า ถูกกรองที่ไหน อาจเป็นไฟร์วอลล์ฝั่งเรา ฝั่งเขา หรือกลางทาง ต้องไล่ทดสอบทีละช่วง
กรณีที่ 2 — ทราฟฟิกออกไปด้วยที่อยู่ที่ไม่ได้ตั้งใจ
ตั้งกฎไฟร์วอลล์ปลายทางให้อนุญาตที่อยู่ IPv6 ของเซิร์ฟเวอร์ตัวหนึ่งไว้แล้ว แต่ การเชื่อมต่อยังถูกปฏิเสธเป็นบางครั้ง
อ่านอย่างไร ดูว่าเครื่องมีที่อยู่กี่อัน และอันไหนถูกใช้ตอนส่งออก
$ ifconfig en0 | grep inet6
inet6 fe80::1a2b:3c4d:5e6f:7a8b%en0 prefixlen 64 secured scopeid 0xc
inet6 2001:db8::a41:2f77:81c3:5e02 prefixlen 64 autoconf secured
inet6 2001:db8::5d19:c8ae:3b40:9f61 prefixlen 64 autoconf temporary
$ sysctl -n net.inet6.ip6.prefer_tempaddr
1
ที่อยู่ที่เอาไปใส่ในกฎน่าจะเป็นอันที่ลงท้าย secured แต่ที่อยู่ที่ใช้ส่งออก จริงคืออันที่เป็น temporary ซึ่งเปลี่ยนตัวเองทุกวัน กฎจึงผ่านบ้างไม่ผ่านบ้าง ตามจังหวะที่ที่อยู่เปลี่ยน
นี่คือความต่างจาก IPv4 ที่ต้องปรับความคิด ที่อยู่ขาออกกับที่อยู่ขาเข้าไม่ จำเป็นต้องเป็นอันเดียวกัน และค่าตั้งต้นก็ไม่ใช่อันเดียวกันด้วย
สิ่งที่ยังไม่ได้พิสูจน์ เราเห็นว่าเครื่องตั้งให้ใช้ที่อยู่ชั่วคราวก่อน แต่ยัง ไม่ได้ยืนยันว่าแพ็กเก็ตที่ถูกปฏิเสธใช้ที่อยู่นั้นจริง ให้เก็บทราฟฟิกดูต้นทางจริง ของแพ็กเก็ตที่โดนปฏิเสธ
กรณีที่ 3 — เราต์ default ชี้ไปที่ที่อยู่ที่ดูเหมือนผิด
ตารางเราต์ IPv6 มีเกตเวย์เป็น fe80::1 ซึ่งดูไม่เหมือนที่อยู่ของเราเตอร์
$ netstat -rn -f inet6 | head -4
Destination Gateway Flags Netif
default fe80::1%en0 UGcg en0
default fe80::%utun0 UGcIg utun0
default fe80::%utun1 UGcIg utun1
อ่านอย่างไร นี่ถูกต้องแล้ว ไม่ใช่ความผิดพลาด เกตเวย์ในเครือข่าย IPv6 ใช้ ที่อยู่ link-local เป็นปกติ เพราะเราเตอร์ประกาศตัวเองด้วยที่อยู่นั้นใน RA และมันมีเสมอไม่ว่าจะตั้ง prefix อะไร
สังเกตว่ามีบรรทัด default หลายบรรทัดที่ขึ้นต้นด้วย fe80:: เหมือนกัน แต่ต่างกัน ที่ส่วนหลัง % นั่นคือเหตุผลที่ zone index ต้องมี ถ้าไม่มี จะไม่มีทางรู้ว่า fe80::1 หมายถึงเราเตอร์บนสายไหน
สิ่งที่ยังไม่ได้พิสูจน์ ตารางบอกว่าเราต์มีอยู่ ไม่ได้บอกว่าเราต์นั้นใช้ได้ ให้ยืนยันด้วยการปิงเกตเวย์พร้อมระบุ zone ต่อท้าย
กรณีที่ 4 — IPv6 พังมาหลายเดือนโดยไม่มีใครรู้
ทุกอย่างทำงานปกติ ไม่มีใครบ่น แล้ววันหนึ่งเปิดกราฟดูพบว่าทราฟฟิก IPv6 เป็นศูนย์ มาตลอด
อ่านอย่างไร นี่คือผลข้างเคียงของกลไกที่ออกแบบมาให้ผู้ใช้ไม่รู้สึกอะไร เบราว์เซอร์ลองทั้ง IPv6 และ IPv4 พร้อมกัน แล้วใช้ทางที่ตอบก่อน เมื่อ IPv6 ไม่ตอบ มันถอยไป IPv4 ภายในเสี้ยววินาที
การทดสอบต้องบังคับให้ใช้ IPv6 อย่างเดียว ซึ่งตัดทางถอยออกไป
$ curl -6 -sS -o /dev/null -w '%{http_code}\n' https://example.com
curl: (7) Failed to connect: Network is unreachable
$ curl -4 -sS -o /dev/null -w '%{http_code}\n' https://example.com
200
บรรทัดแรกคือคำตอบ และเหตุผลที่ไม่มีใครเจอมาก่อนคือไม่เคยมีใครบังคับให้ใช้ ทางเดียว
บทเรียนกว้างกว่านั้นคือ กลไกที่ซ่อนความล้มเหลวได้ดี จะซ่อนมันไว้นานมาก ถ้าต้องดูแล IPv6 ต้องมีการตรวจที่บังคับใช้ IPv6 อย่างเดียว ไม่ใช่ตรวจว่าเว็บ เปิดได้
สิ่งที่ยังไม่ได้พิสูจน์ เรารู้ว่าออกไปข้างนอกไม่ได้ แต่ยังไม่รู้ว่าพังที่ชั้น ไหน ให้ไล่จากใกล้ไปไกล ปิงเกตเวย์ link-local ก่อน แล้วค่อยขยับออกไปทีละช่วง
อ้างอิง
มาตรฐานหลัก
- RFC 8200 — Internet Protocol, Version 6 (IPv6) Specification กรกฎาคม 2017 แทนที่ RFC 2460 ที่มาของโครงหัว 40 ไบต์ กฎว่าเราเตอร์ห้ามแบ่ง แพ็กเก็ต พื้น MTU 1280 และข้อกำหนดว่า checksum ของ UDP ปิดไม่ได้
- RFC 4291 — IP Version 6 Addressing Architecture กุมภาพันธ์ 2006 ตารางประเภทที่อยู่ กฎ
::ใช้ได้ครั้งเดียว ประโยคที่บอกว่า ไม่มี broadcast และสูตรของ solicited-node multicast - RFC 5952 — A Recommendation for IPv6 Address Text Representation สิงหาคม 2010 กฎการเขียนให้ที่อยู่เดียวมีรูปแบบเดียว
การค้นหาเพื่อนบ้านและการตั้งค่าเอง
- RFC 4861 — Neighbor Discovery for IP version 6 กันยายน 2007 ทั้งห้าข้อความ เลข 2^24 กลุ่ม และเทคนิค hop limit 255
- RFC 4862 — IPv6 Stateless Address Autoconfiguration กันยายน 2007 ลำดับสี่ขั้นของ SLAAC และกฎของ DAD
- RFC 8981 — Temporary Address Extensions for SLAAC กุมภาพันธ์ 2021 แทนที่ RFC 4941 ที่มาของที่อยู่ชั่วคราวและเหตุผลด้าน ความเป็นส่วนตัว
ขนาดแพ็กเก็ต และ ICMPv6
- RFC 8201 — Path MTU Discovery for IP version 6 กรกฎาคม 2017 แทนที่ RFC 1981 และคำเตือนเรื่องจับมือสำเร็จแล้วค้าง
- RFC 4890 — Recommendations for Filtering ICMPv6 Messages in Firewalls พฤษภาคม 2007 รายการข้อความที่ห้ามทิ้ง และผลของการทิ้งแต่ละข้อ
การอยู่ร่วมกัน
- RFC 6146 — Stateful NAT64 เมษายน 2011 ขอบเขตของการแปล และข้อจำกัดว่าฝั่ง IPv4 เริ่มติดต่อเข้ามาไม่ได้
ของจริงบนเครื่อง
ifconfig en0 | grep inet6ที่อยู่สี่รายการบนการ์ดใบเดียว พร้อมแฟล็กsecuredtemporaryและdynamicที่บอกว่าแต่ละอันมาจากไหนnetstat -rn -f inet6เราต์ default ที่ชี้ไปยังที่อยู่ link-local และหลาย บรรทัดที่แยกกันด้วย zone index เท่านั้นsysctl net.inet6.ip6.use_tempaddrและ.prefer_tempaddrที่ยืนยันว่าเครื่อง ใช้ที่อยู่ชั่วคราวเป็นที่อยู่ขาออก- คำนวณกลุ่ม solicited-node ของที่อยู่ทั้งสี่ตามสูตรใน RFC 4291 แล้วเทียบกับ
netstat -g -f inet6พบว่าเครื่องเข้าร่วมกลุ่มครบทั้งสี่จริง
อ่านต่อในชุดนี้
- Internet Protocol — รุ่นที่หน้านี้มาแทน
- NAT — ของชั่วคราวที่อยู่มาสามสิบปี และประโยคที่เรียก หน้านี้ว่าทางออกระยะยาว
- ซับเน็ต — การแบ่งที่ความหมายเปลี่ยนไปเมื่อ /64 กลายเป็น หน่วยเดียวที่ใช้
- TCP กับ UDP — ชั้นบนที่ต้องแก้ checksum เพราะหัวเปลี่ยน