Networking Fundamentals & Debugging

OSI Model, TCP/IP, Packet Analysis, DNS, Port Scanning

Introduction

Before you can secure a network or debug a connectivity issue, you need to understand how networks actually work. This lesson covers the OSI and TCP/IP models, the Python tools for exploring networks, and the debugging utilities (tcpdump, Wireshark) that security engineers use daily. You'll also write an educational port scanner in Python, a key reconnaissance tool.

OSI Model, 7 Layers

The OSI (Open Systems Interconnection) model describes how data travels from an application through the network stack to another system. Each layer has a specific responsibility.

L7
Application
Protocols: HTTP, HTTPS, SMTP, DNS, FTP
Data
App-level auth, TLS termination
L6
Presentation
Protocols: SSL/TLS, JPEG, ASCII, encryption
Data
Encryption/decryption, encoding
L5
Session
Protocols: NetBIOS, RPC, SIP
Data
Session management, auth
L4
Transport
Protocols: TCP, UDP, SCTP
Segments
Port filtering, TLS, rate limiting
L3
Network
Protocols: IP, ICMP, OSPF, BGP
Packets
Firewalls, IP ACLs, routing
L2
Data Link
Protocols: Ethernet, Wi-Fi, ARP, MAC
Frames
MAC filtering, 802.1X
L1
Physical
Protocols: Cable, Radio, Fiber, Hubs
Bits
Physical access controls

TCP/IP Model, 4 Layers

The TCP/IP model is the practical implementation used by the internet. It maps OSI layers to a simpler 4-layer model.

TCP/IP LayerOSI EquivalentKey ProtocolsPython Module
ApplicationL5–L7HTTP, HTTPS, DNS, SSH, SMTPhttp.client, smtplib
TransportL4TCP, UDPsocket
InternetL3IP, ICMP, ARPsocket, scapy
LinkL1–L2Ethernet, Wi-Fi, ARPscapy

Python: Network Information & DNS

# network_info.py
import socket
import ipaddress

# ── Basic socket information ─────────────────────────────────
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"Hostname:  {hostname}")
print(f"Local IP:  {local_ip}")

# ── DNS resolution ────────────────────────────────────────────
domain = "python.org"
ip = socket.gethostbyname(domain)
print(f"\n{domain} → {ip}")

# Reverse lookup
try:
    reverse = socket.gethostbyaddr(ip)
    print(f"{ip} → {reverse[0]}")
except socket.herror:
    print(f"No reverse DNS for {ip}")

# Get all IPs (IPv4 + IPv6)
results = socket.getaddrinfo(domain, 443, proto=socket.IPPROTO_TCP)
print(f"\nAll addresses for {domain}:443 (HTTPS):")
for family, _type, _proto, _canonname, sockaddr in results:
    family_name = "IPv6" if family == socket.AF_INET6 else "IPv4"
    print(f"  {family_name}: {sockaddr[0]}")

# ── IP address utilities ─────────────────────────────────────
network = ipaddress.ip_network("192.168.1.0/24", strict=False)
print(f"\nNetwork: {network}")
print(f"Hosts:   {network.num_addresses - 2} (first: {list(network.hosts())[0]})")
print(f"Is private: {network.is_private}")

# Check if IP is in network
test_ip = ipaddress.ip_address("192.168.1.100")
print(f"192.168.1.100 in {network}: {test_ip in network}")
Expected Output:
Hostname:  my-machine
Local IP:  192.168.1.42

python.org → 151.101.128.223
151.101.128.223 → dualstack.python.map.fastly.net

All addresses for python.org:443 (HTTPS):
  IPv4: 151.101.128.223
  IPv6: 2a04:4e42:1e::223

Network: 192.168.1.0/24
Hosts:   254 (first: 192.168.1.1)
Is private: True
192.168.1.100 in 192.168.1.0/24: True

tcpdump & Wireshark Cheat Sheet

# tcpdump, capture and filter packets on the command line

# Capture all traffic on interface eth0
sudo tcpdump -i eth0

# Capture only HTTP/HTTPS traffic
sudo tcpdump -i eth0 'tcp port 80 or tcp port 443'

# Capture traffic to/from specific host
sudo tcpdump -i eth0 host 192.168.1.100

# Save capture to file for Wireshark analysis
sudo tcpdump -i eth0 -w capture.pcap

# Read saved capture
sudo tcpdump -r capture.pcap

# Show packet contents in ASCII
sudo tcpdump -i eth0 -A 'tcp port 80'

# Show packet contents in hex+ASCII (very detailed)
sudo tcpdump -i eth0 -X 'tcp port 8080'

# Capture DNS queries
sudo tcpdump -i eth0 'udp port 53'

# Filter by source IP and port
sudo tcpdump -i eth0 'src 10.0.0.1 and dst port 443'

# ── Wireshark filter expressions ─────────────────────────────
# ip.addr == 192.168.1.100       → All traffic to/from IP
# tcp.port == 443                → HTTPS traffic
# http.request.method == "POST"  → HTTP POST requests only
# tcp.flags.syn == 1             → TCP SYN packets (new connections)
# frame contains "password"      → Frames with the word "password"

Python: Port Scanner (Educational)

Authorized Use Only

Only scan systems you own or have explicit written permission to test. Unauthorized port scanning is illegal in most jurisdictions. The following script is for learning on your own localhost or lab environment.

# port_scanner.py, Educational port scanner for authorized use only
import socket
import concurrent.futures
from datetime import datetime

COMMON_PORTS = {
    21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP",
    53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP",
    443: "HTTPS", 3306: "MySQL", 5432: "PostgreSQL",
    6379: "Redis", 8080: "HTTP-Alt", 8443: "HTTPS-Alt",
    27017: "MongoDB",
}

def scan_port(host: str, port: int, timeout: float = 1.0) -> dict | None:
    """Return port info if open, None if closed."""
    try:
        with socket.create_connection((host, port), timeout=timeout):
            service = COMMON_PORTS.get(port, "unknown")
            return {"port": port, "service": service, "status": "OPEN"}
    except (socket.timeout, ConnectionRefusedError, OSError):
        return None

def scan_host(host: str, ports: list[int], workers: int = 50) -> list[dict]:
    """Scan a host for open ports using a thread pool."""
    open_ports = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
        futures = {executor.submit(scan_port, host, p): p for p in ports}
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            if result:
                open_ports.append(result)
    return sorted(open_ports, key=lambda x: x["port"])

# ── Scan localhost only (safe for educational use) ────────────
target = "127.0.0.1"
ports_to_scan = list(COMMON_PORTS.keys())

print(f"Scanning {target}, {datetime.now().strftime('%H:%M:%S')}")
print(f"Ports: {len(ports_to_scan)}")
print("-" * 40)

results = scan_host(target, ports_to_scan)

if results:
    for r in results:
        print(f"  PORT {r['port']:5d}/tcp   OPEN   {r['service']}")
else:
    print("  No open ports found")

print(f"\nScan complete: {len(results)} open port(s) found")
Expected Output:
Scanning 127.0.0.1, 14:23:07
Ports: 15
----------------------------------------
  PORT    22/tcp   OPEN   SSH
  PORT    80/tcp   OPEN   HTTP
  PORT  5432/tcp   OPEN   PostgreSQL
  PORT  6379/tcp   OPEN   Redis

Scan complete: 4 open port(s) found

Key Takeaways

  • OSI has 7 layers, each layer handles a specific function and security controls exist at every layer
  • TCP/IP simplifies to 4 layers, Application, Transport, Internet, Link, the internet actually runs on this
  • Python's socket module, provides direct access to L4 (TCP/UDP) and lower via raw sockets
  • tcpdump is your first debugging tool, use it to see exactly what traffic is on the wire
  • Port scanning reveals the attack surface, security engineers regularly scan their own infrastructure
  • ThreadPoolExecutor speeds up I/O-bound scanning, concurrent connections dramatically reduce scan time
What's Next?

Lesson 6 goes deep into socket programming, building real TCP and UDP servers and clients in Python.

  • TCP socket server and client, full working examples with output
  • UDP sockets, connectionless communication
  • Multi-client server with threading, handling concurrent connections
  • Async sockets with asyncio, modern non-blocking I/O