Linux จัดการ `ping` ผ่าน NAT อย่างไร?
(devnonsense.com)- โดยปกติ IPv4 NAT จะแยกปลายทางของคำตอบด้วย พอร์ต ของ TCP/UDP แต่ ICMP echo ของ
pingไม่มีพอร์ต ดังนั้นประเด็นสำคัญคือ Linux ใช้ค่าใดเป็นคีย์ในการแมป - การทดลองใช้ network namespace สร้าง
client1,client2,natbox,serverและจำลองโครงสร้างที่ NAT จาก 192.168.99.0/24 ไปยัง 10.0.100.0/24 ด้วยiptablesMASQUERADE - เมื่อเทียบ RFC 792 กับ packet capture จะเห็นว่า Identifier และ Sequence Number ของ ICMP echo ถูกใช้จับคู่คำขอและคำตอบ และในเส้นทาง ICMP
SOCK_DGRAMของ Linux ค่า local port ของซ็อกเก็ตจะถูกใส่เป็น ID - ถ้าไคลเอนต์สองเครื่องใช้ ICMP ID
999เหมือนกัน netfilter จะเปลี่ยน ICMP ID ของฝั่งหนึ่งเป็นค่าสุ่ม เพื่อหลีกเลี่ยงการชนกัน และเมื่อมีคำตอบกลับมาก็จะแปลงกลับเป็น IP และ ID เดิมของไคลเอนต์ - Linux NAT จะเก็บสถานะทิศทางต้นทางและทิศทางตอบกลับไว้ใน conntrack tuple แม้เป็น ICMP ที่ไม่มีพอร์ต และใช้ ICMP ID เป็นคีย์ที่สามารถแก้ไขได้เพื่อแมปคำตอบกลับไปยังโฮสต์ภายในที่ถูกต้อง
สภาพแวดล้อมการทดลองและการตั้งค่า NAT
- ใช้ network namespace เพื่อจำลองอุปกรณ์หลายตัวบนเครื่อง Linux เครื่องเดียว
- สร้างสองไคลเอนต์,
natboxที่ทำหน้าที่เป็นเราเตอร์ NAT และเซิร์ฟเวอร์แยกเป็นคนละ namespace พร้อมแยกเครือข่ายฝั่ง private กับฝั่งเซิร์ฟเวอร์client1:192.168.99.1/24client2:192.168.99.2/24- อินเทอร์เฟซภายในของ
natbox:192.168.99.3/24 - อินเทอร์เฟซภายนอกของ
natbox:10.0.100.1/24 server:10.0.100.2/24
- รัน
ip,iptables,tcpdumpเป็นต้น ด้วยสิทธิ์ root บน Fedora 38 Server VM และ Linux kernel 6.2.9 - ไคลเอนต์ทั้งสองเชื่อมกับบริดจ์
br0และnatboxเชื่อมทั้งกับบริดจ์และ veth pair ฝั่งเซิร์ฟเวอร์ - ตั้งค่า default route ของไคลเอนต์เป็น
192.168.99.3เพื่อให้ทราฟฟิกไปยังเซิร์ฟเวอร์วิ่งผ่านnatbox - บน
natboxเปิด packet forwarding ด้วยnet.ipv4.ip_forward=1และเพิ่มกฎ MASQUERADE ในเชนPOSTROUTINGของตารางnatในiptablesip netns exec natbox iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
ICMP NAT ที่เห็นจาก packet capture
- ดักจับแพ็กเก็ต ICMP ด้วย
tcpdump -n icmpใน namespace ของclient1และserver - ฝั่งไคลเอนต์จะเห็น echo request
192.168.99.1 > 10.0.100.2และ echo reply10.0.100.2 > 192.168.99.1 - ฝั่งเซิร์ฟเวอร์จะเห็น source IP ของคำขอเดียวกันเปลี่ยนเป็น
10.0.100.1ยืนยันว่า NAT ได้เขียนทับ source address เป็น IP ภายนอกของnatbox - คำขอ ICMP จากไคลเอนต์คนละเครื่องมี ฟิลด์ id ไม่เหมือนกัน
- ในตัวอย่าง
client1ใช้ ID31428 client2ใช้ ID33391
- ในตัวอย่าง
- ข้อสังเกตนี้ชี้ว่า
natboxสามารถใช้ฟิลด์ ID เมื่อต้องส่ง ICMP response กลับเข้าไปยังไคลเอนต์ภายใน
RFC 792 และ ICMP ID ของ ping
- ICMP เป็นโปรโตคอลเก่าที่นิยามไว้ใน RFC 792 ซึ่งเผยแพร่ในปี 1981
- ข้อความ ICMP echo และ echo reply มี Type, Code, Checksum, Identifier, Sequence Number และ Data
- Type ใช้แยก echo request กับ echo reply
- Type ของ echo request คือ
8 - ในข้อความที่อ้างจาก RFC ระบุ echo reply เป็น
1 - Code คือ
0
- Type ของ echo request คือ
- RFC 792 อธิบายว่า Identifier และ Sequence Number สามารถใช้จับคู่ echo request กับ reply ได้
- Identifier สามารถใช้ระบุเซสชันได้คล้ายพอร์ตของ TCP/UDP และ Sequence Number สามารถเพิ่มขึ้นในแต่ละ echo request
- RFC ไม่ได้กำหนดว่าต้องเลือก ID อย่างไรจริง ๆ จึงต้องไปดูซอร์สโค้ดของ
pingซึ่งเป็นตัว implementation
วิธีที่ ping ใน iputils กำหนด ID
- คำสั่ง
pingอยู่ในแพ็กเกจ iputils - คอมเมนต์ใกล้
ping4_send_probeอธิบายว่าเวลาสร้าง ICMP echo request นั้น ฟิลด์ ID เป็นตัวเลขสุ่ม และ Sequence Number เป็นเลขจำนวนเต็มที่เพิ่มขึ้น - ภายใน
pingมีฟิลด์identของstruct ping_rts- ค่าเริ่มต้นคือ
-1 - สามารถเขียนทับได้ด้วยออปชัน CLI
-eโดยกำหนดค่าระหว่าง0ถึงIDENTIFIER_MAXซึ่งคือ0xFFFF
- ค่าเริ่มต้นคือ
- ถ้า
rts->ident == -1คำสั่งpingจะ bind ซ็อกเก็ตด้วยชนิดSOCK_DGRAMและโปรโตคอลIPPROTO_ICMP - ตามคำอธิบายของซ็อกเก็ต
IPPROTO_ICMPใน Linux ส่วนหัว ICMP จะถูกตรวจสอบและจัดการตอนsend()และ id จะถูกตั้งเป็นหมายเลข local port ของซ็อกเก็ต - หาก
pingไม่ได้ระบุ source port เคอร์เนล Linux จะเลือกพอร์ตว่างแบบสุ่ม และพอร์ตนั้นจะถูกใช้เป็น ID ของแพ็กเก็ต ICMP
เมื่อ ICMP ID เดียวกันชนกัน
- บนไคลเอนต์ทั้งสองใช้
ping -e 999เพื่อส่ง ping ไปยังเซิร์ฟเวอร์ด้วย ICMP ID 999 เหมือนกัน - จากการดักจับฝั่งเซิร์ฟเวอร์ พบว่าคำขอจากไคลเอนต์เครื่องหนึ่งยังคงใช้ ID
999แต่ของอีกเครื่องถูกเปลี่ยนเป็น ID30218 - อุปกรณ์ NAT เปลี่ยน ID ของฝั่งหนึ่งเพื่อไม่ให้ชุดค่าของ external IP และ ICMP ID เดียวกันเกิดการชนกัน
- เพื่อหาว่าจัดการการชนกันตรงไหน จึงตรวจดูโค้ดในไดเรกทอรี
net/netfilterของ Linux ที่ใช้ฟิลด์ ICMPid
บทบาทของ netfilter, conntrack และ NAT
- ซับซิสเต็มในเคอร์เนลที่ทำให้กฎ
iptablesทำงานคือ netfilter - เนื่องจากกฎ MASQUERADE เป็นผู้ทำ NAT ดังนั้น implementation ของ ICMP NAT ก็อยู่ใน netfilter
nf_nat_setup_infoในnf_nat_core.cเรียกget_unique_tupleและต่อไปยังnf_nat_l4proto_unique_tuple- ใน
nf_nat_l4proto_unique_tupleมีเคสIPPROTO_ICMPและอ้างถึงtuple->src.u.icmp.id nf_nat_manip_pktในnf_nat_proto.cจะผ่านnf_nat_ipv4_manip_pktและl4proto_manip_pktแล้วเรียกicmp_manip_pktเมื่อเป็น ICMPicmp_manip_pktเขียน ICMP ID ลงแพ็กเก็ตจริงด้วยhdr->un.echo.id = tuple->src.u.icmp.id
วิธีที่ conntrack tuple ใช้แทน ICMP
- ใน netfilter คำว่า connection ไม่ได้หมายถึงแค่การเชื่อมต่อ TCP แต่หมายถึงสถานะที่ใช้เชื่อมโยงแพ็กเก็ตขาออกและแพ็กเก็ตขาเข้าของโปรโตคอลที่ไม่มีการเชื่อมต่ออย่าง UDP หรือ ICMP ด้วย
nf_connมีtuplehash[IP_CT_DIR_MAX]IP_CT_DIR_ORIGINAL: ทิศทางของแพ็กเก็ตขาออกIP_CT_DIR_REPLY: ทิศทางของคำตอบขาเข้า
- แต่ละ
nf_conntrack_tuple_hashมีnf_conntrack_tupleที่ใช้ระบุ connection - tuple แบ่งเป็น
srcที่แก้ไขได้ และdstที่คงเดิมsrcมีทั้ง IP address และฟิลด์เฉพาะโปรโตคอล- สำหรับ ICMP ฟิลด์เฉพาะโปรโตคอลคือ
__be16 id dstมี IP address ที่ไม่ถูกเปลี่ยน รวมถึง ICMPtypeและcode
- NAT จะเก็บไว้ใน connection ว่าตอนแพ็กเก็ตขาออกถูกเปลี่ยนอย่างไร แล้วค่อยย้อนการเปลี่ยนนั้นกลับตอนมีแพ็กเก็ตตอบกลับ
เส้นทางโค้ดที่เลือก ICMP ID
- เมื่อ
natboxรับ ICMP echo,nf_nat_setup_infoจะสร้าง connection ใหม่และตัดสินใจว่าต้องเปลี่ยน source IP กับ ICMP ID หรือไม่ - หลังจากนั้นสำหรับทุกแพ็กเก็ต ICMP,
nf_nat_manip_pktจะตั้งค่า source หรือ destination IP รวมถึง ICMP ID ตามค่าที่เก็บไว้ใน connection get_unique_tupleคือเส้นทางหลักในการเลือก NAT tuple ที่ใช้งานได้find_best_ips_protoจะเขียนทับ source IP addressnf_nat_used_tupleตรวจว่า tuple นี้ถูกใช้อยู่แล้วหรือไม่ และถ้ายังไม่ถูกใช้ก็จะคืน tuple ปัจจุบันกลับมาเลย- ด้วยเหตุนี้ ถ้า ICMP ID ของไคลเอนต์สองเครื่องต่างกัน ID ก็จะถูกคงไว้แม้หลัง NAT
- ถ้า tuple นั้นถูกใช้อยู่แล้ว จะเรียก
nf_nat_l4proto_unique_tupleเพื่อทำ NAT เฉพาะระดับโปรโตคอล
- สำหรับ ICMP,
tuple->src.u.icmp.idถูกเลือกเป็น คีย์ สำหรับทำ NAT find_free_idจะสร้าง ID แบบสุ่มด้วยget_random_u16()ปรับให้อยู่ในช่วง ICMP ID ที่ใช้ได้ แล้วตรวจว่าถูกใช้อยู่หรือไม่- ช่วง ID เริ่มต้นคือช่วง ID ทั้งหมด และในกฎ
iptablesMASQUERADE สามารถกำหนดช่วงเช่น100-200ได้ด้วย--to-ports - ถ้าหา tuple ที่ยังไม่ถูกใช้ไม่ได้ connection จะยังมี ID ซ้ำอยู่ และ
__nf_conntrack_confirmจะตรวจพบความซ้ำแล้ว drop แพ็กเก็ตในภายหลัง
ยืนยันพฤติกรรมของเคอร์เนลด้วย bpftrace
- เพื่อยืนยันความเข้าใจเกี่ยวกับการทำงานของ netfilter ได้ใช้
bpftrace - ฟังก์ชันในเคอร์เนลที่ติดตามคือ
nf_nat_setup_infoและnf_nat_manip_pkt kprobeใช้ติดตามตอนฟังก์ชันถูกเรียก และkretprobeใช้ติดตามตอนฟังก์ชันคืนค่า- ใน
kretprobeไม่สามารถเข้าถึงอาร์กิวเมนต์ของฟังก์ชันได้โดยตรง จึงต้องเก็บอาร์กิวเมนต์ไว้ใน BPF map ตอนเข้า แล้วอ่านกลับมาอีกครั้งตอนคืนค่า struct sk_buffคือโครงสร้างที่เคอร์เนล Linux ใช้แทนแพ็กเก็ตbswapใช้สลับ network byte order แบบ big endian ให้เป็น little endianntopใช้แปลง IP address เป็นสตริง- ด้วย BPF Type Format (BTF) ของเคอร์เนล Linux รุ่นใหม่ ทำให้โปรแกรม BPF อ้างถึงโครงสร้างข้อมูลของเคอร์เนลอย่าง
sk_buff,nf_connได้โดยไม่ต้อง include header - โปรแกรม
bpftraceนี้ทดสอบบน Linux kernel 6.2.9 และอาจทำงานต่างออกไปบนเคอร์เนลเวอร์ชันอื่น
ผลการติดตามและข้อสรุป
- เมื่อไคลเอนต์สองเครื่องส่ง ping ด้วย ICMP ID
999เหมือนกันnf_nat_setup_infoจะถูกเรียกหนึ่งครั้งต่อไคลเอนต์ - ไคลเอนต์ตัวแรก
192.168.99.1คง ICMP ID999ไว้ทั้งใน original tuple และ reply tuple - ไคลเอนต์ตัวที่สอง
192.168.99.2มี ICMP ID ใน reply tuple ถูกเขียนทับเป็น32809 nf_nat_manip_pktจะเปลี่ยน source IP เป็น10.0.100.1ด้วยNF_NAT_MANIP_SRCใน echo request และจะเปลี่ยน destination IP กลับเป็น IP เดิมของไคลเอนต์ด้วยNF_NAT_MANIP_DSTในแพ็กเก็ตตอบกลับ- ICMP ID ในแพ็กเก็ตตอบกลับก็ถูกกู้คืนจากค่าที่ NAT เปลี่ยนไว้ให้กลับเป็นค่าที่ไคลเอนต์เดิมส่งมา
- ค่า timeout เริ่มต้นของ ICMP conntrack ดูได้จาก
/proc/sys/net/netfilter/nf_conntrack_icmp_timeoutโดยค่าดีฟอลต์ที่สังเกตได้คือ 30 วินาที - ถ้าไคลเอนต์ไม่ส่งแพ็กเก็ตนานเกิน 30 วินาที
nf_nat_setup_infoจะถูกเรียกใหม่ใน ping ครั้งถัดไป - พฤติกรรม NAT ของ Linux สำหรับ ping ถูกบันทึกไว้ใน Netfilter Hacking HOWTO เช่นกัน และแก่นสำคัญคือ conntrack tuple กับการเขียนทับ ICMP ID
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เมื่อเซิร์ฟเวอร์เริ่มทำงาน มันจะเริ่มส่งแพ็กเก็ตคำขอ ICMP echo แบบคงที่ไปยังที่อยู่คงที่ 3.3.3.3 และคาดว่าแพ็กเก็ตนี้จะไม่ย้อนกลับมา
3.3.3.3 ไม่ใช่โฮสต์ที่เข้าถึงได้ และไม่ใช่เป้าหมายที่จะสปูฟ แต่เมื่อไคลเอนต์ต้องการเชื่อมต่อ มันรู้ IP ของเซิร์ฟเวอร์อยู่แล้ว จึงส่งแพ็กเก็ต ICMP Time Exceeded ไปให้เซิร์ฟเวอร์ ภายในแพ็กเก็ต ICMP นั้นมีแพ็กเก็ตคงที่ “ต้นฉบับ” ที่เซิร์ฟเวอร์เคยส่งไปยัง 3.3.3.3 อยู่ และแพ็กเก็ตที่ฮาร์ดโค้ดไว้นี้ทำหน้าที่เป็นตัวระบุของ pwnat
กล่าวคือ ไคลเอนต์ทำทีเหมือนเป็นฮอปหนึ่งบนอินเทอร์เน็ต แล้วแจ้งว่า “ICMP echo request” ต้นฉบับของเซิร์ฟเวอร์ส่งต่อไปไม่ได้ NAT เห็นว่าแพ็กเก็ตภายใน ICMP Time Exceeded ตรงกับแพ็กเก็ตที่เซิร์ฟเวอร์ส่งออกไป จึงส่งต่อไปยังเซิร์ฟเวอร์หลัง NAT และเนื่องจากมี IP header ทั้งชุดของไคลเอนต์รวมอยู่ด้วย เซิร์ฟเวอร์จึงรู้ ที่อยู่ IP ของไคลเอนต์ ได้
การทำงานหลักของเครื่องมือนี้หลังจากนั้นคือการสร้าง UDP tunnel ระหว่างไคลเอนต์กับเซิร์ฟเวอร์
แต่จากที่ดูคร่าว ๆ ดูเหมือนจะสมมติว่า NAT ไม่เขียนพอร์ตต้นทางของ UDP ใหม่ จึงน่าจะใช้ไม่ได้กับเราเตอร์ทุกตัว STUN ที่ใช้ใน WebRTC และอย่างอื่น ๆ ใช้เทคนิคที่ซับซ้อนกว่า แต่ในกรณีที่ยังใช้ไม่ได้ ก็ต้องใช้ TURN ซึ่งเป็นรีเลย์
ปัญหาเดียวกันนี้มีแนวโน้มสูงว่าจะเกิดกับเทคนิค ping 3.3.3.3 ด้วย หาก NAT เขียนตัวระบุของ ping ใหม่เหมือนในบทความ เทคนิคนี้ก็จะพัง
เมื่อได้รับคำตอบ เราเตอร์จะใช้ค่า ID ที่ไม่ซ้ำกันนั้นเพื่อส่งต่อคำตอบไปยังอุปกรณ์ที่ถูกต้องในเครือข่ายภายใน
ตรวจสอบได้ด้วยคอมพิวเตอร์เครื่องเดียวกับ Wireshark/tcpdump
ตัวบทความเองดี และสำหรับคนที่ไม่เคยเข้าใจเรื่อง networking มาก่อนเลย อาจทำให้ตาสว่างได้ แต่โดยแก่นแล้วดูเหมือนเป็นวิธีสร้างแล็บเครือข่ายที่ถูกต้องแล้วขุดลงไปในซอร์สมากกว่าการคิดเอาเองโดยตรง
ตัวอย่างแบบที่ลองทำตามด้วยมือได้แบบนี้น่าขอบคุณจริง ๆ และตั้งใจว่าจะลองทำตามเอง
บทความอื่นแทบจะเรื่องเดียวที่ผมเข้าใจหัวข้อนี้ได้ดีคือบทความนี้ของ Tailscale เพราะมี “ตัวอย่างที่อธิบายละเอียด” เยอะ ทำให้เห็นชัดว่าทั้งหมดประกอบเข้าด้วยกันอย่างไร
https://tailscale.com/blog/how-nat-traversal-works/
บังเอิญว่าช่วงสุดสัปดาห์นี้ผมเพิ่งปล้ำกับ Netfilter เพื่อเปิด transparent proxy บนเราเตอร์ OpenWRT
แหล่งอ้างอิงพื้นฐานเวลาอ่าน Netfilter คือ https://wiki.nftables.org/wiki-nftables/index.php/Main_Page และ https://www.netfilter.org/projects/nftables/manpage.html
แต่ ICMP echo request มี ID และโดยพฤตินัยแล้วทำหน้าที่เหมือนหมายเลขพอร์ตต้นทาง
หากต้องการ NAT สำหรับ ICMP echo ให้ถูกต้อง ก็ต้อง remap ID ทั้งสองทิศทางเหมือนกับการ remap พอร์ตต้นทางของ UDP
เพราะถ้าเครื่องหลัง NAT ได้รับ ping จากโฮสต์สองตัวพร้อมกัน และโฮสต์สองตัวนั้นบังเอิญใช้หมายเลขคำขอเดียวกัน ก็จะกำกวม
ความเป็นไปได้อีกอย่างคือไม่เขียน identifier ใหม่ แต่เก็บรายการเครื่องระยะไกลที่เชื่อมกับแต่ละ ID ไว้ ถ้า ID ชนกัน รายการก็จะมีที่อยู่ IP ระยะไกลมากกว่าหนึ่งรายการ และเมื่อมีคำตอบจากเครื่องหลัง NAT มา NAT ก็เลือกหนึ่งรายการจากลิสต์ ส่งคำตอบไปยังเครื่องนั้น แล้วลบรายการออกก็ได้
ถ้าเป็น IPv6 คงต้องเปลี่ยนทุกโหนดในเครือข่ายและอัปเดต DNS ภายในด้วย
ในทางทฤษฎี ผมสามารถมี /48 ของตัวเองที่พกพาไปได้ แต่ ISP ใหม่ต้องโฆษณาให้ และแม้ ISP ปัจจุบันจะทำให้ได้ แต่นั่นก็ไม่ใช่เรื่องทั่วไป
เมื่อสัปดาห์ก่อนสายโทรศัพท์ขาด ผมก็หยิบ 5G MiFi ออกมาแล้วย้ายการเชื่อมต่อ WAN ไปทางนั้น จากนั้นแค่ทำ masquerade ง่าย ๆ บนอินเทอร์เฟซนั้นก็พอ สัญญาณอ่อนเลยไม่ดีนัก แต่ก็ใช้งานได้
ปัญหาคือแม้จะใช้ IPv6 ก็ยังต้องรัน dual stack หรือใช้ abstraction ของ NAT ที่สกปรกอยู่ดี สำหรับผมมีแต่งานเพิ่มโดยไม่ได้ประโยชน์
ฝั่งงานก็เหมือนกัน รถที่ใช้ซับเน็ตภายใน 172.16/12 เชื่อมต่อและ route หากัน และเชื่อมออกภายนอกผ่าน VPN หลากหลายแบบ โครงสร้างคือจอดใต้ดินบ่อยจนแทบไม่มีสัญญาณ จึงหวังว่าอย่างน้อยหนึ่งในหลายวิธีจะใช้งานได้
ถ้าย้ายไป IPv6 ก็ต้องย้าย /48 เหล่านั้นอีกรอบ แถมรถพวกนี้ยังรับอินเทอร์เน็ตจากสนามกีฬาหลายแห่ง ซึ่งหลายที่แค่ปิด MITM/443 หรือปลดการบล็อก UDP ก็ยังทำได้ยาก ในสภาพแวดล้อมที่ไปถึงวันเสาร์ 10 โมงเช้า แล้วต้องใช้งานได้ในอีก 2 ชั่วโมง วิธีนี้ใช้ไม่ได้
ไม่เห็นว่าประโยชน์ทางธุรกิจของการย้ายไปใช้ dual stack เพื่อเพิ่มงานและความเสี่ยงเป็นสองเท่าคืออะไร
https://en.wikipedia.org/wiki/Lindy_effect
https://stackoverflow.com/questions/31857419/how-to-send-a-m...
น่าเสียดายที่ ping ถูกจัดการโดยระบบปฏิบัติการ ดังนั้นแอปบน IP ของเพียร์จึงอ่านข้อความไม่ได้
อาจถึงเวลาแล้วหรือเปล่าที่จะมี hook ใน user space ให้บริการบางอย่างพวกนี้ เพื่อให้ P2P จริง ๆ ที่ทั้งสองฝั่งอยู่หลัง NAT เป็นไปได้ อย่างน้อยก็เป็นสตรีมเหตุการณ์แบบอ่านอย่างเดียวก็ยังดี ตอนนี้กำแพงที่ขวางอยู่ทั้งหมดดูเหมือนเป็นสิ่งที่มนุษย์สร้างขึ้นล้วน ๆ
อย่างไรก็ตาม เคยเห็นกลยุทธ์การรั่วไหลของข้อมูลที่ใช้ ping หรือรูปแบบการสื่อสารอื่น ๆ อยู่บ้าง ทุกวันนี้ firewall ส่วนใหญ่มักตั้งค่าเริ่มต้นให้ทิ้ง ICMP ทั้งหมด รวมถึง ping แบบเงียบ ๆ ดังนั้นสำหรับการใช้แบบ P2P คงแทบเป็นไปไม่ได้
idดูเหมือนเทียบได้กับ(sport, dport)โดยพฤตินัย แต่เป็น 16 บิต จึงมีพื้นที่น้อยกว่า 32 บิตมากแต่คิดว่าปัญหาหลักของ NAT hole punching คือ ถ้าจะสร้างการเชื่อมต่อ ทั้งสองปลายทางต้องมีกิจกรรมด้วยไม่ใช่หรือ ดังนั้นจึงต้องมี เซิร์ฟเวอร์ประสานงาน เสมอ เพื่อบอก T ว่าโหนด S ต้องการคุยกับโหนด T
ถึงอย่างนั้นก็ทำให้มีเรื่องให้คิดต่อ สงสัยว่าจะมีวิธีทำด้วยข้อความ routing ของ ICMP เช่น unreachable หรือ TTL expired ได้ไหม ถ้า traceroute ไปยัง IP ใด IP หนึ่ง ก็จะได้รับแพ็กเก็ตตอบกลับจาก IP อื่น ๆ ตามอำเภอใจ และโดยทั่วไปสิ่งนี้ผ่าน NAT ได้
ลองจินตนาการว่าโฮสต์ T ที่อยากรับการเชื่อมต่อขาเข้า เลือกที่อยู่ IP “dummy” แบบสุ่ม แล้วเผยแพร่
(IP ของเราเตอร์, IP dummy)เป็นตัวระบุของตัวเอง จากนั้นส่งแพ็กเก็ตไปยัง IP dummy นั้นเป็นระยะ ๆ โฮสต์ S ที่ต้องการคุยกับ T ก็สามารถส่ง ICMP TTL-expired เกี่ยวกับที่อยู่ dummy นั้นไปยังเราเตอร์ของ T และเราเตอร์อาจเห็นแล้วส่งต่อไปยัง T ได้แน่นอนว่าขึ้นอยู่กับว่าที่อยู่ IP ภายในฟิลด์ ICMP ถูก ingress filtering เหมือนที่อยู่ใน IP header หรือไม่
แก้ไข: มีคอมเมนต์ระดับบนสุดที่ชี้ไปยังการใช้งานจริงของไอเดียนี้แล้ว
[1] - https://github.com/yarrick/pingfs
ถ้าเป็น GitHub ก็ผูกกับชุดของ commit hash, ชื่อไฟล์ และเลขบรรทัดที่เฉพาะเจาะจงได้ แต่ถ้า codebase เปลี่ยนไปมาก มันก็ไม่ค่อยมีประโยชน์นัก ส่วน git webview ที่ใช้น้อยกว่าอย่าง git.blender.org ก็ทำได้ไม่ดี
https://elixir.bootlin.com/linux/latest/source