Loading learning content...
Imagine you're a network engineer troubleshooting connectivity issues. Packets are reaching their destination, but something seems wrong—latency is higher than expected, and you suspect they're taking a suboptimal path. How do you determine the actual route packets are traveling?
The Record Route (RR) option solves this problem by having each router along the path record its IP address directly into the packet header. By the time the packet arrives at its destination, it carries a complete log of every router it traversed—a built-in path trace.
By the end of this page, you will understand the complete Record Route option structure, how the pointer field manages address recording, router processing behavior, the 9-address limitation and its implications, and how to interpret recorded routes for network diagnostics.
The Record Route option is a Class 0 (Control) option that requests each router processing the packet to record its IP address. The recorded addresses form a trace of the packet's path through the network.
Option Identification:
| Field | Value | Description |
|---|---|---|
| Type | 7 (0x07) | Binary: 00000111 |
| Copy Flag | 0 | Not copied to fragments |
| Class | 0 | Control option |
| Number | 7 | Identifier within class |
Key Characteristics:
Record Route captures the path in a single packet, while traceroute sends multiple probes with incrementing TTL values. Record Route is more efficient but limited to 9 hops. Traceroute has no hop limit but generates significant traffic and relies on ICMP responses that may be filtered.
The Record Route option uses the standard TLV (Type-Length-Value) format with an additional pointer field that manages the recording position.
+--------+--------+--------+--------+
| Type | Length | Pointer| |
| (7) | | | IP #1 |
+--------+--------+--------+--------+
| IP Address #1 |
+-----------------------------------+
| IP Address #2 |
+-----------------------------------+
| ... |
+-----------------------------------+
| IP Address #N |
+-----------------------------------+
Field Descriptions:
| Field | Size | Offset | Value Range | Purpose |
|---|---|---|---|---|
| Type | 1 byte | 0 | 7 | Identifies Record Route option |
| Length | 1 byte | 1 | 7, 11, 15, ..., 39 | Total option length |
| Pointer | 1 byte | 2 | 4, 8, 12, ..., 40 | Next write position |
| Route Data | 4×N bytes | 3+ | IP addresses | Recorded router addresses |
The source host must pre-allocate space for addresses by setting an appropriate length. Routers can only fill existing slots—they cannot extend the option. The source must estimate the path length and allocate enough space.
The pointer field is the key to understanding how Record Route operates. It indicates where the next router should write its address, using a clever 1-indexed offset scheme.
Pointer Semantics:
Pointer Progression:
| Addresses Recorded | Pointer Value | Status | Next Action |
|---|---|---|---|
| 0 | 4 | Empty | Record at byte 4-7 |
| 1 | 8 | 1 of 9 | Record at byte 8-11 |
| 2 | 12 | 2 of 9 | Record at byte 12-15 |
| 3 | 16 | 3 of 9 | Record at byte 16-19 |
| ... | ... | ... | ... |
| 8 | 36 | 8 of 9 | Record at byte 36-39 |
| 9 | 40 | Full | Pointer > Length → No recording |
1234567891011121314151617181920
// Router logic for checking if recording is possiblebool can_record_route(uint8_t *option) { uint8_t length = option[1]; uint8_t pointer = option[2]; // Check if pointer points beyond the option // We need 4 bytes for an IP address if (pointer + 3 > length) { // No room to record - option is full return false; } // Pointer should be at least 4 (first valid slot) if (pointer < 4) { // Invalid pointer - corrupted option return false; } return true;}When a router receives a packet with the Record Route option, it follows a specific processing algorithm defined in RFC 791.
Processing Steps:
Which IP Address Gets Recorded?
This is a crucial detail: routers record the IP address of the outgoing interface, not the incoming interface. This means the recorded route shows the IP addresses seen from the perspective of the next hop.
Consider a router with interfaces 10.1.1.1 and 10.2.2.1. If a packet enters on 10.1.1.1 and exits on 10.2.2.1, the recorded address is 10.2.2.1 (the exit interface). This design allows the destination to understand the path from the perspective of how to send a reply back.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
#include <stdint.h>#include <stdbool.h>#include <string.h> typedef struct { uint32_t src_ip; uint32_t dst_ip; // ... other fields uint8_t options[40]; uint8_t options_len;} IPv4Packet; typedef struct { uint32_t ip; // ... other interface info} NetworkInterface; /** * Process Record Route option on a router * * @param packet The IPv4 packet being forwarded * @param egress_if The outgoing interface * @return true if option was modified, false otherwise */bool process_record_route(IPv4Packet *packet, NetworkInterface *egress_if) { // Find Record Route option in options area int offset = 0; while (offset < packet->options_len) { uint8_t type = packet->options[offset]; if (type == 0) break; // EOL if (type == 1) { // NOP offset++; continue; } if (type == 7) { // Record Route found uint8_t length = packet->options[offset + 1]; uint8_t pointer = packet->options[offset + 2]; // Check if there's room (need 4 bytes for IP) if (pointer + 3 > length) { // No room - option is full return false; } // Validate pointer (must be >= 4) if (pointer < 4) { // Malformed option - could drop packet or continue return false; } // Record our egress interface IP // Pointer is 1-indexed from start of option int data_offset = offset + pointer - 1; // Convert to 0-indexed // Write IP address in network byte order uint32_t ip_network_order = htonl(egress_if->ip); memcpy(&packet->options[data_offset], &ip_network_order, 4); // Advance pointer packet->options[offset + 2] = pointer + 4; return true; } // Multi-byte option - skip by length uint8_t length = packet->options[offset + 1]; offset += length; } return false; // No Record Route option found}The Record Route option is fundamentally constrained by the 40-byte maximum options size in IPv4. This creates a strict limitation on how many addresses can be recorded.
Space Calculation:
| Component | Size | Description |
|---|---|---|
| Type | 1 byte | Value 7 |
| Length | 1 byte | Total option size |
| Pointer | 1 byte | Current write position |
| Route Data | Up to 36 bytes | 9 × 4-byte IP addresses |
Maximum Capacity:
Practical maximum length value: 39 bytes (3 + 36 = 3 + 9×4)
Internet paths often exceed 9 hops. A path from New York to Sydney might traverse 15-20 routers. Record Route cannot capture the full path in such cases, making it suitable only for local or well-understood network segments.
Let's trace through a complete example of Record Route in action. Consider a packet traveling from Host A (192.168.1.10) to Host B (10.20.30.40) through three routers.
Network Topology:
Host A ──[R1]──[R2]──[R3]── Host B
192.168.1.10 10.20.30.40
↓ ↓ ↓
10.0.1.1 10.0.2.1 10.0.3.1
(egress) (egress) (egress)
| Location | Pointer | Slot 1 | Slot 2 | Slot 3 | Slot 4 |
|---|---|---|---|---|---|
| Host A (origin) | 4 | (empty) | (empty) | (empty) | (empty) |
| After R1 | 8 | 10.0.1.1 | (empty) | (empty) | (empty) |
| After R2 | 12 | 10.0.1.1 | 10.0.2.1 | (empty) | (empty) |
| After R3 | 16 | 10.0.1.1 | 10.0.2.1 | 10.0.3.1 | (empty) |
| At Host B | 16 | 10.0.1.1 | 10.0.2.1 | 10.0.3.1 | (empty) |
Initial packet from Host A:══════════════════════════════════════════════════════════════Offset: 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | 7 | 19 | 4 | 0 | 0 | 0 | 0 | ... +-----+-----+-----+-----+-----+-----+-----+-----+ Type Len Ptr ----Slot 1 (empty)---- After R1 (egress 10.0.1.1 = 0x0A000101):══════════════════════════════════════════════════════════════ +-----+-----+-----+-----+-----+-----+-----+-----+ | 7 | 19 | 8 | 10 | 0 | 1 | 1 | ... +-----+-----+-----+-----+-----+-----+-----+-----+ Type Len Ptr ----10.0.1.1 recorded---- ↑ Advanced from 4 to 8 After R2 (egress 10.0.2.1 = 0x0A000201):══════════════════════════════════════════════════════════════ +-----+-----+-----+-----+-----+-----+-----+-----+ | 7 | 19 | 12 | 10 | 0 | 1 | 1 | 10 | ... +-----+-----+-----+-----+-----+-----+-----+-----+ Type Len Ptr ----10.0.1.1---- --10.0.2.1-- ↑ Advanced from 8 to 12 After R3 (egress 10.0.3.1 = 0x0A000301):══════════════════════════════════════════════════════════════Pointer = 16 (next slot after 3 addresses)Recorded: [10.0.1.1, 10.0.2.1, 10.0.3.1] Host B receives packet with complete route trace!Interpreting a Record Route option requires understanding what information it provides and what it doesn't.
What Recorded Routes Tell You:
What They Don't Tell You:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
import structfrom typing import List, Optionalfrom dataclasses import dataclass @dataclassclass RecordRouteInfo: """Parsed Record Route option information""" length: int pointer: int addresses: List[str] is_full: bool addresses_recorded: int max_addresses: int def parse_record_route(option_bytes: bytes) -> Optional[RecordRouteInfo]: """ Parse a Record Route option from raw bytes. Args: option_bytes: Raw bytes of the Record Route option Returns: RecordRouteInfo object or None if invalid """ if len(option_bytes) < 3: return None opt_type = option_bytes[0] if opt_type != 7: return None # Not Record Route length = option_bytes[1] pointer = option_bytes[2] # Validate length if length < 3 or length > 40: return None # Calculate addresses data_length = length - 3 max_addresses = data_length // 4 # Determine how many addresses are recorded # Pointer starts at 4, advances by 4 for each address if pointer < 4: addresses_recorded = 0 else: addresses_recorded = (pointer - 4) // 4 # Ensure we don't read beyond recorded data addresses_recorded = min(addresses_recorded, max_addresses) # Extract addresses addresses = [] for i in range(addresses_recorded): offset = 3 + (i * 4) if offset + 4 <= len(option_bytes): ip_bytes = option_bytes[offset:offset + 4] ip = ".".join(str(b) for b in ip_bytes) addresses.append(ip) return RecordRouteInfo( length=length, pointer=pointer, addresses=addresses, is_full=(pointer > length), addresses_recorded=addresses_recorded, max_addresses=max_addresses ) # Example usageoption_data = bytes([ 7, # Type: Record Route 19, # Length: 19 bytes (space for 4 addresses) 16, # Pointer: 16 (3 addresses recorded) 10, 0, 1, 1, # 10.0.1.1 10, 0, 2, 1, # 10.0.2.1 10, 0, 3, 1, # 10.0.3.1 0, 0, 0, 0 # Empty slot]) info = parse_record_route(option_data)print(f"Addresses recorded: {info.addresses_recorded}")print(f"Route: {' -> '.join(info.addresses)}")print(f"Option full: {info.is_full}") # Output:# Addresses recorded: 3# Route: 10.0.1.1 -> 10.0.2.1 -> 10.0.3.1# Option full: FalseThe ping command on most operating systems supports Record Route via the -R flag (Linux/macOS) or -r flag (Windows). This provides a practical way to examine paths without packet capture tools.
Linux/macOS:
ping -R destination_ip
Windows:
ping -r 9 destination_ip
The Windows version requires specifying the number of hops to record (1-9).
# Linux example$ ping -R -c 1 8.8.8.8PING 8.8.8.8 (8.8.8.8) 56(124) bytes of data.64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=15.2 msRR: 192.168.1.1 10.0.0.1 172.16.50.1 [partially recorded - option filled] # Windows example> ping -r 9 8.8.8.8 Pinging 8.8.8.8 with 32 bytes of data:Reply from 8.8.8.8: bytes=32 time=16ms TTL=57 Route: 192.168.1.1 -> 10.0.0.1 -> 172.16.50.1 -> [recording stopped - 9 hop maximum] # Note: Many ISPs and cloud providers block or ignore# Record Route, so you may not see intermediate hopsMany routers are configured to ignore Record Route for security and performance reasons. You may see gaps in the recorded path or no addresses at all. Record Route works best in controlled environments where you manage all the routing infrastructure.
Record Route has specific fragmentation behavior indicated by its type value encoding.
Copy Flag Analysis:
Type value 7 in binary: 00000111
0 → NOT copied00 → Control00111 → 7Because the copy flag is 0, the Record Route option is NOT copied to fragment packets. Only the first fragment retains the option.
Why Not Copy?
The design rationale is practical:
| Fragment | Offset | Has Record Route? | Recorded Path |
|---|---|---|---|
| Fragment 1 | 0 | Yes | R1 → R2 → R3 (continues recording) |
| Fragment 2 | 1480 | No | N/A |
| Fragment 3 | 2960 | No | N/A |
| Fragment 4 | 4440 | No | N/A |
Since only the first fragment carries the Record Route option, the destination host must be aware that the recorded route applies to the original packet, not each individual fragment. This is typically handled automatically by the IP stack during reassembly.
Record Route reveals internal network topology, which has significant security implications.
Information Disclosed:
Attack Facilitation:
RFC 7126 recommends filtering packets with IP options at network borders. Most enterprise and ISP networks drop or ignore Record Route packets to prevent information disclosure. Only use Record Route in controlled, trusted environments.
We've explored the Record Route option in depth. Let's consolidate the key points:
What's Next:
Now that you understand passive path recording, we'll explore active path control with Strict Source Routing. Unlike Record Route which observes the path, Source Routing lets the sender specify exactly which routers the packet must traverse.
You now have complete knowledge of the Record Route option—its structure, pointer mechanics, router processing, limitations, and security implications. Next, we'll examine how Source Routing enables sender-controlled path selection.