13  Routing Labs: Advanced

In 60 Seconds

Hands-on lab implementing packet forwarding on ESP32 with TTL decrement and route lookup, simulating routing table decisions with multiple route types in embedded C, and visualizing network convergence using Python and matplotlib.

13.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Implement packet forwarding logic on ESP32 with TTL decrement and route lookup
  • Simulate routing table decisions using embedded C code with multiple route types
  • Visualize network convergence using Python and matplotlib to understand route propagation
  • Analyze packet drop scenarios including TTL expiration and missing routes
  • Debug routing issues by understanding forwarding statistics and error conditions

These labs let you experiment with more complex routing scenarios, like networks with multiple paths, failures, and changing conditions. Think of it as practicing emergency detour planning – you will see how networks automatically find new routes when the usual paths are blocked.

Lab Series:

Core Concepts:

IoT Routing:

Learning:

“This is the most exciting lab,” said Sammy the Sensor. “We actually implement packet forwarding on an ESP32 – the same microcontroller used in real IoT devices! You will write code that looks up routes, decrements TTL, and forwards packets.”

“The TTL decrement is critical,” explained Max the Microcontroller. “Every time we forward a packet, we subtract one from the TTL field. If it reaches zero, we drop the packet and send an error message back. This is how real routers work, and we implement it in embedded C.”

“The convergence visualization uses Python and matplotlib,” added Lila the LED. “You can watch how routing tables evolve over time as routers exchange updates. Seeing the algorithm converge step by step really helps you understand why some protocols are faster than others.”

“Pay close attention to the error handling,” advised Bella the Battery. “What happens when there is no matching route? What about when the TTL expires? These edge cases are where most routing bugs hide in production deployments.”

13.2 Prerequisites

Before working through these advanced labs, ensure you have completed:

Software Requirements:

  • Arduino IDE with ESP32 board support
  • Python 3.x with matplotlib and networkx libraries

13.3 Lab 4: ESP32 Packet Forwarding Simulator

Time: ~30 min | Difficulty: Advanced | Code: P07.C20.U01

Objective: Simulate packet forwarding with TTL decrement on ESP32.

Hardware Required:

  • ESP32 development board
  • USB cable
  • Computer with Arduino IDE

Software Setup:

  1. Install ESP32 board support in Arduino IDE
  2. No additional libraries needed

13.3.1 Understanding the Simulation

This lab simulates how a router processes packets:

Router packet forwarding flowchart showing decision process. Packet arrives, check if TTL equals 0 - if yes, drop packet and send ICMP Time Exceeded. If TTL greater than 0, decrement TTL by 1. Then lookup route in routing table using longest prefix match. If route found, forward to next hop or deliver directly. If no route found, drop packet and send ICMP Destination Unreachable.
Figure 13.1: Packet Forwarding Process: A router first checks TTL - if zero, the packet is dropped. Otherwise, TTL is decremented and the routing table is consulted. Connected routes result in direct delivery while static/default routes forward to the next hop.

Objective: Run the packet forwarding simulator on ESP32 and observe how a router processes packets: TTL decrement, route lookup, and forwarding decisions.

Paste the code below into the simulator. Watch the Serial Monitor to see the routing table, packet forwarding decisions, and statistics for forwarded vs dropped packets. Try modifying the TTL values or adding new routing table entries to observe different behaviors.

What to Observe:

  1. Packets with TTL=0 are dropped immediately with a “Time Exceeded” message
  2. Packets matching a connected route are delivered directly (metric 0)
  3. Packets with no matching route use the default route (0.0.0.0/0)
  4. The statistics at the end show forwarding efficiency

13.3.2 Complete ESP32 Code

#include <WiFi.h>

// Simulated packet structure
struct IPPacket {
  char source_ip[16];
  char dest_ip[16];
  uint8_t ttl;
  uint8_t protocol;
  char payload[64];
  uint32_t packet_id;
};

// Simulated routing table entry
struct RouteEntry {
  char destination[16];
  char next_hop[16];
  char interface[10];
  int metric;
};

// Simulated routing table
RouteEntry routing_table[] = {
  {"192.168.1.0/24", "0.0.0.0", "eth0", 0},      // Connected
  {"10.0.0.0/8", "0.0.0.0", "eth1", 0},          // Connected
  {"172.16.0.0/16", "10.0.0.2", "eth1", 10},     // Static
  {"0.0.0.0/0", "192.168.1.1", "eth0", 1}        // Default
};

