This reference covers three critical network services that underpin almost every IP network: DHCP (automatic IP address assignment), NAT (Network Address Translation, allowing private networks to reach and be reached from the public internet), and DNS (translating human-readable names into IP addresses). The material builds a complete working lab network step by step, using Cisco routers and Ubuntu Linux servers virtualized inside Cisco Modeling Labs (CML), progressing from basic IP assignment to a fully routed, NAT’d, and name-resolved infrastructure.
Table of Contents
- Module 1: Understanding and Configuring DHCP on a Cisco Router
- Module 2: Managing Network Address Translation (NAT) on a Router
- Module 3: Understanding DNS Fundamentals and the Global DNS Hierarchy
- Module 4: Managing DNS on a Linux Server
- Summary
Module 1: Understanding and Configuring DHCP on a Cisco Router
This module covers IP address assignment using DHCP, the Dynamic Host Configuration Protocol (RFC 2131). The lab environment builds toward a complete working network infrastructure, starting with a single DHCP-enabled LAN and expanding to a multi-LAN topology using DHCP relay agents.
DHCP and the DORA Process
Manually assigning an IP address to every device that connects to a network — including mobile devices — is impractical. It requires maintaining a running list of already-assigned addresses and manually releasing them when devices leave the network. DHCP automates this entire process.
DHCP address assignment follows the DORA sequence:
- Discover – When a client starts up, it sends an IP broadcast on UDP port 67, looking for any DHCP server on the network.
- Offer – One or more DHCP servers can respond with a DHCP offer. This is still an IP broadcast, but it can now target the client’s MAC address (Layer 2 address) directly, because that MAC address was included in the Discover packet.
- Request – The client broadcasts a DHCP request back to the network, saying, in effect, “I would like this IP address.” It is still a broadcast, but it targets the MAC address of the server that issued the offer.
- Acknowledge – The server responds with an acknowledgement, again as a broadcast but targeted at the client’s MAC address. Once the client receives the acknowledgement, it has an IP address it can use.
sequenceDiagram
participant C as DHCP Client
participant S as DHCP Server
C->>S: 1. DHCP Discover (broadcast, UDP/67)
S->>C: 2. DHCP Offer (broadcast, targeted at client MAC)
C->>S: 3. DHCP Request (broadcast, targeted at server MAC)
S->>C: 4. DHCP Acknowledge (broadcast, targeted at client MAC)
| Step | Message | Direction | Transport | Targeting |
|---|---|---|---|---|
| 1 | Discover | Client → Network | Broadcast, UDP/67 | None (client has no IP yet) |
| 2 | Offer | Server → Network | Broadcast | Targets client MAC address |
| 3 | Request | Client → Network | Broadcast | Targets server MAC address |
| 4 | Acknowledge (ACK) | Server → Network | Broadcast | Targets client MAC address |
Lab Environment: Cisco Modeling Labs
The labs in this course use Cisco Modeling Labs (CML), available in a free tier, to run virtualized Cisco and Linux operating systems. CML itself runs as a virtual machine, so the host system needs to support nested virtualization.
Reference documentation: https://developer.cisco.com/docs/modeling-labs/cml-free/#cisco-modeling-labs---free
| Requirement | Minimum |
|---|---|
| Virtualization support | Nested virtualization support in the host CPU |
| CPU cores | 4 cores |
| RAM | 8 GB |
Observing the DORA Process with Wireshark
A practical way to observe DORA on the wire is to capture the traffic with Wireshark, a network protocol analyzer, on a Windows client:
- Download and install the Wireshark Windows x64 installer, accepting the GNU Public License and default component selection.
- During installation, also install Npcap (from nmap.org) — this is the packet-capture driver Wireshark relies on to actually capture traffic on the wire. USBPcap can be skipped if there are no USB capture devices involved.
- Launch Wireshark, select the active network interface, and apply a capture filter for
port 67 or port 68(UDP port 67 is where the DHCP server listens; UDP port 68 is where the DHCP client listens). - Open a PowerShell window alongside Wireshark and check the current IP configuration:
ipconfig
- Release the current lease. This immediately tells the DHCP server that the client is finished with its address — note this itself does not trigger a new DORA exchange:
ipconfig /release
After a release, the interface typically falls back to an Automatic Private IP Addressing (APIPA) address in the 169.254.0.0/16 range, which is a strong indicator that no DHCP server is currently reachable.
- Trigger the full DORA exchange by renewing the lease:
ipconfig /renew
Wireshark then captures all four DORA packets in sequence: DHCP Discover sent by the client, DHCP Offer returned by the server, DHCP Request sent back by the client, and the final DHCP Acknowledgement from the server — confirming the four-way handshake used to automate IP address assignment.
Bootstrapping DHCP on Cisco Routers
DHCP is not only a Windows or Linux server role — infrastructure devices such as routers and switches are excellent hosts for critical services because they are powered on and available at all times. Cisco routers (and other vendors’ routers) can pick up their configuration through bootstrap files, retrieved either from the network or loaded locally.
The bootstrap configuration for the first router in the lab (Router1) sets the hostname, configures a “backbone” interface that connects to other routers, configures a second interface facing the internal/departmental network, and defines two DHCP pools:
hostname Router1
interface GigabitEthernet0/0
ip address 192.168.20.1 255.255.255.0
no shutdown
interface GigabitEthernet0/1
ip address 192.168.10.1 255.255.255.0
no shutdown
ip route 192.168.30.0 255.255.255.0 192.168.20.2
ip dhcp excluded-address 192.168.10.1 192.168.10.10
ip dhcp pool LAN_POOL_10
network 192.168.10.0 255.255.255.0
default-router 192.168.10.1
dns-server 8.8.8.8
ip dhcp excluded-address 192.168.30.1 192.168.30.10
ip dhcp pool LAN_POOL_30
network 192.168.30.0 255.255.255.0
default-router 192.168.30.1
dns-server 8.8.8.8
end
Key points in this configuration:
- GigabitEthernet0/0 is the backbone interface (192.168.20.1) connecting Router1 to other routers.
- GigabitEthernet0/1 faces the internal/departmental network (192.168.10.1). These two interfaces sit on different subnets.
- A static route to the
192.168.30.0/24network is pre-configured, pointing at Router2 (192.168.20.2), even though that network is not directly connected to Router1 and will only exist once Router2 is added. ip dhcp excluded-addressreserves the first addresses of each pool (.1–.10) for statically-addressed devices such as the router itself or servers.- LAN_POOL_10 serves the
192.168.10.0/24network, advertising Router1 (192.168.10.1) as the default router and a public DNS server (8.8.8.8) initially. - LAN_POOL_30 serves the
192.168.30.0/24network (physically attached to Router2), advertising192.168.30.1as the default router, again with8.8.8.8as DNS initially. Router1 can manage this remote pool because Router2 will be configured with a DHCP relay pointing back at Router1 (covered later in this module).
Building a Single-LAN DHCP Topology in CML
CML lets you build a network topology visually and inspect its topological layout. The initial single-LAN lab is built as follows:
- From the Nodes palette, drag in an IOSv virtual router node and an Ubuntu Linux server node.
- Rename the router node to
Router1and the Ubuntu node toClient1via the node’s Settings tab. - Connect them: right-click on
Client1, choose Add Link, and drag the connection toRouter1. On the router side, select GigabitEthernet0/1 (not the default0/0, which is reserved for the backbone network). CML displays the resulting interface names on the canvas (e.g.,G0/1andens2). - Open the Config tab on
Router1and paste in the bootstrap configuration shown above (fromhostname Router1throughend). - Save the configuration, then start the node. Startup can take a few minutes.
- Open the console on
Router1. During boot, the router will time out trying to reach a network-based configuration source and fall back to the locally-provided configuration. - At the unprivileged prompt, use
enableto reach privileged mode, then inspect the interfaces:
Router1> enable
Router1# show ip interface brief
By default the router has four interfaces; two have been configured (20.1 on the backbone, 10.1 on the internal LAN) and the rest remain unassigned.
- Check DHCP bindings (command can be abbreviated, e.g.
sh ip dhcp bind):
Router1# show ip dhcp binding
With no clients powered on yet, no leases will be shown.
- Start
Client1. Ubuntu boots as a DHCP client by default. Log in with the default credentialscisco/cisco, then check the assigned address:
ip a
Expected result: 192.168.10.11 — the first usable address in the pool after the excluded range.
- Back on
Router1,show ip dhcp bindingnow shows the leased address, the client identifier (the client’s MAC address), and the lease expiration time (24 hours by default, since no explicit lease time was configured in the pool).
Centralizing DHCP with Relay Agents
Rather than configuring and maintaining a DHCP server on every subnet, a DHCP relay agent can forward DHCP requests from a remote subnet to a central DHCP server. This means DHCP only needs to be configured and managed on one server, even though it can service multiple LANs.
To extend the lab, a second router (Router2) is introduced, connected to Router1 over a new “backbone” network (192.168.20.0/24). Router2’s own LAN becomes the 192.168.30.0/24 network — a pool that was already pre-configured on Router1 (LAN_POOL_30). Router2 does not need to run its own DHCP server; it simply relays DHCP requests back to Router1.
Router2’s bootstrap configuration:
hostname Router2
interface GigabitEthernet0/0
ip address 192.168.20.2 255.255.255.0
no shutdown
interface GigabitEthernet0/1
ip address 192.168.30.1 255.255.255.0
ip helper-address 192.168.20.1
no shutdown
ip route 0.0.0.0 0.0.0.0 192.168.20.1
end
Key points:
- GigabitEthernet0/0 is the backbone interface (
192.168.20.2), consistently facing the same direction as Router1’s backbone interface. - GigabitEthernet0/1 faces the internal network (
192.168.30.1), and carries theip helper-address 192.168.20.1command — this is the DHCP relay agent configuration. It tells Router2 to forward any DHCP broadcast received on this interface as a unicast to192.168.20.1(Router1), which is reachable because both routers share the backbone network. - A default route (
0.0.0.0 0.0.0.0via192.168.20.1) sends any traffic Router2 doesn’t have an explicit route for back through Router1.
flowchart LR
Client2[Client2 on LAN30] -->|DHCP Discover broadcast| R2[Router2 G0/1]
R2 -->|Unicast via ip helper-address 192.168.20.1| R1[Router1 - DHCP Server]
R1 -->|DHCP Offer and Ack for LAN_POOL_30| R2
R2 -->|Relayed to client| Client2
Building a Multi-LAN DHCP Relay Topology
- Add a second IOSv node and rename it
Router2; add a second Ubuntu node and rename itClient2. - Link the backbone: right-click
Router2, drag a connection toRouter1, matching GigabitEthernet0/0 to GigabitEthernet0/0. - Link
Client2toRouter2using GigabitEthernet0/1 as the internal-facing interface. - Paste the Router2 bootstrap configuration into its Config tab, save, and start the node (startup takes a few minutes, same as before).
- Start
Client2. Log in withcisco/cisco, then check the address:
ip a
Expected result: 192.168.30.11 — the first usable address from the LAN_POOL_30 range, obtained via the relay agent on Router2 even though DHCP is only running on Router1.
- Because DHCP is centralized on Router1, both leases are visible from a single point of management:
Router1# show ip dhcp binding
This shows both the 10.11 and 30.11 leases, including each client’s client-identifier (MAC address) and lease expiry time — proof that a single DHCP server, combined with relay agents, can serve multiple LANs.
Overall topology at the end of Module 1:
flowchart LR
subgraph Router1
R1G0[G0/0 - Backbone 192.168.20.1]
R1G1[G0/1 - LAN10 192.168.10.1]
end
subgraph Router2
R2G0[G0/0 - Backbone 192.168.20.2]
R2G1[G0/1 - LAN30 192.168.30.1 helper-address to R1]
end
R1G0 --- R2G0
R1G1 --- Client1[Client1 192.168.10.11]
R2G1 --- Client2[Client2 192.168.30.11]
Module 2: Managing Network Address Translation (NAT) on a Router
This module covers routing between the private networks built in Module 1, connecting the lab to the internet, and enabling Network Address Translation (NAT) so private hosts can reach — and, selectively, be reached from — the public internet. It covers Source NAT (SNAT), Destination NAT (DNAT), and Port Mapping (PNAT), along with access control lists that govern who may use NAT.
Routing Fundamentals and Static Routes
A router automatically knows about the networks it is physically connected to. At this point in the lab:
- Router1 is connected to
192.168.10.0/24(LAN10) and192.168.20.0/24(the backbone), and manages DHCP pools for both192.168.10.0/24and192.168.30.0/24. - Router1 has no knowledge of
192.168.30.0/24, because that network is only directly connected to Router2.
Private IP address ranges (such as 192.168.0.0/16) can be reused by anyone, so routers cannot automatically forward or propagate routing information about them across administrative boundaries — the routes must be configured explicitly as static routes.
- On Router1, a static route sends traffic for the
192.168.30.0/24network via Router2’s backbone address:
ip route 192.168.30.0 255.255.255.0 192.168.20.2
- On Router2, rather than pointing explicitly at
192.168.10.0/24, a default route sends any unknown destination back through Router1:
ip route 0.0.0.0 0.0.0.0 192.168.20.1
Each router’s DHCP pool also advertises the local router as the default gateway for its clients (10.1 or 30.1, depending on the subnet).
To inspect the routing table, use show ip route (which can be abbreviated, e.g. sh ip ro, as long as the abbreviation is unambiguous):
| Command | Purpose |
|---|---|
show ip route | Show the full routing table |
show ip route connected | Show only directly-connected networks |
show ip route static | Show only statically-configured routes |
show ip route 0.0.0.0 | Show only the default route (if configured) |
Verifying Routing and Route Tables
With the lab started (both routers boot faster on subsequent starts since they already have a saved configuration), connectivity can be verified from Client1:
ip a
# 192.168.10.11
ping 192.168.30.11
# Ctrl+C to stop, since ping runs continuously without a count
On Router1:
show ip route # full table
show ip route connected # shows 10 and 20 networks
show ip route static # shows the 30 network (via 192.168.20.2)
show ip route 0.0.0.0 # not present yet: no default route configured on Router1
On Router2:
show ip route # full table
show ip route static # only route is the default route, via 192.168.20.1
show ip route connected # shows 20 and 30 networks
show ip route 0.0.0.0 # default route present: via 192.168.20.1
This confirms end-to-end connectivity between the known, internally-managed networks — but it does not yet provide access to the outside world.
Connecting the Lab to the Internet
To reach the internet, an External Connector node is added in CML:
- Drag an External Connector node onto the canvas (this is not a virtual machine — it is simply a connector to the outside world).
- Link it to
Router1, using GigabitEthernet0/2 (since0/0is already used for the backbone network). - Configure the External Connector: set its target to System Bridge (bridging to the physical network the CML VM itself is attached to, rather than the default NAT interface).
- Start the External Connector.
- Configure Router1’s external-facing interface to obtain an address via DHCP from the outside network:
Router1> enable
Router1# configure terminal
Router1(config)# interface G0/2
Router1(config-if)# ip address dhcp
Router1(config-if)# no shutdown
Router1(config-if)# exit
Router1(config)# exit
Router1# write memory
Verification:
Router1# show ip route 0.0.0.0
# default route via 192.168.4.1 (the lab host's own network router)
Router1# show ip interface brief
# GigabitEthernet0/2 now shows an address such as 192.168.4.28
At this point, Router1 itself can reach the internet:
Router1# ping 8.8.8.8
# Success — Router1 is directly connected to the 192.168.4.0/24 network
However, from Router2:
Router2# ping 8.8.8.8
# No response
Router2 can send packets outward via its default route through Router1, but the response cannot find its way back, because the wider internet has no route back to Router2’s private address (192.168.20.2) — private addressing is not routable across the public internet. This is precisely the problem that Network Address Translation (NAT) solves.
Understanding Source NAT (SNAT)
Source NAT (SNAT) is the most common form of NAT: internal hosts share the router’s single external/public IP address when initiating connections to the internet. This is often referred to as NAT overload, or Port Address Translation (PAT), because multiple internal hosts multiplex their traffic through one public IP address using different source ports.
Configuration proceeds as follows:
- Mark the “inside” interfaces (those facing internal networks):
interface GigabitEthernet0/0
ip nat inside
interface GigabitEthernet0/1
ip nat inside
- Mark the “outside” interface (facing the internet — already configured for
ip address dhcp):
interface GigabitEthernet0/2
ip nat outside
- Define which internal networks are permitted to use NAT via an access control list:
access-list 1 permit 192.168.10.0 0.0.0.255
access-list 1 permit 192.168.20.0 0.0.0.255
access-list 1 permit 192.168.30.0 0.0.0.255
- Bind the ACL to the outside interface with
overload, so the single public address onGigabitEthernet0/2can be shared by many internal devices:
ip nat inside source list 1 interface GigabitEthernet0/2 overload
Configuring SNAT on a Cisco Router
Only Router1 needs to be configured, since it hosts the external connection. Full command sequence, as entered at the console:
Router1> enable
Router1# configure terminal
Router1(config)# interface G0/0
Router1(config-if)# ip nat inside
Router1(config-if)# exit
Router1(config)# interface G0/1
Router1(config-if)# ip nat inside
Router1(config-if)# exit
! GigabitEthernet0/2 is already configured as "ip nat outside"
Router1(config)# access-list 1 permit 192.168.10.0 0.0.0.255
Router1(config)# access-list 1 permit 192.168.20.0 0.0.0.255
Router1(config)# access-list 1 permit 192.168.30.0 0.0.0.255
Router1(config)# ip nat inside source list 1 interface GigabitEthernet0/2 overload
Router1(config)# end
Router1# write memory
Verification from a client (e.g. Client1 / Ubuntu):
ping 8.8.8.8
# Success — the response comes back through NAT
sudo apt update
# Full two-way communication with package repository servers over the internet
All internal hosts now share Router1’s single external IP address when accessing internet resources.
flowchart LR
C1[Client 192.168.10.11] -->|"Source: 192.168.10.11"| R1NAT[Router1 NAT Table - SNAT Overload]
R1NAT -->|"Source: 192.168.4.28 public IP"| Internet((Internet))
Internet -->|"Destination: 192.168.4.28 port 80 or 22"| R1NAT
R1NAT -->|"Destination: 192.168.10.11 port 80 or 22 via PNAT"| C1
Understanding Destination NAT (DNAT) and Port Mapping
Destination NAT (DNAT) is the reverse scenario: instead of hiding many internal hosts behind one external IP, multiple public IP addresses on the router’s external interface are each mapped to a specific internal host, making the whole internal server reachable from the outside. For example, a public IP address 203.192.56.12 could be mapped through to internal server 192.168.10.11.
Port Mapping (PNAT), also a form of Destination NAT, is more granular: rather than exposing an entire internal host, only specific services are exposed on the router’s single external IP address, differentiated by port number. For example:
- Incoming web traffic (port 80) is redirected to the internal web server.
- Incoming SSH traffic (port 22) is redirected to the internal console/management server.
Example configuration for redirecting incoming HTTP traffic:
Router1> enable
Router1# configure terminal
! Redirect external HTTP (port 80) to the internal web server
Router1(config)# ip nat inside source static tcp 192.168.10.11 80 interface GigabitEthernet0/2 80
The external listening port does not have to match the internal port — for example, the router could listen on 8080 externally while still forwarding to port 80 internally. The same pattern applies to SSH:
Router1(config)# ip nat inside source static tcp 192.168.10.11 22 interface GigabitEthernet0/2 22
| NAT type | Direction | Public IP usage | Typical use case |
|---|---|---|---|
| SNAT (Source NAT / overload / PAT) | Outbound (internal → internet) | Many internal hosts share one public IP | Giving internal clients internet access |
| DNAT (Destination NAT) | Inbound (internet → internal) | One public IP per internal host | Making an entire internal server reachable |
| PNAT (Port Mapping) | Inbound (internet → internal) | One public IP, differentiated by port | Exposing specific services (web, SSH) only |
Configuring Port Mapping (PNAT) for Private Hosts
On Ubuntu1 (192.168.10.11), first confirm the address and check which services are listening:
ip a
# 192.168.10.11
ss -ntl
# Port 53 (DNS) and Port 22 (SSH) are open
Install a web server so port 80 is also available:
sudo apt install -y apache2
Re-check listening sockets (!ss re-runs the last command beginning with ss):
!ss
# Now listening on port 22 (SSH) and port 80 (Apache web server)
These services are only reachable on the private IP at this point. To expose them externally, configure Port Mapping on Router1:
Router1> enable
Router1# configure terminal
Router1(config)# ip nat inside source static tcp 192.168.10.11 80 interface GigabitEthernet0/2 80
Router1(config)# ip nat inside source static tcp 192.168.10.11 22 interface GigabitEthernet0/2 22
Router1(config)# end
Router1# write memory
Confirm the router’s public-facing address:
Router1# show ip interface brief
# GigabitEthernet0/2: 192.168.4.27 (example address on the lab's outer network)
Verification from an external host:
# Browse to http://192.168.4.27 (default port 80)
# -> The Apache default welcome page is returned, even though the browser
# connected to the router's IP address, because traffic was forwarded
# to 192.168.10.11 on port 80.
ssh cisco@192.168.4.27
# Connects successfully; the shell session is running on 192.168.10.11
# even though the client connected to 192.168.4.27.
sequenceDiagram
participant U as External User
participant R as Router1 Public IP
participant S as Ubuntu1 192.168.10.11
U->>R: HTTP request to PublicIP port 80
R->>S: Forward to 192.168.10.11 port 80
S->>R: HTTP response
R->>U: HTTP response
U->>R: SSH request to PublicIP port 22
R->>S: Forward to 192.168.10.11 port 22
S->>R: SSH session data
R->>U: SSH session data
Module 2 Summary
Internal routing between the private networks was established using static routes, since private addresses cannot be automatically advertised or propagated between routers. Connecting the lab out to the internet introduced a new problem: the internet does not know how to route back to private addresses. SNAT solves this for outbound, established connections initiated from inside the network, letting internal hosts share the router’s single public IP. Port Mapping (PNAT), a form of Destination NAT, solves the opposite problem — allowing new connections from the internet into the private network, but in a tightly controlled way, exposing only specific services rather than entire hosts.
Module 3: Understanding DNS Fundamentals and the Global DNS Hierarchy
This module explains what happens when a domain name is typed into a browser, tracing the history and structure of the Domain Name System (DNS).
From a Single Hosts File to the Global DNS Hierarchy
Before DNS — and before the modern internet, back when the ARPANET was in use — name resolution relied on a single text file, HOSTS.TXT, containing every known host. Every machine on the network downloaded this file, and it mapped hostnames to IP addresses, since humans generally find names easier to remember than numbers. There was no hierarchy and no distributed system — just one replicated list, standardized as RFC 952.
This approach was workable in 1977, but does not scale: there are now on the order of hundreds of millions of registered domain names, each potentially containing many hosts, adding up to billions of individual entries. This is why the single flat file was replaced by a hierarchical, distributed database: the DNS.
DNS is structured as an inverted tree:
- At the very top is the root domain.
- Below that are the top-level domains (TLDs), such as
.com,.org,.uk. - Below the TLDs are individual domains.
Using dig with the +trace flag shows this resolution process directly: looking up a domain such as example.com first queries the root zone servers, which refer to the .com servers, which in turn refer to the authoritative servers for example.com. A country-code lookup such as example.co.uk follows the same pattern, starting at the root, then to .uk, then to .co.uk, and finally to the domain itself.
flowchart TD
Root[Root Zone] --> COM[.com TLD]
Root --> ORG[.org TLD]
Root --> UK[.uk TLD]
COM --> Example[example.com]
UK --> COUK[.co.uk]
COUK --> ExampleCoUk[example.co.uk]
Example --> WWW[www.example.com A record]
Example --> MX[Mail exchanger MX record]
Example --> NS[Name servers NS record]
DNS is not just about host records. A domain can have several different record types:
| Record type | Purpose |
|---|---|
| A | Maps a hostname to an IPv4 address (the default record type) |
| NS | Identifies the name servers authoritative for a domain |
| MX | Identifies mail exchanger(s) responsible for handling email for a domain, each with a priority |
| SOA | Start of Authority — administrative information about a zone (serial number, refresh/retry/expire timers) |
For example, sending email to support@example.com does not resolve the domain’s A record — it looks up the domain’s MX records to find the mail server(s) responsible for handling that domain’s email.
Resolving Names with dig
The dig command is the primary tool for interactively querying DNS and observing how resolution works, typically run from a Linux system:
dig www.example.com
The critical part of the output is the ANSWER SECTION — if no answer section is returned, the record was not found.
For a brief, IP-only response:
dig www.example.com +short
To see the entire resolution path from the root down, use +trace:
dig +trace www.example.com
To force IPv4-only lookups (skip AAAA/IPv6 queries) and page long output:
dig -4 +trace www.example.com | less
Looking up the name servers responsible for a domain:
dig ns example.com +short
Looking up mail servers, where a lower priority number indicates a higher-priority mail server:
dig mx example.com
Looking up standard address records explicitly:
dig a www.example.com
dig flag/usage | Effect |
|---|---|
dig <name> | Full query and response, including the ANSWER SECTION |
+short | Brief output — just the resolved value(s) |
+trace | Show the full resolution path from the root servers down |
-4 | Force IPv4-only lookups |
ns <name> | Query name server (NS) records |
mx <name> | Query mail exchanger (MX) records |
a <name> | Query address (A) records explicitly |
@<server> | Query a specific DNS server directly |
Module 4: Managing DNS on a Linux Server
Having seen how the global DNS hierarchy works, this module sets up an authoritative DNS server for a private zone on a Linux system, using BIND9 on Ubuntu.
Installing and Configuring BIND9 on Ubuntu
On Ubuntu1 (which will act as the DNS server), first refresh package metadata and install BIND along with useful utilities:
sudo apt update
sudo apt install bind9 bind9-utils bind9-dnsutils dnsutils
Three configuration files then need to be edited (a text editor such as vim or nano can be used):
1. /etc/bind/named.conf.options — global server options (recursion, allowed queries, forwarders, DNSSEC, listening interfaces):
// /etc/bind/named.coonf.options
//
options {
directory "/var/cache/bind";
recursion yes;
allow-query { any; };
forwarders {
8.8.8.8;
};
dnssec-validation auto;
listen-on { any; };
};
2. /etc/bind/named.conf.local — defines the zone this server is authoritative for:
// /etc/bind/named.conf.local
// Do any local configuration here
//
// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";
zone "example.lab" {
type master;
file "/etc/bind/db.example.lab";
};
3. /etc/bind/db.example.lab — the zone file itself, containing the actual records:
$TTL 604800
@ IN SOA ubuntu1.example.lab. admin.example.lab. (
2026022501 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ubuntu1.example.lab.
; --- Host Records ---
ubuntu1 IN A 192.168.10.11
router1 IN A 192.168.10.1
router2 IN A 192.168.30.1
ubuntu2 IN A 192.168.30.11
Key details of the zone file:
$TTL 604800— the default cache time-to-live, in seconds (604,800 seconds = one week, a common default).- The SOA (Start of Authority) record declares the primary name server (
ubuntu1.example.lab.) and the zone’s administrative contact, followed by:- Serial — a change counter, conventionally based on the date plus a revision number for that day (e.g.
2026022501= year-month-day + revision01). - Refresh (604800s / 1 week), Retry (86400s / 1 day), Expire (2419200s / 4 weeks), and Negative Cache TTL (604800s / 1 week).
- Serial — a change counter, conventionally based on the date plus a revision number for that day (e.g.
- The NS record identifies
ubuntu1.example.lab.as the authoritative name server for the zone. - A records map each host in the lab to its address:
ubuntu1(192.168.10.11, the DNS server itself),router1(192.168.10.1),router2(192.168.30.1), andubuntu2(192.168.30.11).
After saving all three files, validate the configuration before restarting the service:
named-checkconf
named-checkzone example.lab. /etc/bind/db.example.lab
No output from named-checkconf indicates the syntax is correct; named-checkzone should report the zone as loaded correctly along with its serial number. Once validated, restart the name daemon so it picks up the changes:
sudo systemctl restart named
Test resolution directly against the local server:
dig @127.0.0.1 router1.example.lab
dig @127.0.0.1 router1.example.lab +short
dig @127.0.0.1 router2.example.lab +short
dig @127.0.0.1 ubuntu2.example.lab +short
| Zone file element | Value in this lab | Meaning |
|---|---|---|
$TTL | 604800 (1 week) | Default record cache lifetime |
| SOA Serial | 2026022501 | Change/version counter (date + revision) |
| SOA Refresh | 604800 (1 week) | How often secondaries check for updates |
| SOA Retry | 86400 (1 day) | Retry interval if refresh fails |
| SOA Expire | 2419200 (4 weeks) | When a secondary should stop answering if it cannot reach the primary |
| SOA Negative Cache TTL | 604800 (1 week) | How long negative (“not found”) answers are cached |
| NS | ubuntu1.example.lab. | Authoritative name server for the zone |
| A records | ubuntu1, router1, router2, ubuntu2 | Host-to-IP mappings for the lab |
Integrating the DNS Server with DHCP Pools
Configuring a DNS server does not automatically inform DHCP clients about it — the DHCP pools must be explicitly updated to advertise the new DNS server’s address, and existing clients need to renew their lease (or explicitly refresh DNS settings) before they pick up the change.
On Router1, check the current DHCP configuration, update each pool, and save:
Router1> enable
Router1# show running-config | section dhcp
Router1# configure terminal
Router1(config)# ip dhcp pool LAN_POOL_10
Router1(dhcp-config)# dns-server 192.168.10.11
Router1(dhcp-config)# exit
Router1(config)# ip dhcp pool LAN_POOL_30
Router1(dhcp-config)# dns-server 192.168.10.11
Router1(dhcp-config)# exit
Router1(config)# end
Router1# write memory
Both pools are updated to point clients at 192.168.10.11 (the Ubuntu1 BIND9 server) instead of the public 8.8.8.8 server used initially. Confirm with:
Router1# show running-config | section dhcp
On each Ubuntu client, check the current DNS configuration and force a lease renewal so the new DNS server takes effect immediately, without waiting for the existing lease to expire:
networkctl status
sudo networkctl renew ens2
networkctl status
Before the renewal, networkctl status shows the DNS server as the original public address (8.8.8.8); after renewal, it correctly shows 192.168.10.11. The same steps apply on Ubuntu2.
Final verification — with DNS now resolved automatically via the configured server (no need to specify @server explicitly):
dig ubuntu2.example.lab +short
# 192.168.30.11
Course Summary
The complete lab built a working small network from the ground up:
- The free tier of Cisco Modeling Labs (CML) was used to run virtualized Cisco routers and Ubuntu Linux hosts without requiring physical hardware.
- DHCP was configured on Cisco IOS to issue IP addresses automatically, first on a single LAN, then extended with DHCP relay agents so a single central DHCP server could serve multiple LANs.
- Static routes were configured so each router knew how to reach every network in the topology, since private address ranges are not automatically propagated between routers.
- Network Address Translation was configured so internal machines could reach the internet by sharing the router’s public IP address (SNAT), and so that specific inbound services could be selectively exposed back into the private network (Port Mapping / PNAT), including a working SSH connection into the Ubuntu1 host from outside the lab.
- DNS was installed and configured using BIND9 on an Ubuntu Linux host, hosting an authoritative zone for the lab’s private domain, and the DHCP pools were updated so clients automatically learned the correct internal DNS server.
Summary
This material covers three foundational network services that, together, make a private network usable both internally and against the public internet:
- DHCP removes the need for manual IP address management by automating assignment through the four-step DORA process (Discover, Offer, Request, Acknowledge). On Cisco IOS, DHCP can be configured locally per-router with
ip dhcp pool, or centralized using DHCP relay agents (ip helper-address) so a single server can support multiple LANs. - Routing between private networks requires explicit static routes, since private address space is not globally routable and cannot be automatically advertised between independently-administered routers.
- NAT bridges private, non-routable address space and the public internet:
- SNAT / overload (PAT) lets many internal hosts share one public IP address for outbound connections.
- DNAT maps a dedicated public IP address to a specific internal host.
- PNAT (Port Mapping) exposes only specific ports/services on a shared public IP address, giving fine-grained, controlled inbound access.
- DNS replaced the original flat
HOSTS.TXTfile (RFC 952) with a scalable, hierarchical, distributed database structured as an inverted tree (root → TLD → domain). Tools likedig(with+short,+trace,-4, and record-type queries such asa,ns,mx) make it possible to observe and troubleshoot the resolution process directly. Running an authoritative zone (e.g., with BIND9) requires an options file, a zone-definition file, and a zone database file containing SOA, NS, and A records — and DHCP pools must be updated so that clients actually learn about the private DNS server.
Quick Reference
| Service | Cisco IOS / Linux command highlights |
|---|---|
| DHCP pool | ip dhcp pool <name>, network, default-router, dns-server, ip dhcp excluded-address |
| DHCP relay | ip helper-address <dhcp-server-ip> on the interface facing the remote LAN |
| View DHCP leases | show ip dhcp binding |
| Static route | ip route <network> <mask> <next-hop> |
| Default route | ip route 0.0.0.0 0.0.0.0 <next-hop> |
| View routes | show ip route, show ip route connected, show ip route static |
| Mark NAT interfaces | ip nat inside / ip nat outside on the relevant interfaces |
| SNAT / overload | access-list <n> permit <net> <wildcard> then ip nat inside source list <n> interface <if> overload |
| Port mapping (PNAT) | ip nat inside source static tcp <internal-ip> <port> interface <if> <public-port> |
| Install DNS server | sudo apt install bind9 bind9-utils bind9-dnsutils dnsutils |
| Validate DNS config | named-checkconf, named-checkzone <zone> <file> |
| Restart DNS server | sudo systemctl restart named |
| Query DNS | dig <name>, dig <name> +short, dig +trace <name>, dig @<server> <name> |
| Renew DHCP lease (Linux) | sudo networkctl renew <interface>, networkctl status |
Checklist for Building a Similar Lab
- Confirm host supports nested virtualization, with at least 4 CPU cores and 8 GB RAM, before installing CML.
- Design the IP addressing plan up front (backbone network, per-LAN networks, excluded static ranges).
- Configure DHCP pools with appropriate exclusions, default routers, and DNS servers.
- Add DHCP relay (
ip helper-address) on any router that should not run its own DHCP service. - Add static/default routes on every router so all networks in the topology are reachable.
- Add an external connector only once internal routing is verified, and bridge it to the real network.
- Enable
ip nat inside/ip nat outsideand configure an ACL-scoped, overloaded SNAT rule for outbound access. - Add PNAT rules only for the specific services that must be reachable from the internet — avoid full DNAT exposure unless required.
- Stand up an authoritative DNS zone with correct SOA, NS, and A records, and validate with
named-checkconf/named-checkzonebefore restarting the service. - Update DHCP pools to advertise the internal DNS server, and force clients to renew so they pick up the change immediately.
Search Terms
critical · network · services · dns · dhcp · nat · networking · fundamentals · systems · security · cisco · configuring · router · dora · global · hierarchy · hosts · managing · mapping · port · process · relay · routing · server