رفتن به محتوا
LoopX

مبانی شبکه در لینوکس

توی این درس یاد می‌گیری هر ماشین در شبکه با یک آدرس IP روی یک interface (مثل eth0) شناخته می‌شود؛ بسته‌ها برای بیرون‌رفتن از gateway (دروازه) می‌گذرند و جدول مسیر تصمیم می‌گیرد از کجا؛ DNS اسم سایت را به IP تبدیل می‌کند (/etc/resolv.conf) و فایل /etc/hosts یک فهرست محلی است که قبل از DNS دیده می‌شود. با ip a، ip route، ping، dig/nslookup و traceroute اتصال را بررسی می‌کنی. تمرین: مسیر اتصال به یک سایت را قدم‌به‌قدم بررسی می‌کنی: IP داری؟ gateway جواب می‌دهد؟ اینترنت هست؟ DNS درست است؟

مسئله: «سایت باز نمی‌شود»، مشکل کجاست؟

Section titled “مسئله: «سایت باز نمی‌شود»، مشکل کجاست؟”

از لحظه‌ای که آدرس یک سایت را می‌زنی تا صفحه بیاید، ده‌ها اتفاق در کار است: کامپیوتر باید آدرس IP داشته باشد، بداند اسم سایت چه IP ای است (DNS)، بداند بسته را از کجا بفرستد (gateway)، و مقصد هم جواب بدهد. «اینترنت قطع است» یعنی کدام‌یک؟ اگر ترتیب بررسی را بدانی، در یک دقیقه پیدایش می‌کنی.

آدرس IP آدرس خانه‌ی توست. gateway اداره‌ی پست محله است: هر نامه‌ای که مقصدش بیرون از محله باشد را به آنجا می‌دهی. جدول مسیر دفترچه‌ی راهنمای پست است: «نامه‌های این کد پستی مستقیم برو، بقیه را بده اداره‌ی پست». و DNS دفترچه‌ی تلفن است: اسم («علی») را به آدرس تبدیل می‌کند.

ابتدا DNS اسم را به IP تبدیل می‌کند؛ بعد کامپیوتر بسته را برای آن IP به gateway می‌دهد و gateway آن را تا مقصد می‌رساند؛ جواب از همان مسیر برمی‌گردد.

همه‌ی آزمایش‌ها روی ماشین آزمایشی من (یک کانتینر Ubuntu) اجرا شده‌اند؛ آدرس‌های خصوصی (مثل 172.17.0.2) مال شبکه‌ی داخلی داکر است و روی کامپیوتر تو عددهای دیگری می‌بینی، ولی ساختار خروجی‌ها و دستورها یکی است.

مثال ۱: من چه آدرسی دارم؟ ip a

Section titled “مثال ۱: من چه آدرسی دارم؟ ip a”

ip a (کوتاه‌شده‌ی ip address) همه‌ی interface ها و آدرس‌هایشان را می‌دهد. روی ماشین آزمایشی من چند interface مجازیِ بی‌استفاده هم هست که شلوغش می‌کنند، پس فقط آدرس‌های IPv4 را می‌خواهم (-4)؛ و نسخه‌ی خلاصه‌ی -br (brief):

Terminal window
echo "--- ip -4 a:"
ip -4 a
echo "--- نسخه‌ی خلاصه (-br):"
ip -br -4 a
echo "--- آدرس سخت‌افزاری (MAC) کارت شبکه:"
ip link show eth0 | grep link/ether | awk '{print $2}'
echo "--- فقط IP:"
hostname -I
خروجی
--- ip -4 a:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
11: eth0@if2471: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65535 qdisc noqueue state UP group default link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
--- نسخه‌ی خلاصه (-br):
lo UNKNOWN 127.0.0.1/8
eth0@if2471 UP 172.17.0.2/16
--- آدرس سخت‌افزاری (MAC) کارت شبکه:
2e:05:9a:4f:b5:63
--- فقط IP:
172.17.0.2

دو interface:

  • lo (loopback): کارت مجازی «خودِ همین ماشین» با آدرس 127.0.0.1 (همان localhost). ترافیکش هرگز بیرون نمی‌رود.
  • eth0: کارت شبکه‌ی اصلی، با آدرس 172.17.0.2/16. (روی سرورهای دیگر نامش ens3، enp0s3، wlan0… است.) UP یعنی فعال، و link/ether آدرس MAC سخت‌افزاری.

عدد بعد از / اندازه‌ی شبکه را می‌گوید (CIDR): 172.17.0.2/16 یعنی «۱۶ بیت اول آدرس، شبکه است». هر آدرسی با همان ۱۶ بیت اول (172.17.x.x) هم‌شبکه (همسایه) است و مستقیم در دسترس؛ بقیه را باید از gateway فرستاد. با ماژول ipaddress پایتون چند نمونه را حساب می‌کنم:

Terminal window
python3 - <<'EOF'
import ipaddress
for cidr in ['172.17.0.2/16', '192.168.1.37/24', '10.0.0.5/8']:
n = ipaddress.ip_interface(cidr).network
print(f"{cidr:16} شبکه={n.network_address} ماسک={n.netmask} تعداد آدرس={n.num_addresses}")
print("--- خصوصی (private) یا عمومی؟")
for a in ['172.17.0.2', '192.168.1.37', '10.0.0.5', '1.1.1.1', '8.8.8.8']:
print(f"{a:14}", "خصوصی" if ipaddress.ip_address(a).is_private else "عمومی")
EOF
خروجی
172.17.0.2/16 شبکه=172.17.0.0 ماسک=255.255.0.0 تعداد آدرس=65536
192.168.1.37/24 شبکه=192.168.1.0 ماسک=255.255.255.0 تعداد آدرس=256
10.0.0.5/8 شبکه=10.0.0.0 ماسک=255.0.0.0 تعداد آدرس=16777216
--- خصوصی (private) یا عمومی؟
172.17.0.2 خصوصی
192.168.1.37 خصوصی
10.0.0.5 خصوصی
1.1.1.1 عمومی
8.8.8.8 عمومی

سه محدوده‌ی خصوصی (فقط برای شبکه‌های داخلی، در اینترنت مسیریابی نمی‌شوند) این‌هاست: 10.0.0.0/8، 172.16.0.0/12 و 192.168.0.0/16. هر چه بیرون از این‌ها باشد (مثل 1.1.1.1) عمومی است. (روتر خانه با NAT آدرس‌های خصوصی شبکه‌ی داخل را به یک آدرس عمومی ترجمه می‌کند.)

مثال ۲: gateway و جدول مسیر، ip route

Section titled “مثال ۲: gateway و جدول مسیر، ip route”
Terminal window
echo "--- ip route (جدول مسیر):"
ip route
echo "--- بسته برای 1.1.1.1 از کجا می‌رود؟ (ip route get)"
ip route get 1.1.1.1
echo "--- و برای یک همسایه‌ی هم‌شبکه (172.17.0.5)؟"
ip route get 172.17.0.5
خروجی
--- ip route (جدول مسیر):
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
--- بسته برای 1.1.1.1 از کجا می‌رود؟ (ip route get)
1.1.1.1 via 172.17.0.1 dev eth0 src 172.17.0.2 uid 1001
cache
--- و برای یک همسایه‌ی هم‌شبکه (172.17.0.5)؟
172.17.0.5 dev eth0 src 172.17.0.2 uid 1001
cache

دو سطر جدول مسیر را ببین:

  • default via 172.17.0.1 dev eth0: مسیر پیش‌فرض: «هر مقصدی که در سطرهای دیگر نبود، از eth0 به gateway 172.17.0.1 بده». gateway یک مسیریاب (router) است که بسته را به بیرون می‌رساند.
  • 172.17.0.0/16 dev eth0 ... src 172.17.0.2: «مقصدهای 172.17.x.x همسایه‌ی ما هستند، مستقیم بفرست».

ip route get مقصد دقیقاً نشان می‌دهد برای یک مقصد خاص کدام سطر انتخاب می‌شود: برای 1.1.1.1 via (از gateway) و برای همسایه بدون via (مستقیم). هسته برای هر بسته دقیق‌ترین سطر منطبق را انتخاب می‌کند.

مثال ۳: ping، آیا مقصد جواب می‌دهد؟

Section titled “مثال ۳: ping، آیا مقصد جواب می‌دهد؟”

ping یک بسته‌ی ICMP «echo request» می‌فرستد و منتظر پاسخ می‌ماند. -c 2 یعنی دو بار (بدون آن تا Ctrl+C ادامه می‌دهد). برای عیب‌یابی از نزدیک به دور ping بزن: خودم، gateway، یک IP در اینترنت، و یک اسم:

Terminal window
echo "=== ۱) خودم (loopback):"
ping -c 2 127.0.0.1
echo "=== ۲) gateway:"
ping -c 2 $(ip route | awk '/^default/ {print $3}')
echo "=== ۳) یک آدرس در اینترنت (فقط IP، بدون DNS):"
ping -c 2 1.1.1.1
echo "=== ۴) یک اسم (DNS هم لازم است):"
ping -c 2 example.com
خروجی
=== ۱) خودم (loopback):
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.051 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.074 ms
--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1022ms
rtt min/avg/max/mdev = 0.051/0.062/0.074/0.011 ms
=== ۲) gateway:
PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.124 ms
64 bytes from 172.17.0.1: icmp_seq=2 ttl=64 time=0.064 ms
--- 172.17.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1015ms
rtt min/avg/max/mdev = 0.064/0.094/0.124/0.030 ms
=== ۳) یک آدرس در اینترنت (فقط IP، بدون DNS):
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=63 time=126 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=63 time=145 ms
--- 1.1.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1009ms
rtt min/avg/max/mdev = 126.318/135.719/145.120/9.401 ms
=== ۴) یک اسم (DNS هم لازم است):
PING example.com (172.66.147.243) 56(84) bytes of data.
64 bytes from 172.66.147.243: icmp_seq=1 ttl=63 time=140 ms
64 bytes from 172.66.147.243: icmp_seq=2 ttl=63 time=134 ms
--- example.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 133.652/136.861/140.070/3.209 ms

خروجی هر سطر: 64 bytes from آدرس: icmp_seq=N ttl=T time=X ms: time زمان رفت‌وبرگشت (RTT)، و ttl عدد «طول عمر» بسته که با هر مسیریاب یکی کم می‌شود. آخر کار آمار می‌آید: 0% packet loss یعنی همه‌ی بسته‌ها جواب گرفتند و rtt min/avg/max خلاصه‌ی زمان‌ها. (در اینترنت واقعی زمان ping به gateway چند میلی‌ثانیه و به یک سرور دور صدها میلی‌ثانیه است.) وقتی کار نکند، دو شکل خطا داری:

Terminal window
echo "--- آدرسی که هیچ‌کس جوابش را نمی‌دهد:"
ping -c 1 -W 2 10.255.255.1
echo "--- اسمی که وجود ندارد:"
ping -c 1 no-such-host.invalid
خروجی
--- آدرسی که هیچ‌کس جوابش را نمی‌دهد:
PING 10.255.255.1 (10.255.255.1) 56(84) bytes of data.
--- 10.255.255.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
--- اسمی که وجود ندارد:
ping: no-such-host.invalid: Name or service not known
  • 100% packet loss: بسته رفت ولی جوابی برنگشت (مقصد خاموش، مسیر بسته، یا فایروال ICMP را رد می‌کند).
  • Name or service not known: اسم تبدیل نشد؛ مشکل DNS است، نه شبکه‌ی IP.

این دو را قاطی نکن: مشکل DNS و مشکل اتصال، دو بیماری متفاوت‌اند.

DNS (Domain Name System) اسم را به آدرس IP تبدیل می‌کند. سیستم از فایل /etc/resolv.conf می‌فهمد DNS را از کجا بپرسد:

Terminal window
echo "--- /etc/resolv.conf (بدون توضیح):"
grep -v '^#' /etc/resolv.conf | grep -v '^$'
echo "--- dig example.com:"
dig example.com
خروجی
--- /etc/resolv.conf (بدون توضیح):
nameserver 192.168.65.7
--- dig example.com:
; <<>> DiG 9.18.39-0ubuntu0.24.04.7-Ubuntu <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63602
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 149 IN A 172.66.147.243
example.com. 149 IN A 104.20.23.154
;; Query time: 2 msec
;; SERVER: 192.168.65.7#53(192.168.65.7) (UDP)
;; WHEN: Sat Oct 03 21:51:30 UTC 2026
;; MSG SIZE rcvd: 83

خط nameserver آدرس سرور DNS است (اینجا یک آدرس خصوصی، سرور DNS داخلی داکر). خروجی dig:

  • QUESTION SECTION: چه چیزی پرسیده شد (example.com و نوع رکورد A یعنی IPv4).
  • ANSWER SECTION: جواب‌ها. عدد وسط (147) TTL است: کش‌ها تا چند ثانیه می‌توانند این جواب را نگه دارند. هر نشانی که IN A دارد یک سرور برای این اسم است (چند تا یعنی بار بین چند سرور پخش می‌شود).
  • status: NOERROR یعنی موفق (NXDOMAIN یعنی چنین اسمی نیست).
  • Query time و SERVER: چقدر طول کشید و از کدام DNS پرسیده شد.

برای خروجی کوتاه +short و برای انواع رکوردها بعد از اسم نوع را بنویس:

Terminal window
echo "--- A (IPv4) و AAAA (IPv6):"
dig +short example.com
dig +short AAAA example.com
echo "--- NS (سرورهای نام دامنه):"
dig +short NS example.com
echo "--- MX (سرورهای ایمیل gmail.com، عدد = اولویت):"
dig +short MX gmail.com
echo "--- TXT:"
dig +short TXT example.com | head -2
echo "--- nslookup (ابزار قدیمی‌تر، همان کار):"
nslookup example.com
خروجی
--- A (IPv4) و AAAA (IPv6):
172.66.147.243
104.20.23.154
--- NS (سرورهای نام دامنه):
hera.ns.cloudflare.com.
elliott.ns.cloudflare.com.
--- MX (سرورهای ایمیل gmail.com، عدد = اولویت):
10 alt1.gmail-smtp-in.l.google.com.
20 alt2.gmail-smtp-in.l.google.com.
40 alt4.gmail-smtp-in.l.google.com.
5 gmail-smtp-in.l.google.com.
30 alt3.gmail-smtp-in.l.google.com.
--- TXT:
"_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9"
"v=spf1 -all"
--- nslookup (ابزار قدیمی‌تر، همان کار):
Server: 192.168.65.7
Address: 192.168.65.7#53
Non-authoritative answer:
Name: example.com
Address: 172.66.147.243
Name: example.com
Address: 104.20.23.154

یک نکته‌ی مهم: DNS ممکن است از هر resolver جواب متفاوتی بدهد. با @آدرس یک سرور مشخص را می‌پرسی؛ مثلاً همین اسم را از DNS سیستم و از دو DNS عمومی مقایسه کن:

Terminal window
echo "--- DNS سیستم:"
dig +short example.com
echo "--- 8.8.8.8:"
dig +short @8.8.8.8 example.com
echo "--- 1.1.1.1:"
dig +short @1.1.1.1 example.com
echo "--- سرور DNS که وجود ندارد (خطا):"
dig +time=2 +tries=1 @192.0.2.1 example.com | head -1
خروجی
--- DNS سیستم:
172.66.147.243
104.20.23.154
--- 8.8.8.8:
104.20.23.154
172.66.147.243
--- 1.1.1.1:
104.20.23.154
172.66.147.243
--- سرور DNS که وجود ندارد (خطا):
;; communications error to 192.0.2.1#53: timed out

در این لحظه هر سه یک مجموعه‌ی آدرس را دادند و فقط ترتیبشان فرق می‌کند (DNS معمولاً ترتیب آدرس‌ها را می‌چرخاند تا بار پخش شود). ولی همیشه چنین نیست: سایت‌های بزرگ از CDN استفاده می‌کنند و بسته به مکان تو آدرس‌های متفاوتی می‌دهند، و بعضی DNS ها (برای فیلتر یا سرویس‌های دیگر) جواب را عوض می‌کنند. وقتی «سایت یک‌جا باز می‌شود و جای دیگر نه»، مقایسه‌ی DNS ها اولین ابزار توست. آخرین خط: وقتی سرور DNS جواب نمی‌دهد (communications error ... timed out) تا مدتی منتظر می‌ماند و بعد خطا می‌دهد.

مثال ۵: /etc/hosts، فهرست محلی اسم‌ها

Section titled “مثال ۵: /etc/hosts، فهرست محلی اسم‌ها”

قبل از پرسیدن از DNS، سیستم یک فایل محلی را نگاه می‌کند. ترتیب در /etc/nsswitch.conf تعیین می‌شود:

Terminal window
echo "--- /etc/hosts:"
cat /etc/hosts
echo "--- ترتیب جستجوی اسم‌ها:"
grep '^hosts' /etc/nsswitch.conf
خروجی
--- /etc/hosts:
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 pc
--- ترتیب جستجوی اسم‌ها:
hosts: files dns

hosts: files dns یعنی اول files (همان /etc/hosts) و بعد dns. هر خط /etc/hosts اول آدرس IP و بعد یک یا چند اسم دارد. پس می‌شود هر اسمی را موقتاً به یک آدرس بست، مثلاً برای آزمایش یک سایت قبل از انتقال دامنه، یا مسدود کردن یک سایت:

Terminal window
echo "127.0.0.1 example.com" | sudo tee -a /etc/hosts > /dev/null
echo "--- getent hosts (همان راهی که برنامه‌ها اسم را پیدا می‌کنند: hosts و بعد DNS):"
getent hosts example.com | head -1
echo "--- ping هم از همان راه:"
ping -c 1 example.com | head -1
echo "--- ولی dig به /etc/hosts نگاه نمی‌کند و مستقیم DNS می‌پرسد:"
dig +short example.com | head -1
echo "--- برگرداندن به حالت اول:"
sudo sh -c "grep -v 'example.com' /etc/hosts > /tmp/hosts.new; cat /tmp/hosts.new > /etc/hosts; rm /tmp/hosts.new"
getent hosts example.com | head -1
خروجی
--- getent hosts (همان راهی که برنامه‌ها اسم را پیدا می‌کنند: hosts و بعد DNS):
127.0.0.1 example.com
--- ping هم از همان راه:
PING example.com (127.0.0.1) 56(84) bytes of data.
--- ولی dig به /etc/hosts نگاه نمی‌کند و مستقیم DNS می‌پرسد:
172.66.147.243
--- برگرداندن به حالت اول:
172.66.147.243 example.com

ping و برنامه‌ها آدرس 127.0.0.1 را گرفتند، ولی dig همان IP واقعی را نشان داد. نتیجه: dig و nslookup فقط DNS را می‌سنجند؛ برای دیدن آنچه برنامه‌ها می‌بینند getent hosts را بزن. (در این ماشین آزمایشی /etc/hosts را با cat > عوض کردم نه sed -i، چون در کانتینر داکر آن فایل mount شده است و sed -i فایل را جابه‌جا می‌کند و خطا می‌دهد؛ روی سرور معمولی هر دو شکل کار می‌کنند.)

مثال ۶: traceroute، مسیر بسته‌ها

Section titled “مثال ۶: traceroute، مسیر بسته‌ها”

traceroute نشان می‌دهد بسته برای رسیدن به مقصد از چه مسیریاب‌هایی (hop) می‌گذرد. ترفندش این است که بسته‌ها را با TTL کوچک می‌فرستد: مسیریاب اول TTL را صفر می‌کند و خبر می‌دهد، دومی با TTL بعدی، و همین‌طور تا مقصد:

Terminal window
echo "--- traceroute پیش‌فرض (بسته‌های UDP):"
traceroute -n -m 4 -w 1 1.1.1.1
echo "--- با بسته‌های ICMP (-I):"
traceroute -I -n -m 6 -w 2 1.1.1.1
خروجی
--- traceroute پیش‌فرض (بسته‌های UDP):
traceroute to 1.1.1.1 (1.1.1.1), 4 hops max, 60 byte packets
1 172.17.0.1 0.498 ms 0.417 ms 0.397 ms
2 * * *
3 * * *
4 * * *
--- با بسته‌های ICMP (-I):
traceroute to 1.1.1.1 (1.1.1.1), 6 hops max, 60 byte packets
1 172.17.0.1 0.120 ms * 0.015 ms
2 * 1.1.1.1 168.432 ms 168.328 ms

-n یعنی اسم‌ها را DNS نکن (سریع‌تر)، -m حداکثر تعداد hop، و -w چند ثانیه منتظر هر پاسخ بماند. هر سطر یک hop با سه زمان‌سنجی است. * * * یعنی از آن hop جوابی نیامد: همیشه به معنی قطع‌بودن نیست، چون بسیاری از مسیریاب‌ها (و فایروال‌ها) پاسخ‌دادن به این بسته‌ها را بسته‌اند. در این ماشین آزمایشی، شبکه‌ی داکر (NAT) مسیریاب‌های بعد از gateway را از دید ما پنهان می‌کند و فقط hop اول و مقصد دیده می‌شوند؛ روی یک سرور واقعی معمولاً ۸ تا ۱۵ hop می‌بینی و آنجا اولین hop که تأخیر یا ناپایداری ناگهانی نشان داد، مشکوک است.

مثال ۷: قدم‌به‌قدم، اتصال به یک سایت را بررسی کن (تمرین اصلی)

Section titled “مثال ۷: قدم‌به‌قدم، اتصال به یک سایت را بررسی کن (تمرین اصلی)”

همه‌ی دانسته‌ها را در یک اسکریپت کوچک می‌گذارم که ترتیب «از نزدیک به دور» را دنبال می‌کند: IP ← gateway ← اینترنت با IP ← DNS ← اتصال به سایت:

netcheck.sh
#!/bin/bash
# بررسی قدم‌به‌قدم اتصال به یک سایت (از نزدیک به دور)
site=${1:-example.com}
ok() { echo " ✓ $1"; }
bad() { echo " ✗ $1"; }
echo "۱) interface و IP:"
ip -4 -br a | awk '$1 !~ /^lo/ {print " ", $1, $3}'
gw=$(ip route | awk '/^default/ {print $3; exit}')
echo "۲) gateway ($gw):"
ping -c 1 -W 2 "$gw" > /dev/null 2>&1 && ok "جواب می‌دهد" || bad "جواب نمی‌دهد"
echo "۳) اینترنت بدون DNS (1.1.1.1):"
ping -c 1 -W 3 1.1.1.1 > /dev/null 2>&1 && ok "در دسترس" || bad "در دسترس نیست"
echo "۴) DNS ($site):"
ip=$(dig +short +time=2 +tries=1 "$site" 2>/dev/null | grep -E '^[0-9.]+$' | head -1)
[ -n "$ip" ] && ok "$site ← $ip" || bad "اسم تبدیل نشد"
echo "۵) اتصال HTTPS به $site:"
code=$(curl -s -o /dev/null -m 6 -w '%{http_code}' "https://$site")
[ "$code" != 000 ] && ok "پاسخ HTTP $code" || bad "اتصال برقرار نشد"
Terminal window
bash netcheck.sh example.com
خروجی
۱) interface و IP:
eth0@if2471 172.17.0.2/16
۲) gateway (172.17.0.1):
✓ جواب می‌دهد
۳) اینترنت بدون DNS (1.1.1.1):
✓ در دسترس
۴) DNS (example.com):
✓ example.com ← 172.66.147.243
۵) اتصال HTTPS به example.com:
✓ پاسخ HTTP 200

همه‌ی پنج قدم موفق بود. حالا یک خرابی واقعی بسازم: DNS سیستم را موقتاً به یک آدرس بی‌جواب (192.0.2.1) تغییر می‌دهم و همان اسکریپت را اجرا می‌کنم:

Terminal window
sudo cp /etc/resolv.conf /tmp/resolv.bak
echo "nameserver 192.0.2.1" | sudo tee /etc/resolv.conf > /dev/null
bash netcheck.sh example.com
echo "--- برگرداندن DNS:"
sudo cp /tmp/resolv.bak /etc/resolv.conf
grep -v '^#' /etc/resolv.conf | grep -v '^$'
خروجی
۱) interface و IP:
eth0@if2471 172.17.0.2/16
۲) gateway (172.17.0.1):
✓ جواب می‌دهد
۳) اینترنت بدون DNS (1.1.1.1):
✓ در دسترس
۴) DNS (example.com):
✗ اسم تبدیل نشد
۵) اتصال HTTPS به example.com:
✗ اتصال برقرار نشد
--- برگرداندن DNS:
nameserver 192.168.65.7

الگو را ببین: قدم‌های ۱ تا ۳ سبز (IP، gateway و اینترنت با IP سالم‌اند) و از قدم ۴ قرمز. نتیجه: «اینترنت هست؛ DNS خراب است»، یعنی دقیقاً همان سناریوی ابتدای درس. اگر قدم ۲ قرمز بود مشکل اتصال به شبکه‌ی محلی (کابل، Wi-Fi، gateway) است؛ اگر ۱ قرمز بود، حتی IP نداری (DHCP، کارت شبکه).

⚡ بررسی سریع

ping 1.1.1.1 جواب می‌دهد ولی ping example.com می‌گوید Name or service not known. مشکل کجاست؟

پشت پرده: فایل‌های شبکه

Section titled “پشت پرده: فایل‌های شبکه”

همه‌ی این ابزارها در نهایت چند فایل و یک پایگاه داده‌ی داخل هسته را می‌خوانند:

چیز کجا چه می‌گوید
IP و interface ها هسته (ip a) آدرس هر کارت شبکه
جدول مسیر هسته (ip route) بسته برای هر مقصد از کجا برود
سرور DNS /etc/resolv.conf از چه آدرسی اسم را بپرس
فهرست محلی اسم‌ها /etc/hosts اسم ← IP، قبل از DNS
ترتیب جستجو /etc/nsswitch.conf hosts: files dns
نام این ماشین /etc/hostname نام ماشین؛ با hostname هم دیده می‌شود
Terminal window
echo "--- نام ماشین:"
hostname
cat /etc/hostname
echo "--- هر برنامه برای باز کردن یک اسم همین مسیر را می‌رود: getent hosts localhost"
getent hosts localhost
خروجی
--- نام ماشین:
pc
pc
--- هر برنامه برای باز کردن یک اسم همین مسیر را می‌رود: getent hosts localhost
::1 localhost ip6-localhost ip6-loopback

مسیر یک اسم در برنامه‌ها (مثل ping، curl، مرورگر): ۱) /etc/nsswitch.conf را می‌خواند؛ ۲) اول /etc/hosts؛ ۳) اگر نبود، DNS آدرس‌های /etc/resolv.conf؛ ۴) آدرس را می‌گیرد و بسته را طبق جدول مسیر می‌فرستد. فایل‌هایی مثل /etc/resolv.conf را در بعضی سیستم‌ها یک سرویس مدیریت می‌کند و ویرایش دستی‌شان گاهی بعد از مدتی بازنویسی می‌شود؛ در این ماشین آزمایشی داکر آن را ساخته و ویرایش دستی مانده، ولی روی سرور خودت اول ببین چه کسی مدیریتش می‌کند.

دستور کار
ip a / ip -br -4 a interface ها و آدرس‌ها (کامل / خلاصه)
ip route جدول مسیر و gateway
ip route get مقصد مسیر انتخاب‌شده برای یک مقصد
ping -c 3 مقصد آیا مقصد جواب می‌دهد؟
traceroute -n مقصد hop های مسیر (-I ICMP)
dig اسم / dig +short اسم پرسش DNS (کامل / کوتاه)
dig +short MX اسم نوع رکورد (A، AAAA، MX، NS، TXT)
dig @8.8.8.8 اسم پرسیدن از یک DNS مشخص
nslookup اسم پرسش ساده‌ی DNS
getent hosts اسم همان که برنامه‌ها می‌بینند (hosts + DNS)
hostname / hostname -I نام ماشین / IP ها
نوع رکورد DNS معنی
A / AAAA آدرس IPv4 / IPv6
CNAME نام مستعار (اسم دیگری که ارجاع می‌دهد)
MX سرور ایمیل (با اولویت)
NS سرورهای نام دامنه
TXT متن (تأیید دامنه، SPF و…)
Terminal window
ifconfig
خروجی
bash: ifconfig: command not found

ifconfig (و netstat، route) از بسته‌ی قدیمی net-tools هستند و روی توزیع‌های جدید نصب نمی‌شوند. راه‌حل: ip a (به‌جای ifconfig)، ip route (به‌جای route) و ss (به‌جای netstat؛ درس بعد).

۲) فرض‌کردن اینکه «ping جواب نمی‌دهد یعنی سرویس خاموش است»

Section titled “۲) فرض‌کردن اینکه «ping جواب نمی‌دهد یعنی سرویس خاموش است»”

بعضی فایروال‌ها فقط ping (ICMP) را می‌بندند و بقیه‌ی ترافیک سالم می‌ماند. با یک وب‌سرور کوچک و یک قانون فایروال روی ماشین systemd‌دار آزمایشی ببین:

Terminal window
python3 -m http.server 8099 --bind 127.0.0.1 > /dev/null 2>&1 &
sleep 1
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
echo "--- ping (ICMP بسته شده):"
ping -c 1 -W 1 127.0.0.1 | tail -2
echo "--- ولی وب‌سرور سالم است:"
curl -sI http://127.0.0.1:8099 | head -1
iptables -D INPUT -p icmp --icmp-type echo-request -j DROP
kill %1
echo "--- بعد از برداشتن قانون، ping دوباره جواب می‌دهد:"
ping -c 1 -W 1 127.0.0.1 | tail -2
خروجی
--- ping (ICMP بسته شده):
1 packets transmitted, 0 received, 100% packet loss, time 0ms
--- ولی وب‌سرور سالم است:
HTTP/1.0 200 OK
--- بعد از برداشتن قانون، ping دوباره جواب می‌دهد:
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.061/0.061/0.061/0.000 ms

100% packet loss ولی HTTP/1.0 200 OK: سرویس سالم بود و فقط ping بسته. راه‌حل: برای سنجش «سرویس» به‌جای ping، همان پورت را آزمایش کن (curl، nc -zv؛ درس بعد).

۳) استفاده از dig برای دیدن آنچه برنامه می‌بیند

Section titled “۳) استفاده از dig برای دیدن آنچه برنامه می‌بیند”

مثال ۵: با یک خط /etc/hosts اسم به 127.0.0.1 رفت ولی dig همان آدرس واقعی را نشان داد. راه‌حل: getent hosts اسم.

۴) نوشتن /etc/hosts با ترتیب اشتباه

Section titled “۴) نوشتن /etc/hosts با ترتیب اشتباه”
Terminal window
echo "myapp.test 127.0.0.1" | sudo tee -a /etc/hosts > /dev/null
echo "--- ترتیب اشتباه (اسم قبل از IP):"
getent hosts myapp.test || echo "(پیدا نشد)"
ping -c 1 myapp.test
echo "--- ترتیب درست (IP بعد اسم):"
sudo sh -c "grep -v 'myapp.test' /etc/hosts > /tmp/hosts.new; cat /tmp/hosts.new > /etc/hosts; rm /tmp/hosts.new"
echo "127.0.0.1 myapp.test" | sudo tee -a /etc/hosts > /dev/null
getent hosts myapp.test
ping -c 1 myapp.test | head -1
sudo sh -c "grep -v 'myapp.test' /etc/hosts > /tmp/hosts.new; cat /tmp/hosts.new > /etc/hosts; rm /tmp/hosts.new"
خروجی
--- ترتیب اشتباه (اسم قبل از IP):
(پیدا نشد)
ping: myapp.test: Name or service not known
--- ترتیب درست (IP بعد اسم):
127.0.0.1 myapp.test
PING myapp.test (127.0.0.1) 56(84) bytes of data.

ستون اول /etc/hosts همیشه آدرس IP است و بعدش اسم‌ها. با ترتیب برعکس، خط نادیده گرفته می‌شود و اسم از DNS پرسیده می‌شود (که چنین اسمی ندارد؛ برای همین ping خطای تبدیل اسم می‌دهد). راه‌حل: IP نام. (پسوند .test برای آزمایش رزرو شده و در اینترنت واقعی وجود ندارد؛ پس برای نام‌های محلی تمرینی امن است.)

ping بدون -c تا ابد ادامه می‌دهد (تا Ctrl+C). در اسکریپت‌ها همیشه ping -c 1 (یا -c 3) و بهتر -W 2 (حداکثر انتظار) بگذار تا گیر نکنی.

✎ تمرینآسان

آدرس IP کارت شبکه‌ی eth0 و آدرس gateway پیش‌فرض را با دو دستور پیدا کن.

دیدن جواب
Terminal window
ip -br -4 a show eth0
ip route | awk '/^default/ {print "gateway:", $3}'
خروجی
eth0@if2471 UP 172.17.0.2/16
gateway: 172.17.0.1
✎ تمرینمتوسط

تمرین اصلی: مسیر اتصال به یک سایت را قدم‌به‌قدم بررسی کن. اسکریپت netcheck.sh را برای wikipedia.org اجرا کن. بعد خودت قدم ۴ و ۵ را با دو دستور مستقیم بزن (dig +short و curl -I) و ببین نتیجه مثل اسکریپت است.

دیدن جواب
Terminal window
bash netcheck.sh wikipedia.org
echo "=== دستی:"
dig +short wikipedia.org | head -1
curl -sI -m 6 https://wikipedia.org | head -1
خروجی
۱) interface و IP:
eth0@if2471 172.17.0.2/16
۲) gateway (172.17.0.1):
✓ جواب می‌دهد
۳) اینترنت بدون DNS (1.1.1.1):
✓ در دسترس
۴) DNS (wikipedia.org):
✓ wikipedia.org ← 185.15.59.224
۵) اتصال HTTPS به wikipedia.org:
✓ پاسخ HTTP 301
=== دستی:
185.15.59.224
HTTP/2 301
✎ تمرینسخت

یک نام محلی بساز: با /etc/hosts اسم myapp.test را به 127.0.0.1 بیند، با python3 -m http.server 8097 یک وب‌سرور بالا بیاور، و با curl http://myapp.test:8097 به آن وصل شو. بعد همه‌چیز را پاک کن (سرور و خط hosts).

دیدن جواب
Terminal window
echo "127.0.0.1 myapp.test" | sudo tee -a /etc/hosts > /dev/null
python3 -m http.server 8097 --bind 127.0.0.1 > /dev/null 2>&1 &
sleep 1
getent hosts myapp.test
curl -sI http://myapp.test:8097 | head -1
kill %1
sudo sh -c "grep -v 'myapp.test' /etc/hosts > /tmp/hosts.new; cat /tmp/hosts.new > /etc/hosts; rm /tmp/hosts.new"
getent hosts myapp.test || echo "(پاک شد)"
خروجی
127.0.0.1 myapp.test
HTTP/1.0 200 OK
(پاک شد)
؟ آزمونک
  1. در 192.168.1.37/24 عدد 24 چه می‌گوید؟

  2. خط default via 172.17.0.1 dev eth0 در ip route یعنی…

  3. ping 1.1.1.1 جواب می‌دهد ولی ping example.com می‌گوید Name or service not known. مشکل چیست؟

  4. مرتبه‌ی جستجوی اسم با hosts: files dns در nsswitch.conf یعنی…

  5. برای دیدن اینکه یک برنامه (مثل curl) اسم را به چه آدرسی تبدیل می‌کند، کدام درست‌تر است؟

  6. ping به یک سرور 100% packet loss دارد ولی سایتش باز می‌شود. چرا ممکن است؟

  • ip a: interface ها و آدرس‌ها (lo = خودِ ماشین، eth0 = کارت اصلی). /16، /24 و… یعنی چند بیت اول، بخش شبکه است. خصوصی: 10/8، 172.16/12، 192.168/16.
  • ip route: جدول مسیر؛ default via X یعنی gateway. ip route get مقصد مسیر انتخاب‌شده را نشان می‌دهد.
  • ping -c N برای سنجش دسترسی؛ 100% packet loss (جواب برنگشت) در برابر Name or service not known (DNS). فرق ICMP با سرویس را فراموش نکن.
  • DNS: /etc/resolv.conf (سرور)، dig و nslookup؛ dig +short TYPE اسم، dig @سرور اسم؛ جواب‌ها ممکن است از resolver های مختلف فرق کنند.
  • /etc/hosts قبل از DNS خوانده می‌شود (nsswitch.conf)؛ getent hosts همان را می‌بیند که برنامه‌ها می‌بینند.
  • traceroute -n hop ها را نشان می‌دهد؛ * * * همیشه قطع نیست.
  • عیب‌یابی قدم‌به‌قدم: IP ← gateway ← اینترنت با IP ← DNS ← اتصال به سرویس.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
ip -br -4 ainterface ها و IP ها (خلاصه)
ip routeجدول مسیر و gateway
ip route get 1.1.1.1مسیر یک مقصد
ping -c 3 -W 2 مقصدآیا جواب می‌دهد؟
dig +short اسم / dig +short MX اسمDNS (A یا نوع دیگر)
dig +short @8.8.8.8 اسمپرسیدن از یک DNS مشخص
nslookup اسمپرسش ساده‌ی DNS
getent hosts اسمهمان که برنامه‌ها می‌بینند
cat /etc/resolv.confDNS سیستم
echo "127.0.0.1 نام" | sudo tee -a /etc/hostsنام محلی
traceroute -n -I مقصدمسیر hop ها (با ICMP)
hostname / hostname -Iنام و IP ماشین