Foundationপ্রথম নীতি থেকে
LEVEL 7লেসন ৮/২২কঠিন৪৫ মিনিট

NAT — address শেষ হয়ে যাওয়ার hack যা স্থায়ী হয়ে গেল

Network Address Translation

1994-এর এক temporary patch — public IP না থাকা device-গুলোকে ইন্টারনেটে ছুড়ে দেওয়ার hack — যেভাবে পুরো ইন্টারনেটের default হয়ে বসে গেল, আর end-to-end connectivity কীভাবে hostage হলো।

এই লেসন শেষে আপনি পারবেন

  • NAPT translation-table-এর entry-anatomy বর্ণনা করতে পারবেন — কোন packet-এ কোন field বদলায়, entry কখন জন্মায়, মেলে ও মারা যায়
  • End-to-end principle কী ছিল আর NAT ঠিক তার কোন অংশটা ভাঙল — inbound-initiation-এর সংকটসহ ব্যাখ্যা করতে পারবেন
  • Port forwarding, STUN, hole-punching, TURN — traversal-এর সিঁড়িটা আঁকতে পারবেন, প্রতিটা ধাপ কখন যথেষ্ট আর কখন ব্যর্থ
  • CGNAT double-NAT-এর ব্যবহারিক যন্ত্রণা — port exhaustion, geo-IP ভুল, shared-ban — উদাহরণসহ বলতে পারবেন
  • Accidental-firewall ফ্রেমিংটা দুই দিক থেকে মূল্যায়ন করতে পারবেন — NAT কী আটকায়, কী ভাঙে, আর stateful firewall থেকে কোথায় আলাদা

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

IPv4-লেসনে দেখেছিলাম: পৃথিবীতে মাত্র ~৪.৩ বিলিয়ন public ঠিকানা, আর device তার কয়েক গুণ। CIDR অপচয় কমালো, IPv6 আসছে — কিন্তু transition বছরের পর বছর লাগবে। মাঝের এই দশকগুলোতে লাখো office, শত-কোটি home internet-এ ঢুকবে — ঠিকানা নিয়ে কী?

1994-এ একটা answer এলো, RFC 1631: যদি একটা organization-এর কাছে একটাই public IP থাকে, তার পেছনের শত-shত device নিজেদের private ঠিকানা (RFC1918) নিক, আর border-এ এক box বসে packet-গুলোর ঠিকানা মুহূর্তে-মুহূর্তে রিলেবল করে দিক। নাম Network Address Translation। ডিজাইনাররা ভেবেছিলেন এটা সেতু-কাঠামো — IPv6 এলেই ভাঙা হবে।

সেতুটা আজও দাঁড়িয়ে আছে। আপনার বাড়ির Wi-Fi-এর প্রতিটা packet আজ NAT ঘুরে যাচ্ছে, আপনার phone-এর mobile-data CGNAT ঘুরে যাচ্ছে, হয়তো আপনার office-ও। এই লেসনে দেখব যন্ত্রটা ভেতর থেকে কীভাবে কাজ করে, ইন্টারনেটের মূল design-principle-কে কীভাবে জিম্মি করে রেখেছে, আর developers-রা তার চারপাশে কীভাবে tunnel খোঁড়ে।

মূল ধারণা

NAPT — table-এ একটা চৌকো ব্র্যাকেট

আধুনিক NAT আসলে NAPT (Network Address Port Translation) — ঠিকানা ছাড়া port-ও ব্যবহার করে। Router-এর ভেতরে একটা table, প্রতিটা entry এক connection:

(proto, lan-ip:lan-port)  ↔  (wan-ip:wan-port)   +   (remote-ip:remote-port)

চলুন concrete trace করি। Laptop 192.168.1.20 Google-DNS-এ UDP query পাঠাল — router-এর WAN-side public ঠিকানা 203.0.113.7:

── outbound ──────────────────────────────────────────────
packet ঢোকে:   src 192.168.1.20:52344  →  dst 8.8.8.8:53
table-check:   entry নেই → নতুন বানাও, খালি wan-port নাও (40000)
packet বেরোয়: src 203.0.113.7:40000   →  dst 8.8.8.8:53
               └── বাইরের পৃথিবী শুধু এটাই দেখে!

── reply inbound ─────────────────────────────────────────
packet ঢোকে:   src 8.8.8.8:53         →  dst 203.0.113.7:40000
table-match:   (udp, 40000, 8.8.8.8:53) → 192.168.1.20:52344 ✓
packet বেরোয়: src 8.8.8.8:53          →  dst 192.168.1.20:52344

