09. Introduction to Routing - Distance Vector Algorithm

A dynamic, decentralized and asynchronous routing algorithm.

Both RIP and BGP use the Bellman-Ford (distance-vector) algorithm.

Bellman-Ford Equation

if dx(y)d_x(y) is the least-cost path from xx to yy, then:

dx(y)=minvc(x,v)+dv(y)d_x(y) = min_v{c(x, v) + d_v(y)} for all vv where vv is a neighbor of xx.

Distance-Vector Algorithm

Dx(y)D_x(y) is an estimation of the least-cost path from xx to yy

The distance vector, Dx\vec{D_x}, maintains a list of estimates for each node in the graph: [Dx(y):yN][ D_x(y): y \in N ], where NN is the set of all destinations.

Each node, xx knows the cost, c(x,v)c(x, v) to each neighbor vv, its distance vector, and its neighbors’ distance vectors.

In the algorithm, each node periodically sends its own distance vector estimates to its neighbors. When it receives a new estimate, it can update its own distance vector and if it changes, update its own neighbors. The distance estimates for any node will eventually converge to the actual least cost, dx(y)d_x(y).

The algorithm is iterative and asynchronous: each local iteration is called by either a local link cost change or a DV update message from its neighbor.

The algorithm is distributed: each node notifies its neighbors only when its DV changes, and each node only knows the next hop, not the entire path.

Hence, each node is in one of three state:

Pseudo-code

distance_vector = []
neighbor_distance_vectors = {}

def init():
  for y in destinations:
    distance_vector[y] = cost(self, y) # Infinity if not a neighbor
  
  for w in neighbors:
    neighbor_distance_vectors[w] = [INFINITY for y in destinations]
    send(w, distance_vector)
  
  while True:
    get_update_or_link_cost_change()

    for y in destinations:
      distance_vector[y] = min([cost(self, v) + neighbor_distance_vectors[v][y] for v in neighbors])

    if change_occurred:
      for y in neighbors:
        send(y, distance_vector)

Count-to-infinity problem

Good news travels fast: if a link cost is lowered, this change propagates through the network quickly.

However, there is an issue when a link cost increases. For example, assume xz>xyzx \rightarrow z > x \rightarrow y \rightarrow z. If the link cost from xyx \rightarrow y increases such that the new best path from xx to yy is xzyx \rightarrow z \rightarrow y, an issue will occur as the algorithm is asynchronous: zz still thinks the best path from zz to yy is zxyz \rightarrow x \rightarrow y. Hence, it will generate a routing loop: xzxzx \rightarrow z \rightarrow x \rightarrow z\dots.

Initial state: link costs of c(x,y)=1c(x, y) = 1, c(y,z)=2c(y, z) = 2, c(z,x)=20c(z, x) = 20.

x y z
x 0 1 3 (y)
y 1 0 2
z 3 (y) 2 0

At t0t_0, yy detects c(x,y)=40c(x, y) = 40. yy thinks the best route to xx is now through zz:

Dy(x)=min(cost(y,x)+Dx(x),cost(y,z)+Dz(x))=min(40+0,2+3)=5D_y(x) = min(cost(y, x) + D_x(x), cost(y, z) + D_z(x)) = min(40 + 0, 2 + 3) = 5

x y z
x 0 1 3 (y)
y 5 (z) 0 2
z 3 (y) 2 0

At t1t_1, zz receives an update from yy and calculates Dz(x)D_z(x):

Dz(x)=min(cost(z,x)+Dx(x),cost(z,y)+Dy(x))=min(20+0,2+5)=7D_z(x) = min(cost(z, x) + D_x(x), cost(z, y) + D_y(x)) = min(20 + 0, 2 + 5) = 7

x y z
x 0 1 3 (y)
y 5 (z) 0 2
z 7 (y) 2 0

Thus, at each iteration, Dz(x)D_z(x) and Dy(x)D_y(x) increases by 2 (link cost between yy and zz) and eventually converge:

x y z
x 0 1 3 (y)
y 22 (z) 0 2
z 20 2 0