const int ROUTING_TABLE_SIZE = 4;
uint32_t packets_forwarded = 0;
uint32_t packets_dropped = 0;
uint32_t ttl_expired = 0;

void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println("\n========================================");
  Serial.println("  ESP32 Packet Forwarding Simulator");
  Serial.println("  Routing & TTL Management Demo");
  Serial.println("========================================\n");

  displayRoutingTable();

  Serial.println("\n=======================================");
  Serial.println("Starting Packet Forwarding Simulation");
  Serial.println("=======================================\n");

  // Simulate various packet scenarios
  simulatePacketScenarios();

  displayStatistics();
}

void loop() {
  // Simulation runs once in setup()
}

void displayRoutingTable() {
  Serial.println("Routing Table:");
  Serial.println("-----------------------------------------------------");
  Serial.println("Destination       Next Hop        Interface  Metric");
  Serial.println("-----------------------------------------------------");

  for (int i = 0; i < ROUTING_TABLE_SIZE; i++) {
    char line[100];
    sprintf(line, "%-17s %-15s %-10s %d",
            routing_table[i].destination,
            routing_table[i].next_hop,
            routing_table[i].interface,
            routing_table[i].metric);
    Serial.println(line);
  }
  Serial.println("-----------------------------------------------------");
}

RouteEntry* lookupRoute(const char* dest_ip) {
  // Simplified lookup - in reality would do longest prefix matching

  // Check for local network matches (simplified)
  if (strncmp(dest_ip, "192.168.1.", 10) == 0) {
    return &routing_table[0];  // 192.168.1.0/24
  } else if (strncmp(dest_ip, "10.", 3) == 0) {
    return &routing_table[1];  // 10.0.0.0/8
  } else if (strncmp(dest_ip, "172.16.", 7) == 0) {
    return &routing_table[2];  // 172.16.0.0/16
  } else {
    return &routing_table[3];  // Default route
  }
}

enum ForwardResult {
  FORWARDED,
  TTL_EXPIRED,
  NO_ROUTE
};

ForwardResult forwardPacket(IPPacket* packet) {
  Serial.println("\n-----------------------------------------");
  Serial.printf("| Packet ID: %lu\n", packet->packet_id);
  Serial.println("-----------------------------------------");
  Serial.printf("  Source: %s -> Destination: %s\n",
                packet->source_ip, packet->dest_ip);
  Serial.printf("  TTL: %d  Protocol: %d\n", packet->ttl, packet->protocol);
  Serial.printf("  Payload: %s\n", packet->payload);

  // Step 1: Check TTL
  if (packet->ttl == 0) {
    Serial.println("  X Action: DROP (TTL expired)");
    Serial.println("  -> Would send ICMP Time Exceeded to source");
    ttl_expired++;
    packets_dropped++;
    return TTL_EXPIRED;
  }

  // Step 2: Decrement TTL
  packet->ttl--;
  Serial.printf("  -> TTL decremented to %d\n", packet->ttl);

  // Step 3: Route lookup
  RouteEntry* route = lookupRoute(packet->dest_ip);

  if (route == NULL) {
    Serial.println("  X Action: DROP (No route to destination)");
    Serial.println("  -> Would send ICMP Destination Unreachable");
    packets_dropped++;
    return NO_ROUTE;
  }

  // Step 4: Forward packet
  Serial.printf("  OK Route found: %s\n", route->destination);

  if (strcmp(route->next_hop, "0.0.0.0") == 0) {
    Serial.printf("  -> Direct delivery on %s\n", route->interface);
  } else {
    Serial.printf("  -> Forward to next hop: %s via %s\n",
                  route->next_hop, route->interface);
  }

  Serial.printf("  OK Packet forwarded (metric: %d)\n", route->metric);
  packets_forwarded++;
  return FORWARDED;
}

void simulatePacketScenarios() {
  IPPacket packet;
  packet.protocol = 6;  // TCP

  // Scenario 1: Local network packet
  Serial.println("\n=== Scenario 1: Local Network ===");
  strcpy(packet.source_ip, "192.168.1.100");
  strcpy(packet.dest_ip, "192.168.1.50");
  packet.ttl = 64;
  packet.packet_id = 1001;
  strcpy(packet.payload, "HTTP Request");
  forwardPacket(&packet);

  // Scenario 2: Remote network via static route
  Serial.println("\n\n=== Scenario 2: Static Route ===");
  strcpy(packet.source_ip, "192.168.1.100");
  strcpy(packet.dest_ip, "172.16.50.100");
  packet.ttl = 64;
  packet.packet_id = 1002;
  strcpy(packet.payload, "IoT Sensor Data");
  forwardPacket(&packet);

  // Scenario 3: Internet via default route
  Serial.println("\n\n=== Scenario 3: Default Route (Internet) ===");
  strcpy(packet.source_ip, "192.168.1.100");
  strcpy(packet.dest_ip, "8.8.8.8");
  packet.ttl = 64;
  packet.packet_id = 1003;
  strcpy(packet.payload, "DNS Query");
  forwardPacket(&packet);

  // Scenario 4: TTL expiration
  Serial.println("\n\n=== Scenario 4: TTL Expiration ===");
  strcpy(packet.source_ip, "192.168.1.100");
  strcpy(packet.dest_ip, "10.0.0.50");
  packet.ttl = 1;  // Will expire after decrement
  packet.packet_id = 1004;
  strcpy(packet.payload, "Data packet");
  forwardPacket(&packet);
  forwardPacket(&packet);  // Second hop - TTL will be 0

  // Scenario 5: Multiple hops
  Serial.println("\n\n=== Scenario 5: Multiple Hops (TTL tracking) ===");
  strcpy(packet.source_ip, "192.168.1.100");
  strcpy(packet.dest_ip, "10.0.0.50");
  packet.ttl = 5;
  packet.packet_id = 1005;
  strcpy(packet.payload, "Multi-hop packet");

  Serial.println("Simulating packet traverse across multiple routers:");
  for (int hop = 1; hop <= 5; hop++) {
    Serial.printf("\n--- Hop %d ---\n", hop);
    ForwardResult result = forwardPacket(&packet);
    if (result != FORWARDED) {
      break;
    }
  }
}

void displayStatistics() {
  Serial.println("\n\n========================================");
  Serial.println("       Forwarding Statistics");
  Serial.println("========================================");
  Serial.printf("  Packets Forwarded: %lu\n", packets_forwarded);
  Serial.printf("  Packets Dropped:   %lu\n", packets_dropped);
  Serial.printf("  TTL Expired:       %lu\n", ttl_expired);
  Serial.printf("  Total Processed:   %lu\n",
                packets_forwarded + packets_dropped);

  if (packets_forwarded + packets_dropped > 0) {
    float success_rate = (float)packets_forwarded /
                         (packets_forwarded + packets_dropped) * 100.0;
    Serial.printf("  Success Rate:      %.1f%%\n", success_rate);
  }

  Serial.println("\n=======================================");
  Serial.println("Simulation Complete");
  Serial.println("=======================================\n");
}

13.3.3 Expected Serial Monitor Output

========================================
  ESP32 Packet Forwarding Simulator
  Routing & TTL Management Demo
========================================

Routing Table:
-----------------------------------------------------
Destination       Next Hop        Interface  Metric
-----------------------------------------------------
192.168.1.0/24    0.0.0.0         eth0       0
10.0.0.0/8        0.0.0.0         eth1       0
172.16.0.0/16     10.0.0.2        eth1       10
0.0.0.0/0         192.168.1.1     eth0       1
-----------------------------------------------------

=======================================
Starting Packet Forwarding Simulation
=======================================


=== Scenario 1: Local Network ===

-----------------------------------------
| Packet ID: 1001
-----------------------------------------
  Source: 192.168.1.100 -> Destination: 192.168.1.50
  TTL: 64  Protocol: 6
  Payload: HTTP Request
  -> TTL decremented to 63
  OK Route found: 192.168.1.0/24
  -> Direct delivery on eth0
  OK Packet forwarded (metric: 0)


=== Scenario 2: Static Route ===

-----------------------------------------
| Packet ID: 1002
-----------------------------------------
  Source: 192.168.1.100 -> Destination: 172.16.50.100
  TTL: 64  Protocol: 6
  Payload: IoT Sensor Data
  -> TTL decremented to 63
  OK Route found: 172.16.0.0/16
  -> Forward to next hop: 10.0.0.2 via eth1
  OK Packet forwarded (metric: 10)

[... more scenarios ...]

13.3.4 Learning Outcomes

  • See TTL decrement in action
  • Understand routing table lookup process
  • Observe different forwarding decisions (connected, static, default)
  • Visualize packet drop scenarios
Try It: TTL and Forwarding Statistics Calculator

Adjust the initial TTL and number of hops to predict packet fate and forwarding efficiency.


13.4 Lab 5: Python Network Convergence Simulator

Time: ~25 min | Difficulty: Advanced | Code: P07.C20.U02

Objective: Visualize routing protocol convergence and link failure recovery.

13.4.1 Setup

pip install matplotlib networkx

13.4.2 Understanding Convergence

Distance vector convergence visualization showing four rounds of routing table updates. Round 0: Each router knows only directly connected neighbors. Round 1: Routers exchange tables, learn 2-hop destinations. Round 2: Knowledge propagates further, 3-hop destinations learned. Round 3: Network converged, all routers know all destinations with optimal costs.
Figure 13.2: Convergence Process: Distance vector protocols converge by exchanging routing tables. Each round, routers learn about destinations one hop further away. Convergence is complete when no routing table changes occur between rounds.

13.4.3 Python Simulation Code

"""
Network Convergence Simulator
Demonstrates distance vector routing protocol convergence
"""

import matplotlib.pyplot as plt
import networkx as nx
from collections import defaultdict

class DistanceVectorRouter:
    """Simulates a router running distance vector protocol"""

    def __init__(self, name):
        self.name = name
        self.neighbors = {}  # neighbor_name: cost
        self.routing_table = {}  # destination: (cost, next_hop)

    def add_neighbor(self, neighbor_name, cost):
        """Add a directly connected neighbor"""
        self.neighbors[neighbor_name] = cost
        self.routing_table[neighbor_name] = (cost, neighbor_name)

    def receive_update(self, from_neighbor, neighbor_table):
        """
        Process routing update from a neighbor
        Returns True if routing table changed
        """
        changed = False
        cost_to_neighbor = self.neighbors.get(from_neighbor, float('inf'))

        for destination, (dest_cost, _) in neighbor_table.items():
            if destination == self.name:
                continue

            new_cost = cost_to_neighbor + dest_cost
            current = self.routing_table.get(destination)

            if current is None or new_cost < current[0]:
                self.routing_table[destination] = (new_cost, from_neighbor)
                changed = True

        return changed

    def get_routing_table(self):
        """Return copy of routing table for sharing"""
        return dict(self.routing_table)


