1 คะแนน โดย GN⁺ 2025-01-06 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • NAT Traversal เป็นเทคโนโลยีพื้นฐานที่ทำให้อุปกรณ์ที่อยู่หลัง NAT และไฟร์วอลล์สามารถ แลกเปลี่ยนแพ็กเก็ต UDP กันโดยตรง ช่วยให้ Tailscale เชื่อมต่ออุโมงค์ WireGuard ได้โดยไม่ต้องมีฮับกลาง
  • สมมติฐานหลักคือโปรโตคอลต้อง อิง UDP และต้องสามารถรับส่งแพ็กเก็ตสำหรับค้นหาเส้นทางผ่าน NAT กับแพ็กเก็ตสื่อสารจริงผ่านซ็อกเก็ตเครือข่ายเดียวกันได้
  • ไฟร์วอลล์แบบเก็บสถานะจะอนุญาตเฉพาะคำตอบที่ตรงกับแพ็กเก็ต UDP ที่ส่งออกไปก่อน ดังนั้นหาก peer รู้ ip:port ของกันและกันและส่งแพ็กเก็ตแทบพร้อมกัน ก็สามารถ เปิดสถานะของไฟร์วอลล์ได้
  • เนื่องจาก NAT เปลี่ยน IP และพอร์ตต้นทาง จึงต้องใช้เทคนิคเสริมอย่าง STUN, port mapping, การจัดการ NAT64, การค้นหาพอร์ตตาม birthday paradox และ relay
  • ICE จะทดสอบเส้นทางตัวเลือกที่เป็นไปได้พร้อมกันแล้วเลือกเส้นทางที่ดีที่สุด ส่วน Tailscale จะเชื่อมต่อทันทีผ่าน DERP relay แล้วสลับแบบโปร่งใสเมื่อพบเส้นทางตรงที่ดีกว่า

เงื่อนไขพื้นฐานของ NAT Traversal

  • เป้าหมายคือสร้าง การไหลของแพ็กเก็ต UDP แบบสองทาง ระหว่างอุปกรณ์สองเครื่อง แล้วให้โปรโตคอลอย่าง WireGuard, QUIC, WebRTC ทำงานอยู่ด้านบน
  • หากจะนำไปใช้งานเอง มีเงื่อนไขสำคัญสองข้อ
    • โปรโตคอลต้อง อิง UDP
      • TCP ก็ทำได้ แต่ซับซ้อนกว่า และอาจต้องแก้ไขเคอร์เนลขึ้นอยู่กับวิธี implement
      • หากต้องการการเชื่อมต่อแบบสตรีม สามารถพิจารณา QUIC ที่ทำงานบน UDP ได้
    • โปรแกรมต้อง ควบคุมซ็อกเก็ตเครือข่าย ที่ใช้ส่งและรับแพ็กเก็ตได้โดยตรง
      • NAT Traversal ต้องส่งแพ็กเก็ตเพิ่มเติมนอกเหนือจากโปรโตคอลหลัก จึงยากที่จะนำไปต่อเติมเข้ากับไลบรารีเครือข่ายเดิมแบบง่ายๆ
      • โครงสร้างที่ให้ลอจิก NAT Traversal กับโปรโตคอลหลักใช้ซ็อกเก็ตเดียวกันและทำงานขนานกันจะมีประโยชน์
  • หากเข้าถึงซ็อกเก็ตโดยตรงได้ยาก อาจวางพร็อกซีไว้ภายในเครื่องได้
    • โปรโตคอลเดิมสื่อสารกับพร็อกซี
    • พร็อกซีรับผิดชอบ NAT Traversal และการ relay แพ็กเก็ตไปยัง peer

การทะลุผ่านไฟร์วอลล์แบบเก็บสถานะ

  • ไฟร์วอลล์แบบเก็บสถานะจะจดจำแพ็กเก็ตที่เคยเห็นมาก่อน เพื่อใช้ตัดสินว่าจะอนุญาตแพ็กเก็ตใหม่หรือไม่
    • มีรูปแบบอย่าง Windows Defender firewall, Ubuntu ufw, BSD pf, pf ของ macOS, AWS Security Groups
    • การตั้งค่าที่พบบ่อยคืออนุญาตการเชื่อมต่อ outbound ทั้งหมด และบล็อกการเชื่อมต่อ inbound ทั้งหมด
  • สำหรับ UDP กฎค่อนข้างเรียบง่าย
    • หากไฟร์วอลล์เห็นแพ็กเก็ต UDP ที่ออกจาก 2.2.2.2:1234 ไปยัง 5.5.5.5:5678 ก็จะอนุญาตแพ็กเก็ตที่เข้ามากลับกันจาก 5.5.5.5:5678 ไปยัง 2.2.2.2:1234
    • ไฟร์วอลล์บางตัวที่ผ่อนปรนอาจอนุญาตทราฟฟิกจากที่ใดก็ได้ให้เข้ามายังพอร์ตภายในเครื่องที่เคยสื่อสารไปแล้วหนึ่งครั้ง แต่เริ่มพบได้น้อยลงเรื่อยๆ
  • ในโครงสร้างแบบเซิร์ฟเวอร์และไคลเอนต์ ปัญหามีน้อย เพราะอุปกรณ์หลังไฟร์วอลล์แค่เริ่มการเชื่อมต่อก่อนก็พอ
    • ใน VPN จะกลายเป็นโครงสร้างแบบ hub-and-spoke ที่ฮับไม่มีไฟร์วอลล์ ส่วน spoke อยู่หลังไฟร์วอลล์
  • หากไคลเอนต์สองตัวต้องสื่อสารกันโดยตรง จะเกิดสถานการณ์ที่ไฟร์วอลล์ทั้งสองฝั่งบล็อกกันเอง
    • ทั้งสองฝั่งต้องส่งออกไปก่อนจึงจะรับคำตอบได้ แต่อีกฝ่ายก็อยู่ในเงื่อนไขเดียวกัน
    • วิธีให้ผู้ใช้ตั้งค่าเปิดพอร์ตเองนั้นไม่สะดวก และมีความสามารถในการขยายต่ำในเครือข่ายแบบ mesh อย่าง Tailscale
    • ยังมีไฟร์วอลล์จำนวนมากที่ผู้ใช้ควบคุมไม่ได้ เช่น เราเตอร์ในสนามบินหรือร้านกาแฟ
  • หัวใจของวิธีแก้คือกฎไฟร์วอลล์ UDP ไม่ได้ตรวจสอบความสัมพันธ์ของการตอบกลับจริงๆ แต่ดูเพียง ชุด IP และพอร์ต เท่านั้น
    • หาก peer ทั้งสองฝั่งรู้ ip:port ของอีกฝ่ายล่วงหน้าและส่งแพ็กเก็ต UDP พร้อมกัน แพ็กเก็ตชุดแรกบางส่วนอาจถูกบล็อก แต่สถานะไฟร์วอลล์จะเปิดขึ้น
    • หลังจากนั้นแพ็กเก็ตที่อีกฝ่ายส่งมาจะดูเหมือนคำตอบและผ่านเข้าไปได้
  • วิธีนี้ต้องมี side channel
    • endpoint ทั้งสองฝั่งต้องพยายามสื่อสารเกือบพร้อมกัน
    • แค่มีเส้นทางสื่อสารที่หน่วงเวลาได้ไม่กี่วินาทีและส่งข้อมูลได้เพียงไม่กี่พันไบต์ก็เพียงพอ
    • WebRTC ต้องใช้ signaling channel ส่วน Tailscale ใช้ coordination server และ DERP server เป็น side channel
  • สถานะของไฟร์วอลล์ไม่ถาวร
    • ค่าทั่วไปของ timeout สำหรับเซสชัน UDP คือ 30 วินาที
    • หากต้องการคงการเชื่อมต่อไว้ ต้องส่งแพ็กเก็ตเป็นระยะ หรือเริ่มการเชื่อมต่อใหม่ด้วยวิธี out-of-band เมื่อจำเป็น
  • แม้มีไฟร์วอลล์แบบเก็บสถานะซ้อนกันหลายชั้น ตราบใดที่อนุญาต outbound ก็สามารถทะลุผ่านได้ด้วยวิธีส่งพร้อมกัน

