# Module 3: DHCP Options Deep Dive

**Nav:** [← Module 2](02-dhcp-message-flow.md) | [Course Home](../README.md) | [Module 4 →](04-dhcp-relay.md)

---

## Overview

In Module 2, you captured a DORA exchange and learned that the first 236 bytes of every DHCP message are fixed-format header fields (`op`, `xid`, `yiaddr`, `chaddr`, and so on). Everything after byte 236 is the **options field** — and that is where DHCP becomes truly powerful.

The header tells the client *which* IP address it is getting. The options tell it *everything else*: subnet mask, default gateway, DNS servers, lease duration, TFTP server for firmware downloads, VLAN assignments for IP phones, and hundreds of other parameters. When a Cisco IP phone boots and finds its call manager, it is DHCP options doing the work. When a lightweight access point discovers its wireless controller, DHCP options are the reason it knows where to look. When a relay agent stamps the circuit-ID of the switch port a client connected to, that stamp lives in a DHCP option.

In this module, you will learn how options are encoded at the byte level (TLV — Type-Length-Value), how to decode every major option in Wireshark's packet detail pane, how to configure options on Cisco IOS DHCP pools, and how to build Wireshark display filters that isolate specific option problems. By the end, you will be able to look at a hex dump of an options field and read it like English.

**Estimated time:** ~3 hours (reading + labs)

**Prerequisites:** Module 2 completed. DORA exchange understood. CML lab environment accessible.

---

## 3.1 — Option Encoding: The TLV Structure (RFC 2132)

Every DHCP option (with two exceptions) is encoded as a **Type-Length-Value** triplet:

```
+------+--------+-------------------------+
| Type | Length |         Value           |
| 1 B  |  1 B   |     Length bytes        |
+------+--------+-------------------------+
```

- **Type** (1 byte): The option number (0–255). This identifies *what* the option is.
- **Length** (1 byte): The number of bytes in the Value field. Does **not** include the Type or Length bytes themselves.
- **Value** (variable): The option data, whose format depends on the Type.

Options are concatenated back-to-back in the options field with no padding or separators between them. The options field always begins with a 4-byte **magic cookie** (`0x63825363`) that tells the parser "what follows is DHCP options, not legacy BOOTP vendor data."

### The Two Exceptions

Two options break the TLV pattern because they carry no data:

| Option | Name | Encoding | Purpose |
|--------|------|----------|---------|
| **0** | Pad | Single byte `0x00` — no Length, no Value | Alignment padding |
| **255** | End | Single byte `0xFF` — no Length, no Value | Marks end of options field |

Every valid options field terminates with Option 255. Anything after it is ignored.

### Byte-Level Example

Here is a raw hex dump of three options as they appear on the wire:

```
35 01 01    0C 09 4C 69 6E 75 78 2D 50 43 31    FF
│  │  │     │  │  └─────────────────────────┘     └── Option 255 (End)
│  │  │     │  │         "Linux-PC1"
│  │  │     │  └── Length: 9 bytes
│  │  └──── │  Option 12 (Host Name)
│  │  Value: 1 (DHCP Discover)
│  └── Length: 1 byte
└── Option 53 (DHCP Message Type)
```

Breaking this down:
1. **`35 01 01`** → Type 53 (`0x35`), Length 1, Value 1 (Discover)
2. **`0C 09 4C696E75782D504331`** → Type 12 (`0x0C`), Length 9, Value = ASCII `Linux-PC1`
3. **`FF`** → Type 255 (End)

In Wireshark, you never need to decode this manually — the dissector does it for you. But understanding the encoding is essential for two situations: debugging options that Wireshark cannot parse (vendor-specific blobs), and building custom options in Cisco IOS using hex strings.

> **Pro tip:** In Wireshark's packet bytes pane, click on any option in the detail pane and the corresponding hex bytes will be highlighted in the hex dump. This makes correlating parsed fields to raw bytes effortless.

---

## 3.2 — Key DHCP Options Reference

The table below summarizes the options you will encounter most often in enterprise environments. We will deep-dive into each one in the sections that follow.

| Option | Name | Length | Value Format | Wireshark Filter Field |
|--------|------|--------|-------------|----------------------|
| 1 | Subnet Mask | 4 | IPv4 address | `dhcp.option.subnet_mask` |
| 3 | Router (Default Gateway) | 4×n | One or more IPv4 addresses | `dhcp.option.router` |
| 6 | Domain Name Server | 4×n | One or more IPv4 addresses | `dhcp.option.domain_name_server` |
| 12 | Host Name | variable | ASCII string | `dhcp.option.hostname` |
| 15 | Domain Name | variable | ASCII string | `dhcp.option.domain_name` |
| 43 | Vendor-Specific Information | variable | Vendor-defined (sub-options) | `dhcp.option.vendor_specific` |
| 51 | IP Address Lease Time | 4 | Unsigned 32-bit integer (seconds) | `dhcp.option.ip_address_lease_time` |
| 53 | DHCP Message Type | 1 | Integer 1–8 | `dhcp.option.dhcp` |
| 54 | Server Identifier | 4 | IPv4 address | `dhcp.option.dhcp_server_id` |
| 55 | Parameter Request List | variable | List of option codes | `dhcp.option.request_list_item` |
| 60 | Vendor Class Identifier | variable | ASCII string | `dhcp.option.vendor_class_id` |
| 61 | Client Identifier | variable | Hardware type + MAC | `dhcp.option.client_id` |
| 82 | Relay Agent Information | variable | Sub-options (Circuit-ID, Remote-ID) | `dhcp.option.agent_information_option` |
| 150 | TFTP Server Address | 4×n | One or more IPv4 addresses | `dhcp.option.tftp_server_address` |