class NetworkSimulator:
    """Simulates a network of distance vector routers"""

    def __init__(self):
        self.routers = {}
        self.history = []  # Track convergence history

    def add_router(self, name):
        """Add a router to the network"""
        self.routers[name] = DistanceVectorRouter(name)
        self.routers[name].routing_table[name] = (0, name)

    def add_link(self, router1, router2, cost):
        """Add a bidirectional link between routers"""
        self.routers[router1].add_neighbor(router2, cost)
        self.routers[router2].add_neighbor(router1, cost)

    def run_convergence(self, max_rounds=10):
        """
        Run distance vector convergence
        Returns number of rounds to converge
        """
        print("\nRunning distance vector convergence...")
        print("=" * 60)

        for round_num in range(max_rounds):
            print(f"\n{'=' * 60}")
            print(f"Round {round_num}")
            print(f"{'=' * 60}")

            # Snapshot current state
            self.history.append(self._get_network_state())

            # Each router sends updates to neighbors
            any_change = False
            updates = {}

            # Collect all updates first
            for name, router in self.routers.items():
                updates[name] = router.get_routing_table()

            # Apply updates
            for name, router in self.routers.items():
                for neighbor in router.neighbors:
                    if router.receive_update(neighbor, updates[neighbor]):
                        any_change = True

            # Print current routing tables
            self._print_routing_tables()

            if not any_change and round_num > 0:
                print(f"\nConverged in {round_num} rounds")
                return round_num

        print(f"\nDid not converge in {max_rounds} rounds")
        return max_rounds

    def _get_network_state(self):
        """Capture current network state for visualization"""
        state = {}
        for name, router in self.routers.items():
            state[name] = dict(router.routing_table)
        return state

    def _print_routing_tables(self):
        """Print all routing tables"""
        for name, router in self.routers.items():
            print(f"\nRouting Table for {name}:")
            print("Destination     Cost   Next Hop")
            print("-" * 40)
            for dest, (cost, next_hop) in sorted(router.routing_table.items()):
                print(f"{dest:<15} {cost:<6} {next_hop}")

    def simulate_link_failure(self, router1, router2):
        """Simulate a link failure between two routers"""
        print(f"\n{'=' * 60}")
        print(f"Link Failure: {router1}-{router2} link goes down")
        print(f"{'=' * 60}")

        # Remove neighbor relationship
        if router2 in self.routers[router1].neighbors:
            del self.routers[router1].neighbors[router2]
        if router1 in self.routers[router2].neighbors:
            del self.routers[router2].neighbors[router1]

        # Mark routes via failed link as invalid
        for name, router in self.routers.items():
            invalid_dests = []
            for dest, (cost, next_hop) in router.routing_table.items():
                if next_hop == router2 and name == router1:
                    invalid_dests.append(dest)
                elif next_hop == router1 and name == router2:
                    invalid_dests.append(dest)

            for dest in invalid_dests:
                del router.routing_table[dest]

    def visualize_topology(self):
        """Create network topology visualization"""
        G = nx.Graph()

        # Add nodes and edges
        for name, router in self.routers.items():
            G.add_node(name)
            for neighbor, cost in router.neighbors.items():
                G.add_edge(name, neighbor, weight=cost)

        # Draw network
        plt.figure(figsize=(10, 8))
        pos = nx.spring_layout(G, seed=42)

        nx.draw_networkx_nodes(G, pos, node_size=700,
                               node_color='#2C3E50')
        nx.draw_networkx_labels(G, pos, font_size=12,
                                font_color='white')
        nx.draw_networkx_edges(G, pos, width=2,
                               edge_color='#16A085')

        # Add edge labels (costs)
        edge_labels = nx.get_edge_attributes(G, 'weight')
        nx.draw_networkx_edge_labels(G, pos, edge_labels,
                                     font_size=10)

        plt.title("Network Topology", fontsize=14)
        plt.axis('off')
        plt.tight_layout()
        plt.savefig('network_topology.png', dpi=150)
        plt.show()
        print("Saved: network_topology.png")


def main():
    """Run the network convergence simulation"""
    print("Network Convergence Simulator")
    print("=" * 60)

    # Create network
    sim = NetworkSimulator()

    # Add routers
    for name in ['R1', 'R2', 'R3', 'R4', 'R5']:
        sim.add_router(name)

    # Add links (creating a mesh topology)
    sim.add_link('R1', 'R2', 1)
    sim.add_link('R1', 'R3', 5)
    sim.add_link('R2', 'R4', 2)
    sim.add_link('R3', 'R4', 1)
    sim.add_link('R4', 'R5', 3)

    print("\nInitial Network Topology:")
    print("""
    R1 ---- 1 ---- R2
    |              |
    5              2
    |              |
    R3 ---- 1 ---- R4 ---- 3 ---- R5
    """)

    # Run convergence
    rounds = sim.run_convergence()

    # Simulate link failure
    sim.simulate_link_failure('R1', 'R2')
    print("\nReconverging after link failure...")
    sim.run_convergence()

    # Visualize (optional - requires display)
    try:
        sim.visualize_topology()
    except Exception as e:
        print(f"Visualization skipped: {e}")


if __name__ == "__main__":
    main()

13.4.4 Expected Output

Network Convergence Simulator
======================================================================

Initial Network Topology:

    R1 ---- 1 ---- R2
    |              |
    5              2
    |              |
    R3 ---- 1 ---- R4 ---- 3 ---- R5


Running distance vector convergence...
============================================================

============================================================
Round 0
============================================================

Routing Table for R1:
Destination     Cost   Next Hop
----------------------------------------
R1              0      R1
R2              1      R2
R3              5      R3

Routing Table for R2:
Destination     Cost   Next Hop
----------------------------------------
R1              1      R1
R2              0      R2
R4              2      R4

[... continues ...]

============================================================
Round 3
============================================================

Routing Table for R1:
Destination     Cost   Next Hop
----------------------------------------
R1              0      R1
R2              1      R2
R3              4      R2
R4              3      R2
R5              6      R2

Converged in 3 rounds

============================================================
Link Failure: R1-R2 link goes down
============================================================

Reconverging after link failure...

[... reconvergence output ...]