তিনটা সূক্ষ্ম নিয়ম লক্ষ্য করুন:

  1. Entry outbound-এ জন্মায় — বাইরে-থেকে-ঢোকা packet-এ নয়। এই asymmetry-ই পরের পুরো গল্প।
  2. Match হয় পূর্ণ tuple-এ: port-এর সাথে remote-address পর্যন্ত মিলতে হয় (port-restricted behavior) — ভুল sender-এর reply ঢোকে না।
  3. Timeout: UDP-entry ~৩০–৬০ সেকেন্ড নীরব থাকলে মুছে যায়; TCP-established ঘণ্টার হারে। Table নইলে ফুলে memory খাবে।

End-to-end principle-এর ভাঙন

Internet-এর মূল নকশা-দর্শন (Saltzer–Reed): বুদ্ধি network-এর edge-এ, core শুধু blind-forward করে — তাই যেকোনো দুই endpoint নিজেদের মধ্যে সরাসরি, অর্থপূর্ণ connection দিতে পারে। Server মানে তাই: আমার ঠিকানা public, যে-কেউ যেকোনো সময় প্রথম packet পাঠাতে পারে।

NAT এই সমীকরণের দ্বিতীয় অর্ধটা মুছে দিল। NAT-এর পেছনের device- এর কোনো stable public identity-ই নেই — বাইরের কেউ প্রথম packet পাঠাতেই পারে না, কারণ table-এ কোনো entry নেই, আর কোন wan-port তার নামে বরাদ্দ হবে জানার উপায় নেই। ফল:

  • Self-hosting (নিজের machine-এ server) বাড়তি config ছাড়া অসম্ভব
  • Peer-to-peer app (game, video-call, file-share) দুই NAT-এর পেছনে আটকে যায়
  • Protocol যারা payload-এর ভেতরে address/port লিখে (FTP, SIP), তারা NAT-এর চোখে অর্থহীন সংখ্যা পাঠায় — ভাঙে

Port forwarding — manual দরজা

Inbound-সংকটের classic সমাধান: router-এ static rule — “WAN- এ 203.0.113.7:2222-এ যা-ই আসুক, translate করে 192.168.1.50:22-এ ফেলে দাও” (DNAT)। Entry এখন permanent, দুই-দিকে চলে। এই এক rule-ই self-hosted SSH/web/game-server-এর জীবনরেখা — আর ঠিক এতটুকু pinhole খুললেই attack-surface-ও সেখানেই: service-টা patched রাখা তখন আপনার দায়।

Traversal — NAT-চারপাশে খোঁড়া কৌশলের সিঁড়ি

P2P-app-দের জন্য manual-config user-এর কাছে অগ্রহণযোগ্য — তাই automated সিঁড়ি:

STUN (Session Traversal Utilities for NAT): peer এক public STUN- server-কে জিজ্ঞেস করে “তুমি আমাকে যে ঠিকানা-port দেখছে, কী?” — server উত্তর দেয় reflexive mapping (203.0.113.7:40000)। এবার peer জানে বাইরের পৃথিবী তাকে কোথায় পাবে — সেই address signaling (অন্য channel-এ) partner-কে জানিয়ে দেয়।

Hole-punching: দুই NAT-এর পেছনের A আর B একসাথে, প্রায়-সমান্তরালে একে-অপরের reflexive-address-এ packet পাঠালে — A-র outbound packet A-র NAT-এ B-এর দিকে entry খুলল, B-এর packet ঠিক সেই entry-তে ঢুকে গেল (উল্টোটাও)। দুই দিকের প্রথম packet হারাতে পারে, দ্বিতীয়গুলো সরাসরি P2P-তে চলে। শর্ত: NAT-গুলো predictable behavior-এর (symmetric না) — hood-section-এ classification।

TURN (Traversal Using Relays around NAT): punching ব্যর্থ (symmetric-NAT, strict filtering) হলে শেষ ভরসা — public-relay server দুই peer-এর সব traffic নিজের পেটে বহন করে। কাজ করে সবসময়, কিন্তু bandwidth-cost provider-এর, latency বাড়ে, privacy relay-র হাতে। WebRTC-র ICE-framework ঠিক এই সিঁড়িটা স্বয়ংক্রিয়ভাবে বেয়ে যায় — STUN আগে, punch মাঝে, TURN শেষে।

UPnP — স্বয়ংক্রিয় দরজার দুই মুখ

UPnP IGD protocol app-কে router-কে নিজে থেকে port-forward চাইতে দেয় — game-console, torrent-client, NAS এভাবেই কাজ করে user- config ছাড়া। সুবিধা বাস্তব; ঝুঁকিও: আপনার machine-এ চলা যেকোনো process (malware-সহ) নীরবে internet-facing দরজা খুলে নিতে পারে। Security-conscious network UPnP disable রাখে, explicit forwarding পছন্দ করে। Balance-টা প্রতিটা network-admin-এর conscious decision।