---

## 3.3 — Deep Dive: Each Option Explained

### Option 1 — Subnet Mask

**RFC:** 2132, Section 3.3
**Purpose:** Tells the client which bits of its assigned IP address identify the network vs. the host.

**Encoding:**
```
Type: 0x01 (1)   Length: 0x04 (4)   Value: 4 bytes (mask in network byte order)

Example: 01 04 FF FF FF 00  →  255.255.255.0  →  /24
```

**In Wireshark:** Expand `Option: (1) Subnet Mask` in the packet detail pane. The value is displayed in dotted decimal notation. Wireshark automatically converts the 4 raw bytes.

**Wireshark filter:**
```
dhcp.option.subnet_mask == 255.255.255.0
```

**Common misconfiguration:** A DHCP pool with the wrong subnet mask (e.g., `/16` instead of `/24`) will let clients reach some hosts but not others. If a client cannot reach its default gateway, check this option first.

---

### Option 3 — Router (Default Gateway)

**RFC:** 2132, Section 3.5
**Purpose:** Provides one or more default gateway addresses. The client uses the first address as its primary gateway.

**Encoding:**
```
Type: 0x03   Length: 0x04 (one router) or 0x08 (two routers)   Value: 4 bytes per address

Example: 03 04 0A 01 01 01  →  10.1.1.1
```

Multiple gateways are encoded as consecutive 4-byte addresses. The Length field will be a multiple of 4.

**Wireshark filter:**
```
dhcp.option.router == 10.1.1.1
```

**Real-world note:** Most enterprise networks supply a single gateway per subnet. If you see multiple routers in this option, the client uses the first one and may try subsequent addresses only if the primary fails (behavior is OS-dependent).

---

### Option 6 — Domain Name Server (DNS)

**RFC:** 2132, Section 3.8
**Purpose:** Lists one or more DNS server addresses the client should use for name resolution.

**Encoding:**
```
Type: 0x06   Length: 0x08 (two servers)   Value: 4 bytes per address

Example: 06 08 08080808 08080404  →  8.8.8.8, 8.8.4.4
```

**Wireshark filter:**
```
dhcp.option.domain_name_server == 8.8.8.8
```

**Troubleshooting scenario:** Client gets an IP address but cannot resolve hostnames. Check Option 6 — if it is missing or points to an unreachable DNS server, name resolution fails even though Layer 3 connectivity is fine. This is one of the most common "I have an IP but can't reach anything" complaints.

---

### Option 43 — Vendor-Specific Information

**RFC:** 2132, Section 8.4
**Purpose:** Carries vendor-defined data as a block of **sub-options**. The interpretation of Option 43 depends entirely on the vendor identified in Option 60 (Vendor Class ID).

This is the option that makes lightweight access points discover their wireless LAN controller, and it is one of the most misunderstood options in enterprise networking.

**Encoding:**
```
Type: 0x2B (43)   Length: varies   Value: one or more sub-options in TLV format

Sub-option structure (nested TLV):
  +----------+--------+-------------------+
  | Sub-Type | Sub-Len|    Sub-Value      |
  |   1 B    |  1 B   |  Sub-Len bytes    |
  +----------+--------+-------------------+
```

**Example — Cisco Lightweight AP Controller Discovery:**

A Cisco lightweight AP sends Option 60 with the value `Cisco AP c3802` (identifying itself). The DHCP server responds with Option 43 containing a sub-option that provides the WLC (Wireless LAN Controller) IP address:

```
Sub-option format for Cisco WLC:
  Type: 0xF1 (241)   Length: 0x04   Value: WLC IP address

Full Option 43 hex:  2B 06 F1 04 0A 01 01 64
                     │     │  │  └──────────── 10.1.1.100 (WLC IP)
                     │     │  └── Sub-Length: 4
                     │     └── Sub-Type: 241 (0xF1)
                     └── Option 43, Length 6
```

**In Wireshark:** Option 43 is displayed as `Option: (43) Vendor-Specific Information`. If Wireshark does not recognize the vendor, it shows the value as a raw hex string. If the Vendor Class ID (Option 60) was present in the Discover, Wireshark may attempt to decode the sub-options — but this is not always reliable. Manual decoding using the hex pane is a critical skill.