13.4.5 Key Concepts Demonstrated

  • Distance Vector Propagation: Each round, knowledge spreads one hop further
  • Bellman-Ford Algorithm: Cost via neighbor = cost_to_neighbor + neighbor’s_cost
  • Convergence Detection: No changes between rounds indicates convergence
  • Link Failure Recovery: Routes via failed link are removed, new paths discovered

13.5 Routing Metrics in IoT Networks

Different routing protocols use different metrics to determine the best path. Understanding these metrics helps you choose the right protocol for your IoT deployment.

Comparison of three routing metrics shown as three separate subgraphs. Top: Hop Count Metric shows path with total cost of 3 hops, treating all links equally. Middle: ETX (Expected Transmission Count) Metric shows same path but with packet reception rates affecting total ETX cost of 3.36. Bottom: Latency Metric shows path with router delays for total 30ms end-to-end latency.
Figure 13.3: Routing Metrics Comparison: Three common routing metrics used in IoT networks. Hop Count (top) counts the number of routers traversed. ETX (middle) measures expected transmission attempts accounting for packet loss. Latency (bottom) measures end-to-end delay.
Choosing the Right Metric

Use Hop Count when:

  • Network links are reliable (wired connections)
  • Minimizing routing overhead is critical
  • Simple routing decisions are sufficient

Use ETX when:

  • Wireless links with variable quality
  • Retransmissions impact performance
  • Link reliability varies significantly (typical in IoT)

Use Latency when:

  • Real-time applications (video, voice, industrial control)
  • Delay-sensitive IoT use cases
  • Network congestion is a concern

For IoT sensor networks, ETX is often the best choice because wireless links experience packet loss, and minimizing retransmissions saves battery power.


How many convergence rounds does a distance-vector protocol need to propagate routing information across a network? Consider a 10-router linear topology: R1 ↔︎ R2 ↔︎ R3 ↔︎ … ↔︎ R10.

Round-by-round propagation (each router exchanges with neighbors every 30 seconds): - Round 1: R1 knows itself (0 hops). R2 learns R1 (1 hop). Distance information propagates 1 hop. - Round 2: R3 learns R1 (2 hops via R2). Distance propagates 2 hops total. - Round n: Information propagates \(n\) hops from source.

Full convergence (R10 learns about R1): - Requires 9 rounds (R1 is 9 hops away) - Time: \(9 \times 30\text{ s} = 270\text{ seconds} = 4.5\text{ minutes}\)

Bellman-Ford iterations: \(O(V - 1) = 10 - 1 = 9\) rounds

Link failure impact (R5 ↔︎ R6 link breaks): - All routers on either side of the break must relearn paths to destinations on the other side - R10’s route to R1 becomes invalid; without an alternative path, R10 marks R1 as unreachable - Count-to-infinity: Without split horizon, routers may falsely believe they can still reach the disconnected side via each other, incrementing cost until it hits the maximum (16 for RIP). This can take many rounds. With split horizon + poison reverse, the failure propagates immediately and converges in about 5 rounds = 150 seconds.

Why OSPF is faster: Link-state protocols flood topology changes instantly (< 1 second), then all routers recompute locally. Distance-vector waits for multiple rounds of neighbor exchanges.

Common Pitfalls

Advanced routing labs use software simulators that may not accurately model link quality variations and timing. Always follow up simulator exercises with physical hardware testing when designing production networks.

Labs that only test successful routing scenarios miss important behaviors. Deliberately inject link failures, packet loss, and node failures in lab exercises to develop troubleshooting skills.

Advanced lab configurations are complex enough that reproducing them from memory is unreliable. Document all router configurations, route tables, and experiment parameters before changing anything.

13.6 Summary

  • ESP32 packet forwarding simulation demonstrates TTL decrement, route lookup, and forwarding decisions in embedded systems
  • Routing table structures include destination network, next hop, interface, and metric for making forwarding decisions
  • Python convergence simulation visualizes how distance vector protocols propagate routing information across a network
  • Link failure recovery shows how routers automatically discover new paths when existing routes become unavailable
  • Routing metrics (hop count, ETX, latency) serve different purposes depending on network characteristics and application requirements
  • Forwarding statistics help diagnose routing issues by tracking successful forwards, drops, and TTL expirations

13.7 Knowledge Check

When configuring routing for IoT deployments, selecting the right metric dramatically impacts network performance. Use this framework to choose the optimal metric based on your network characteristics and application requirements.

Metric Type Best For Avoid When Example Use Case
Hop Count Stable wired networks, simple topologies Wireless links with variable quality Office Ethernet backbone with reliable switches
ETX (Expected Transmission Count) Wireless mesh with lossy links, battery-powered devices Low-latency requirements (computation overhead) ZigBee home automation with 15-30% packet loss
Latency Real-time control systems, VoIP, video Best-effort sensor data, infrequent updates Industrial control with <50ms response requirements
Energy (Remaining Battery) Battery-powered networks prioritizing network lifetime Unlimited power sources Solar-powered agricultural sensors
Bandwidth High-throughput video surveillance, bulk data transfer Low-rate sensor networks (overkill) Security camera mesh network

Step-by-Step Selection Process:

Step 1: Identify Your Primary Constraint

  • Power-constrained (battery)? → Consider Energy or ETX
  • Latency-sensitive? → Use Latency metric
  • Lossy wireless links? → Use ETX
  • Simple wired topology? → Use Hop Count

Step 2: Evaluate Secondary Factors

  • Computational resources: ETX requires link quality probing (CPU cost)
  • Protocol support: Does your protocol (RPL, AODV, etc.) support the metric?
  • Stability: More complex metrics may cause routing instability if links fluctuate rapidly

Step 3: Test and Validate

  • Deploy test network with chosen metric
  • Measure actual vs expected performance (delivery rate, latency, battery life)
  • Adjust metric or thresholds if results don’t match requirements

Worked Example: Smart Agriculture Deployment

Network: 200 soil moisture sensors across 50-acre farm - Wireless: LoRaWAN (long-range, low-power) - Power: Solar + battery backup - Data rate: 1 reading every 30 minutes - Environment: Open field with occasional rain/fog affecting RF propagation

Metric Comparison:

Metric Configuration Observed Performance Issues
Hop Count Default (all links cost 1) 72% delivery rate Chose 3-hop path through wet area (30% loss/hop) instead of 4-hop reliable path
ETX Probe interval 60s 94% delivery rate, +12% battery drain Better paths selected, but probing cost noticeable
ETX (optimized) Probe interval 300s 93% delivery rate, +3% battery drain Acceptable trade-off: slightly slower adaptation, much lower power

Decision: Use ETX with 300-second probe interval - Why: Balances link quality awareness (ETX) with energy efficiency (infrequent probing) - Trade-off: 5-minute delay to detect link quality changes, but farm environment changes slowly - Result: Network operates 18 months on solar + 5000mAh battery vs 8 months with hop count

Configuration Example (RPL with ETX):

// Contiki-NG RPL configuration
#define RPL_CONF_MOP RPL_MOP_STORING_NO_MULTICAST
#define RPL_CONF_OF RPL_OF_MRHOF  // Minimum Rank with Hysteresis Objective Function
#define RPL_CONF_PROBING_INTERVAL (5 * 60 * CLOCK_SECOND)  // 300s probe interval
#define RPL_MRHOF_CONF_MAX_LINK_METRIC 512  // Avoid excessively lossy links

Key Insight: Don’t just accept default hop count! IoT networks with wireless links benefit significantly from link-quality-aware metrics like ETX, with proper tuning to balance responsiveness and energy consumption.

Prerequisites:

  • Routing Fundamentals → TTL, routing tables, forwarding decisions
  • C Programming → Struct definitions, pointers, function calls
  • Python Basics → Classes, dictionaries, loops

Builds Toward:

  • Production Routing Implementations → Real router firmware development
  • Network Simulation → Large-scale network modeling
  • Protocol Development → Custom routing protocol design

Relates To:

  • RPL Implementation → Real-world IoT routing code
  • Network Debugging → Packet trace analysis techniques
  • Embedded Systems → Resource-constrained routing

Within This Module:

Related Modules:

Tools & Frameworks:

  • ESP32 Arduino Core - Embedded routing development
  • Python NetworkX - Network topology and routing visualization
  • Matplotlib - Convergence visualization
Previous: Routing Labs: Fundamentals Next: Routing Labs: Algorithms

13.8 What’s Next

If you want to… Read this
Review fundamental routing concepts Routing Fundamentals
Study RPL production deployments RPL Production
Test knowledge with routing quiz Routing Labs and Quiz
Explore IoT routing challenges Routing for IoT

Continue to Routing Labs: Algorithms for Python implementations of:

Or return to Routing Labs: Overview for quiz questions testing your understanding.