NAT — address শেষ হয়ে যাওয়ার hack যা স্থায়ী হয়ে গেল
Network Address Translation
1994-এর এক temporary patch — public IP না থাকা device-গুলোকে ইন্টারনেটে ছুড়ে দেওয়ার hack — যেভাবে পুরো ইন্টারনেটের default হয়ে বসে গেল, আর end-to-end connectivity কীভাবে hostage হলো।
আগে এটা বুঝি
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তিনটা সূক্ষ্ম নিয়ম লক্ষ্য করুন:
- Entry outbound-এ জন্মায় — বাইরে-থেকে-ঢোকা packet-এ নয়। এই asymmetry-ই পরের পুরো গল্প।
- Match হয় পূর্ণ tuple-এ: port-এর সাথে remote-address পর্যন্ত মিলতে হয় (port-restricted behavior) — ভুল sender-এর reply ঢোকে না।
- 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-এ আলাদা mapping | punch 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-এ ফেরতTraversal-সিঁড়ি
- ১. STUN discoveryreflexive ip:port জানা — প্রায় free
- ২. Simultaneous hole-punchpredictable NAT-এ direct P2P — এক RTT-র খরচ
- ৩. TURN relaysymmetric-NAT-এর শেষ রাস্তা — সব bandwidth relay-র পেট দিয়ে
- ৪. Manual port-forwarduser-config — automated নয়, কিন্তু সবচেয়ে deterministic
নিজে চালিয়ে দেখুন
দুই ঠিকানার প্রমাণ — router আপনাকে কী নামে চেনে?
ধাপ ১ — দুই দৃষ্টিতে দুই ঠিকানা:
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।
নিজে বানান
NAT simulator — outbound-create, inbound-match, timeout-expiry
- Entry-dataclass: proto, lan-tuple, wan-port, remote-tuple, last-seen
- outbound(): বিদ্যমান entry reuse, নইলে নতুন wan-port allocate
- inbound(): strict remote-tuple match — mismatch হলে DROP log
- tick(): নীরবতা timeout-পার হলে entry expire, port pool-এ ফেরত
- Static DNAT-rule যোগ করুন — port-forwarding-র প্রতিরূপ
- 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 ছাড়াই দরজা খোলে।
নিজে বাড়ান:
- Per-flow timeout আলাদা করুন — TCP-ESTABLISHED 5 ঘণ্টা vs UDP 30s
- Hairpin-mode যোগ করুন — ভেতরের host public-address ডাকলে translate-loop সঠিকভাবে ফেরায়
- 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-এর মূল প্রক্রিয়া)।
2Table-এ আছে 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-এর মূল বিষয়।
3Laptop-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-র ভেতরে সমাধান হয়ে যায়, কেউ ভাঙে না।
4End-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 ফেরত কেনার খরচ।
5Symmetric 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।
6Home-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-বিরোধ তর্কটার সংক্ষিপ্ত উপস্থাপনা