---
id: networking-cli-tools
title: "Công cụ chẩn đoán mạng"
track: networking
trackTitle: "Networking Fundamentals"
order: 2
level: core
status: published
estimatedMinutes: 35
prerequisites:
  - networking-osi-protocols
next: networking-dns
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "dig — BIND 9 Administrator Reference Manual"
    url: https://bind9.readthedocs.io/en/stable/manpages.html
  - title: "ss(8) — Linux man-pages"
    url: https://man7.org/linux/man-pages/man8/ss.8.html
  - title: "curl — command line tool"
    url: https://curl.se/docs/manpage.html
---

# Công cụ chẩn đoán mạng

Các lệnh trong bài là bản Linux. `ping -c` là cú pháp của iputils; ping trên Windows dùng `-n`.

## Để làm gì

Bạn kiểm tra lần lượt DNS, port TCP, process đang listen và HTTP response, để khoanh sự cố vào đúng lớp đã học ở bài trước.

## Gồm những gì

- `dig` cho DNS.
- `ping` cho ICMP.
- Bash `/dev/tcp` hoặc `nc` để thử port TCP.
- `ss -tuln` / `-tulnp` cho socket đang listen trên máy.
- `systemctl is-active`, `status`, `enable --now` cho service đang mở port.
- `curl` cho HTTP và TLS.

`netstat` thuộc gói `net-tools` và không còn được cài mặc định trên nhiều distro. `ss` thuộc `iproute2`.

## Dùng khi nào

Khi một URL không mở được, bạn đi lần từ domain xuống tới app. Dừng lại ở lớp đầu tiên bị fail, vì các lớp sau đều phụ thuộc vào nó.

`ping` fail không chứng minh web đã chết. Server có thể chặn ICMP nhưng vẫn nhận connection TCP vào port `443`.

## Kiến thức cốt lõi

`dig` gửi truy vấn DNS rồi in câu trả lời kèm status. `NOERROR` chỉ nghĩa là truy vấn DNS hợp lệ, không có nghĩa app đang khỏe. `NXDOMAIN` nghĩa là authoritative server mà resolver đã hỏi xác nhận tên đó không tồn tại.

`dig +short` chỉ in giá trị gọn, tiện dùng trong script. Nếu cần biết mình đã hỏi server nào thì bỏ `+short`.

`ss -tuln` lọc TCP (`-t`), UDP (`-u`), socket đang listen (`-l`), và in số thay vì tên (`-n`). Thêm `-p` để xem process nào giữ socket; với socket của user khác, bạn cần đủ quyền mới thấy được process.

Service mạng do systemd quản thì đọc unit trước khi đoán port. `systemctl is-active nginx` in `active` hoặc `inactive`. `systemctl status nginx` thêm PID và vài dòng log. `systemctl enable --now nginx` vừa bật ngay vừa đặt enable để lần boot sau unit lên. Port nghe được chỉ khi process đã bind. Unit `active` mà `ss` không có dòng listen thì process chưa mở port, hoặc đang nghe địa chỉ khác.

Các bản `nc` khác nhau không hỗ trợ cùng một bộ cờ. Với OpenBSD netcat, `nc -vz host port` thử kết nối rồi thoát. Nếu máy không có `nc`, bạn có thể dùng Bash để thử TCP trực tiếp:

```bash
timeout 3 bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/8080'
```

Lệnh này thành công khi có process chấp nhận connection TCP. Lúc đó bạn mới biết port đang mở, chưa biết HTTP sẽ trả gì.

`curl -sS -o /dev/null -w '%{http_code}\n' URL` in ra HTTP status. `-sS` tắt thanh progress nhưng vẫn hiện lỗi. Khi cần xem certificate hoặc redirect, dùng `curl -v` để thấy cả phần TLS lẫn header.

## Lệnh và code mẫu

Kiểm tra từ DNS tới HTTP với một endpoint public:

```bash
dig +noall +answer A example.com
dig +noall +answer AAAA example.com
dig +noall +answer NS example.com

curl -sS -o /dev/null -w 'http_code=%{http_code} time=%{time_total}\n' \
  https://example.com
```

Trên chính máy đang chạy service:

```bash
ss -tuln
ss -tulnp
systemctl is-active nginx sshd 2>/dev/null || true
```

Thử một port local trước khi đổ lỗi cho app:

```bash
timeout 3 bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/3000' \
  && echo 'tcp open' \
  || echo 'tcp closed'
```

`example.com` là domain dành riêng cho tài liệu nhưng vẫn có server chạy thật, nên rất hợp để tập đọc DNS và HTTP. Đừng dùng nó để bắn tải lớn.

## Thực hành

1. Chạy hai lệnh `dig` cho record `A` và `AAAA` của `example.com`.
2. Chạy `curl` và ghi lại status.
3. Chạy `ss -tuln` và tìm các dòng có `127.0.0.1` hoặc `0.0.0.0`.
4. Thử port TCP `9` trên loopback. Trong lab, port này thường không có service nào.
5. Nếu máy đang chạy lab Nginx hoặc Node, thử đúng port đó và đối chiếu với dòng tương ứng trong `ss`.
6. Chạy `systemctl is-active` cho một unit bạn biết tên. Khớp với dòng listen trong `ss`.

## Kết quả mong đợi

- `dig` in ít nhất một record `A` cho `example.com`.
- `curl` in `http_code=200`.
- Port `9` trên `127.0.0.1` cho `tcp closed`, trừ khi bạn cố ý chạy một service ở đó.
- Mỗi socket local của app mà bạn biết đều có một dòng listen trong `ss`.

## Checklist

- [ ] Đọc được `NXDOMAIN` và `NOERROR`.
- [ ] Giải thích được máy không trả ICMP vẫn phục vụ được HTTPS.
- [ ] Dùng `ss` thay `netstat` trên distro hiện tại.
- [ ] Tách "TCP mở" khỏi "HTTP trả 200".
- [ ] Unit `active` mà `ss` không có listen thì chưa kết luận service đã nhận connection.

## Thẻ ôn

### networking-cli-tools-dig
kind: distinguish
cue: `NOERROR` chỉ nói truy vấn DNS hợp lệ. `NXDOMAIN` nói tên không tồn tại. Cả hai chưa nói app sống hay chết.

Q: `dig` một tên miền và status là `NOERROR`. Có kết luận được ứng dụng phía sau tên đó đang khỏe không? `NXDOMAIN` thì khác chỗ nào?

A: `NOERROR` nghĩa là máy chủ DNS xử lý được câu hỏi và trả lời theo giao thức, không báo lỗi truy vấn. Tên có thể trỏ tới một IP mà trên IP đó chẳng có app nào nghe. `NXDOMAIN` là lời của authoritative server: tên này không tồn tại trong zone. Đó là sự thật về DNS, chưa phải sự thật về process. Cả hai status đều dừng ở tầng tên. App khỏe hay không phải hỏi tiếp tới port và HTTP.

### networking-cli-tools-ping
kind: trap
cue: `ping` fail chưa chứng minh HTTPS chết. Máy có thể chặn ICMP mà vẫn nhận TCP vào port 443.

Q: `ping` tới server không trả lời. Có đủ để báo web đã chết và gọi cả đội không?

A: Không. `ping` dùng ICMP. Nhiều server và firewall chặn ICMP có chủ đích, trong khi TCP vào port 443 vẫn được nhận. Không ping được chỉ nói ICMP không về, không nói dịch vụ web down. Bước tiếp là thử TCP tới đúng port dịch vụ đang công bố, rồi mới tới HTTP hoặc HTTPS. Kết luận "web chết" từ mỗi ping là kết luận thiếu một tầng.

### networking-cli-tools-ss
kind: command
cue: Xem ai đang listen bằng `ss -tuln`. `0.0.0.0:3000` là nhận trên mọi địa chỉ IPv4. `127.0.0.1` chỉ nhận từ chính máy đó.

Q: Trên một distro hiện tại, bạn cần biết process nào đang lắng nghe port nào. Lệnh nào dùng, từng cờ nghĩa là gì, và `0.0.0.0:3000` có phải cấu hình lỗi không?

