Homelab Academy / Modules
Guided practice + break/fix + lab gate.
Module: Network Operations
Activity type: Lab (Practice)
Lab risk: low
Objective: Distinguish an IPv4 address from a subnet prefix, network address, broadcast address, and usable host range
Read-Only Network Inspection and Offline Route Modeling
Do not change interface addresses, routes, DNS settings, network services, or router settings.
All created or modified lab artifacts must remain under /opt/lab-classroom/class23/.
The commands that inspect interfaces and routes are read-only.
The classroom directory must be provisioned before the lab begins; stop if it does not exist.
1
Confirm that the authorized lab directory already exists and enter it. Do not create a substitute elsewhere.
test -d /opt/lab-classroom/class23/ && test -w /opt/lab-classroom/class23/ && cd /opt/lab-classroom/class23/ && pwd
2
Inspect interface names, operational state, and assigned addresses without changing them.
ip -brief link
ip -brief address
3
Inspect the IPv4 routing table. Identify connected prefixes, any default route, its next hop, and the selected interface.
ip -4 route show
4
Ask the kernel which existing route it would use for two documentation addresses. This performs route lookup only and does not send application traffic or alter the table.
ip -4 route get 192.0.2.1
ip -4 route get 198.51.100.1
5
Run an offline Python exercise. It validates subnet boundaries and models longest-prefix route selection, then writes one JSON artifact inside the authorized directory.
python3 - <<'PY'
from ipaddress import ip_interface, ip_network, ip_address
from json import dumps
from pathlib import Path
base = Path('/opt/lab-classroom/class23')
if not base.is_dir():
raise SystemExit('Authorized lab directory is missing')
examples = ['192.168.50.77/26', '10.20.30.140/27', '172.16.8.9/24']
subnets = []
for value in examples:
iface = ip_interface(value)
net = iface.network
hosts = list(net.hosts())
subnets.append({
'input': value,
'network': str(net),
'netmask': str(net.netmask),
'broadcast': str(net.broadcast_address),
'first_host': str(hosts[0]),
'last_host': str(hosts[-1]),
'conventional_host_count': len(hosts)
})
routes = [
(ip_network('0.0.0.0/0'), '192.168.50.65', 'default'),
(ip_network('10.0.0.0/8'), '192.168.50.66', 'private-summary'),
(ip_network('10.20.0.0/16'), '192.168.50.67', 'site-route'),
(ip_network('192.168.50.64/26'), None, 'connected')
]
def select_route(destination):
target = ip_address(destination)
matches = [route for route in routes if target in route[0]]
chosen = max(matches, key=lambda route: route[0].prefixlen)
return {
'destination': destination,
'selected_prefix': str(chosen[0]),
'next_hop': chosen[1],
'route_name': chosen[2]
}
selections = [select_route(value) for value in ['192.168.50.100', '10.20.30.40', '10.30.1.1', '203.0.113.20']]
assert subnets[0]['network'] == '192.168.50.64/26'
assert subnets[0]['broadcast'] == '192.168.50.127'
assert selections[1]['selected_prefix'] == '10.20.0.0/16'
assert selections[2]['selected_prefix'] == '10.0.0.0/8'
assert selections[3]['selected_prefix'] == '0.0.0.0/0'
result = {'subnet_calculations': subnets, 'route_selections': selections}
out = base / 'model_results.json'
out.write_text(dumps(result, indent=2) + '\n', encoding='utf-8')
print(out.read_text(encoding='utf-8'))
PY
6
Validate the generated artifact and review the key modeled decisions.
python3 -m json.tool /opt/lab-classroom/class23/model_results.json
python3 - <<'PY'
import json
from pathlib import Path
p = Path('/opt/lab-classroom/class23/model_results.json')
data = json.loads(p.read_text(encoding='utf-8'))
assert data['subnet_calculations'][0]['network'] == '192.168.50.64/26'
assert data['subnet_calculations'][0]['first_host'] == '192.168.50.65'
assert data['subnet_calculations'][0]['last_host'] == '192.168.50.126'
assert data['route_selections'][1]['selected_prefix'] == '10.20.0.0/16'
print('Class 23 model verification passed')
PY
| Symptom | Likely cause | Fix |
|---|---|---|
| The directory check fails. | The classroom directory was not provisioned or the current account cannot write to it. | Stop the lab and ask the lab administrator to provision /opt/lab-classroom/class23/ with appropriate ownership. Do not redirect artifacts to another location. |
| The ip command is not found. | The system does not have Linux iproute2 installed or the lab is being run on another operating system. | Use the compatibility commands for read-only inspection, or move to a supported Linux lab host. The offline Python portion can still run if Python 3 is available. |
| No default route appears. | The host may be isolated, use policy routing, be inside a restricted container, or have incomplete network configuration. | Do not add a route for this exercise. Record the observation and continue with the offline model. |
| The route lookup reports unreachable. | The selected network namespace or host has no route matching the documentation destination. | Treat the result as evidence about the current routing table. Do not alter networking; continue with the deterministic model. |
| Python reports ModuleNotFoundError for ipaddress. | The interpreter is obsolete or incomplete. | Use a supported Python 3 standard installation. The ipaddress module is part of the Python 3 standard library. |
| Writing model_results.json fails with PermissionError. | The current user cannot write to the authorized lab directory. | Stop and have the directory ownership corrected by the lab administrator. Do not use another path and do not elevate commands merely to bypass the classroom permissions. |
| The learner expects a lower route metric to beat a more-specific prefix. | Metric comparison has been confused with longest-prefix matching. | First choose the most-specific matching prefix. Compare route preferences or metrics only among routes considered equivalent under the platform's route-selection rules. |
Interface and route output can reveal internal addressing, gateway addresses, interface names, virtual network design, and VPN presence. Redact this information before sharing screenshots or logs.
Use the documentation networks 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24 in examples rather than presenting real public targets as lab infrastructure.
A wrong prefix length can unintentionally classify a remote host as local or a local host as remote, causing outages or unexpected traffic paths.
An untrusted default gateway can observe or redirect traffic. Gateway configuration should be controlled through authenticated administrative processes.
Route inspection does not prove end-to-end reachability. Path policy, forwarding state, filtering, return routes, and service availability can all affect delivery.
Do not publish model_results.json if it has been modified to include real infrastructure details.
This lesson intentionally avoids privileged network changes so that an addressing mistake cannot disconnect the lab host.
The lab does not change networking. Rollback only removes the generated JSON artifact inside the authorized classroom directory.
python3 - <<'PY'
from pathlib import Path
p = Path('/opt/lab-classroom/class23/model_results.json')
if p.exists():
p.unlink()
print('Lab artifact removed' if not p.exists() else 'Artifact still exists')
PY
test ! -e /opt/lab-classroom/class23/model_results.json
ip -4 route show