CGNAT — এক ঠিকানায় এক পাড়া

Carrier-grade NAT হলো NAT-এর NAT: operator-রা হাজারো subscriber- কে ভেতরে 100.64/10-space-এ বসিয়ে, বাইরে এক-handful public IP-তে translate করে। আপনার দৈনন্দিন যন্ত্রণাগুলোর উৎস এখানেই:

  • Port-exhaustion: এক public IP-তে ~৬৪K port; subscriber-প্রতি limit রাখা হয় — ভারী-usage সময়ে নতুন connection ব্যর্থ
  • Geo-IP ভুল: আপনার public IP শত-সহোদরের — geo-database আপনাকে অন্য শহর/দেশে বসিয়ে দেয়; streaming-region, CAPTCHA-ঝামেলার উৎস
  • Shared-ban: এক abuser-এর জন্য পুরো IP ban মানে পাড়া-জোড়া innocent-ও বাইরে
  • Inbound zero: port-forward তো দূরের কথা — আপনার control-ই নেই

ভেতরে কী ঘটছে

Conntrack — kernel-এর স্মৃতি

Linux-এ NAT-র হৃৎপিণ্ড kernel-এর conntrack (connection tracking) subsystem — netfilter-র এই module প্রতিটা packet- flow-কে entry হিসেবে মনে রাখে। /proc/net/nf_conntrack-এ একটা UDP-entry এরকম দেখায়:

udp      17 src=192.168.1.20 dst=8.8.8.8 sport=52344 dport=53
         src=8.8.8.8 dst=203.0.113.7 sport=53 dport=40000
         [ASSURED] mark=0 use=1

প্রথম লাইন ভেতরের-দৃষ্টিতে flow, দ্বিতীয় লাইন বাইরের-দৃষ্টিতে — দুটোর মাঝেই translation-এর প্রমাণ। [ASSURED] মানে দুই দিকেই traffic হয়েছে, timeout বড় হয়ে গেছে। Timeout-tuning sysctls-এ: nf_conntrack_udp_timeout, _stream, _tcp_timeout_established।

Router-firmware-রা একই concept বিক্রি করে “stateful firewall” নামে — সত্যি বলতে NAT-table-ই তাদের state-machine। এক system call পর্যন্ত দূরের ব্যাপার না — আপনার app-এর প্রতিটা socket-connection এই table-এ প্রতিধ্বনিত হয়।

Masquerade বনাম SNAT/DNAT

iptables-ভাষায় দুই mode: MASQUERADE — exit-interface-এর বর্তমান IP dynamically নেয় (home-router-র প্রিয়, DHCP-তে WAN-IP বদলালেও rule টেকে), SNAT — fixed public-IP লেখা (ISP-line, predictable); উল্টো দিক DNAT — inbound-rewrite (port-forwarding-র engine)।

Behavior-classification — কেন কোনো punching কাজ করে না

RFC 4787 NAT-দের দুই axis-এ ভাগ করে:

Mapping/FilteringআচরণPunching-fate
Endpoint-independentযে-কারো কাছে একই wan-portসহজ — STUN-এই যথেষ্ট
Address-restrictedনতুন destination-এ নতুন port, কিন্তু reply-তে ঢুকতে শুধু ঠিকানা মিলsimultaneous-punch works
Port-restricted+ port-ও মিলতে হবেpunch works, timing কঠোর
Symmetricপ্রতিটা destination-এ আলাদা mappingpunch fails — TURN-ই রাস্তা

আমাদের example-trace-এর NAT port-restricted — home-router-র typical setting। Symmetric NAT সবচেয়ে কঠোর: STUN-এ যে reflexive-mapping জানলে, সেটা third-party-র কাছে ভিন্ন — prediction-ই অসম্ভব।

Hairpin আর ALG — দুই বাস্তব-কুৎসিত

Hairpin NAT: ভেতরের device বাইরের public-address দিয়ে ভেতরের আরেক device-কে ডাকলে — প্রথম-দিনের NAT-রা এটা সামলাত না (connection বাইরে গিয়ে ফেরে, entry mismatch)। Modern firmware hairpin support করে, পুরনো hardware না-ও করতে পারে — “LAN-থেকে domain দিয়ে self-server ঢোকা যায় না” ticket-এর আসল কারণ।

ALG (Application Layer Gateway): FTP/SIP-জাত payload-embedded- address protocol-এর জন্য কিছু router payload-ও rewrite করে বসে। সাহায্য মনে হলেও আজ বেশিরভাগ expert advice: ALG off — buggy implementations VoIP/SIP-ভাঙার প্রধান উৎস; modern protocols TLS ব্যবহার করে, payload পড়াই যায় না।