วิธีที่ NAT ทำให้ปัญหายากขึ้น

  • NAT (Network Address Translator) ทำงานคล้ายไฟร์วอลล์แบบเก็บสถานะ แต่ยังเปลี่ยนที่อยู่ IP หรือพอร์ตของแพ็กเก็ตด้วย
  • สิ่งที่เป็นปัญหาใน NAT Traversal ส่วนใหญ่คือ Source NAT (SNAT)
    • SNAT ทำให้อุปกรณ์หลายเครื่องแชร์ที่อยู่ IP จำนวนน้อยกว่า โดยมักเป็นที่อยู่ IPv4 สาธารณะเพียงหนึ่งรายการ
    • DNAT ก็มีอยู่เช่นกัน แต่ไม่ค่อยเกี่ยวกับปัญหา NAT Traversal ที่กล่าวถึงที่นี่
  • ตัวอย่างเช่น หากแล็ปท็อปส่งแพ็กเก็ต UDP จาก 192.168.0.20:1234 ไปยังเซิร์ฟเวอร์บนอินเทอร์เน็ต 7.7.7.7:5678 เราเตอร์ที่บ้านจะเลือกพอร์ตว่างของ IP สาธารณะ เช่น 2.2.2.2:4242
    • เราเตอร์สร้าง NAT mapping ที่ระบุว่า 192.168.0.20:1234 กับ 2.2.2.2:4242 เป็นรายการเดียวกัน
    • หลังจากนั้นแพ็กเก็ตขาออกจะถูกเปลี่ยนให้เหมือนมาจาก 2.2.2.2:4242
    • คำตอบขาเข้าจะถูกเปลี่ยนกลับไปยัง 192.168.0.20:1234
  • หลักการเดียวกันนี้ใช้กับเครือข่ายองค์กรด้วย
    • ความแตกต่างคือชั้น NAT อาจประกอบด้วยอุปกรณ์หลายตัวเพื่อความพร้อมใช้งานสูงหรือรองรับปริมาณงาน และอาจมี IP สาธารณะหลายรายการ

STUN และการค้นหา NAT mapping

  • peer ที่อยู่หลัง NAT ไม่สามารถรู้ ip:port สาธารณะของตัวเองตามที่อีกฝ่ายจะเห็นได้ และโดยทั่วไป NAT mapping จะถูกสร้างขึ้นเมื่อมีทราฟฟิกออกสู่อินเทอร์เน็ต
  • STUN คือโปรโตคอลที่ใช้ดูว่าไคลเอนต์หลัง NAT ปรากฏบนอินเทอร์เน็ตอย่างไร
    • ไคลเอนต์ถาม STUN server ว่า “endpoint ของฉันดูเป็นอย่างไรสำหรับคุณ”
    • STUN server ตอบกลับด้วย ip:port สาธารณะที่แพ็กเก็ต UDP ส่งมาถึง
  • เมื่อแชร์ ip:port สาธารณะที่ STUN แจ้งให้ peer แล้ว ก็สามารถใช้เทคนิคส่งพร้อมกันที่ใช้ในการทะลุไฟร์วอลล์ได้
  • นี่เองคือเหตุผลที่ลอจิก NAT Traversal กับโปรโตคอลสื่อสารจริงต้องใช้ซ็อกเก็ตเดียวกัน
    • อุปกรณ์ NAT จะสร้าง mapping ที่ต่างกันสำหรับแต่ละซ็อกเก็ต
    • หากทำ STUN ด้วยซ็อกเก็ตอื่นที่ไม่ใช่ซ็อกเก็ตที่จะใช้สื่อสารจริง ก็จะได้ ip:port ที่ไม่มีประโยชน์
  • STUN เพียงอย่างเดียวไม่สามารถแก้ NAT ได้ทุกแบบ
    • อาจทำงานได้กับเราเตอร์บ้านส่วนใหญ่
    • แต่อาจล้มเหลวกับ NAT gateway ขององค์กรบางแห่ง
    • สมมติฐานที่ว่า 2.2.2.2:4242 ที่เห็นผ่าน STUN มีความหมายเดียวกันบนอินเทอร์เน็ตทั้งหมดนั้นไม่ได้ถูกต้องเสมอไป

NAT แบบง่ายและ NAT แบบยาก

  • อุปกรณ์ NAT อาจสร้าง mapping แตกต่างกันตามปลายทาง หรืออาจคง mapping เดิมโดยไม่ขึ้นกับปลายทางก็ได้
  • RFC 4787 เรียกรูปแบบที่ง่าย ซึ่ง mapping คงเดิมโดยไม่ขึ้นกับปลายทางว่า Endpoint-Independent Mapping (EIM)
  • รูปแบบที่ยาก ซึ่ง mapping เปลี่ยนตามปลายทาง เรียกว่า Endpoint-Dependent Mapping (EDM)
    • อาจเปลี่ยนโดยอิงเฉพาะ IP ปลายทาง หรืออิงทั้ง IP และพอร์ตปลายทางก็ได้
    • จากมุมมองของ NAT Traversal ทั้งสองแบบไม่ดีทั้งคู่
  • คำศัพท์เก่าอย่าง Full Cone, Restricted Cone, Port-Restricted Cone, Symmetric NAT เป็นการอธิบายโดยผสมพฤติกรรม NAT mapping กับพฤติกรรมไฟร์วอลล์เข้าด้วยกัน
    • ในการ implement ที่ใช้งานจริง การแยก “Symmetric เทียบกับแบบอื่นๆ” หรือ EIM เทียบกับ EDM สำคัญกว่า
  • เทคนิคส่งพร้อมกันสามารถผ่านไฟร์วอลล์ได้หลายรูปแบบ
    • ในสภาพแวดล้อมจริง ไฟร์วอลล์ที่ผูกกับ IP และพอร์ตมีจำนวนมากอย่างท่วมท้น
    • แต่หากมี hard NAT เพียงตัวเดียวอยู่ที่ใดสักแห่งบนเส้นทาง แค่ STUN กับการส่งพร้อมกันก็จะเริ่มมีปัญหา