**Wireshark filter:**
```
dhcp.option.vendor_specific
```

> **Real-world impact:** If Option 43 is missing or misconfigured, lightweight APs will fail to discover their controller and will not serve clients. This is one of the most common "AP stuck in discovery" issues.

---

### Option 51 — IP Address Lease Time

**RFC:** 2132, Section 9.2
**Purpose:** Specifies how long (in seconds) the client may use the assigned IP address.

**Encoding:**
```
Type: 0x33 (51)   Length: 0x04   Value: unsigned 32-bit integer (seconds)

Example: 33 04 00001C20  →  7200 seconds  →  2 hours
```

**Wireshark display:** Wireshark helpfully converts this to human-readable format: `IP Address Lease Time: (7200s) 2 hours`.

**Wireshark filter:**
```
dhcp.option.ip_address_lease_time == 7200
```

**Design note:** Short leases (minutes) are appropriate for guest Wi-Fi or hotspot networks with high device turnover. Long leases (days/weeks) suit stable wired environments. If you see a lease time of `0xFFFFFFFF` (4,294,967,295 seconds), the lease is **infinite** — the address never expires.

---

### Option 53 — DHCP Message Type

**RFC:** 2132, Section 9.6
**Purpose:** Identifies the type of DHCP message. This is the single most important option in the protocol — without it, the receiver cannot determine what the message means.

**Encoding:**
```
Type: 0x35 (53)   Length: 0x01   Value: single byte

Values:
  1 = DHCP Discover
  2 = DHCP Offer
  3 = DHCP Request
  4 = DHCP Decline
  5 = DHCP ACK
  6 = DHCP NAK
  7 = DHCP Release
  8 = DHCP Inform
```

**Wireshark filter (by message type value):**
```
dhcp.option.dhcp == 1    # Discover
dhcp.option.dhcp == 5    # ACK
dhcp.option.dhcp == 6    # NAK — always investigate these
```

You used this filter extensively in Module 2. By now, it should be second nature.

---

### Option 55 — Parameter Request List

**RFC:** 2132, Section 9.8
**Purpose:** Sent by the client in Discover and Request messages. Contains a list of option codes the client wants the server to provide. This tells the server which options the client cares about.

**Encoding:**
```
Type: 0x37 (55)   Length: varies   Value: one byte per requested option code

Example: 37 07 01 03 06 0F 1C 33 3A
         → Requesting: 1 (Subnet Mask), 3 (Router), 6 (DNS), 15 (Domain Name),
           28 (Broadcast Address), 51 (Lease Time), 58 (T1 Renewal Time)
```

**In Wireshark:** Expand `Option: (55) Parameter Request List` to see each requested option code with its name.

**Wireshark filter:**
```
# Show packets where a specific option was requested
dhcp.option.request_list_item == 150
```

**Why this matters:** If a Cisco IP phone requests Option 150 (TFTP server) in its Parameter Request List but the DHCP server does not include it in the response, the phone will not receive its call manager address and will fail to register. Checking Option 55 in the Discover is the first step in diagnosing "the server didn't send what the client needed."

> **Diagnostic technique:** Compare the client's Parameter Request List (Option 55 in the Discover) to the options actually returned in the server's Offer/ACK. Any option the client requested but the server did not include is a potential configuration gap.

---

### Option 60 — Vendor Class Identifier

**RFC:** 2132, Section 9.13
**Purpose:** Sent by the client to identify its vendor and hardware/software type. The server uses this to determine which vendor-specific options (Option 43) to return.

**Encoding:**
```
Type: 0x3C (60)   Length: varies   Value: ASCII string

Examples:
  "Cisco AP c3802"          → Cisco lightweight access point
  "Cisco Systems, Inc. IP Phone CP-8841"  → Cisco IP phone
  "MSFT 5.0"                → Microsoft DHCP client (Windows)
  "udhcp 1.24.2"            → BusyBox DHCP client (embedded Linux)
```

**Wireshark filter:**
```
# Find all Cisco IP phones
dhcp.option.vendor_class_id contains "IP Phone"

# Find all Cisco APs
dhcp.option.vendor_class_id contains "Cisco AP"
```

**Real-world use:** In many enterprise environments, the DHCP server uses Option 60 to conditionally assign options. For example, a Cisco IOS DHCP server can match on the vendor class identifier and return Option 150 only to IP phones, not to laptops. This is the foundation of device-specific DHCP provisioning.

---

### Option 82 — Relay Agent Information

**RFC:** 3046
**Purpose:** Inserted by DHCP relay agents (typically switches or routers) to provide the server with information about *where* the client is physically connected. This option is **not** set by the client — it is injected by intermediate infrastructure.

**Encoding:**
```
Type: 0x52 (82)   Length: varies   Value: one or more sub-options

Common sub-options:
  Sub-option 1 — Circuit ID: identifies the port/VLAN the client is on
  Sub-option 2 — Remote ID: identifies the relay agent device (e.g., switch MAC or hostname)
```

**Byte-level example:**
```
52 12                           ← Option 82, total length 18
  01 06 00 04 00 01 00 06       ← Sub-option 1 (Circuit-ID): length 6, value = port info
  02 08 00 06 00 01 AC 10 0A 01 ← Sub-option 2 (Remote-ID): length 8, value = switch identifier
```

**In Wireshark:** Expand `Option: (82) Agent Information Option` to see the parsed sub-options. Wireshark decodes both Circuit-ID and Remote-ID as hex values. The actual meaning of the bytes depends on the relay agent vendor:

| Vendor | Circuit-ID Encoding | Remote-ID Encoding |
|--------|--------------------|--------------------|
| Cisco IOS | VLAN + module + port number | Switch MAC address |
| Cisco Catalyst | VLAN number + port ifIndex | Switch hostname or MAC |
| Aruba | Port label or AP name | Controller IP/name |

**Wireshark filters:**
```
dhcp.option.agent_information_option
dhcp.option.agent_information_option.circuit_id
dhcp.option.agent_information_option.remote_id
```

**Why this matters:** Option 82 allows the DHCP server to make assignment decisions based on the physical location of the client — for example, assigning a specific VLAN or IP range depending on which switch port the device is connected to. This is foundational for 802.1X deployments and ISP subscriber management. We will explore this in depth in Module 4 (DHCP Relay).

---

### Option 150 — TFTP Server Address

**RFC:** Cisco proprietary (not in RFC 2132, but widely implemented)
**Purpose:** Provides the IP address of a TFTP server for IP phone firmware and configuration file downloads. This is the primary mechanism by which Cisco IP phones discover their Cisco Unified Communications Manager (CUCM).

**Encoding:**
```
Type: 0x96 (150)   Length: 0x04 (one server) or 0x08 (two servers)   Value: 4 bytes per address

Example: 96 04 0A 01 01 64  →  10.1.1.100
```

**In Wireshark:** Look for `Option: (150) TFTP Server Address`. The value is one or more IP addresses.

**Wireshark filter:**
```
dhcp.option.tftp_server_address == 10.1.1.100
```

**IP phone boot sequence:**
1. Phone powers on and sends a DHCP Discover with Option 60 = `"Cisco Systems, Inc. IP Phone ..."` and Option 55 requesting Option 150.
2. DHCP server replies with Option 150 pointing to the CUCM TFTP service (e.g., `10.1.1.100`).
3. Phone downloads its configuration file (e.g., `SEP<MAC>.cnf.xml`) from the TFTP server.
4. Configuration file tells the phone which Call Manager to register with.

If Option 150 is missing, the phone has no way to find its configuration and will display "Registering..." indefinitely.

> **Option 150 vs. Option 66 (TFTP Server Name):** Option 66 provides a TFTP server as a hostname (ASCII string), while Option 150 provides it as one or more IP addresses. Cisco IP phones prefer Option 150 because it avoids DNS dependency during boot. Use Option 150 for Cisco phone deployments.

---

## 3.4 — Decoding Options in Wireshark's Packet Detail Pane

When you click on a DHCP packet in Wireshark, the middle pane shows the protocol tree. Here is how to navigate to options:

```
▼ Dynamic Host Configuration Protocol (ACK)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    ...
    ▼ Option: (53) DHCP Message Type (ACK)
        Length: 1
        DHCP: ACK (5)
    ▼ Option: (51) IP Address Lease Time
        Length: 4
        IP Address Lease Time: (86400s) 1 day
    ▼ Option: (1) Subnet Mask
        Length: 4
        Subnet Mask: 255.255.255.0
    ▼ Option: (3) Router
        Length: 4
        Router: 10.1.1.1
    ▼ Option: (6) Domain Name Server
        Length: 8
        Domain Name Server: 10.1.1.10
        Domain Name Server: 10.1.1.11
    ▼ Option: (150) TFTP Server Address
        Length: 4
        TFTP Server Address: 10.1.1.100
    ▼ Option: (43) Vendor-Specific Information
        Length: 6
        Value: f104c0a80164
    ► Option: (255) End
```

### Tips for Efficient Option Inspection

1. **Click an option → hex highlights:** Selecting any option in the detail pane highlights its corresponding bytes in the hex dump below. This is invaluable for Option 43 where you need to manually parse sub-options.

2. **Right-click → Apply as Column:** Right-click any option value and select "Apply as Column" to add it as a permanent column in the packet list. This lets you see option values at a glance across all packets without expanding each one.

3. **Right-click → Apply as Filter:** Right-click an option value to instantly create a display filter for that exact value. For example, right-clicking a Subnet Mask of `255.255.255.0` generates `dhcp.option.subnet_mask == 255.255.255.0`.

4. **Packet Bytes pane math:** For Option 43 sub-options, count bytes in the hex pane: start at the first byte after the Option 43 length byte, read Sub-Type (1 byte), Sub-Length (1 byte), then Sub-Length bytes of Sub-Value. Repeat until you have consumed the entire Option 43 value.

---

## 3.5 — Wireshark Display Filters for DHCP Options

### Filtering by Option Presence

These filters match packets that *contain* a specific option, regardless of value:

| Filter | Matches Packets Containing |
|--------|---------------------------|
| `dhcp.option.subnet_mask` | Option 1 (Subnet Mask) |
| `dhcp.option.router` | Option 3 (Router) |
| `dhcp.option.domain_name_server` | Option 6 (DNS) |
| `dhcp.option.hostname` | Option 12 (Host Name) |
| `dhcp.option.vendor_specific` | Option 43 (Vendor-Specific) |
| `dhcp.option.ip_address_lease_time` | Option 51 (Lease Time) |
| `dhcp.option.vendor_class_id` | Option 60 (Vendor Class ID) |
| `dhcp.option.agent_information_option` | Option 82 (Relay Agent Info) |
| `dhcp.option.tftp_server_address` | Option 150 (TFTP Server) |

### Filtering by Option Value

| Filter | What It Matches |
|--------|----------------|
| `dhcp.option.subnet_mask == 255.255.255.0` | Specific subnet mask |
| `dhcp.option.router == 10.1.1.1` | Specific default gateway |
| `dhcp.option.domain_name_server == 8.8.8.8` | Specific DNS server |
| `dhcp.option.ip_address_lease_time == 86400` | 24-hour lease |
| `dhcp.option.dhcp == 5` | ACK messages |
| `dhcp.option.vendor_class_id contains "IP Phone"` | Cisco IP phones |
| `dhcp.option.vendor_class_id contains "Cisco AP"` | Cisco APs |
| `dhcp.option.request_list_item == 150` | Clients requesting TFTP server |
| `dhcp.option.tftp_server_address == 10.1.1.100` | Specific TFTP server |

### Combination Filters for Real-World Scenarios

```
# IP phones that got an ACK but did NOT receive Option 150
dhcp.option.dhcp == 5 && dhcp.option.vendor_class_id contains "IP Phone" && !dhcp.option.tftp_server_address

# Discover messages from Cisco APs (looking for WLC via Option 43)
dhcp.option.dhcp == 1 && dhcp.option.vendor_class_id contains "Cisco AP"

# Packets with Option 82 (relayed traffic) — useful for multi-subnet captures
dhcp.option.agent_information_option

# Lease time shorter than 10 minutes (600 seconds) — may indicate misconfiguration
dhcp.option.ip_address_lease_time < 600

# NAKs for a specific client — trouble indicator
dhcp.option.dhcp == 6 && dhcp.hw.mac_addr == 00:50:79:66:68:01
```

---

## 3.6 — DHCP Options for Network Devices: IP Phones, APs, and More

### Cisco IP Phone Boot Process

The DHCP conversation for a Cisco IP phone is more complex than a simple laptop. Here is the full sequence:

```
Phone → DHCP Server:
  DHCP Discover
    Option 60: "Cisco Systems, Inc. IP Phone CP-8841"
    Option 55: [1, 66, 3, 6, 15, 35, 51, 150]     ← note: requests 66 AND 150

Server → Phone:
  DHCP Offer
    Option  1: 255.255.255.0        (Subnet Mask)
    Option  3: 10.10.10.1           (Gateway)
    Option  6: 10.10.10.5           (DNS)
    Option 51: 86400                (Lease: 1 day)
    Option 66: "cucm.example.com"   (TFTP server hostname — fallback)
    Option 150: 10.10.10.50         (TFTP server IP — preferred)

Phone → DHCP Server:
  DHCP Request  (accepts offer)

Server → Phone:
  DHCP ACK      (confirms)

Phone → TFTP (10.10.10.50):
  GET SEP001122334455.cnf.xml       (phone config file, named by MAC)

Phone → CUCM (from config file):
  SCCP/SIP registration
```

**Troubleshooting checklist for phones stuck on "Registering...":**
1. Does the DHCP ACK contain Option 150? → Filter: `dhcp.option.dhcp == 5 && dhcp.option.tftp_server_address`
2. Is the TFTP server reachable from the voice VLAN?
3. Does the TFTP server have the correct config file (`SEP<MAC>.cnf.xml`)?
4. Does the config file point to the correct CUCM server?

### Cisco Lightweight AP (CAPWAP) Controller Discovery

Lightweight APs use several methods to discover their wireless LAN controller. DHCP Option 43 is one of the primary mechanisms:

```
AP → DHCP Server:
  DHCP Discover
    Option 60: "Cisco AP c3802"

Server → AP:
  DHCP Offer/ACK
    Option 43: F1 04 0A 0A 0A 32    ← Sub-option 241, WLC IP: 10.10.10.50
```

**Option 43 sub-option format for Cisco APs:**

| Sub-option | Length | Description |
|------------|--------|-------------|
| 241 (0xF1) | 4×n | WLC management IP address(es). 4 bytes per address. |

For multiple WLCs, concatenate addresses:
```
F1 08 0A0A0A32 0A0A0A33  →  10.10.10.50, 10.10.10.51
```

> **Critical encoding note:** Cisco APs expect the sub-option type `0xF1` (241). Some administrators mistakenly use sub-option 1 or sub-option 102, which will not work. Always verify the correct sub-option number for your AP platform.

---

## 3.7 — CML Lab Topology: Testing DHCP Options

### Extended Lab Topology

Build on the Module 2 topology by adding a voice VLAN and simulated IP phone/AP:

```
                                                         ┌─────────────────┐
                                                         │   Linux-Phone   │
                                                         │ (simulates      │
┌─────────────────┐          ┌──────────────┐   Gi0/2    │  Cisco IP phone)│
│   DHCP-Server   │──────────│   Switch-1   │────────────│  VLAN 100       │
│  (IOSv / CSR)   │  Gi0/0   │  (IOSvL2)    │            └─────────────────┘
│  10.1.1.1/24    │          │              │   Gi0/3    ┌─────────────────┐
│  10.100.1.1/24  │          │              │────────────│   Linux-AP      │
│  (SVI for       │          │              │            │ (simulates      │
│   VLAN 100)     │          └──────────────┘            │  Cisco LWAP)    │
└─────────────────┘                │                     │  VLAN 100       │
                                   │ Gi0/1               └─────────────────┘
                           ┌───────────────┐
                           │  Linux-Client │
                           │  DHCP Client  │
                           │  VLAN 1       │
                           └───────────────┘
```

### Server Configuration — Multiple Pools with Custom Options

```
configure terminal

!--- Pool for standard data clients (VLAN 1) ---
ip dhcp pool DATA-POOL
 network 10.1.1.0 255.255.255.0
 default-router 10.1.1.1
 dns-server 10.1.1.10 10.1.1.11
 domain-name lab.example.com
 lease 1 0 0
 ! Lease: 1 day

ip dhcp excluded-address 10.1.1.1 10.1.1.20

!--- Pool for IP Phones (VLAN 100) ---
ip dhcp pool VOICE-POOL
 network 10.100.1.0 255.255.255.0
 default-router 10.100.1.1
 dns-server 10.100.1.5
 option 150 ip 10.100.1.50
 ! Option 150: TFTP server for phone configs
 lease 0 8 0
 ! Lease: 8 hours

ip dhcp excluded-address 10.100.1.1 10.100.1.20

!--- Pool for Lightweight APs (VLAN 100, using class matching) ---
ip dhcp pool AP-POOL
 network 10.100.1.0 255.255.255.0
 default-router 10.100.1.1
 dns-server 10.100.1.5
 option 43 hex F104.0A64.0132
 ! Option 43: sub-option 241 (0xF1), length 4, WLC IP 10.100.1.50
 lease 1 0 0

ip dhcp excluded-address 10.100.1.1 10.100.1.20

!--- SVI for VLAN 100 (if using L3 switch or router-on-a-stick) ---
interface Vlan100
 ip address 10.100.1.1 255.255.255.0
 no shutdown

end
write memory
```

### Key Configuration Notes

**Option 150 syntax:**
```
option 150 ip 10.100.1.50
```
Cisco IOS natively supports Option 150 with the `ip` keyword — no hex encoding needed.

**Option 43 syntax (hex encoding):**
```
option 43 hex F104.0A64.0132
```
Breaking down the hex:
- `F1` = Sub-option type 241 (Cisco WLC discovery)
- `04` = Sub-option length (4 bytes for one IP)
- `0A64.0132` = `10.100.1.50` in hex (`0A` = 10, `64` = 100, `01` = 1, `32` = 50)

> **IP-to-hex conversion:** Convert each octet to hex separately: `10` → `0A`, `100` → `64`, `1` → `01`, `50` → `32`. Concatenate: `0A640132`. Add dots every 4 characters for readability in IOS: `0A64.0132`.

**Option 43 for multiple WLCs:**
```
option 43 hex F108.0A64.0132.0A64.0133
!                    └── 10.100.1.50    └── 10.100.1.51
! F1 = type 241, 08 = length 8 (two 4-byte addresses)
```

---

## 3.8 — Verifying Options on the Server

After configuring your pools, verify the options are correct before capturing:

```
! Show all DHCP pool details
show ip dhcp pool

! Expected output for VOICE-POOL:
!   Pool VOICE-POOL :
!    Utilization mark (high/low)    : 100 / 0
!    Subnet size (first/next)       : 0 / 0
!    Total addresses                : 234
!    Leased addresses               : 0
!    Pending event                  : none
!    1 subnet is currently in the pool :
!    Current index      IP address range         Leased addresses
!    10.100.1.21        10.100.1.1 - 10.100.1.254    0

! Show specific option configuration
show running-config | section dhcp

! Verify active bindings
show ip dhcp binding

! Real-time debug (use sparingly)
debug ip dhcp server events
debug ip dhcp server packet
```

---

## Hands-On Labs

### Lab 3.1 — Option TLV Decoding from Raw Hex

**Objective:** Manually decode DHCP options from a hex dump to solidify your understanding of TLV encoding.

**Steps:**

1. In your Module 2 DORA capture, select the DHCP ACK packet.
2. In the hex dump pane, locate the magic cookie: `63 82 53 63`. Everything after this is options.
3. Starting at the first byte after the magic cookie, decode each option manually:
   - Read the first byte: this is the **Type**.
   - Read the second byte: this is the **Length** (call it `L`).
   - Read the next `L` bytes: this is the **Value**.
   - Repeat until you hit `FF` (End).
4. Build a table of your decoded options:

| Offset | Type (dec) | Type (name) | Length | Value (hex) | Value (decoded) |
|--------|-----------|-------------|--------|-------------|-----------------|
| +0 | 53 | DHCP Message Type | 1 | 05 | ACK |
| +3 | 51 | Lease Time | 4 | 00001C20 | 7200s |
| +9 | 1 | Subnet Mask | 4 | FFFFFF00 | 255.255.255.0 |
| ... | ... | ... | ... | ... | ... |

5. Compare your manual decoding to Wireshark's parsed output. They should match exactly.

**Deliverable:** Completed table with at least 6 options decoded manually.

---

### Lab 3.2 — Configure and Capture Option 150 (IP Phone Simulation)

**Objective:** Configure Option 150 on the DHCP server, simulate a phone request, and verify in Wireshark.

**Steps:**

1. Configure the VOICE-POOL on the DHCP server (see Section 3.7 configuration).
2. On the Linux-Phone node, start a capture:
   ```bash
   sudo tcpdump -i ens2 -w /tmp/dhcp-phone.pcap port 67 or port 68
   ```
3. Simulate a Cisco IP phone by requesting a DHCP lease with a custom vendor class ID:
   ```bash
   # Create a dhclient config that mimics a Cisco IP phone
   cat > /tmp/dhclient-phone.conf << 'EOF'
   send vendor-class-identifier "Cisco Systems, Inc. IP Phone CP-8841";
   request subnet-mask, routers, domain-name-servers, tftp-server-name;
   also request option-150;
   EOF

   sudo dhclient -cf /tmp/dhclient-phone.conf -v ens2
   ```
4. Stop the capture and open in Wireshark.
5. Apply the filter:
   ```
   dhcp.option.dhcp == 5 && dhcp.option.tftp_server_address
   ```
6. Verify the ACK contains Option 150 with the correct TFTP server IP (`10.100.1.50`).
7. Expand Option 150 in the packet detail pane. Note the TLV encoding (Type `0x96`, Length `0x04`, Value).

**Deliverable:** Screenshot of Wireshark showing the ACK with Option 150 expanded in the detail pane.

---

### Lab 3.3 — Configure and Capture Option 43 (AP Controller Discovery)

**Objective:** Configure Option 43 for WLC discovery, capture the DHCP exchange, and decode the sub-options.

**Steps:**

1. Configure the AP-POOL on the DHCP server with the Option 43 hex value (see Section 3.7).
2. On the Linux-AP node, start a capture and simulate a Cisco AP:
   ```bash
   sudo tcpdump -i ens2 -w /tmp/dhcp-ap.pcap port 67 or port 68

   cat > /tmp/dhclient-ap.conf << 'EOF'
   send vendor-class-identifier "Cisco AP c3802";
   EOF

   sudo dhclient -cf /tmp/dhclient-ap.conf -v ens2
   ```
3. Open the capture in Wireshark.
4. Find the ACK and expand `Option: (43) Vendor-Specific Information`.
5. If Wireshark shows the value as a raw hex blob, decode it manually:
   - First byte = sub-option type (should be `0xF1` = 241)
   - Second byte = sub-option length (should be `0x04`)
   - Next 4 bytes = WLC IP address
6. Convert the hex IP to dotted decimal and verify it matches your configuration.

**Deliverable:** The decoded WLC IP address extracted from Option 43, with hex-to-decimal work shown.

---

### Lab 3.4 — Parameter Request List Analysis

**Objective:** Compare what a client requests (Option 55) vs. what the server delivers, and identify gaps.

**Steps:**

1. Open any DORA capture from a previous lab.
2. Select the Discover packet and expand `Option: (55) Parameter Request List`.
3. Write down every option code the client requested.
4. Select the corresponding ACK packet and expand every option present.
5. Build a comparison table:

| Option Code | Option Name | Requested? | Delivered? | Notes |
|-------------|-------------|------------|------------|-------|
| 1 | Subnet Mask | ✅ | ✅ | Match |
| 3 | Router | ✅ | ✅ | Match |
| 6 | DNS | ✅ | ✅ | Match |
| 15 | Domain Name | ✅ | ❌ | **Gap** — server not configured |
| 150 | TFTP Server | ✅ | ✅ | Match |
| ... | ... | ... | ... | ... |

6. For any gaps (requested but not delivered), determine if the server pool is missing the configuration.
7. Add the missing option to the server pool, trigger a new DORA, and confirm the gap is closed.

**Deliverable:** Completed comparison table with at least one gap identified and resolved.

---

### Lab 3.5 — Option 82 Observation (Preview for Module 4)

**Objective:** Configure DHCP relay on Switch-1 and observe Option 82 being inserted.

> **Note:** This lab is a preview of Module 4 concepts. If your topology does not yet support inter-VLAN routing, skip this lab and return to it after completing Module 4.

**Steps:**

1. On Switch-1, enable DHCP snooping and relay (ip helper-address):
   ```
   configure terminal
   ip dhcp snooping
   ip dhcp snooping vlan 100
   ip dhcp snooping information option
   ! This enables Option 82 insertion

   interface Vlan100
    ip helper-address 10.1.1.1
   end
   ```
2. Start a capture on the DHCP server's interface (Gi0/0).
3. Trigger a DHCP request from Linux-Phone on VLAN 100.
4. In Wireshark, apply the filter: `dhcp.option.agent_information_option`
5. Expand `Option: (82) Agent Information Option` and examine:
   - **Sub-option 1 (Circuit-ID):** What port/VLAN information is encoded?
   - **Sub-option 2 (Remote-ID):** What switch identifier is present?
6. Note that Option 82 appears in the **relayed** Discover and Request (server-side capture), but NOT in the original client-side capture. The relay agent inserts it.

**Deliverable:** Screenshot showing Option 82 sub-options with Circuit-ID and Remote-ID decoded.

---

## Quick-Reference: Option Hex Codes

When reading raw hex in captures or building `option 43 hex` commands on Cisco IOS, this conversion table saves time:

| Option | Decimal | Hex | Common Name |
|--------|---------|-----|-------------|
| 1 | 1 | `0x01` | Subnet Mask |
| 3 | 3 | `0x03` | Router |
| 6 | 6 | `0x06` | DNS |
| 12 | 12 | `0x0C` | Host Name |
| 15 | 15 | `0x0F` | Domain Name |
| 43 | 43 | `0x2B` | Vendor-Specific |
| 50 | 50 | `0x32` | Requested IP |
| 51 | 51 | `0x33` | Lease Time |
| 53 | 53 | `0x35` | Message Type |
| 54 | 54 | `0x36` | Server Identifier |
| 55 | 55 | `0x37` | Parameter Request List |
| 60 | 60 | `0x3C` | Vendor Class ID |
| 61 | 61 | `0x3D` | Client Identifier |
| 66 | 66 | `0x42` | TFTP Server Name |
| 82 | 82 | `0x52` | Relay Agent Info |
| 150 | 150 | `0x96` | TFTP Server Address |
| 255 | 255 | `0xFF` | End |

---

## Module Summary

| Concept | Key Takeaway |
|---------|-------------|
| TLV encoding | Every option is Type (1 byte) + Length (1 byte) + Value (L bytes). Exception: Pad (0) and End (255). |
| Magic cookie | `0x63825363` marks the start of the options field. |
| Option 1 (Subnet Mask) | 4-byte mask. Wrong mask = broken routing. |
| Option 3 (Router) | Default gateway. Clients use the first address listed. |
| Option 6 (DNS) | DNS servers. Missing or wrong = "I have IP but nothing works." |
| Option 43 (Vendor-Specific) | Nested sub-options, vendor-dependent. Critical for AP → WLC discovery. |
| Option 51 (Lease Time) | 32-bit seconds value. `0xFFFFFFFF` = infinite. |
| Option 53 (Message Type) | The option that identifies every DHCP message (1–8). |
| Option 55 (PRL) | Client's wish list. Compare to ACK contents to find configuration gaps. |
| Option 60 (Vendor Class ID) | Client self-identification string. Used by server for conditional options. |
| Option 82 (Relay Agent Info) | Injected by infrastructure, not clients. Contains physical location data. |
| Option 150 (TFTP Server) | Cisco IP phone config delivery. Missing = phone never registers. |
| Wireshark technique | Click option → hex highlights. Right-click → Apply as Column/Filter. |

---

## What's Next

In **Module 4: DHCP Relay**, we take Option 82 from a preview to a deep dive. You will build a multi-subnet lab with relay agents, understand how `giaddr` changes the DHCP server's pool selection logic, see exactly how Option 82 sub-options are inserted and stripped, and troubleshoot the classic "relay is configured but clients aren't getting addresses" scenario.

---

**Nav:** [← Module 2](02-dhcp-message-flow.md) | [Course Home](../README.md) | [Module 4 →](04-dhcp-relay.md)