IPv6-এর শেষ-হিসাব

IPv6-তে প্রতিটা device global ঠিকানা পায় — NAT-র মূল অজুহাতই মরে। NAT66/NPTv6-জাতিঃ translation v6-তে discouraged: header-rewrite ছাড়া security-র আসল চাহিদা stateful firewall-এ মেটে, address বদলানো ছাড়া। NAT-যুগের শিক্ষাই সবচেয়ে বড় যুক্তি: hack-টা যা permanent হলো, তার খরচ আমরা তখনই বুঝি যখন P2P-feature প্রতিটা product-roadmap-এ “NAT traversal” line-item হয়ে বসে।

উদাহরণ

এক NAT-box-এর সন্ধ্যা — পূর্ণ transcript

Home-network: laptops A (192.168.1.20) আর B (192.168.1.21), router WAN 203.0.113.7, DNS 8.8.8.8।

t=0   A→dns  udp 192.168.1.20:52344 → 8.8.8.8:53
      NAT: নতুন entry, wan-port 40000 বরাদ্দ
      wire:  203.0.113.7:40000 → 8.8.8.8:53

t=0.02 dns→A  udp 8.8.8.8:53 → 203.0.113.7:40000
      NAT: tuple-match ✓ (src 8.8.8.8, port 40000)
      LAN:   8.8.8.8:53 → 192.168.1.20:52344

t=1   B→dns  udp 192.168.1.21:52344 → 8.8.8.8:53
      NAT: collision! (40000 busy, remote-tuple same)
           → নতুন wan-port 40001
      wire:  203.0.113.7:40001 → 8.8.8.8:53
      ← দুই laptop একই internal-port নিলেও বাইরে distinct!

t=2   attacker→router  udp 6.6.6.6:9999 → 203.0.113.7:40000
      NAT: entry-40000 চায় remote=(8.8.8.8,53) — mismatch!
           DROP (এটাই port-restricted filtering)

t=30  idle-check: A-র entry ৩০ সেকেন্ড নীরব → EXPIRE, 40000 মুক্ত

t=35  B-র ssh-server-এর জন্য pre-set rule:
      DNAT  203.0.113.7:2222 → 192.168.1.50:22
      inbound  client:49152 → 203.0.113.7:2222
      → static-rule hit, LAN: client:49152 → 192.168.1.50:22 ✓
      (outbound-entry লাগেইনি — rule নিজেই দরজা)

পাঁচটা দৃশ্যে পুরো theory: creation-on-outbound, tuple-match, collision-resolution, restricted-filtering, expiry, আর static-DNAT। Build-section-এ এই transcript-টাই code হবে।

ছবিতে

Packet-এর দুই-মুখোশ যাত্রা

LAN side                    NAT box                     Internet
────────                    ───────                     ────────
A 192.168.1.20:52344 ──►  [entry তৈরি: 40000]  ──►  203.0.113.7:40000
                                                        │
                                              (remote দেখে শুধু এটা)
                                                        ▼
A 192.168.1.20:52344 ◄──  [tuple-match ✓]      ◄──  8.8.8.8:53 → :40000

ভুল sender 6.6.6.6:9999 → :40000   ✗ DROP (remote-tuple mismatch)

t+30s: entry নীরব → EXPIRE → wan-port pool-এ ফেরত
Laptop-এর প্রতিটা packet NAT-এ দুইবার পোশাক বদলায় — ভেতরে private-tuple, wire-এ public-tuple; ফেরার পথে উল্টো ক্রম। Table-ই স্মৃতি, timeout-ই মৃত্যু।

Traversal-সিঁড়ি

NAT-এর পেছন থেকে P2P — খরচ-ক্রমে কৌশল
  1. ১. STUN discoveryreflexive ip:port জানা — প্রায় free
  2. ২. Simultaneous hole-punchpredictable NAT-এ direct P2P — এক RTT-র খরচ
  3. ৩. TURN relaysymmetric-NAT-এর শেষ রাস্তা — সব bandwidth relay-র পেট দিয়ে
  4. ৪. Manual port-forwarduser-config — automated নয়, কিন্তু সবচেয়ে deterministic

নিজে চালিয়ে দেখুন

EXPERIMENT

দুই ঠিকানার প্রমাণ — router আপনাকে কী নামে চেনে?

Linux / macOS (ধাপ ২ Linux-only)· ১৫ মিনিট

ধাপ ১ — দুই দৃষ্টিতে দুই ঠিকানা:

curl -s https://api.ipify.org; echo     # বিশ্ব আপনাকে যে IP-তে দেখে
ip -4 addr show | grep 'inet '          # (macOS: ifconfig en0 | grep 'inet ')
                                        # LAN-এ আপনি যে IP বিশ্বাস করেন

দুই output মিলবে না — প্রথমটা router/CGNAT-এর public, দ্বিতীয়টা RFC1918-private। ipify-র কাছে আপনার request এমনিতেই translated পৌঁছেছে — এই এক curl-ই NAT-এর অস্তিত্বের প্রমাণ। দুই device (phone-hotspot + Wi-Fi) থেকে চালালে দুই public-IP দেখবেন — বা CGNAT হলে একই public দেখতে পারেন: shared-ঠিকানার চিরচেনা মুখ।

ধাপ ২ (Linux, sudo-optional) — conntrack-এর ledger পড়া:

এক terminal-এ চলুক:

dig @8.8.8.8 example.com       # বা nslookup example.com 8.8.8.8

আরেক terminal-এ তাৎক্ষণিক:

sudo cat /proc/net/nf_conntrack | grep 'dport=53' | tail -3
# nf_conntrack module না লোড থাকলে: sudo modprobe nf_conntrack

দেখবেন আপনার LAN-ip → 8.8.8.8 flow-র দুই-মুখী entry — ঠিক আজকের theory-table-এর জীবন্ত রূপ। এবার traffic বন্ধ করে ৩০+ সেকেন্ড অপেক্ষা করে আবার grep করুন: UDP-entry নিখোঁজ — timeout-expiry নিজের চোখে। (macOS-এ local pf-state pfctl -ss দেখায়, কিন্তু NAT-table router-এর — সেখানে পৌঁছানো যায় না; তাই এই ধাপ Linux- specific।)

ধাপ ৩ (read-only) — নিজের router-এর দরজাগুলো:

Router admin-page-এ (সাধারণত 192.168.1.1) Port Forwarding/Virtual Server section-টা শুধু দেখুন — কোন rule আছে, কোনটা UPnP-নির্মিত। অজানা rule পেলে সেটাই আজকের সবচেয়ে দামি discovery: কে কোন দরজা খুলেছে, আপনি জানতেন কি?

এটা কী প্রমাণ করে

আপনার machine যে ঠিকানায় বিশ্বাস করে আর বিশ্ব যে ঠিকানায় আপনাকে দেখে — দুটো আলাদা; এই ফাঁকটাই NAT-এর translation-zone, আর kernel-এর conntrack-table সেই translation-এর live ledger।

নিজে বানান

BUILD IT

NAT simulator — outbound-create, inbound-match, timeout-expiry

Python · ●●●○○
  1. Entry-dataclass: proto, lan-tuple, wan-port, remote-tuple, last-seen
  2. outbound(): বিদ্যমান entry reuse, নইলে নতুন wan-port allocate
  3. inbound(): strict remote-tuple match — mismatch হলে DROP log
  4. tick(): নীরবতা timeout-পার হলে entry expire, port pool-এ ফেরত
  5. Static DNAT-rule যোগ করুন — port-forwarding-র প্রতিরূপ
  6. Scripted event-log চালিয়ে table-evolution print করুন

লক্ষ্য: example-section-এর পাঁচ-দৃশ্য transcript-টা executable করা — প্রতিটা event-এর পরে table-এর অবস্থা ছাপা।

from dataclasses import dataclass

UDP_TIMEOUT = 30.0

@dataclass
class Entry:
    proto: str; lan_ip: str; lan_port: int
    wan_port: int; r_ip: str; r_port: int
    last: float

class NatBox:
    def __init__(self, wan_ip, port_base=40000):
        self.wan, self.next_port = wan_ip, port_base
        self.out_tbl = {}    # (proto, lan_ip, lan_port, rip, rport) → Entry
        self.dnat = {}       # (proto, wan_port) → (lan_ip, lan_port)

    def alloc(self):
        p = self.next_port; self.next_port += 1; return p

    def outbound(self, t, proto, lip, lp, rip, rp):
        key = (proto, lip, lp, rip, rp)
        e = self.out_tbl.get(key)
        if e is None:
            e = Entry(proto, lip, lp, self.alloc(), rip, rp, t)
            self.out_tbl[key] = e
            tag = 'CREATE'
        else:
            e.last = t; tag = 'REUSE '
        print(f't={t:<5} OUT {tag} {lip}:{lp} → {self.wan}:{e.wan_port}'
              f' ({rip}:{rp})')
        return self.wan, e.wan_port

    def inbound(self, t, proto, sip, sp, dip, dp):
        rule = self.dnat.get((proto, dp))            # static আগে
        if rule is not None:
            print(f't={t:<5} IN  DNAT-RULE {sip}:{sp} → {rule[0]}:{rule[1]}')
            return rule
        for k, e in self.out_tbl.items():
            if e.proto == proto and e.wan_port == dp \
               and (e.r_ip, e.r_port) == (sip, sp):
                e.last = t
                print(f't={t:<5} IN  MATCH {sip}:{sp} → {e.lan_ip}:{e.lan_port}')
                return (e.lan_ip, e.lan_port)
        print(f't={t:<5} IN  DROP  {sip}:{sp} → {dip}:{dp}'
              f' (tuple-mismatch/no-entry)')
        return None

    def tick(self, t):
        dead = [k for k, e in self.out_tbl.items() if t - e.last > UDP_TIMEOUT]
        for k in dead:
            e = self.out_tbl.pop(k)
            print(f't={t:<5} EXPIRE port {e.wan_port} মুক্ত '
                  f'({e.lan_ip}:{e.lan_port}, idle {t - e.last:.0f}s)')

