เน็ตบ้านหลัง CGNAT: ISP เปิดให้แค่ 10 พอร์ต ต้องเช็กอะไรบ้าง
ตั้ง port forward ถูกทุกบรรทัดแล้วก็ยังเข้าจากข้างนอกไม่ได้ เพราะพอร์ตบางพอร์ตไม่เคยผ่านหน้าบ้านของ ISP มาตั้งแต่แรก และเครื่องมือทดสอบที่เรามักใช้ก็ตอบไม่ตรงความจริงด้วย
สรุปใน 1 นาที
- อาการ/ปัญหา
- ตั้ง port forward ถูกหมดแล้วแต่เข้าจากข้างนอกไม่ได้ และปุ่ม port test แดงเสมอ
- ใครควรอ่าน
- คนใช้เน็ตบ้านหลัง CGNAT ที่ ISP ให้ DDNS กับพอร์ตช่วงจำกัด
- ทางแก้แบบสั้น
- ให้ external port อยู่ในช่วง 10 พอร์ตที่ ISP เปิดให้ แล้วทดสอบด้วยเครื่องข้างนอกต่อเข้ามา ดูที่ปลายทาง ไม่เชื่อปุ่ม port test
พอดีผมตั้ง port forward ที่เราเตอร์บ้านครบทุกกฎแล้ว แต่พอลองเข้าจากข้างนอกกลับเข้าไม่ได้ ปัญหาไม่ได้อยู่ที่เราเตอร์ ทั้งที่ผมเช็กอยู่ตรงนั้นก่อนเสมอ บ้านผมใช้ไฟเบอร์ของ ISP ไทยรายหนึ่งที่เป็น CGNAT ไม่มี public IP ของตัวเอง ต่อให้เปิดพอร์ตที่เราเตอร์ คนข้างนอกก็เข้าไม่ถึง ISP เลยมีบริการ DDNS ให้ ชี้ชื่อโดเมนมาที่บ้านเสมอ และส่งต่อพอร์ตจากข้างนอกเข้ามาเฉพาะชุดพอร์ตที่จัดสรรให้ 10 พอร์ต บทความนี้เล่าว่ากติกานั้นคืออะไร ผมเจออะไรที่ไม่เคยใช้ได้โดยไม่มีใครรู้ ทดสอบอย่างไรไม่ให้หลงทาง และเรื่องที่ผมยังแก้ไม่จบ ชื่อโดเมนกับเลขพอร์ตในบทความเป็นค่าตัวอย่างหมด
อาการ: ตั้งครบแล้วแต่เข้าจากข้างนอกไม่ได้
หน้าตาของปัญหาคือ ในเราเตอร์มีกฎ forward ครบ เครื่องปลายทางในบ้านเปิดพอร์ตรอรับอยู่ ในบ้านเข้าหากันได้ปกติ แต่พอเอามือถือที่ปิด Wi-Fi ไปเรียกชื่อ DDNS ตามด้วยพอร์ต ไม่มีอะไรตอบกลับ ผมเลยไล่ตรวจตามลำดับที่เล่าในหัวข้อเช็กลิสต์ด้านล่าง แต่ก่อนอื่นต้องเข้าใจกติกาของบริการก่อน
ISP เปิดให้แค่ 10 พอร์ต: external ต้องอยู่ในช่วง internal ใช้อะไรก็ได้
ตามคู่มือของบริการ วิธีตั้งมีสามข้อ external หรือ WAN port บนเราเตอร์ต้องตรงกับพอร์ตที่ได้รับจัดสรร ส่วน internal port เป็นพอร์ตจริงของบริการ ใช้อะไรก็ได้ จะ 80, 443, 8006 หรือ 22 ก็ได้ และ internal IP คือเครื่องในบ้านที่รับ จากข้างนอกก็เรียก home.example.net:<พอร์ตในช่วง> ส่วนที่คนมักเข้าใจผิดคือไปจับเลข internal กับ external ให้เท่ากัน เช่น เครื่องฟัง 8006 ก็นึกว่าต้องเปิด 8006 จากข้างนอกด้วย ซึ่งไม่จำเป็นเลย และถ้า 8006 อยู่นอกช่วงก็ไม่มีวันเข้าได้

