Skip to content
DevOps AI ToolKit
Newsletter

Kali Linux for DevOps Engineers · Part 6 of 15

Kali Linux Networking Fundamentals for DevOps

Difficulty: Intermediate ~22 min Part 6/15
Series progress6 / 15
Series curriculum (15 lessons)

Almost every infrastructure incident you will ever debug is, at some level, a networking problem: a service that cannot reach its database, a DNS name that resolves to the wrong address, a firewall that silently drops a packet, or a load balancer sending traffic to a port nothing is listening on. Kali Linux ships with a complete, up-to-date networking toolkit already installed, which makes it an excellent environment for learning to read a network the way a DevOps engineer needs to. This lesson builds the mental model and the muscle memory: the core concepts, then the exact commands you will reach for again and again.

You do not need any prior networking or penetration-testing knowledge. We will define each term as it comes up and connect every command back to a real operational question you will face on the job.

The Layers You Actually Need to Know

Networking is often taught as the seven-layer OSI model, but day-to-day troubleshooting only needs a handful of concepts working together. Think of a request leaving your machine as passing through a stack of checkpoints:

ConceptQuestion it answersCommand family
InterfaceDo I have a working network card / link?ip link
IP addressWhat is my address on the network?ip addr
CIDR / subnetWhich hosts are “local” vs. remote?ip addr, ip route
RouteWhere does traffic for this destination go?ip route
Default gatewayHow do I reach the rest of the world?ip route
ARP / neighborsWhat is the MAC address for a local IP?ip neigh
DNSWhat IP does this name resolve to?dig, nslookup
TCP / UDP portIs a service reachable and listening?ss, ping, traceroute
ApplicationDoes the service actually respond correctly?curl, wget

Keep this table in mind — the troubleshooting workflow later in the lesson walks these rows top to bottom.

A network interface is a connection point between your machine and a network — a physical NIC (eth0), a wireless card (wlan0), the loopback (lo, which is always 127.0.0.1 and lets a machine talk to itself), or a virtual interface created by Docker (docker0), a VPN (tun0), or a bridge. The link is the layer-2 state of that interface: is the cable plugged in / is the virtual link up?

IP addresses and CIDR

An IP address is your machine’s identity on a network, e.g. 10.0.5.23. It never travels alone — it comes with a subnet mask written in CIDR notation, like 10.0.5.23/24. The /24 means the first 24 bits identify the network and the remaining 8 bits identify the host. Practically:

  • /2410.0.5.010.0.5.255 (256 addresses, 254 usable) — everything in 10.0.5.x is “local.”
  • /1610.0.0.010.0.255.255 (65,536 addresses).
  • /32 → a single exact host.

Why a DevOps engineer cares: the CIDR block tells you which destinations your machine considers local (reached directly) versus remote (sent to the gateway). Misjudged subnet boundaries are behind a surprising number of “why can’t these two servers see each other” tickets and misconfigured security-group / NACL rules.

Default gateway

The default gateway is the router your machine sends traffic to when the destination is not on your local subnet — the on-ramp to everything outside your immediate network, including the internet. If your gateway is wrong or unreachable, local traffic works but everything else fails, which is a classic and very diagnosable symptom.

DNS

DNS (Domain Name System) translates human names like api.example.com into IP addresses. Your app almost always connects by name, so a DNS failure looks identical to the service being down even when the service is perfectly healthy. Separating “name resolution” from “connectivity” is one of the most valuable skills in this lesson.

ARP and the neighbor table

Within a single subnet, machines actually deliver frames using MAC addresses (hardware addresses), not IP addresses. ARP (Address Resolution Protocol) maps a local IP to its MAC, and the results are cached in the neighbor table. When two hosts on the same subnet cannot talk despite correct IPs, a stale or incomplete neighbor entry is a prime suspect.

TCP, UDP, and ports

TCP and UDP are the two transport protocols you will meet constantly:

  • TCP is connection-oriented and reliable — it performs a handshake, guarantees ordered delivery, and retransmits lost data. HTTP/HTTPS, SSH, and most databases use TCP.
  • UDP is connectionless and fire-and-forget — lower overhead, no delivery guarantee. DNS queries, DHCP, and many metrics/telemetry protocols use UDP.

A port is a 16-bit number (0–65535) that identifies which service on a host a connection is for. 443 is HTTPS, 22 is SSH, 5432 is PostgreSQL. “The service is listening on a port” means a process has bound to that port and is ready to accept connections. A huge share of outages come down to one of three states: nothing is listening on the expected port, something is listening on the wrong interface (e.g. 127.0.0.1 only), or a firewall is blocking the port in between.

💡 Note — Kali is Debian-based, so everything here applies directly to Ubuntu/Debian servers too. The ip and ss commands come from the modern iproute2 suite. You may still see older tutorials use ifconfig, route, and netstat — those are deprecated and sometimes missing on minimal images, so learn the modern equivalents shown below.

Start every investigation by confirming what interfaces exist and which addresses they carry.

# Show every interface and its IP address(es)
ip addr

# Shorter alias
ip a

Typical output for one interface:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ... state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

What this tells a DevOps engineer, line by line:

  • state UP — the link is up (layer 2 is healthy).
  • link/ether 02:42:ac:11:00:02 — the interface’s MAC address.
  • inet 172.17.0.2/16 — the IPv4 address and CIDR. Instantly you know the machine’s address and that its local subnet is 172.17.0.0/16.
  • mtu 1500 — the maximum packet size. MTU mismatches (common with VPNs and some overlay networks) cause the frustrating “small requests work, large ones hang” class of bug.

To focus on link state without addresses:

# Layer-2 status of every interface (UP/DOWN, MTU, MAC)
ip link show

# Bring an interface administratively up or down (needs sudo)
sudo ip link set eth0 up

ip link answers the very first troubleshooting question — is the interface even up? — before you waste time on higher layers. An interface in state DOWN or a missing interface entirely explains total connectivity loss immediately.

🛠️ DevOps Perspective — On cloud VMs and inside containers you will see interfaces you did not create: docker0, veth* pairs, cni0, flannel.1, eth0 in a namespace. Recognizing which interface carries the traffic you care about (and its CIDR) is essential for debugging container and Kubernetes networking, where a pod’s real address lives on a virtual interface, not the host’s eth0.

Where Traffic Goes: ip route and the Default Gateway

The routing table decides, for any destination IP, which interface and next hop to use.

# Show the routing table
ip route

# Alias
ip r

Example:

default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2

Read it like this:

  • default via 172.17.0.1 dev eth0 — the default gateway is 172.17.0.1; anything not matched by a more specific route goes there via eth0.
  • 172.17.0.0/16 dev eth0 — traffic for the local subnet is delivered directly on eth0, no gateway needed.

You can also ask the kernel exactly how it would route a specific destination — extremely useful for confirming whether traffic to a particular service leaves the expected interface (important on multi-homed or VPN-connected hosts):

# "How would I reach this specific address?"
ip route get 1.1.1.1

If ip route shows no default line, your machine cannot reach anything off-subnet — that alone explains “local works, internet fails.” If the default gateway is present but wrong, traffic leaves toward a dead end.

Local Delivery: ip neigh (ARP / Neighbor Table)

When a destination is on your local subnet, your machine needs the target’s MAC address. ip neigh shows that cache.

# Show the neighbor (ARP) table
ip neigh

# Alias
ip n
172.17.0.1 dev eth0 lladdr 02:42:5f:1a:0b:03 REACHABLE
172.17.0.5 dev eth0  INCOMPLETE

Interpreting the states:

  • REACHABLE — a valid, recently confirmed MAC mapping. Healthy.
  • STALE — cached but not recently verified; usually fine, will be re-checked on use.
  • INCOMPLETE / FAILED — the machine asked “who has this IP?” and got no answer. The host is not responding at layer 2 — it may be down, on a different subnet than you think, or blocked.

🔎 Troubleshooting Tip — When two hosts on the same subnet cannot connect but routing looks correct, check ip neigh. An INCOMPLETE entry for the target means the problem is below IP — the peer isn’t answering ARP at all. That points you at a downed host, a wrong subnet assumption, or layer-2 isolation (e.g. a security group / VLAN) rather than anything in your application.

What Is Listening: ss -tulpn

ss (socket statistics, the modern replacement for netstat) answers one of the most important questions in operations: which services are listening, and on which addresses and ports?

# List listening TCP + UDP sockets with process names and port numbers
sudo ss -tulpn

The flags:

  • -t — TCP sockets.
  • -u — UDP sockets.
  • -l — only listening sockets.
  • -p — show the owning process (needs sudo to see other users’ processes).
  • -nnumeric ports and addresses (don’t resolve names — faster and clearer).
Netid State  Local Address:Port  Peer Address:Port Process
tcp   LISTEN 127.0.0.1:5432      0.0.0.0:*         users:(("postgres",pid=812,...))
tcp   LISTEN 0.0.0.0:80          0.0.0.0:*         users:(("nginx",pid=940,...))

The Local Address column is where the real insight lives:

  • 0.0.0.0:80 — listening on all interfaces; reachable from the network.
  • 127.0.0.1:5432 — listening on loopback only; reachable from the local machine but not from other hosts. This single detail explains a huge number of “the database is up but my app on another server times out” incidents.

🛠️ DevOps Perspectivess -tulpn is your ground truth for “is the service actually up and bound where I expect?” Before blaming the network, firewall, or DNS, confirm the process is listening on the right address and port. If ss shows nothing on port 8080, no amount of firewall tweaking will help — the app never bound the port.

🔐 Security Note — Reviewing listening sockets is also a defensive habit: an unexpected process bound to a public 0.0.0.0 address is exactly what you want to catch on a server you operate. Auditing your own exposed ports is good hygiene. Only inspect and test systems you own or are explicitly authorized to test.

Reachability and Path: ping and traceroute

ping sends ICMP echo requests to confirm a host is reachable and to measure round-trip latency.

# Send 4 pings and stop (otherwise it runs forever; Ctrl-C to stop)
ping -c 4 1.1.1.1
64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=11.9 ms
--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss

What it tells you: whether basic IP connectivity works, the latency, and whether packets are being lost. Pinging an IP isolates connectivity from DNS; pinging a name additionally tests resolution. A useful trick: if ping 1.1.1.1 succeeds but ping example.com fails, connectivity is fine and your problem is DNS.

💡 Note — Many clouds and hosts deliberately block ICMP, so a failed ping does not always mean the host is down. Treat ping as a positive signal when it works, not proof of failure when it doesn’t — confirm with a TCP-level test like curl to the actual port.

traceroute maps the hops between you and a destination, showing where latency spikes or where the path dies.

# Trace the route to a host
traceroute example.com
 1  172.17.0.1   0.4 ms
 2  10.0.0.1     1.2 ms
 3  * * *
 4  93.184.216.34  12.0 ms

Each line is one router along the path. A run of * * * shows where responses stop — often the edge of your network or a firewall — which helps you tell “the problem is inside my infrastructure” from “the problem is out on the internet.”

Name Resolution: dig and nslookup

DNS deserves special attention because so many “outages” are really resolution problems. dig is the precise, scriptable tool of choice.

# Resolve the A record (IPv4 address) for a name
dig example.com

# Just the answer, no noise
dig +short example.com

# Ask a specific resolver (here, Cloudflare's 1.1.1.1) instead of the system default
dig +short example.com @1.1.1.1

# Look up other record types
dig +short api.example.com CNAME
dig +short example.com MX

What dig tells a DevOps engineer:

  • The answer — which IP(s) a name currently resolves to. Compare this against what you expect after a deploy or DNS change.
  • Using @1.1.1.1 lets you bypass your local resolver to test whether the problem is your machine’s resolver or the authoritative record itself.
  • Record types matter: a CNAME pointing at a decommissioned host, or an MX/A record that changed, are common root causes.

nslookup is the older, more conversational tool — handy and available almost everywhere, including on machines where dig isn’t installed:

# Simple resolution
nslookup example.com

# Query a specific DNS server
nslookup example.com 1.1.1.1

nslookup gives you the resolved address plus which server answered. For deep DNS work, prefer dig; for a quick check on an unfamiliar box, nslookup is fine.

🔎 Troubleshooting Tip — When a name fails, compare dig +short name @1.1.1.1 (a public resolver) with dig +short name (your system resolver). If the public resolver answers correctly but your system one doesn’t, the fault is local — check /etc/resolv.conf, a broken internal resolver, or a caching layer, not the authoritative DNS. For a deeper walkthrough see the DNS troubleshooting lesson.

Talking to the Application: curl and wget

Once you know a host is reachable and its name resolves, you need to confirm the application actually responds. curl is the DevOps Swiss-army knife for exactly this.

# Fetch a URL and print the body
curl https://example.com

# Show only response headers and status (great for a fast health check)
curl -I https://example.com

# Verbose: DNS, TCP connect, TLS handshake, headers — the whole request lifecycle
curl -v https://example.com

# Test a specific host:port directly, bypassing DNS entirely
curl -v http://127.0.0.1:8080/health

Why this is powerful: curl -v narrates every layer of the workflow in one command — it resolves the name, opens the TCP connection, performs the TLS handshake, and shows the HTTP status. Reading that output tells you exactly where a request breaks:

  • Fails at “Could not resolve host” → DNS.
  • Hangs at “Trying …” then times out → connectivity / firewall / nothing listening.
  • Fails during TLS → certificate or TLS problem.
  • Returns a 5xx/4xx status → the network is fine; the application is the problem.

wget overlaps with curl but shines for downloading files and mirroring:

# Download a file, saving it locally
wget https://example.com/file.tar.gz

# Quietly check that a URL is reachable without saving output
wget -q --spider https://example.com && echo "reachable"

Reach for curl when you are inspecting a request/response (headers, status, TLS, APIs) and wget when you are retrieving files.

🛠️ DevOps Perspectivecurl -v against a service’s health endpoint is often the single most efficient troubleshooting command you can run, because it exercises DNS, TCP, TLS, and the application in one shot and tells you which one failed. For deeper HTTP and API debugging — headers, methods, JSON payloads, status codes — continue with the HTTP & API troubleshooting lesson.

DevOps Networking Troubleshooting Workflow

When something can’t connect, resist the urge to guess. Work up the stack in order — each step rules out a whole class of causes so you never chase the wrong layer:

Interface

IP Address

Route

Gateway

DNS

TCP Port

Application

Walk it top to bottom, and stop at the first step that fails — that’s your root cause:

  1. Interfaceip link. Is the interface UP? If it’s DOWN or missing, nothing else matters. Fix the link first.
  2. IP Addressip addr. Does the interface have the address and CIDR you expect? No IP (or a stray 169.254.x.x link-local address) means DHCP failed or config is wrong.
  3. Routeip route get <destination>. Does traffic for your target leave the correct interface? On multi-homed or VPN hosts this catches traffic going out the wrong path.
  4. Gatewayip route shows the default gateway; ping <gateway> confirms it answers. If there’s no default route or the gateway is unreachable, off-subnet traffic dies here.
  5. DNSdig +short <name>. Does the name resolve to the IP you expect? Compare against a public resolver with @1.1.1.1. If resolution is wrong, fix DNS before touching anything else.
  6. TCP Portss -tulpn locally (is the service even listening, and on the right address?) and curl -v telnet-style or nc -vz host port remotely (can you open a connection?). This separates “nothing is listening” from “a firewall is blocking it.”
  7. Applicationcurl -v <url>. The connection succeeds but the service returns an error or hangs. The network is exonerated; the problem is the app, its config, or a dependency.

A worked example: your app on web-01 can’t reach the database db-01:5432.

  • ip link / ip addr on web-01 → interface UP, sensible IP. Move on.
  • ip route get <db-01-ip> → leaves via eth0 as expected. Move on.
  • ping <gateway> → replies (or the DB is on the same subnet, so skip). Move on.
  • dig +short db-01.internal → returns 10.0.5.9, the address you expect. DNS is fine. Move on.
  • On db-01, sudo ss -tulpn | grep 5432 → shows 127.0.0.1:5432, not 0.0.0.0:5432. Stop — root cause found. PostgreSQL is bound to loopback only, so remote hosts can never connect. Fix the bind address and the incident is resolved — no firewall spelunking required.

That is the entire value of the workflow: it turns “the network is broken” into a specific, provable finding in a few minutes.

🧪 Try It — In your Kali VM (see the installing Kali in a VM lesson if you haven’t set one up), run the whole stack against a benign public target and read each layer’s output:

ip link            # is my interface up?
ip addr            # my IP and CIDR
ip route           # my default gateway
dig +short example.com          # DNS resolution
ping -c 4 example.com           # connectivity + latency
curl -I https://example.com     # application responds (status line)
sudo ss -tulpn                  # what is listening on THIS machine

Then start a tiny local service — python3 -m http.server 8080 — and confirm you can see it appear in ss -tulpn and reach it with curl -v http://127.0.0.1:8080/. You now have the full workflow in your hands. Only run scans or probes against systems you own or are explicitly authorized to test.

Where to Go Next

You now have the fundamentals to inspect a network. The next lessons build directly on this foundation:

  • Discovering what’s on a network — map hosts and open ports with Nmap for DevOps (always against your own lab or authorized infrastructure).
  • Seeing the actual packets — when higher-level tools aren’t enough, capture and read traffic with tcpdump for DevOps.
  • Going deeper on names — the dedicated DNS troubleshooting lesson.
  • Debugging services end to endHTTP & API troubleshooting for status codes, headers, and payloads.

If your day job leans heavily on containers, the same interface, route, and port concepts reappear inside Docker networking — the Docker Academy is a good companion once you’re comfortable here.

What You Learned

  • The layered model that matters operationally — interface → IP/CIDR → route → gateway → DNS → port → application — and how each layer maps to a specific concept and command.
  • The modern iproute2 toolkitip addr and ip link to read interfaces and links, ip route to inspect routes and the default gateway, and ip neigh to check local (ARP) delivery.
  • ss -tulpn to see exactly which services are listening and, critically, on which address — the difference between 0.0.0.0 and 127.0.0.1 is behind countless connectivity incidents.
  • The right tool per questionping/traceroute for reachability and path, dig/nslookup for DNS (and how to test against a public resolver), and curl/wget to confirm the application itself responds.
  • A repeatable troubleshooting workflow that walks up the stack, stops at the first failing layer, and turns vague “the network is broken” reports into specific, provable root causes.

Affiliate Disclosure: Some links on this page are affiliate links. If you purchase through one of these links, DevOps AI Toolkit may earn a commission at no additional cost to you. See our affiliate disclosure.

← Back to Kali Linux for DevOps Engineers

Related on DevOps AI Toolkit