nat = NatBox('203.0.113.7')
nat.dnat[('tcp', 2222)] = ('192.168.1.50', 22)   # admin-লিখিত rule

nat.outbound(0,   'udp', '192.168.1.20', 52344, '8.8.8.8', 53)
nat.inbound (0.02,'udp', '8.8.8.8', 53, '203.0.113.7', 40000)
nat.outbound(1,   'udp', '192.168.1.21', 52344, '8.8.8.8', 53)
nat.inbound (2,   'udp', '6.6.6.6', 9999, '203.0.113.7', 40000)
nat.tick(31)                                       # A-র entry নীরব 31s
nat.outbound(32,  'udp', '192.168.1.20', 52344, '8.8.8.8', 53)
nat.inbound(33,   'tcp', '198.51.100.9', 49152, '203.0.113.7', 2222)

প্রত্যাশিত output:

t=0     OUT CREATE 192.168.1.20:52344 → 203.0.113.7:40000 (8.8.8.8:53)
t=0.02  IN  MATCH 8.8.8.8:53 → 192.168.1.20:52344
t=1     OUT CREATE 192.168.1.21:52344 → 203.0.113.7:40001 (8.8.8.8:53)
t=2     IN  DROP  6.6.6.6:9999 → 203.0.113.7:40000 (tuple-mismatch/no-entry)
t=31    EXPIRE port 40000 মুক্ত (192.168.1.20:52344, idle 31s)
t=32    OUT CREATE 192.168.1.20:52344 → 203.0.113.7:40002 (8.8.8.8:53)
t=33    IN  DNAT-RULE 198.51.100.9:49152 → 192.168.1.50:22

তিনটা মূল শিক্ষা output-এ স্পষ্ট: একই lan-port দুই host-এ নিলেও wire-এ distinct (40000/40001), ভুল-sender-এর packet DROP হয় (port-restricted), আর expired-port-এর পরে একই flow নতুন port (40002)-তে ফেরে — পুরনো reflexive-address তখন মৃত। DNAT-rule outbound-entry ছাড়াই দরজা খোলে।

নিজে বাড়ান:

  1. Per-flow timeout আলাদা করুন — TCP-ESTABLISHED 5 ঘণ্টা vs UDP 30s
  2. Hairpin-mode যোগ করুন — ভেতরের host public-address ডাকলে translate-loop সঠিকভাবে ফেরায়
  3. Mapping-behavior flag (independent/symmetric) যোগ করুন — symmetric mode-এ প্রতি remote-এ নতুন port, punching-test fail দেখান

বাস্তব সিস্টেমে

বাস্তব জগতে NAT আজ

WebRTC-র hidden bill। প্রতিটা browser video-call ICE-framework চালায়: STUN-candidates exchange, punching attempt, ব্যর্থ হলে TURN relay। Relay-traffic provider-এর bandwidth-বিল — তাই production WebRTC-team-দের প্রথম cost-dashboard-ই TURN-usage। আপনার call মাঝেমধ্যে quality নামল — সম্ভবত symmetric-CGNAT-এ দুই peer পড়ে relay-তে গেল।

Game-console-এর NAT-Type। Console-রা দেখায় NAT Type A/B/C বা Open/Moderate/Strict — আসলে আমাদের classification-table: open = endpoint-independent, strict = port-restricted/symmetric। Strict-এ multiplayer voice/matchmaking ভাঙে; forum-fix (“DMZ-তে ছুড়ে দাও”) আসলে console-কে all-inbound-open করা — কাজ করে, দামও তার।

Docker-এর -p flag। docker run -p 8080:80 আসলে host-kernel- এ DNAT-rule লেখে — container-এর 80 পেতে host-এর 8080-এ ঢুকুন। আপনি NAT শিখলে Docker networking-এর half-documentation আর দরকার হয় না — এটা সেই এক concept-এর নতুন পোশাক।

Cloud-এর NAT-gateway। Private-subnet-এর VM-রা internet বের হয় managed NAT-gateway দিয়ে — এবং billing হয় per-GB processed: NAT আজ product। Architecture-review-এ প্রশ্ন “কেন private-subnet?” উত্তর যদি হয় security, মনে রাখুন — সেটা stateful-firewall-ও দেয়; NAT-র অতিরিক্ত দাম আসলে address-hiding-এর।

CGNAT-যুগের support-ticket। “আমার site বাইরে থেকে খোলে না”, “CCTV app remote-view কাজ করছে না”, “torrent connect হচ্ছে না” — mobile-ISP-র পেছনে হলে উত্তর প্রায় একটাই: আপনার কাছে inbound-পথ নেই। VPN/tunnel-service-রা এই ঠিক এই ফাঁকে business করে।

যে ভুলগুলো সবাই করে

“NAT একটা security-feature — এটা আছে বলেই আমি protected।”

NAT-এর protection accidental — by-product মাত্র। Unsolicited-inbound আটকানোর আসল যন্ত্র stateful firewall, যেটা NAT ছাড়াও থাকে (IPv6- network-এ থাকেই) আর NAT থাকলেও logging/policy-control দেয় না। “NAT = security” ভেবে firewall-policy উপেক্ষা করা হলে UPnP বা একটা port-forward-ই সেই নিরাপত্তার পুরো ভিত গুঁড়িয়ে দেয়।

“Port forwarding মানে পুরো machine/network বিশ্বের সামনে।”

Forwarding খোলে ঠিক একটা tuple: এক WAN-port ↔ এক internal ip:port। Attacker পৌঁছায় শুধু ওই service-এ — না whole-machine, না network। বাস্তব ঝুঁকি scoped কিন্তু সত্যি: ওই এক service vulnerable হলে সেটাই gateway। DMZ-host option-টাই আসলে পুরো machine খোলা — ওটা ভিন্ন, বেশি বিপজ্জনক সিদ্ধান্ত।

“দুই device একই source-port ব্যবহার করলে conflict-এ একটা ভাঙবে।”

Internal port-এ collision NAT-র কাছে non-issue: translation per-flow — (lan-ip, lan-port, remote) প্রতিটার আলাদা external-port allocate হয়। আমাদের simulator-এই দেখেছেন: 52344 দুই laptop-এ, বাইরে 40000/40001। Conflict হয় শুধু external-side-এ — আর সেটা NAT নিজেই resolve করে।

“STUN দিলেই যেকোনো দুই peer সরাসরি যুক্ত হবে।”

STUN শুধু নিজের reflexive-address জানায় — দরজা খোলে না। Direct-P2P লাগে predictable mapping (non-symmetric) + সফল punch। Symmetric-NAT (CGNN-যুগে মোবাইল-নেটওয়ার্কে common) reflexive-value প্রতি destination- এ বদলায় — partner যা জানল তা অচল। তখন TURN-relay — যা কাজ করে সবসময়, খরচে।

“IPv6-এও একইভাবে NAT রাখা উচিত — অভ্যাস ভালো।”

IPv6-এর মূল design-goal-ই restored end-to-end: প্রতিটা device global ঠিকানায়, inbound-initiation firewall-policy দিয়ে govern হয়, address- rewrite-এ নয়। NAT66/NPTv6 spec-এই discouraged — breakage তো আছে, benefit নেই। যে security আপনি NAT থেকে চান, সেটা v6-এ default-deny firewall দেয় — স্বচ্ছভাবে, debug-যোগ্যভাবে।

বুঝেছেন কি না দেখুন

1

একটা NAPT-entry-তে কোন কোন তথ্য থাকে? Entry কোন দিকের packet-এ জন্মায়?

স্মরণ

Proto, lan-tuple (ip:port), allocated wan-port, remote-tuple (ip:port), last-seen/timeout। Entry জন্মায় outbound packet-এ — ভেতরের device বাইরে যেতে চাইলে; inbound-first packet-এ কখনোই না (এটাই accidental-firewall-এর মূল প্রক্রিয়া)।

2

Table-এ আছে entry (udp, 40000, remote=8.8.8.8:53)। Packet এলো: src 8.8.4.4:53 → dst 203.0.113.7:40000। কী হবে, কেন?

প্রয়োগ