ตัวอย่างตารางกฎที่ใช้ได้ (ค่าตัวอย่างทั้งหมด)
ชื่อกฎ external internal โปรโตคอล internal IP
web-admin 40003 8006 TCP 192.168.10.50
ssh-host-a 40005 2200 TCP 192.168.10.50
กฎ SSH เดิมที่ไม่เคยใช้ได้จากข้างนอกเลย
ตอนไล่ตรวจกฎเก่า ผมเจอ forward เดิมกฎหนึ่งสำหรับ SSH ที่ external เป็น 2200 ซึ่งอยู่นอกช่วง หมายความว่ามันไม่เคยใช้ได้จากข้างนอกเลยสักครั้ง และไม่มีใครรู้ เพราะไม่มีใครเคยทดสอบจากข้างนอกจริง ๆ ในบ้านเข้าได้ปกติ เลยดูเหมือนทุกอย่างปกติมาตลอด
ผมแก้โดยย้าย external ไปใช้พอร์ตว่างพอร์ตหนึ่งในช่วง (internal ยังเป็น 2200 ตามเดิม) ซึ่งตรงตามกติกาข้างบนพอดี ตอนที่กำลัง cutover ผมตั้ง DMZ บนเราเตอร์ ISP ชี้ไปที่ WAN ของ gateway ตัวใหม่ชั่วคราว เป็น double NAT แล้วค่อยตั้ง forward จริงบน gateway เรื่องลำดับการสลับเราเตอร์ผมเขียนไว้แล้วใน บทความย้ายเน็ตบ้านไปเป็น bridge ไม่เล่าซ้ำตรงนี้
การตั้งค่าที่ดูถูกต้องในหน้าเราเตอร์ไม่ได้พิสูจน์อะไรเลยว่าใช้ได้จริงจากอินเทอร์เน็ต กฎ SSH เดิมของผมผ่านการตรวจด้วยตาในหน้าเราเตอร์มาตลอด ปัญหาอยู่ที่ตัวเลขเดียว ซึ่งหน้าเราเตอร์ไม่มีทางบอก
เช็กลิสต์ห้าข้อก่อนโทษใคร
เวลา forward ไม่ผ่าน ผมไล่ตามนี้ทีละข้อ ไม่ข้าม
- external port อยู่ในช่วงที่ ISP จัดสรรหรือเปล่า เปิดหน้าบริการ DDNS เทียบตัวเลขกับกฎทีละบรรทัด
- กฎ forward ชี้ internal IP และ internal port ถูกเครื่องหรือเปล่า และใช้ TCP กับ UDP ตรงกับบริการ
- เครื่องปลายทางฟังพอร์ตนั้นอยู่จริงไหม ดูด้วย `ss -ltnp`
- firewall ของเครื่องปลายทางและ ACL ของ gateway ไม่ได้บล็อกอยู่
- ทดสอบจากข้างนอกจริงด้วยเครื่องที่สาม แล้วดูที่เครื่องปลายทางว่ามี connection โผล่มาไหม
สามข้อแรกตรวจจากในบ้านได้ทั้งหมด
# บนเครื่องปลายทาง (host-a): ฟังพอร์ตนี้อยู่จริงไหม
sudo ss -ltnp | grep ':8006'
# firewall: ดูกฎ INPUT ว่ามีอะไรบล็อกพอร์ตนี้หรือเปล่า
sudo iptables -S INPUT | head -3
ในเคสของผมผ่านหมดทุกข้อ peer-port ตรง forward ตรงทั้ง TCP และ UDP และไม่มีอะไรบล็อกระหว่างทาง แล้วทำไมยังดูเหมือนไม่ผ่านอยู่ คำตอบอยู่ที่ข้อห้า และเครื่องมือทดสอบที่ผมไปหยิบมาใช้
กับดัก: ปุ่ม port test ของ torrent แดงเสมอ
ผมใช้โปรแกรม torrent (Transmission) เป็นตัวทดสอบ เพราะมีปุ่มเช็กว่า port เปิดไหม แล้วมันขึ้น "port is Closed" ตลอด แม้เช็กทุกจุดข้างบนแล้วว่าถูก สาเหตุคือปุ่มนั้นให้เว็บภายนอกต่อกลับมาที่ IP ขาออกของเรา ซึ่งใน CGNAT เป็น IP ที่ใช้ร่วมกับลูกค้าคนอื่น ผลเลยแดงเสมอ ไม่ว่า forward ของเราจะถูกหรือผิด
ที่ทำให้งงยิ่งขึ้นคือยังมี peer เข้ามาได้ ซึ่งเห็นได้ใน conntrack ของเครื่องปลายทาง มี UDP จากภายนอกวิ่งเข้าพอร์ตของ torrent แต่ที่ peer เข้ามาได้ เพราะเป็น peer ที่เราเริ่มต่อออกไปเอง กับ uTP ที่เจาะ NAT ผ่านทางขาออกได้ เท่ากับว่า "เห็น peer" ไม่ได้แปลว่า "inbound ใช้ได้" และ "port test แดง" ก็ไม่ได้แปลว่า "forward ผิด" สองเรื่องนี้ต่างเป็นหลักฐานที่ไม่พิสูจน์อะไรทั้งคู่

วิธีทดสอบที่ถูก: ให้เครื่องข้างนอกต่อเข้ามา แล้วดูที่ปลายทาง
วิธีที่ผมยืนยันได้ว่าถูกคือ ให้เซิร์ฟเวอร์ภายนอก (host-b) เปิด TCP ค้างไว้ไปที่ชื่อ DDNS ตามด้วยพอร์ตในช่วง แล้วไปดูที่เครื่องปลายทางในบ้านว่ามี connection จาก host-b โผล่มาไหม
# บน host-b (เซิร์ฟเวอร์ภายนอก): เปิด TCP ค้างไว้ 2 นาที
sleep 120 | nc -v home.example.net 40003
# บน host-a (ปลายทางในบ้าน) ระหว่างที่ host-b ค้างอยู่:
sudo ss -tn state established '( sport = :8006 )'
sudo conntrack -L 2>/dev/null | grep 203.0.113.50
ถ้าทดสอบจากในบ้านเอง อาจเจอ NAT loopback ที่ให้ผลไม่ตรงความจริง ให้ใช้มือถือ 4G หรือ 5G ที่ปิด Wi-Fi ไปแทน ผลที่ต้องอ่านคือสองอย่างนี้ ถ้า host-b ต่อติดและปลายทางเห็น connection แปลว่า inbound ใช้ได้จริง แต่ถ้า host-b ต่อติดแต่ปลายทางไม่เห็นอะไรเลย แปลว่าคนที่รับ connection ไว้คืออุปกรณ์หน้าบ้านของ ISP เอง ไม่ใช่เครื่องของเรา
ไม่ได้แปลว่าถึงเครื่องเรา
handshake TCP อาจถูกอุปกรณ์ระหว่างทางรับไว้แทน เลยต้องดูที่ปลายทางว่ามี connection ขึ้นจริง ผลจากฝั่งคนต่อไม่พอ
นี่คือหลักฐาน
ss หรือ conntrack ของเครื่องปลายทางเห็น IP ของ host-b แปลว่าแพ็กเก็ตวิ่งผ่านหน้าบ้านของ ISP และเราเตอร์เรามาถึงเครื่องจริง
สิ่งที่ยังไม่จบ: หลัง bridge แล้ว ข้อมูลไม่มาถึง gateway
ตรงนี้ผมต้องเขียนตามจริงว่ายังแก้ไม่จบ หลังจากให้ ISP เปลี่ยนอุปกรณ์เป็น bridge และให้ gateway ของผมทำ PPPoE เอง ผลทดสอบคือ จากข้างนอกต่อ TCP ไปที่ IP หน้าบ้านของ ISP ได้ทุกพอร์ต (รับ handshake ทุกพอร์ต) แต่ข้อมูลไม่มาถึง gateway ของเรา ตรงนี้คือสาเหตุที่การทดสอบแบบ C ต้องดูที่ปลายทาง ถ้าผมเชื่อแค่ "ต่อติด" ผมจะนึกว่าพอร์ตเปิดแล้ว
ผมยังสรุปไม่ได้ว่าทำไม สิ่งที่มีหลักฐานจริงมีแค่ว่าไม่มีแพ็กเก็ตมาถึงเรา ส่วนสาเหตุว่าเกิดจากฝั่งไหนผมยังไม่รู้ สิ่งที่ผมยังไม่ได้ลอง และจดเป็นแผนไว้มีดังนี้
กดยืนยันพอร์ตซ้ำในหน้าบริการ DDNS โทรถาม ISP ว่าบริการนี้ใช้กับ PPPoE บนเราเตอร์ของเราเองได้ไหม ขอ public IP ใช้ IPv6 (ISP แจก IPv6 ให้ แต่ gateway ของผมยังไม่ได้เปิด) หรือเลี่ยงด้วย tunnel กับ VPN ซึ่งผมเขียนไว้แล้วใน [บทความ WireGuard จาก VPS เข้าบ้าน](/article/wireguard-vps-home-vpn), [บทความ DDNS ของ WireGuard ที่ endpoint ค้าง](/article/wireguard-ddns-endpoint-stale-reresolve) และ [บทความ cloudflared tunnel](/article/cloudflared-tunnel-single-ingress-remote-desktop)
เรื่องที่ผมยังบอกไม่ได้และขอไม่เดา ได้แก่ บริการ DDNS ของ ISP ผูกกับ PPPoE session ได้หรือไม่ IPv6 inbound ใช้ได้จริงไหม และปัญหาอยู่ที่ฝั่ง ISP แน่ ๆ หรือเปล่า ทั้งสามข้อผมยังไม่มีหลักฐาน ส่วนอีกด้านของปัญหาเดียวกัน คือเมื่อ IP ย้ายไปอยู่ CGNAT แล้ว allowlist ที่ผูกกับ IP เดิมพัง ผมเล่าไว้ใน บทความ SSH จำกัด from กับ CGNAT
สรุป
ถ้าอยู่หลัง CGNAT ที่ ISP ให้ DDNS แบบช่วง 10 พอร์ต ให้ตั้ง external ในช่วงเท่านั้น internal ใช้อะไรก็ได้ ตรวจกฎเก่าทุกข้อว่า external อยู่ในช่วงไหม ทดสอบด้วยเครื่องที่สามจากข้างนอกจริงแล้วดู ss กับ conntrack ที่ปลายทาง อย่าเชื่อปุ่ม port test ของ torrent และอย่าเชื่อว่า "เห็น peer" แปลว่า inbound ใช้ได้ ส่วนผลของเคสนี้หลัง bridge ผมยังไม่ได้ข้อสรุป ใครที่เจออาการคล้ายกันก็ตามแต่ว่าจะรอคำตอบจาก ISP หรือย้ายไปใช้ tunnel เลยครับ
ตรงไหนที่ผมเข้าใจกติกาของบริการ DDNS คลาดเคลื่อน หรือถ้าเจอสาเหตุของเรื่องข้อมูลไม่ถึง gateway หลัง bridge แล้ว เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
อยากให้เราทำให้ไหม?
ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย