Loading content...
Strict Source Routing requires an impossible level of network knowledge—you must know every router and their direct connections. In practice, this is rarely feasible. But what if you could specify just the key waypoints a packet must visit, while letting normal routing handle the details between them?
Loose Source Routing (LSR) provides exactly this flexibility. You specify the routers you want the packet to traverse, and the network routes normally between each waypoint. It's source routing for the real world—where topologies are complex and direct connections aren't guaranteed.
By the end of this page, you will understand how Loose Source Routing differs from Strict Source Routing, its option structure, router processing behavior, practical use cases, and why LSR carries the same security risks that led to its near-universal blocking.
The fundamental difference between Loose and Strict Source Routing lies in what happens between specified routers.
Strict Source Routing (SSR):
Loose Source Routing (LSR):
| Aspect | Strict (SSR) | Loose (LSR) |
|---|---|---|
| Type Value | 137 (0x89) | 131 (0x83) |
| Copy Flag | 1 (copied) | 1 (copied) |
| Between Waypoints | Must be direct | Normal routing allowed |
| Intermediate Routers | Not allowed | Allowed (invisible to option) |
| Failure Condition | Non-direct hop | Unreachable waypoint |
| Topology Knowledge | Complete path required | Only waypoints needed |
| Practical Usability | Very limited | More practical |
Strict Source Routing is like giving turn-by-turn directions: 'Turn left at Oak Street, then right at Main.' Loose Source Routing is like specifying waypoints: 'Go through Chicago and Denver.' The navigation system (network routing) figures out the details between waypoints.
The Loose Source Route option, also called Loose Source and Record Route (LSRR), shares the same format as SSR but with a different type value.
+--------+--------+--------+--------+
| Type | Length | Pointer| |
| (131) | | | IP #1 |
+--------+--------+--------+--------+
| IP Address #1 |
+-----------------------------------+
| IP Address #2 |
+-----------------------------------+
| ... |
+-----------------------------------+
| IP Address #N |
+-----------------------------------+
Option Identification:
| Field | Value | Binary | Description |
|---|---|---|---|
| Type | 131 | 10000011 | LSR identifier |
| Copy Flag | 1 | bit 0 | Copied to all fragments |
| Class | 0 | bits 1-2 | Control option |
| Number | 3 | bits 3-7 | Option number within class |
Type Value Encoding (131 = 0x83):
Binary: 1 00 00011
│ ││ └─────── Number: 3
│ └┴──────── Class: 0 (Control)
└─────────── Copy: 1 (YES, copy to fragments)
Fields:
| Field | Size | Offset | Valid Values | Purpose |
|---|---|---|---|---|
| Type | 1 byte | 0 | 131 | Identifies LSRR option |
| Length | 1 byte | 1 | 7, 11, 15, ..., 39 | Total option size |
| Pointer | 1 byte | 2 | 4, 8, 12, ... | Next route entry to process |
| Route Data | Variable | 3+ | IP addresses | Waypoints + final destination |
The only field difference between LSR (131) and SSR (137) is the type value. The difference is entirely in how routers PROCESS the option—not in how it's formatted.
Loose Source Routing processing is similar to Strict, with one critical difference: the direct reachability check is removed.
Processing at Non-Waypoint Routers:
Routers that are NOT specified in the source route simply forward the packet normally. They:
Processing at Waypoint Routers:
When the destination IP matches a router (it's a specified waypoint):
In LSR, the router at a waypoint simply routes to the next waypoint using its normal routing table. It doesn't check whether the next waypoint is directly connected. This is what makes LSR 'loose'—it relies on normal routing between specified points.
Let's trace a packet using Loose Source Routing through a multi-hop network.
Scenario:
Network Topology:
Host A ─[R1]─[R2]─[WP1]─[R3]─[R4]─[WP2]─[R5]─ Host B
↑ ↑
Waypoint 1 Waypoint 2
| Location | Destination IP | Pointer | Route[0] | Route[1] | Action |
|---|---|---|---|---|---|
| Host A | 172.16.0.1 | 4 | 10.20.0.1 | 10.30.40.50 | Send to R1 |
| R1 | 172.16.0.1 | 4 | 10.20.0.1 | 10.30.40.50 | Not waypoint, route to WP1 |
| R2 | 172.16.0.1 | 4 | 10.20.0.1 | 10.30.40.50 | Not waypoint, route to WP1 |
| WP1 | 10.20.0.1 | 8 | 172.16.0.1* | 10.30.40.50 | Waypoint! Swap & record |
| R3 | 10.20.0.1 | 8 | 172.16.0.1 | 10.30.40.50 | Not waypoint, route to WP2 |
| R4 | 10.20.0.1 | 8 | 172.16.0.1 | 10.30.40.50 | Not waypoint, route to WP2 |
| WP2 | 10.30.40.50 | 12 | 172.16.0.1 | 10.20.0.1* | Waypoint! Swap & record |
| R5 | 10.30.40.50 | 12 | 172.16.0.1 | 10.20.0.1 | Not waypoint, route to B |
| Host B | 10.30.40.50 | 12 | 172.16.0.1 | 10.20.0.1 | Final destination! |
Loose Source Routing - Detailed Trace═══════════════════════════════════════════════════════════════════ INITIAL: Host A creates packet─────────────────────────────────────────────────────────────────Source: 192.168.1.10Destination: 172.16.0.1 (first waypoint)LSR Option: Type=131, Length=11, Pointer=4Route Data: [10.20.0.1 (WP2)] [10.30.40.50 (final dest)] ↑ Pointer AT R1, R2: (Non-waypoint routers)─────────────────────────────────────────────────────────────────- Destination 172.16.0.1 ≠ our IP- Look up 172.16.0.1 in routing table- Forward toward 172.16.0.1- LSR option unchanged AT WP1 (172.16.0.1): WAYPOINT PROCESSING!─────────────────────────────────────────────────────────────────1. "Destination 172.16.0.1 = my IP, I'm a waypoint!"2. "Pointer (4) ≤ Length (11), more route to go"3. "Read next waypoint at offset 4: 10.20.0.1"4. "Record my IP (172.16.0.1) at offset 4"5. "Set destination = 10.20.0.1"6. "Advance pointer: 4 → 8"7. "Route to 10.20.0.1 via normal routing" After WP1:Destination: 10.20.0.1 (second waypoint)Route Data: [172.16.0.1*] [10.30.40.50] ↑recorded ↑ Pointer(8) AT R3, R4: (Non-waypoint routers)─────────────────────────────────────────────────────────────────- Forward toward 10.20.0.1 using normal routing AT WP2 (10.20.0.1): WAYPOINT PROCESSING!─────────────────────────────────────────────────────────────────1. "Destination = my IP, I'm a waypoint!"2. "Pointer (8) ≤ Length (11), more route to go"3. "Read next address at offset 8: 10.30.40.50"4. "Record my IP (10.20.0.1) at offset 8"5. "Set destination = 10.30.40.50"6. "Advance pointer: 8 → 12" After WP2:Destination: 10.30.40.50 (final destination)Route Data: [172.16.0.1] [10.20.0.1*] ↑recordedPointer: 12 (now > Length 11) AT R5: (Non-waypoint router)─────────────────────────────────────────────────────────────────- Forward toward 10.30.40.50 AT Host B (10.30.40.50): FINAL DESTINATION!─────────────────────────────────────────────────────────────────1. "Destination = my IP"2. "Pointer (12) > Length (11) → I'm the final destination!"3. "Route completed: WP1 → WP2 now recorded in option"4. "Deliver to upper layer" RESULT: Packet traversed exactly the waypoints specified! Route Data contains: [172.16.0.1, 10.20.0.1] - the reverse pathImplementing Loose Source Routing is simpler than Strict because there's no direct reachability check. The router just needs to route toward the next waypoint.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
#include <stdint.h>#include <stdbool.h>#include <string.h> #define OPT_LSR 131 // Loose Source Route type typedef struct { uint32_t src_ip; uint32_t dst_ip; uint8_t options[40]; uint8_t options_len;} IPv4Packet; /** * Process Loose Source Route option * * Key difference from Strict: NO direct reachability check * We simply route to the next waypoint using normal routing */int process_lsr(IPv4Packet *packet, uint32_t my_ip) { // Find LSR 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 == OPT_LSR) { // Found Loose Source Route option uint8_t length = packet->options[offset + 1]; uint8_t pointer = packet->options[offset + 2]; // Is this packet addressed to us (are we a waypoint)? if (packet->dst_ip != my_ip) { // Not a waypoint for us - continue normal routing // The packet passes through us toward its current destination return 0; } // We ARE the destination in the header - we're a waypoint! // Check if there's more route if (pointer > length) { // Route exhausted - we're the FINAL destination // Deliver to upper layer return 1; // 1 = deliver locally } // More route to go - get next waypoint int route_offset = offset + pointer - 1; // 1-indexed to 0-indexed if (route_offset + 4 > offset + length) { // Malformed option return -1; } // Extract next waypoint IP uint32_t next_waypoint; memcpy(&next_waypoint, &packet->options[route_offset], 4); next_waypoint = ntohl(next_waypoint); // ════════════════════════════════════════════════════ // KEY DIFFERENCE FROM STRICT SOURCE ROUTING: // NO direct reachability check here! // We simply route to next_waypoint using normal routing // ════════════════════════════════════════════════════ // Perform the swap: // 1. Record our IP where we read the waypoint uint32_t my_ip_network = htonl(my_ip); memcpy(&packet->options[route_offset], &my_ip_network, 4); // 2. Set new destination (next waypoint) packet->dst_ip = next_waypoint; // 3. Advance pointer packet->options[offset + 2] = pointer + 4; // Return 0 = forward the packet (use normal routing) return 0; } // Skip this option by its length uint8_t length = packet->options[offset + 1]; offset += length; } return 0; // No LSR found, normal processing} /** * Simplified main forwarding logic with LSR support */void forward_packet(IPv4Packet *packet, uint32_t my_ip) { // Check for LSR option int lsr_result = process_lsr(packet, my_ip); if (lsr_result == 1) { // We're the final destination deliver_to_upper_layer(packet); return; } if (lsr_result == -1) { // Malformed LSR option send_icmp_parameter_problem(packet); return; } // Normal forwarding (destination may have been swapped by LSR) uint32_t next_hop = lookup_route(packet->dst_ip); send_to_interface(packet, next_hop);}Loose Source Routing was designed for legitimate network management purposes. Understanding these use cases helps explain why the feature existed, even though it's now deprecated.
Legitimate Use Cases (Historical):
Modern Alternatives:
The functions LSR provided are now handled by more secure mechanisms:
| Original LSR Use | Modern Alternative |
|---|---|
| Policy routing | PBR (Policy-Based Routing) on routers |
| Traffic engineering | MPLS-TE, Segment Routing |
| Waypoint enforcement | VRF (Virtual Routing and Forwarding) |
| Path selection | BGP communities, SDN controllers |
| Monitoring traversal | Inline TAPs, port mirroring |
While LSR doesn't have the strict direct-reachability requirement, it can still fail in several ways.
Potential Failure Modes:
| Error Condition | ICMP Type | Code | Description |
|---|---|---|---|
| Waypoint unreachable | 3 (Dest Unreachable) | 0 | Network unreachable |
| No route to waypoint | 3 (Dest Unreachable) | 5 | Source route failed |
| TTL expired en route | 11 (Time Exceeded) | 0 | TTL expired in transit |
| Malformed option | 12 (Param Problem) | 0 | Pointer indicates error |
| Option processing disabled | 3 (Dest Unreachable) | 5 | Source route failed |
With LSR, failures can occur anywhere between waypoints. Unlike SSR where failure is immediate at the first hop, LSR might succeed through multiple routers before failing—making diagnosis more challenging.
LSR Error Scenarios═══════════════════════════════════════════════════════════════ Scenario 1: Waypoint Unreachable────────────────────────────────Route specified: A → [WP1] → [WP2] → BActual state: WP2's network has no route from WP1 Packet path:A → (routers) → WP1 → (tries to route to WP2) → FAIL └── No route to 10.20.0.0/24 └── ICMP Type 3, Code 0 sent to A Scenario 2: Waypoint Down────────────────────────────────Route specified: A → [WP1] → [WP2] → BActual state: WP1 is powered off Packet path:A → (routers reach subnet, but WP1 doesn't respond) └── ARP fails for WP1's IP └── Eventually: ICMP Type 3, Code 1 (Host Unreachable) Scenario 3: TTL Exhaustion────────────────────────────────Route specified: A → [WP1] → BActual state: 30 hops between A and WP1, TTL starts at 64 Packet path:A → R1 (TTL 63) → R2 (62) → ... → R30 (34) → WP1WP1 → R31 (33) → ... → (TTL reaches 0 before B) └── ICMP Type 11, Code 0 (TTL Exceeded)Loose Source Routing carries the same security risks as Strict Source Routing—and in some ways, worse, because it's more practical for attackers to use.
Attack Scenarios:
RFC 7126 explicitly recommends filtering ALL source-routed packets at network boundaries. This includes both SSR (type 137) and LSR (type 131). Most modern operating systems and routers disable source routing processing by default.
# Linux: Disable loose source routingecho 0 > /proc/sys/net/ipv4/conf/all/accept_source_routeecho 0 > /proc/sys/net/ipv4/conf/default/accept_source_route # Permanent setting in /etc/sysctl.conf:net.ipv4.conf.all.accept_source_route = 0net.ipv4.conf.default.accept_source_route = 0 # iptables: Explicitly drop source-routed packetsiptables -A INPUT -m ipv4options --lsrr -j DROPiptables -A FORWARD -m ipv4options --lsrr -j DROP # Cisco IOS: Disable source routingRouter(config)# no ip source-route # FreeBSDsysctl net.inet.ip.sourceroute=0sysctl net.inet.ip.accept_sourceroute=0 # Windows PowerShell (check status)Get-NetIPv4Protocol | Select-Object SourceRouting# Should show: DisabledDespite its deprecation, understanding LSR remains valuable for several reasons:
Why Study Deprecated Features?
| Platform/System | Default Status | Notes |
|---|---|---|
| Linux (kernel 2.6.37+) | Disabled by default | Sysctl controls processing |
| Windows (Vista+) | Disabled by default | Registry setting available |
| macOS | Disabled by default | Sysctl controls processing |
| Cisco IOS | Enabled by default (older) | Must explicitly disable |
| Juniper Junos | Disabled by default | Firewall filter recommended |
| Major ISPs | Filtered at borders | Per RFC 7126 recommendations |
| Cloud providers (AWS, Azure, GCP) | Blocked | Not supported in virtual networks |
IPv6 learned from IPv4's source routing issues. The IPv6 Routing Header (Type 0) was deprecated in RFC 5095 due to similar security concerns. IPv6 Segment Routing (SRv6) provides controlled source routing with proper security considerations built in.
We've thoroughly covered Loose Source Routing. Let's consolidate the key points:
LSR = Waypoints with normal routing between them. SSR = Every single hop must be specified.
Both SSR and LSR are blocked in production networks. Always verify source routing is disabled.
What's Next:
We've covered the path-control options. Now we'll explore the Timestamp option, which adds time information as packets traverse the network—useful for latency measurement, clock synchronization analysis, and network debugging.
You now fully understand Loose Source Routing—how it differs from Strict, its processing algorithm, use cases, and security implications. Combined with SSR knowledge, you have complete mastery of IPv4 source routing mechanisms.