A: `ss -tuln`. `-t` là TCP, `-u` là UDP, `-l` chỉ socket đang listen, `-n` in số thay vì cố phân giải thành tên, đỡ một dòng bị treo vì DNS. `netstat` trên nhiều máy không còn được cài, đừng phụ thuộc vào nó. `0.0.0.0:3000` nghĩa là socket nhận kết nối tới mọi địa chỉ IPv4 của máy, không phải một địa chỉ hỏng. `127.0.0.1:3000` chỉ nhận từ chính máy đó. Khác một địa chỉ bind là khác hẳn ai gọi được.

### networking-cli-tools-curl
kind: distinguish
cue: TCP đã nối chưa phải HTTPS xong. TLS còn đứng giữa TCP và HTTP. `curl` nhận 301 mà không có `-L` vẫn là một HTTP response hợp lệ.

Q: Kiểm tra cho thấy TCP tới port của dịch vụ đã nối được. Có nói được HTTPS đã xong và trang trả 200 chưa? `curl` không có `-L` mà in `301` thì site có hỏng không?

A: Chưa. TCP chỉ là phiên đã mở. Với HTTPS, TLS còn phải bắt tay trên phiên đó. HTTP chỉ chạy sau khi TLS xong, nếu scheme là https. Nối được TCP mà chứng chỉ sai, hoặc request chưa gửi, thì người dùng vẫn không có trang. `301` và `302` là HTTP response hợp lệ: server bảo đi chỗ khác. Không có `-L` thì curl dừng ở redirect và không tự đi theo. Đó chưa phải site chết. Thêm `-L` khi bạn muốn xem đích cuối, và đọc status cuối cùng chứ không đọc mỗi bước redirect.

### networking-cli-tools-service
kind: distinguish
cue: `systemctl is-active` nói unit có đang chạy. `ss -tuln` nói process đã bind port. Hai lệnh phải khớp mới kết luận dịch vụ nhận connection.

Q: `systemctl is-active nginx` in `active`. Từ máy khác vẫn connection refused. Việc nào còn thiếu, và `enable --now` khác `start` chỗ nào?

A: Unit active nghĩa là systemd đã start process. Connection refused nghĩa là không có socket listen trên địa chỉ và port mà client gọi. `ss -tuln` mới nói process đã bind chưa, và bind `127.0.0.1` hay `0.0.0.0`. `systemctl start` chỉ chạy lần này. `systemctl enable --now` vừa start vừa đặt enable, lần boot sau unit lên lại. Đừng kết luận "mạng hỏng" khi mới đọc is-active.

## Lỗi thường gặp

**Lấy `ping` làm bằng chứng duy nhất.** Chặn ICMP là cấu hình bình thường. Hãy kiểm tra tiếp TCP và HTTP trên đúng port của dịch vụ.

**Chạy `curl` không có `-L`, nhận `301` hoặc `302` rồi tưởng site hỏng.** Redirect là một HTTP response hợp lệ. Chỉ thêm `-L` khi bạn cố ý muốn đi theo `Location`, rồi đọc URL cuối cùng bằng `-w '%{url_effective}\n'`.

**Tưởng socket `0.0.0.0:3000` là lỗi.** `0.0.0.0` nghĩa là socket đang nhận connection trên mọi địa chỉ IPv4 của máy. `127.0.0.1` chỉ nhận connection từ chính máy đó.

**`systemctl status` xanh rồi kết luận port đã mở.** Status là unit. Listen là `ss`. Hai thứ lệch nhau thì client vẫn bị refused.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày Linux network services. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [dig](https://bind9.readthedocs.io/en/stable/manpages.html) — BIND 9, man page `dig` do ISC phát hành.
3. [ping(8)](https://man7.org/linux/man-pages/man8/ping.8.html) và [ss(8)](https://man7.org/linux/man-pages/man8/ss.8.html) — Linux man-pages.
4. [systemctl](https://www.freedesktop.org/software/systemd/man/latest/systemctl.html) — `is-active`, `status`, `enable --now`.
5. [iproute2](https://github.com/iproute2/iproute2) — source của bộ tool chứa `ss`.
6. [curl man page](https://curl.se/docs/manpage.html) — curl project.
7. [RFC 2606](https://www.rfc-editor.org/rfc/rfc2606) — domain dành cho ví dụ, gồm `example.com`.

## Tiếp theo

[DNS records](./04-dns.md)