DROP — port-restricted matching-এ remote-tuple পুরোটা মিলতে হয়; 8.8.4.4 ≠ 8.8.8.8। ঠিকানা মিললে হতো (address-restricted)। Filtering-strength বাড়ার সাথে punching-কঠিনতাও বাড়ে — এই trade-off RFC 4787-এর মূল বিষয়।

3

Laptop-A আর B — দুজনেই source-port 50000 নিয়ে একই server-এ connect করল। NAT-এ কী ঘটে, বাইরের পৃথিবী কী দেখে?

প্রয়োগ

দুটা আলাদা flow-entry — (A,50000,server) আর (B,50000,server); external-port দুটা আলাদা allocate (যেমন 40000, 40001)। বাইরের পৃথিবী দুই distinct (ip,port) দেখে — collision শুধু NAT-র ভেতরে সমাধান হয়ে যায়, কেউ ভাঙে না।

4

End-to-end principle কী দাবি করত? NAT ঠিক কোন single capability- টা কেড়ে নিল যে পুরো P2P-industry নতুন করে খুঁড়তে হলো?

যুক্তি

Principle: যেকোনো endpoint যেকোনো endpoint-কে প্রথম packet পাঠাতে পারবে — initiation universal। NAT কেড়ে নিল inbound reachability: পেছনের device-এর কোনো stable public identity-ই নেই। তাই STUN/punch/TURN — প্রতিটা কেবল ওই এক capability ফেরত কেনার খরচ।

5

Symmetric NAT-এ hole-punching কেন ব্যর্থ? TURN কীভাবে তবু সফল — তার দাম কী?

যুক্তি

Symmetric mapping-এ প্রতি destination-এ নতুন external-port — STUN-এ যে mapping শিখলে সেটা partner-এর কাছে ভিন্ন; prediction অসম্ভব, তাই punch-এর কোনো target-ঠিকানাই সঠিক নয়। TURN সমস্যাটা bypass করে: দুই peer public-relay-তে connect করে, relay মাঝে বহন করে — দাম: সব traffic relay-র bandwidth+latency, provider-cost, আর relay-কে trust।

6

Home-server (self-hosted blog) বাইরে থেকে accessible করবেন — safe rollout-এর ধাপগুলো সাজান। VPN-only alternative-এর সাথে তুলনা করুন।

ডিজাইন

Rollout: (১) static port-forward শুধু 443-এ, নির্দিষ্ট internal-host- এ; (২) service hardening — patched software, strong-TLS, rate-limit; (৩) host-firewall-এ allow-list; (৪) fail2ban/log-watch; (৫) UPnP-off নিশ্চিত। Public-access দরকার হলে এটাই সঠিক পথ। Alternative: wireguard-VPN-এ শুধু নিজেরা — attack-surface প্রায় শূন্য, কিন্তু public-reader-দের পৌঁছায় না। সিদ্ধান্ত audience-dependent: ব্লগের reader থাকলে forward+hardening; ব্যক্তিগত dashboard হলে VPN-only unambiguously superior।

এরপর কী

এরপর কী

Packet এখন ঠিকানা পায় (ipv4/ipv6), রাস্তা খোঁজে (routing/BGP), ঠিকানা-সংকটে বাঁচে (NAT) — কিন্তু সব কিছু ঠিক আছে কি না, কে জানবে? Network-এর ডাক্তার-যন্ত্র দুটো এখনো দেখিনি: ping আর traceroute। তাদের ইঞ্জিন ICMP — যে protocol কোনো user-data বহন করে না, শুধু network-এর নিজের কথা।

পরের লেসনে:

  • ICMP message-family — echo, time-exceeded, unreachable, redirect
  • ping-এর RTT-statistics আসলে কী মাপছে, কী মাপছে না
  • traceroute-র TTL-trick — প্রতিটা hop কীভাবে জোর-করে মুখ খোলায়
  • কেন কিছু router ICMP-কে অগ্রাধিকার-নিচু দেয় — আর সেটা মাপে কী ভুল করায়

আজকের conntrack-entry-রা পরের লেসনের tcpdump-output-এর পুরনো বন্ধু — দেখা হবে wire-এ।

আরও পড়ুন

  • RFC 3022 — Traditional IP Network Address Translator (Traditional NAT) — P. Srisuresh, K. Egevang, IETF · NAPT-এর ধ্রুপদী specification — translation-table model এখান থেকে
  • RFC 4787 — NAT Behavioral Requirements for Unicast UDP — F. Audet, C. Jennings (eds.), IETF · NAT-এর behavior-classification ও traversal-প্রত্যাশার মানদণ্ড
  • Computer Networking: A Top-Down Approach — Network Layer: NAT section — Kurose & Ross · End-to-end principle-বিরোধ তর্কটার সংক্ষিপ্ত উপস্থাপনা