Poisoned reverse

If a node xx routes packets to zz through yy, xx will lie to yy, telling it that Dx(z)=D_x(z) = \infty.

Before the link cost change in the example above, zz lies to yy, saying Dz(x)=D_z(x) = \infty as long as it uses yy to route to xx. In yy’s DV table:

x y z
x 0 1 \infty
y 1 0 2
z \infty 2 0

At t0t_0, yy detects c(x,y)=40c(x, y) = 40:

Dy(x)=min(cost(y,x)+Dx(x),cost(y,z)+Dz(x))=min(40+0,2+)=40D_y(x) = min(cost(y, x) + D_x(x), cost(y, z) + D_z(x)) = min(40 + 0, 2 + \infty) = 40

x y z
x 0 1 \infty
y 40 0 2
z \infty 2 0

At t1t_1, zz, receives an update from yy and calculates Dz(x)D_z(x):

Dz(x)=min(cost(z,x)+Dx(x),cost(z,y)+Dy(x))=min(20+0,2+40)=20D_z(x) = min(cost(z, x) + D_x(x), cost(z, y) + D_y(x)) = min(20 + 0, 2 + 40) = 20:

x y z
x 0 1 \infty
y 40 0 2
z 20 2 0

At t2t_2, yy, updates zz, now without lying. yy calculates Dy(x)D_y(x):

Dy(x)=min(cost(y,x)+Dx(x),cost(y,z)+Dz(x))=min(40+0,2+20)=22D_y(x) = min(cost(y, x) + D_x(x), cost(y, z) + D_z(x)) = min(40 + 0, 2 + 20) = 22

x y z
x 0 1 \infty
y 22 (z) 0 2
z 20 2 0

Hence, the lie makes it converge faster.

However, poisoned reverse can make the situation worse and cause routing loops. Example:

cost(w,y)=1cost(w,z)=1cost(x,y)=4cost(x,z)=50cost(y,z)=3 \begin{aligned} cost(w, y) &= 1 \\ cost(w, z) &= 1 \\ cost(x, y) &= 4 \\ cost(x, z) &= 50 \\ cost(y, z) &= 3 \\ \end{aligned}

Consider only entries only to xx:

cost(y,x)=4(direct)cost(w,x)=5(wyx)cost(z,x)=6(zwyx) \begin{aligned} cost(y, x) &= 4 (direct) \\ cost(w, x) &= 5 (w \rightarrow y \rightarrow x) \\ cost(z, x) &= 6 (z \rightarrow w \rightarrow y \rightarrow x) \end{aligned}

So zz tells ww that Dz(x)=D_z(x)=\infty and ww tells yy that Dw(x)=D_w(x)=\infty; they lie to the node they rely on for routing, ensuring they do not send requests back to them.

At t0t_0, yy detects cost(x,y)=60cost(x, y) = 60 and recalculates its vector table: Dy(x)D_y(x) is the minimum of:

Hence, Dy(x)=9D_y(x) = 9 (through zz). Now, yy notifies ww that Dy(x)=9D_y(x) = 9 and zz that Dy(x)=D_y(x) = \infty.

Now ww recomputes Dw(x)D_w(x):

Hence, Dw(x)=10D_w(x) = 10 (through yy). Now, ww notifies zz that Dw(x)=10D_w(x) = 10 and yy that Dw(x)=D_w(x) = \infty.

Now, zz recomputes Dz(x)D_z(x):

Hence, Dz(x)=11D_z(x) = 11 (through ww). Now zz notifies yy that Dz(x)=11D_z(x) = 11 and ww that Dz(x)=D_z(x) = \infty. Now, yy recomputes Dy(x)D_y(x):

Hence, Dy(x)=14D_y(x) = 14 (through zz). The process repeats.

The computed path from yy to xx is thus yzwyy \rightarrow z \rightarrow w \rightarrow y \rightarrow \dots; a routing loop occurs.

To fix this, RIP limits the maximum cost of a path is limited to 15.

Message complexity:

Speed of convergence:

Robustness - behavior when router malfunctions: