RIP protocol is a dynamic routing protocol. A router that periodically swaps routing tables with its neighbors. The router updates its routing table according to the routing table sent by the other party through a certain algorithm. Thus dynamically update the routing table of all routers in the entire autonomous system.

If you just want to complete the RIP experiment at school but don’t know the Go language, I can briefly explain that the structure in Go is similar to that in other languages, except that it is a class with member variables only. Methods of structures, similar to methods of classes (objects).

In the main function of this experiment, I only designed the process of RIP interchange routing table to finally achieve convergence, and did not design to simulate the unreachable situation of a router. Of course, I did not design the “slow transmission of bad news” situation.

Firstly, the routing table item in the data structure RIP message is defined. The network segment with the network segment number of NETID is defined, which needs to be forwarded to the Nexthop router with a Distance of Distance. In this experiment, I set the distance of the network segment where the router is as 1. When the router of the next hop is -1, it represents direct delivery, that is, the target network segment is in the network segment of the current router.

// Rip: int (int); int (int); // Rip: int (int);

The routing table, which contains a slice of the routing table entry type.

// Routing table type RouterTable []Info

The router. Contains the address and routing table. Because this is a simple experiment, I use an integer to act as the router address. Even though the IP address itself is just a 32-bit unsigned integer.

// Router type Router struct {Addr int Table RouterTable}

A method to define the router structure.

Output router information method. Implements the String() interface, which can be called by the built-in output function.

Func (r Router) String() String {return FMT.Sprint(" Router address :", R.addr, "\n routing table ", R.able)}

The FindInfo method can find the index of the routing table entry corresponding to a network segment (netID) in the router’s own routing table. If the current router has a route entry for this segment, return its index and True; if it does not, return -1 and False. We’ll talk more about what it does.

func (r *Router) FindInfo(netId int) (int, bool) {
    for i, info := range r.Table {
        if netId == info.NetId {
            return i, true
        }
    }
    return -1, false
}

Important: Receive routing table method. The current router receives the address (Addr) and routing table (recvTable) of other routers and modifies its own routing table (r.table) through this method.

This experiment will be executed using multiple threads, so in order to avoid conflicts caused by multiple threads simultaneously modifying their routing table, a mutex lock (MUX) is added.

First, the router preprocesses the routing table after receiving it from other routers. Change the next-hop address of all table entries in the receiving routing table to the address of the sender router, adding 1 to all distances.

It then iterates through the received RecvTable. At each traversal, try to find the same (that is, the corresponding) index of its own routing table entry as the currently received routing table entry (info. NET ID) segment.

  1. There is no corresponding table entry in the routing table memory? If it does not exist, add the entry to its own routing table. If it does, execute 2
  2. If a corresponding entry exists, check to see if its next hop is the address of the sender’s router. If so, it updates its corresponding table entry information to the received table entry information. Of course, actually this only changes the distance of the table entry. The reason for the modification is that the received router table is the latest message, and everything shall be subject to the latest information. Regardless of the distance received relative to their own existing record is large or small, everything is subject to the latest. If not, do 3
  3. If the next-hop router for the corresponding table entry is not the sender router address, the distance is compared. If the distance of its corresponding entry is greater than the distance of the received entry, it is updated to the received entry. If not, do nothing and proceed to the next traversal.

In a real RIP protocol, there will be a routing table that cannot receive a reply within 3 minutes after sending its routing table to the target router, so it will set the distance of the table entry of the next hop to the target router as 16, that is, the router is unreachable. I didn’t simulate that in my experiment here.

After modifying its routing table, it releases the lock (mux.unlock) to print the current routing table information.

func (r *Router) Recv(recvRtrAddr int, recvTable RouterTable) { var mux sync.Mutex for i := range recvTable { recvTable[i].NextHop = recvRtrAddr recvTable[i].Distance += 1 } mux.Lock() for _, info := range recvTable { existigInfoIndex, ok := r.FindInfo(info.NetId) if ! ok { r.Table = append(r.Table, info) } else { if r.Table[existigInfoIndex].NextHop == recvRtrAddr { r.Table[existigInfoIndex] = info } else { if r.Table[existigInfoIndex].Distance > info.Distance { r.Table[existigInfoIndex] = info } } } } mux.Unlock() fmt.Printf("\n%v\n", r) }

Send routing table method. The current router sends its own address and routing table to the destination router. Enter the target router object pointer (dstRtr), make a copy of its own routing table, and then call the target router object’s Recv() method to receive the current router’s address and routing table.

The reason you want to make an explicit copy of your own routing table is that the slice in Go is just a reference that is passed directly to the function modification, and the impact is passed outside the function. So you want to make a deep copy and make sure that you’re only sending a copy.

func (r *Router) Send(dstRtr *Router) {
    sendTable := make(RouterTable, len(r.Table))
    copy(sendTable, r.Table)
    dstRtr.Recv(r.Addr, sendTable)

}

Automatic update method. There are two global variables. The ROUTER_LIST is a slice that contains Pointers to all router objects. Reachable matrix is a REACHABLE matrix indicating whether two routers are connected (that is, adjacent) to each other. The term “reachable” refers only to a physical connection. It does not mean that a router is down and cannot reply to a RIP message. All router objects are traversed, and if they are adjacent and not themselves, this sends them their own routing table.

Delay can not be added, in this experiment because there is no design timeout set to unreachable situation, set to no delay is also valid.

func (r *Router) AutoUpdate() { for true { time.Sleep(time.Second * 3) for _, dstRtr := range ROUTER_LIST { if dstRtr ! = r { if REACHABLE[r.Addr][dstRtr.Addr] { r.Send(dstRtr) } } } } }

The whole code. Referring to the first comment of the main function, the network topology in the experiment looks like this.

N0 -R0- N1 -R1- N2 -R2- N3

N* represents the network number, and R* represents the router number (which is the same as the address). – The router is connected to some network segment.

After the automatic update is started through multi-threading, the three routers will exchange messages with their neighboring routers all the time until they reach convergence, that is, they tend to be stable. The last wg.wait () at the end of the code is just to keep the main thread from exiting.

Package main import (" FMT ""sync" "time") var REACHABLE [][]bool var ROUTER_LIST []*Router // RIP Type Info // Router type void {Addr int; // Router type void {Addr int; // Router type void // RouterTable []Info func (r *Router) findInfo (netId int) (int, bool) {for I, info := range r.Table { if netId == info.NetId { return i, true } } return -1, False} func (r Router) String() String {return FMT.Sprint(" Router address :", R.addr, "\n routing table ", r.Table) } func (r *Router) Recv(recvRtrAddr int, recvTable RouterTable) { var mux sync.Mutex for i := range recvTable { recvTable[i].NextHop = recvRtrAddr recvTable[i].Distance += 1 } mux.Lock() for _, info := range recvTable { existigInfoIndex, ok := r.FindInfo(info.NetId) if ! ok { r.Table = append(r.Table, info) } else { if r.Table[existigInfoIndex].NextHop == recvRtrAddr { r.Table[existigInfoIndex] = info } else { if r.Table[existigInfoIndex].Distance > info.Distance { r.Table[existigInfoIndex] = info } } } } mux.Unlock() fmt.Printf("\n%v\n", r) } func (r *Router) Send(dstRtr *Router) { sendTable := make(RouterTable, len(r.Table)) copy(sendTable, r.Table) dstRtr.Recv(r.Addr, sendTable) } func (r *Router) AutoUpdate() { for true { time.Sleep(time.Second * 0) for _, dstRtr := range ROUTER_LIST { if dstRtr ! = r { if REACHABLE[r.Addr][dstRtr.Addr] { r.Send(dstRtr) } } } } } func main() { // N0 -R0- N1 -R1- N2 -R2- N3 a := Router{0, []Info{}} a.Table = append(a.Table, Info{0, 1, -1}) a.Table = append(a.Table, Info{1, 1, -1}) b := Router{1, []Info{}} b.Table = append(b.Table, Info{1, 1, -1}) b.Table = append(b.Table, Info{2, 1, -1}) c := Router{2, []Info{}} c.Table = append(c.Table, Info{2, 1, -1}) c.Table = append(c.Table, Info{3, 1, -1}) ROUTER_LIST = append(ROUTER_LIST, &a) ROUTER_LIST = append(ROUTER_LIST, &b) ROUTER_LIST = append(ROUTER_LIST, &c) REACHABLE = [][]bool{ {true, true, false}, {true, true, true}, {false, true, true}, } //a.Send(ROUTER_LIST[1]) go a.AutoUpdate() go b.AutoUpdate() go c.AutoUpdate() var wg sync.WaitGroup wg.Add(1) wg.Wait() }

So the output at the end of the convergence is going to look something like this

[{21-1} {3 1-1} {1 21} {0 31 1}] [{0 1-1} {1 1} {2 2 1} {3 3 1}] [{0 1 1} {2 2 1} {3 3 1} -1} {3 2 2} {0 2 0}]