รีเลย์เมื่อการเชื่อมต่อโดยตรงล้มเหลว

  • การเชื่อมต่อโดยตรงอาจล้มเหลวได้แม้จะใช้ทุกเทคนิคแล้วก็ตาม
    • หาก NAT จัดการได้ยาก หรืออยู่ในเครือข่ายที่บล็อก outbound UDP ยกเว้น DNS อย่าง guest Wi-Fi ของ UC Berkeley ก็ไม่สามารถแก้ได้ด้วยเทคนิค NAT
  • ในกรณีนี้ ทั้งสองฝ่ายสามารถรับส่งแพ็กเก็ตผ่าน รีเลย์ ที่ทั้งคู่เข้าถึงได้
    • แม้จะไม่ดีเท่าการเชื่อมต่อโดยตรง แต่ถ้ารีเลย์อยู่ใกล้พอกับเส้นทางและมีแบนด์วิดท์เพียงพอ คุณภาพการเชื่อมต่ออาจลดลงไม่มาก
    • ต่อให้ latency เพิ่มขึ้นหรือแบนด์วิดท์ลดลง ก็ยังดีกว่าไม่มีการเชื่อมต่อเลย
  • โปรโตคอลรีเลย์แบบดั้งเดิมคือ TURN
    • ไคลเอนต์ยืนยันตัวตนกับเซิร์ฟเวอร์ TURN
    • เซิร์ฟเวอร์ TURN จัดสรร ip:port สำหรับรีเลย์
    • peer สื่อสารผ่าน ip:port นั้น
  • Tailscale สร้าง DERP(Detoured Encrypted Routing Protocol) แทน TURN
    • DERP ทำงานบน HTTP
    • มีประโยชน์ในเครือข่ายที่มีกฎ outbound เข้มงวด
    • รีเลย์ payload ที่เข้ารหัสโดยอิงจาก public key ของปลายทาง
  • DERP ทำหน้าที่สองบทบาท
    • รีเลย์ข้อมูลเมื่อ NAT Traversal ล้มเหลว
    • เป็น side channel ที่ช่วย NAT Traversal
  • หากทำ STUN, การส่งพร้อมกัน และรีเลย์ครบแล้ว คาดว่ามากกว่า 90% จะเชื่อมต่อโดยตรงได้ และรีเลย์จะสามารถรับประกัน connectivity บางรูปแบบได้เสมอ

เทคนิคเพิ่มเติมสำหรับ Hard NAT

  • ในกรณี hard NAT ฝั่ง peer ที่ง่ายกว่าจะไม่รู้ว่า NAT ฝั่งที่ยากเปิดพอร์ตใดไว้
    • โดยทั่วไปสามารถสมมติได้ว่า IP ที่ได้จาก STUN ถูกต้อง
    • สิ่งที่ไม่รู้คือพอร์ต ซึ่งมีค่าที่เป็นไปได้ 65,535 ค่า
  • หากไล่สแกนทุกพอร์ตแบบตรง ๆ ที่ 100 แพ็กเก็ต/วินาที กรณีแย่ที่สุดจะใช้เวลาประมาณ 10 นาที และดูเหมือนการสแกนพอร์ต
  • สามารถใช้ birthday paradox เพื่อลดต้นทุนการค้นหาได้
    • ฝั่ง hard NAT เปิด 256 พอร์ตด้วย socket 256 ตัว และฝั่ง NAT ที่ง่ายกว่าจะสุ่มค้นหาพอร์ตปลายทาง
    • หากสมมติว่ามี 256 พอร์ตเปิดอยู่ ความน่าจะเป็นที่จะสำเร็จมีดังนี้
      • สุ่มค้นหา 174 ครั้ง: 50%
      • สุ่มค้นหา 256 ครั้ง: 64%
      • สุ่มค้นหา 1024 ครั้ง: 98%
      • สุ่มค้นหา 2048 ครั้ง: 99.9%
    • ถ้าเป็น 100 พอร์ต/วินาที ครึ่งหนึ่งจะผ่านได้ภายใน 2 วินาที และราว 20 วินาทีก็แทบจะสำเร็จแม้ค้นหาน้อยกว่า 4% ของพื้นที่ทั้งหมด
  • หากทั้งสองฝั่งเป็น hard NAT จะยากกว่ามาก
    • คราวนี้คู่ {source port, destination port} ต้องตรงกัน
    • ภายใต้เงื่อนไขเดียวกัน หลัง 20 วินาที ความน่าจะเป็นที่จะสำเร็จคือ 0.01%
    • หากต้องการความน่าจะเป็นสำเร็จ 99.9% ทั้งสองฝั่งต้องส่ง probe ฝั่งละ 170,000 ครั้ง และที่ 100 แพ็กเก็ต/วินาทีจะใช้เวลา 28 นาที
  • วิธีนี้สามารถปรับปรุง connectivity ในสถานการณ์ home-office, home-cloud และบางกรณี office-cloud หรือ cloud-cloud ได้
    • เราเตอร์บ้านมักเป็น NAT แบบง่าย ส่วน hard NAT มักเป็นเราเตอร์สำนักงานหรือ NAT gateway บนคลาวด์

โปรโตคอล Port Mapping

  • มีโปรโตคอลที่ร้องขอ NAT โดยตรงว่า “ช่วยส่งต่อ WAN port นี้ไปยัง LAN ip:port นี้”
  • ตัวอย่างหลัก ๆ มีสามแบบดังนี้
    • UPnP IGD: โปรโตคอลที่ออกมาตั้งแต่ปลายทศวรรษ 1990 ใช้เทคโนโลยีอย่าง XML, SOAP และ multicast HTTP บน UDP การ implement และความปลอดภัยจัดการได้ยาก
    • NAT-PMP: NAT Port Mapping Protocol ที่ Apple สร้างขึ้น ทำเฉพาะ port forwarding และเรียบง่าย
    • PCP: รูปแบบที่ NAT-PMP v2 พัฒนาต่อมาเป็น Port Control Protocol
  • สามารถลองใช้ UPnP IGD, NAT-PMP, PCP กับ local default gateway และหากมีการตอบกลับ ก็สามารถขอ mapping ไปยังพอร์ตสาธารณะได้
    • หากสำเร็จ นอกจากจะรู้ public ip:port ได้เหมือน STUN แล้ว ยังทำให้ NAT ทำงานกับพอร์ตนั้นแบบผ่อนปรนมากขึ้นได้
    • ไม่ว่าแพ็กเก็ตจะมาจากที่ใด หากมาถึงพอร์ตที่ถูก map ไว้ ก็จะถูกส่งต่อไปยังอุปกรณ์ภายใน
  • ไม่สามารถพึ่งพาโปรโตคอลเหล่านี้ได้
    • อุปกรณ์อาจไม่ได้ implement ไว้
    • อาจปิดไว้โดยค่าเริ่มต้น
    • อาจถูกปิดใช้งานตามนโยบาย
  • เนื่องจากช่องโหว่ในอดีตของ UPnP จึงมีกรณีที่ถูกปิดตามนโยบาย
    • อุปกรณ์บางรุ่นใช้ checkbox “UPnP” เพียงอันเดียวเพื่อปิด UPnP, NAT-PMP, PCP ทั้งหมดพร้อมกัน
  • หากใช้งานได้ NAT หนึ่งตัวใน data path จะหายไปในทางปฏิบัติ ทำให้เชื่อมต่อง่ายขึ้น

Double NAT และ CGNAT

  • ใน double NAT ที่มี NAT สองชั้นหน้าอุปกรณ์ การทำงานของ NAT ชั้นนอกสุด หรือ NAT ก่อนถึงอินเทอร์เน็ต สำคัญที่สุด
    • เช่นเดียวกับ stateful firewall หลายชั้น ชั้น NAT เพิ่มเติมโดยทั่วไปจะมองไม่เห็น
    • เทคนิคเดิมสามารถทำงานได้โดยไม่ขึ้นกับจำนวนชั้น NAT
  • สิ่งที่ double NAT ทำให้เสียหายอย่างมากคือโปรโตคอล port mapping
    • port mapping มีผลกับชั้น NAT ที่ใกล้ไคลเอนต์ที่สุด
    • แต่สิ่งที่ peer ระยะไกลต้องผ่านคือ NAT ชั้นนอกสุด
    • ip:port ที่ได้จึงเป็นที่อยู่ของเครือข่ายกลาง ทำให้ peer ระยะไกลเข้าถึงไม่ได้
  • double NAT มองไม่เห็นสำหรับแอปพลิเคชันทั่วไปส่วนใหญ่ที่ไม่ได้ทำ NAT Traversal อย่างชัดเจน
    • แต่อาจทำให้ multiplayer ของเกมจำนวนมากแย่ลง และตัด IPv6 ออก ทำให้ตัวเลือกการเชื่อมต่อแบบไม่มี NAT ลดลง
  • CGNAT(Carrier-Grade NAT) คือโครงสร้างที่ ISP ใช้ SNAT เพิ่มอีกชั้นเพื่อแก้ปัญหาการขาดแคลน IPv4 address
    • เราเตอร์บ้านทำ SNAT ให้อุปกรณ์เป็น IP กลาง
    • ชั้น NAT ที่สองภายในเครือข่าย ISP จะ map IP กลางเหล่านั้นไปยัง public IP ที่มีจำนวนน้อยกว่า
  • ใน CGNAT ผู้ใช้ไม่สามารถรีเซ็ต NAT ของ ISP ได้
    • ในอดีต ผู้ใช้ระดับสูงสามารถหลีกเลี่ยงปัญหาด้วย port forwarding บนเราเตอร์บ้าน แต่ใน CGNAT วิธีนั้นถูกปิดกั้น
  • โดยพื้นฐาน CGNAT ก็เป็น double NAT ดังนั้นเทคนิคเดิมส่วนใหญ่ยังคงทำงานได้
    • โปรโตคอล port mapping เป็นข้อยกเว้นที่มีข้อจำกัด

ปัญหา Hairpinning

  • peer สองตัวที่อยู่หลัง CGNAT เดียวกัน แต่คนละ home NAT จะเจอปัญหาเฉพาะ
    • เซิร์ฟเวอร์ STUN จะบอก public ip:port ที่เห็นจากภายนอกอินเทอร์เน็ต
    • แต่สิ่งที่ peer ทั้งสองต้องการจริง ๆ คือ ip:port ที่ใช้งานได้ในเครือข่ายกลางภายใน CGNAT
  • หาก home NAT อย่างน้อยหนึ่งฝั่งรองรับโปรโตคอล port mapping การเชื่อมต่ออาจง่ายขึ้น
    • เพราะ double NAT ทำให้โปรโตคอล port mapping บอก ip:port ของเครือข่ายกลาง ซึ่งกลับเป็นประโยชน์
  • หากใช้ port mapping ไม่ได้ ก็จำเป็นต้องใช้ hairpinning
    • เช่น peer A ส่งแพ็กเก็ตไปยัง 2.2.2.2:5678 ของ peer B ที่ได้จาก STUN
    • CGNAT ต้องไม่ส่งแพ็กเก็ตนี้ออกไปยังอินเทอร์เน็ตภายนอก แต่ต้องวกกลับภายในไปยัง NAT mapping ของ peer B
  • NAT จำนวนมากไม่รองรับ hairpinning
    • มีอุปกรณ์ที่สมมติว่าแพ็กเก็ตจากเครือข่ายภายในไปยัง IP ที่ไม่ใช่ภายในจะต้องออกสู่อินเทอร์เน็ตเสมอ
    • สมมติฐานนี้อาจฝังอยู่ใน routing silicon จนแก้ไม่ได้หากไม่มีฮาร์ดแวร์ใหม่
  • เมื่อมี CGNAT เข้ามาเกี่ยวข้อง hairpinning จะสำคัญต่อ connectivity
    • หากทั้ง hairpinning และ port mapping ล้มเหลว ก็ต้องใช้รีเลย์

IPv6 และ NAT64

  • หากเป็นโลกที่มีแต่ IPv6 ปัญหา NAT จะเรียบง่ายลงมาก
    • อุปกรณ์ทุกเครื่องสามารถมีแอดเดรสที่เข้าถึงได้โดยไม่ต้องผ่าน NAT
    • แต่ไฟร์วอลล์แบบ stateful ยังคงมีอยู่ จึงยังต้องใช้การเจาะผ่านไฟร์วอลล์และ side channel
    • fallback relay ที่ใช้โปรโตคอลอย่าง HTTP ก็ยังมีประโยชน์สำหรับเครือข่ายที่บล็อก outbound UDP
  • IPv6 อย่างเดียวยังไม่เพียงพอ
    • โลกส่วนใหญ่ยังเป็น IPv4 และมี IPv6 อยู่ประมาณ 33%
    • การใช้งาน IPv6 ไม่ได้กระจายอย่างสม่ำเสมอ ขึ้นอยู่กับคู่ของ peer ว่าอาจเป็น IPv6 ได้ 100% หรือ 0%
    • หากเป้าหมายคือเชื่อมต่อให้ได้เสมอ ก็ยังต้องจัดการ IPv4+NAT ต่อไป
  • การอยู่ร่วมกันของ IPv6 และ IPv4 ทำให้เกิดกรณีเพิ่มเติมที่เรียกว่า NAT64
    • NAT44 แปลง IPv4 เป็น IPv4 อีกอัน
    • NAT64 แปลง IPv6 ภายในเป็น IPv4 ภายนอก
    • เมื่อใช้ร่วมกับ DNS64 จะทำให้ฝั่งอุปกรณ์ดูเหมือนอยู่บนเครือข่าย IPv6-only แต่ยังเข้าถึงอินเทอร์เน็ต IPv4 ได้
  • แอปพลิเคชันที่ใช้แต่ชื่อ DNS แทบไม่ต้องรับรู้ NAT64
    • แต่ NAT Traversal จัดการ IP และพอร์ตที่เป็นรูปธรรมโดยตรง จึงต้องมีการจัดการแยกต่างหาก
  • หากอุปกรณ์รองรับ CLAT(Customer-side translator) ระบบปฏิบัติการจะทำให้ดูเหมือนมีการเชื่อมต่อ IPv4 โดยตรง และจัดการ NAT64 อยู่เบื้องหลัง
    • CLAT พบได้ทั่วไปในอุปกรณ์มือถือ
    • แต่พบได้น้อยในเดสก์ท็อป แล็ปท็อป และเซิร์ฟเวอร์
  • หากไม่มี CLAT ต้องตรวจจับ NAT64+DNS64 ด้วยตัวเอง
    • ส่งคำขอ DNS ไปที่ ipv4only.arpa.
    • ชื่อนี้จะ resolve เป็นที่อยู่ IPv4 คงที่ที่รู้จักเท่านั้น
    • หากได้ที่อยู่ IPv6 กลับมา แปลว่า DNS64 เป็นผู้แปลง จึงสามารถหา NAT64 prefix ได้
  • หลังจากนั้น หากต้องการสื่อสารกับที่อยู่ IPv4 ให้ส่งแพ็กเก็ต IPv6 ไปที่ {NAT64 prefix + IPv4 address}
    • เมื่อทำ STUN ผ่าน NAT64 เพื่อหา ip:port สาธารณะได้แล้ว ก็จะกลับไปเป็นปัญหา NAT Traversal ทั่วไปอีกครั้ง

รวมเส้นทาง candidate ด้วย ICE

  • วิธีที่พยายามจำแนกล่วงหน้าอย่างแม่นยำว่าควรใช้เทคนิคใดในบรรดาทุกเทคนิค มีความสามารถในการขยายต่ำ
    • เพราะวิศวกรเครือข่ายและผู้พัฒนาอุปกรณ์ NAT สร้างพฤติกรรมไว้หลากหลาย
  • แก่นของ ICE(Interactive Connectivity Establishment) คืออัลกอริทึมที่ลองทุกอย่างที่เป็นไปได้พร้อมกัน แล้วเลือกเส้นทางที่ดีที่สุดจากสิ่งที่ใช้งานได้
  • เมื่อเริ่มสื่อสาร จะรวบรวมรายการ endpoint candidate สำหรับซ็อกเก็ตภายในเครื่อง
    • IPv6 ip:ports
    • IPv4 LAN ip:ports
    • IPv4 WAN ip:ports ที่พบด้วย STUN
    • IPv4 WAN ip:ports ที่พบผ่าน NAT64 translator
    • IPv4 WAN ip:port ที่ได้รับการจัดสรรด้วยโปรโตคอล port mapping
    • endpoint ที่ผู้ดูแลระบบจัดเตรียมไว้ เช่น port forwarding ที่ตั้งค่าแบบคงที่
  • จากนั้นแลกเปลี่ยนรายการ candidate ผ่าน side channel และส่งแพ็กเก็ต probe ไปยัง endpoint ทั้งหมดที่อีกฝ่ายให้มา
    • แพ็กเก็ต probe ทำหน้าที่เป็นแพ็กเก็ตสำหรับเปิดไฟร์วอลล์และ NAT
    • พร้อมกันนั้นก็ทำหน้าที่ตรวจสอบสถานะแบบ ping/pong ด้วย
  • หลังผ่านไประยะหนึ่ง จะเลือกเส้นทางที่ดีที่สุดตาม heuristic จาก candidate path ที่ยืนยันแล้วว่าใช้งานได้
    • โดยปกติ ICE ใช้คะแนนล่วงหน้า เช่น LAN > WAN > WAN+NAT
    • ตั้งแต่ v0.100.0 เป็นต้นมา Tailscale ใช้ค่า round-trip latency แทนลำดับความชอบที่ hardcode ไว้
  • Tailscale ไม่ได้แบ่งการเชื่อมต่ออย่างเคร่งครัดเป็นขั้น probe และขั้นสื่อสาร
    • ทุกการเชื่อมต่อเริ่มต้นโดยมี DERP ถูกเลือกไว้ล่วงหน้า
    • ผู้ใช้สามารถใช้งานการเชื่อมต่อผ่านเส้นทาง fallback ได้ทันที
    • การค้นหาเส้นทางทำงานแบบขนาน และหากพบเส้นทางที่ดีกว่าหลังผ่านไปไม่กี่วินาที ก็จะอัปเกรดให้อย่างโปร่งใส

การรักษาเส้นทางระหว่างใช้งานและความปลอดภัย

  • ต้องระวังเส้นทางแบบอสมมาตร
    • ICE พยายามให้ peer ทั้งสองฝั่งเลือกเส้นทางเครือข่ายเดียวกัน เพื่อรักษาการไหลของแพ็กเก็ตสองทิศทาง
    • แม้จะไม่ได้ implement ขั้นตอนในระดับเดียวกัน ก็ควรมีทราฟฟิกสองทิศทางบนทุกเส้นทางที่กำลังใช้งาน
    • เพียงแค่ส่ง probe แบบ ping/pong เป็นระยะก็สามารถรักษาสิ่งนี้ได้
  • เส้นทางที่เลือกอยู่ในปัจจุบันอาจล้มเหลวได้
    • ตัวอย่างเช่นสถานะหายไปเพราะการบำรุงรักษา NAT
    • สามารถ probe เส้นทางที่เป็นไปได้ทั้งหมดต่อไปเพื่อรักษา warm fallback ไว้
    • แต่เนื่องจากการ downgrade เกิดขึ้นไม่บ่อย การถอยไปใช้ relay เป็นทางเลือกสุดท้ายแล้วเริ่มค้นหาเส้นทางใหม่อาจมีประสิทธิภาพกว่า
  • สมมติฐานที่ว่าโปรโตคอลชั้นบนมีความปลอดภัยของตัวเองเป็นสิ่งสำคัญ
    • QUIC ใช้ใบรับรอง TLS
    • WireGuard ใช้ public key ของตัวเอง
  • เมื่อสลับเส้นทางแบบไดนามิก ความปลอดภัยที่อิง IP จะหมดความหมาย
    • อย่างน้อยต้องมีการยืนยันตัวตนแบบ end-to-end
  • หากชั้นบนมีความปลอดภัยแบบ end-to-end ต่อให้ probe แบบ ping/pong ถูก spoof ได้ กรณีเลวร้ายที่สุดคือผู้โจมตีชักนำให้ทราฟฟิกวิ่งผ่านตัวเองเท่านั้น
    • ถึงอย่างนั้นก็ควรยืนยันตัวตนและเข้ารหัสแพ็กเก็ตค้นหาเส้นทางด้วย

องค์ประกอบของ NAT Traversal ที่ทนทาน

  • NAT Traversal ที่ทนทานต้องมีองค์ประกอบต่อไปนี้
    • โปรโตคอลที่จะต่อยอดบน UDP
    • ซ็อกเก็ตที่เข้าถึงได้โดยตรงจากในโปรแกรม
    • side channel สำหรับสื่อสารกับ peer
    • เซิร์ฟเวอร์ STUN สักหลายตัว
    • เครือข่าย fallback relay ซึ่งเป็นตัวเลือกเสริมแต่แนะนำอย่างยิ่ง
  • ขั้นตอนการทำงานมีดังนี้
    • แจกแจง ip:ports ทั้งหมดของซ็อกเก็ตจากอินเทอร์เฟซที่เชื่อมต่อโดยตรง
    • query ไปยังเซิร์ฟเวอร์ STUN เพื่อหา WAN ip:ports และระดับความยากของ NAT
    • หา WAN ip:ports เพิ่มเติมด้วยโปรโตคอล port mapping
    • หากมี NAT64 ให้ตรวจจับและหา WAN ip:port ผ่านเส้นทางนั้นด้วย
    • แลกเปลี่ยน ip:ports ทั้งหมดและคีย์เข้ารหัสกับ peer ผ่าน side channel
    • อาจสื่อสารผ่าน fallback relay ก่อนเพื่อให้ตั้งค่าการเชื่อมต่อได้รวดเร็ว
    • probe ip:ports ทั้งหมดของอีกฝ่าย และหากจำเป็นให้ทำการค้นหาแบบอิง birthday paradox เพื่อผ่าน hard NAT
    • หากพบเส้นทางเชื่อมต่อที่ดีกว่าเส้นทางปัจจุบัน ให้อัปเกรดอย่างโปร่งใส
    • หากเส้นทางที่ใช้งานอยู่หยุดทำงาน ให้ downgrade ตามความจำเป็นเพื่อรักษาการเชื่อมต่อ
    • การสื่อสารทั้งหมดต้องถูกเข้ารหัสและยืนยันตัวตนแบบ end-to-end

1 ความคิดเห็น

 
GN⁺ 2025-01-06
ความคิดเห็นจาก Hacker News
  • เป็นบทความที่ยอดเยี่ยม มักมีความรู้แบบไม่เป็นลายลักษณ์อักษรว่า การเจาะรูผ่าน TCP (TCP-based hole punching) ทำได้ยากกว่า UDP จึงไม่ควรทำ แต่ในความเป็นจริง เมื่อเทียบกับ flow ของ UDP ที่ซับซ้อนอยู่แล้ว ดูเหมือนความซับซ้อนที่เพิ่มขึ้นจะไม่ได้มากนัก
    ในบทความเองก็ยอมรับว่า การทะลุ NAT ด้วย TCP ทำได้ แต่มีความซับซ้อนเพิ่มขึ้น และถ้าลงลึกมาก ๆ อาจถึงขั้นต้องแก้เคอร์เนลด้วย อย่างไรก็ตาม ผมมองว่าแค่เปลี่ยนส่วนที่เริ่มการเชื่อมต่อด้วยแพ็กเก็ต UDP ดิบ ให้เป็นแพ็กเก็ต TCP SYN พร้อมรองรับ การเปิดพร้อมกัน (simultaneous open) ก็พอ
    โดยเฉพาะเมื่อคิดถึงว่า มีเครือข่ายอย่าง Wi‑Fi สำหรับผู้ใช้ชั่วคราวของ UC Berkeley ที่บล็อกการส่ง UDP ทั้งหมดนอกเหนือจาก DNS การมองข้าม TCP hole punching แบบคร่าว ๆ เพียงเพราะ “ยากกว่า UDP” จึงน่าเสียดาย ผมคิดว่ามันทำได้จริงแทบจะใกล้เคียงกัน และความซับซ้อนที่เพิ่มก็จำกัด
    https://ttcplinux.sourceforge.net/documents/one/tcpstate/tcp...

    • เพราะมี ไฟร์วอลล์แบบติดตามสถานะ (stateful firewall) อยู่ และตัวกรอง NAT ส่วนใหญ่เป็น EDF ไม่ใช่ EIF แม้แต่ใน UDP ก็จำเป็นต้องมีการเปิดพร้อมกัน หรือก็คือการส่งพร้อมกัน
      ดังนั้นความซับซ้อนเพิ่มเติมของการทำ simultaneous open ด้วย TCP จึงค่อนข้างน้อย จุดยากหลักคือการส่งต่อ public mapping และประสานจังหวะการเจาะรู/เปิดแบบ “พร้อมกัน” ซึ่งโดยปกติ UDP ก็ต้องทำอยู่แล้ว
      สิ่งที่ซับซ้อนเพิ่มอีกอย่างใน TCP คือ แทนที่จะสร้างแพ็กเก็ต TCP SYN ปลอม ต้องเรียก connect() จริง เพราะไฟร์วอลล์บางตัวดูหมายเลขลำดับ (sequence number)
    • เป็นข้อสังเกตที่ดีจริง ๆ ผมเคยลองทำ TCP hole punching เอง และตอนนี้มี implementation ที่ค่อนข้างดีแล้ว ข้อดีใหญ่ของการใช้ TCP คือ หลังจากเปิดรูได้แล้ว ไม่ต้องเอา TCP เวอร์ชันยากจนไปวางทับบน UDP อีกชั้น
      อย่างไรก็ตาม TCP hole punching อาจดูคล้าย SYN flood มากกว่าแพ็กเก็ต UDP มาก ทำให้ในบางเครือข่ายอัตราความสำเร็จอาจต่ำลง ในทางปฏิบัติผมยังไม่เห็นการกรองแบบนี้มากนัก
      TCP hole punching ค่อนข้างสนุก ผม implement โดยใช้การวัด NTP หลายครั้งเพื่อคำนวณ “clock offset” ว่านาฬิการะบบคลาดจาก NTP เท่าไร แล้วให้ฝ่ายเริ่มต้นกำหนดเวลานัดพบในอนาคตตามเวลา NTP มันแม่นกว่าที่คิด และ TCP hole punching ระหว่างซ็อกเก็ตบนอินเทอร์เฟซเดียวกันก็ใช้งานได้ด้วย
      เหตุผลที่รองรับโหมดเจาะแบบแปลก ๆ ที่อิง local แบบนี้คือ ถ้าการเจาะภายในโฮสต์สำเร็จได้ด้วยประสิทธิภาพระดับนั้น ก็มีโอกาสสูงว่าใน LAN และอินเทอร์เน็ตจะเร็วพอเช่นกัน โค้ดเป็น Python และความพยายามครั้งแรกค่อนข้างช็อกมาก เพราะ TCP hole punching ไวต่อ timing พอใช้การจัดการซ็อกเก็ตตรง ๆ แบบเก่าใน Python, threading และ event loop แบบลวก ๆ ที่ทำจากประสบการณ์ C socket ก็ล้มเหลว
      เพื่อให้โค้ดนั้นทำงานได้ ต้องเพิ่ม priority ของโปรเซส Python เพื่อไม่ให้โปรเซสอื่นสร้างดีเลย์ระหว่างความพยายามเจาะรูได้ ใน implementation ที่ไม่มีประสิทธิภาพ มันไวต่อเวลาขนาดนั้น implementation ปัจจุบันใช้ process pool ที่แต่ละโปรเซสมี event loop ของตัวเอง สร้างรายการงานที่กระจายตามเวลา แล้วให้แต่ละงาน reuse ซ็อกเก็ตเดิมเพื่อเปิดการเชื่อมต่อ หลังจากทดสอบบนระบบปฏิบัติการหลัก ๆ แล้ว ผมตัดสินว่าแนวทางนี้ดีที่สุดสำหรับ Python
      ผมเห็นด้วยว่าความยากของ TCP และ UDP hole punching ใกล้เคียงกัน ส่วนที่ยากที่สุดของทั้งคู่คือขั้นตอน การคาดการณ์ NAT (NAT prediction) ผมยังไม่ได้ใช้โค้ดหลบเลี่ยง symmetric NAT แต่เริ่มเห็นทางที่จะผสานเข้าไปหรือทำเป็นปลั๊กอินใหม่แล้ว
    • ผมนึกถึงข้อเสียอีกอย่างที่ทำให้ TCP punching เสียเปรียบ UDP ได้ TCP ต้องให้เราเตอร์บันทึก สถานะการเชื่อมต่อ
      ตารางสถานะของเราเตอร์มีขนาดเล็กมาก และเทคนิคการเจาะรูบางแบบก็ค่อนข้างดุดัน เช่น ถ้าเปิดการเชื่อมต่อ TCP หลายร้อยรายการเหมือนอัลกอริทึมที่พยายามหลบเลี่ยง symmetric NAT ก็อาจทำให้เราเตอร์เข้าสู่ภาวะปฏิเสธการให้บริการได้
      UDP อาจมีโอกาสน้อยกว่าที่การเจาะรูจะทำให้เราเตอร์ทั้งตัวค้าง เพราะมีการปรับแต่งการจัดการสถานะให้เหมาะสมกว่า อย่างไรก็ตาม นี่เป็นเพียงการคาดเดา
  • แม้ผลลัพธ์จะดีจนน่าสนใจ แต่พอมีคนพูดว่าจะเอาสิ่งนี้ไปใส่ใน เครือข่ายองค์กรระดับโปรดักชัน ก็รู้สึกไม่สบายใจอย่างบอกไม่ถูก
    มันดูเสี่ยงเหมือนเป็นการอ้อม NAT และไฟร์วอลล์แบบดั้งเดิม แล้วไปพึ่งพาแค่ ACL ในซอฟต์แวร์ตัวเดียวแทน เช่น ถ้ามี Tailscale อยู่ใน VM ที่ถูกทิ้งไว้ในสภาพแวดล้อมทดสอบของ AWS และผู้โจมตีเข้าถึงมันได้ ก็เหมือนจะเกิดเส้นทางที่ไปถึงแล็ปท็อปในเครือข่ายองค์กรภายใน โดยมีเพียงโค้ด ACL ของ Tailscale ใน user space หลังผ่านเคอร์เนลเป็นตัวตัดสินว่าอนุญาต/บล็อก
    ไม่แน่ใจด้วยซ้ำว่าจะรู้ได้ไหมถ้ามีคนที่ไม่ได้รับอนุญาตเข้ามาถึงจุดนั้น

    • นั่นจึงเป็นเหตุผลที่หลายคนย้ำซ้ำ ๆ ว่า NAT ไม่ใช่กลไกด้านความปลอดภัย
      การเจาะผ่าน NAT และตัวกรองแบบติดตามสถานะส่วนใหญ่ที่เกี่ยวข้องนั้นง่ายมาก ผมเคยทำของแบบนี้เป็นผลิตภัณฑ์ขายจริงในสภาพแวดล้อมโปรดักชันขององค์กรมาแล้ว มันไม่ใช่เวทมนตร์ แต่เป็นเทคนิคที่คนทำงานภาคปฏิบัติรู้จักกันดี
      ถ้าต้องการการกรองแพ็กเก็ตจริง ๆ หรือก็คือไฟร์วอลล์ ควรวางอินสแตนซ์ไฟร์วอลล์แยกจาก NAT แล้วตั้งกฎให้เหมาะสม แต่ถึงอย่างนั้นมันก็ช่วยหลัก ๆ แค่ลดปริมาณทราฟฟิกเท่านั้น ประโยชน์ด้านความปลอดภัยที่แท้จริงของไฟร์วอลล์เองตอนนี้มีน้อยลงแล้ว เพราะการโจมตีส่วนใหญ่เข้ามาทางเลเยอร์บน ๆ เช่น HTTP/HTTPS, POP/IMAP
    • ถ้าพูดอย่างเป็นธรรม เหตุผลที่ทุกคนเข้าใจผิดว่า NAT เป็นกลไกด้านความปลอดภัย ก็เพราะแต่เดิม NAT มักถูก deploy คู่กับ ไฟร์วอลล์แบบติดตามสถานะ
      ในความเป็นจริงไฟร์วอลล์แบบติดตามสถานะเป็นตัวทำงานส่วนใหญ่ แต่ NAT กลับได้เครดิตไป Tailscale ไม่ได้ตัดไฟร์วอลล์ทิ้ง แต่ให้การตั้งค่าที่ครอบคลุมกว่ามากบนฐาน ACL ที่ถูกต้อง
      อย่างไรก็ตาม ยอมรับว่าเครื่องมือ ACL ของ Tailscale ยังมีพื้นที่ให้ปรับปรุงได้มาก
    • งานเครือข่ายเป็นพื้นที่เหมือนกองขยะพิษของความปลอดภัยและการตั้งค่าผิดพลาดมานานแล้ว และตอนนี้ยังมี โมเดลเครือข่ายแบบอิงโฮสต์ สมัยใหม่สำหรับคอนเทนเนอร์ปนเข้ามาอีก
      ผลคือ network stack ของ Windows ก็เปลี่ยนไปมากและซับซ้อนขึ้น หลังจาก WireGuard เข้าไปอยู่ใน Linux แล้ว ทุกคนก็เหมือนมี VPN สักตัวที่เชื่อมไปยัง VPS ที่ไหนสักแห่ง เพราะเราไม่รู้ด้วยซ้ำว่าเราไม่รู้อะไร สถานการณ์จริงจึงอาจแย่กว่าที่คิดมาก
    • สิ่งนี้มีไว้สำหรับ NAT traversal ซึ่งเป็นเทคนิคสำหรับอุปกรณ์ที่ถูกสร้างมาเพื่อเลี่ยงปัญหาการขาดแคลนที่อยู่ IPv4
      ไฟร์วอลล์เป็นอีกแนวคิดหนึ่ง แต่ถ้าจะพูดโยงเรื่องการเชื่อมต่อกับความปลอดภัย ก็น่าเศร้าและน่ากังวลที่ความปลอดภัยบนอินเทอร์เน็ตมักพึ่งพาวิธีบล็อกแพ็กเก็ตตามพอร์ตปลายทางมาโดยตลอด
      เป็นความจริงที่เราทำสิ่งที่ง่ายแทนสิ่งที่ถูกต้อง แต่ยังถูกเรียกว่า “โซลูชันระดับมืออาชีพ”
    • VoIP ทำงานแบบนี้มาตั้งนานแล้ว และมีโครงสร้างพื้นฐานสาธารณะมาตรฐานจำนวนมากที่ช่วยให้ง่ายขึ้น เช่น ICE, TURN
      ถึงอย่างนั้น สิ่งบางอย่างภายในก็ยังต้องเริ่มพูดกับภายนอกก่อนอยู่ดี ดังนั้นไฟร์วอลล์จริง ๆ ควรจัดการทั้งการเชื่อมต่อขาออกและขาเข้าแบบ allowlist
      กล่าวอีกอย่างคือ ถ้ายังพึ่งพาความปลอดภัยที่ขอบเครือข่าย ก็เป็นเพียงเรื่องของเวลาก่อนที่ใครสักคนจะค้นพบว่า “เสื้อกั๊กสะท้อนแสง” เวอร์ชันขององค์กรตัวเองคืออะไร
  • คงดีถ้ามี ทางเลือกคล้าย Tailscale ที่ไม่เข้ารหัสการเชื่อมต่อ สำหรับอุปกรณ์ที่เข้ารหัสอยู่แล้วในเลเยอร์แอปพลิเคชัน ไม่จำเป็นต้องเข้ารหัสลงไปถึงเลเยอร์ล่างเสมอไป เหมือนกับที่อินเทอร์เน็ตแทบทั้งหมดทำงานกันแบบนั้น
    สำหรับอุปกรณ์พลังงานต่ำ เช่น อุปกรณ์ IoT ที่รันอุโมงค์แบบคล้าย Tailscale ต้นทุนการคำนวณยิ่งสูงเป็นพิเศษ
    มี GRE tunnel และใช้งานกันมากจริง แต่เพราะมันไม่จัดการ UDP hole punching จึงต้องใช้โครงสร้างแบบ hub-and-spoke ไม่สามารถสร้าง mesh ระหว่าง peer ด้วย GRE หรือก็คือ ip fou ได้
    สงสัยว่ามีไลบรารีที่หลังจากทำแฮนด์เชกเข้ารหัสเพื่อยืนยันตัวตนแล้ว ให้ UDP hole punching และ GRE tunnel แบบไม่เข้ารหัสหรือไม่

    • มาตรฐานที่ลงหลักปักฐานในด้านนี้คือ ICE(Interactive Connectivity Establishment) ซึ่ง WebRTC พึ่งพาอยู่ มีไลบรารีดี ๆ ที่อิมพลีเมนต์มันหรือบางองค์ประกอบของมัน
      ถ้าต้องการสิ่งที่เหมาะกับการเชื่อมต่อแบบทั่วไปมากขึ้น libp2p อาจใกล้เคียงกับสิ่งที่ต้องการ
      https://datatracker.ietf.org/doc/html/rfc8445
      https://github.com/pion/webrtc
      https://github.com/algesten/str0m
      https://libp2p.io
    • ไม่ใช่ UDP แต่ผมอิมพลีเมนต์ TCP hole punching และวิธี NAT traversal หลัก ๆ อื่นไว้ที่นี่: https://github.com/robertsdotpm/p2pd
      เขียนด้วย Python แต่เหมือนโค้ดเครือข่ายส่วนใหญ่ มันไม่ได้ตั้งสมมติฐานว่าจะใช้อินเทอร์เฟซเริ่มต้น ผมอยากให้สามารถรันบริการบนอินเทอร์เฟซใดก็ได้ที่ต้องการ เพื่อให้สร้างสิ่งที่หลากหลายและมีประโยชน์มากขึ้น
      ส่วนใหญ่ใช้โมดูลจาก standard library ผมไม่ชอบ C extension เพราะมักทำให้แพ็กเกจข้ามแพลตฟอร์มพังบ่อย
    • ใน VoIP สิ่งที่ทำ hole punching คือ TURN, STUN, ICE ดังนั้นสามารถนำไลบรารีฝั่งนั้นกลับมาใช้ได้
    • อีกวิธีคือลองชุบชีวิต Teredo ขึ้นมา
  • ตรงส่วนที่ peer ต้องรู้ ip:port ที่อีกฝ่ายใช้อยู่ล่วงหน้า และสร้างเซิร์ฟเวอร์ประสานงานเพื่อซิงก์ข้อมูลนี้ ทำให้นึกว่าอยากให้ SIP ทำตัวให้สมชื่อ
    SIP คือ Session Initiation Protocol ชื่อก็บอกว่าควรเริ่ม session ใด ๆ ได้ รวมถึง VPN แต่ในความเป็นจริงมันเป็นความยุ่งเหยิงที่ซับซ้อนเกินไปจนไม่คุ้มจะรับภาระ ผมมองว่าเดิมทีมันถูกสร้างมาเป็นช่องทางสื่อสารด้านข้างเพื่อจัดตั้งสตรีม P2P RTP

    • SIP ทำหลายอย่างมากจนกลัวที่จะโหลดทั้งหมดไว้ในหัวพร้อมกัน
      มันเหมือน HTTP แต่มีสถานะ เป็นสองทิศทาง เป็นแบบ federated และทำงานบน UDP ได้ด้วย
      ถ้าดูปริมาณสิ่งที่ baresip ต้องอิมพลีเมนต์เพียงเพื่อทำ SIP ก็จะเห็นว่ามหาศาล รวมถึง TLS over UDP ด้วย และมันไม่ได้บวมเกินจำเป็นด้วยซ้ำ เพราะฟีเจอร์เหล่านั้นจำเป็นจริง ๆ
  • เป็นบทความจากปี 2020 การอภิปรายก่อนหน้านี้มีดังนี้
    ปี 2022: https://news.ycombinator.com/item?id=30707711
    ปี 2020: https://news.ycombinator.com/item?id=24241105

  • นี่คือบทความที่ผมมักส่งให้คนอื่นเมื่ออธิบายเรื่อง NAT traversal
    ตอนเราสร้างแอป P2P เราอาจต้องพึ่งวิธีนี้ต่อไปเรื่อย ๆ IPv6 ยังไม่ได้แรงส่งมากพอ และ NAT กับ SNI routing ก็แก้ปัญหาส่วนใหญ่ให้คนส่วนใหญ่ได้แล้ว
    ในมุมของ ISP ก็ไม่มีแรงจูงใจมากนักที่จะทำให้สถานการณ์นั้นเปลี่ยนไป

  • ผมคิดว่านี่เป็นหนึ่งในบทความที่ละเอียดที่สุดเกี่ยวกับ NAT traversal บนอินเทอร์เน็ตทั้งหมด แต่ยังขาดข้อมูลเกี่ยวกับพฤติกรรมแบบเดลตา
    ไม่ใช่เรื่องซับซ้อนอะไร หมายถึง NAT บางตัวมีแพตเทิร์นที่สังเกตได้เมื่อจัดสรรพอร์ตภายนอกที่ต่อเนื่องกัน แพตเทิร์นที่พบบ่อยที่สุดคือการคงพอร์ตต้นทางไว้ และอาจมีแพตเทิร์นแบบเพิ่มขึ้นทีละ 1 จาก mapping ก่อนหน้าด้วย
    ในเชิงทฤษฎีเป็นบทความที่ดีมาก แต่ก็สงสัยว่าวิศวกรซอฟต์แวร์จะนำไปใช้จริงได้มากแค่ไหน บทความอธิบายหลายอย่าง แต่รายละเอียดอาจยังไม่พอให้เขียนอัลกอริทึมได้ เช่น ไม่แน่ใจว่าอ่านแค่นี้แล้วจะเขียนอัลกอริทึมทดสอบชนิดของ NAT หรือปรับแต่งโค้ด hole punching ของตัวเองได้หรือไม่
    โดยส่วนตัวเคยเห็นงานวิจัยบางฉบับที่ตารางแบบง่าย ๆ มีประโยชน์กว่าบทความยาว ๆ แบบนี้ แต่ก็ถือเป็นจุดเริ่มต้นที่ดีได้
    ส่วนท้ายของบทความสำคัญเป็นพิเศษ เพราะอาจมีทางหลบเลี่ยง symmetric NAT ที่ใช้ในระบบมือถือ งานวิจัย NAT traversal สมัยใหม่ก็ใช้เทคนิคคล้ายกัน และอ้างอัตราสำเร็จเกือบ 100%

  • เป็นบทความที่น่าสนใจและทำให้นึกถึงอดีต ปี 2010 ผมเคยสร้าง oblivious P2P mesh network ที่ใช้วิธีแบบนี้
    ตอนนั้นผู้คนไม่ได้ใส่ใจเรื่องความปลอดภัยเท่าที่เราคิด และตอนนี้ก็ยังใส่ใจไม่พออยู่ดี อุปกรณ์มีมากขึ้นและมูลค่าก็สูงขึ้น แต่ก็ยังค่อนข้างไม่ปลอดภัย
    hardware root of trust, trust chain ที่ปลอดภัยสำหรับการยืนยันตัวตน/การอนุญาต และ endpoint ที่ปลอดภัยจริง ๆ พร้อมสิทธิ์ชั่วคราวขั้นต่ำยังคงเป็นเรื่องยาก และในเครือข่ายบ้าน เครือข่ายองค์กร รวมถึงเครือข่ายดาต้าเซ็นเตอร์โปรดักชันขนาดใหญ่ ละครความปลอดภัยแบบขอบเขตเครือข่าย ก็ยังดำเนินต่อไป
    เหตุผลเดียวที่สิ่งเหล่านี้ไม่ดูเหมือนเป็นสาเหตุรากหลักของการถูกเจาะระบบ คือยังมีเส้นทางโจมตีที่ง่ายกว่าวางอยู่เต็มไปหมด

  • อาจจะออกนอกประเด็นไปหน่อย แต่เมื่อไม่กี่สัปดาห์ก่อนผมลองอ่านเรื่องนี้ ทั้งที่ก่อนหน้านั้นไม่รู้อะไรเกี่ยวกับสาขานี้เลย
    ความรู้สึกที่ได้คือ IPv6 น่าจะทำให้เรื่องทั้งหมดนี้หายไป และทำให้ไม่ต้องใช้ NAT traversal อีกต่อไป ถ้าอย่างนั้นก็สงสัยว่าทำไม IPv6 ถึงยังไม่แพร่หลายกว่านี้ และถ้าจะเริ่มใช้ในเครือข่ายบ้านกับ Tailscale VPN ควรทำอย่างไร

    • ผมไม่รู้ว่าหนึ่งในเหตุผลที่ IPv6 ไม่ค่อยได้รับความนิยมมีสัดส่วนมากแค่ไหน แต่ประเด็นว่า ใช้งานยากสำหรับมนุษย์ เป็นความท้าทายมาตลอด
      แรงจูงใจทางธุรกิจก็ยังมีไม่พอด้วย
  • การที่มีสิ่งแบบนี้เกิดขึ้นแทน IPv6 เอง แสดงให้เห็นพลังของ แฮ็กที่ดีพอ ได้อย่างชัดเจน