Computer Network 3 | Spanning Tree Protocol Implementation
1. Project Documentation
run.py
The entry of the program. It will take one argument topology_file which is the file name of the map file without .py.
$ python run.py <topology_file>
The control flow in this program is,
Topology.__init__ 
-> Topology.run_spanning_tree()
-> Topology.log_spanning_tree()
Sample.py, NoLoopTopo.py, SimpleLoopTopo.py, TailTopo.py, ComplexLoopTopo.py
The topology files containing the map information.
Message.py
The message to communicate by Spanning Tree Protocol. For example, you will create and send messages in Switch.py by declaring a message msg as,
msg = Message(claimedRoot, distanceToRoot, originID, destinationID, pathThrough)
StpSwitch.py
Definition of a base of Switch class. This file contains abstractions of sending messages and verifying topologies.
Topology.py
Topology.py is the actual program that connects run.py and Switch.py. It initiates with the topo map topology_file.
Topology.send_message(): This function is used to send messages between switches within the given topo map. It has been wrapped intoStpSwitch.send_messagefunction and becauseSwitchclass is based onStpSwitch, we can directly callself.send_messagewithin theSwitchclass.Topology.run_spanning_tree(): The entry point of starting the spanning tree protocol. It first sends the initial messages from each node by invokingsend_intial_messages(). Afterward, each message is delivered to the destination switch, whereprocess_message()is invoked to generate the spanning tree.log_spanning_tree(): This is for generating the pretty output at the end of simulation. It invokes thegenerate_logstring()function for each node and put the result together.
2. Assistant File
Makefile
This file can be used to simplify the testing process.
#Makefile
clean:
	rm -rf ./*.log
run: clean
	python run.py Sample
	python run.py NoLoopTopo
	python run.py SimpleLoopTopo
	python run.py TailTopo
	python run.py ComplexLoopTopo
test: run
	diff ./Sample.log ./Logs/Sample.log
	diff ./NoLoopTopo.log ./Logs/NoLoopTopo.log
	diff ./SimpleLoopTopo.log ./Logs/SimpleLoopTopo.log
	diff ./TailTopo.log ./Logs/TailTopo.log
	diff ./ComplexLoopTopo.log ./Logs/ComplexLoopTopo.log
3. The Goal of This Project
Let’s use the simple single loop topology SimpleLoopTopology as an example to see our goal of this project.
Initially we have the topo map as,
topo = { 1 : [2, 3], 
         2 : [1, 4],
         3 : [1, 4], 
         4 : [2, 3] }
In this loop, we have the following edges,
1 - 2, 1 - 3
2 - 1, 2 - 4
3 - 1, 3 - 4
4 - 2, 4 - 3
However, if we comfirm all these edges, we will create loop in this topo map so some of the packets will never get to the ends. In order to cancel the loops, we have to prun some edges or we have to select only some of the edges. In this example, we can cut any of the edges, for example, 3 - 4 so there will be no loop any more.
1 - 2, 1 - 3
2 - 1, 2 - 4
3 - 1
4 - 2
In fact, the final edges we have are 1 - 2, 1 - 3, and 2 - 4. Note that in the final output, the number of edges must be a multiple of 2 because all edges are round-way.
4. Implement generate_logstring
generate_logstring is the smiplest task among all the functions. Based on the description, it will be called after we have the spanning tree represented by a list called ActiveLinks.
So now our task is to create a list of strings with all the strings represent the activated links from the current node to another node.
The following script can be used for testing,
s1 = Switch(1, None, [])
s1.activeLinks = [2, 3, 4]
assert s1.generate_logstring() == "1 - 2, 1 - 3, 1 - 4"
5. Revisit Message.py and StpSwitch.py
Now let’s review what variables we have for a Message and a based Switch.
For a Message class, we have,
root: the SwitchID thought to be the original rootdistance: distance from current switch to the rootorigin: the SwitchID for message senderdestination: the SwitchID for message receiverpathThrough: indicating the path to the claimed root from the origin passes through the destination
For a based Switch we have,
switchID: ID of the current switchtopology:Topologyobject containing the map. However, it should not read the map information in the object. The only thing helps in this case is thetopology.send_messagefunction.links: direct neighbours the current switch can go
Based on send_initial_messages, we can know that initially, each switch will treat itself as the root and send initial messages to all its neighbours by,
for destinationID in self.links:
    self.send_message(
        Message(self.switchID, 0, self.switchID, destinationID, False)
    )
6. Switch Data Structure
Now we have put the list activateLinks into the Switch data structure, we should consider what else do we need to complete it. From the document we are awared that we need to have,
root: a variable to store the switch ID that this switch sees as the rootdistance: a variable to store the distance to the switch’s rootpathThroughLink: a variable to keep track of which neighbor it goes through to get to the root because for a spanning tree, a switch should onlu go through one neighbor, if any, to go to the root
7. Message Process
When a message comes to a switch, it has to process the message with the following steps,
(1) Whether to activate the link
If the message have
pathThrough == True, the link in the message should be activatedIf the message have
pathThrough == False, and the current switch don’t path through the switch where the message came from, the link in the message should be inactivated
(2) Whether to update root
The switch should update the
rootstored in its data structure if it receives a message with a lower claimedrootvalueUpdate related other fields in the data structure
Send new messages to neighbors for sync
When sending message,
pathThroughshould only beTrueifdestinationIDequals to thepathThroughLinkthat goes through the claimed root
(3) Whether to update distance
The switch should update the
distancestored in its data structure if it receives a message with the same claimedrootvalue but a lowerdistancecompared to thecurrent distance - 1Update related other fields in the data structure
Send new messages to neighbors for sync
(4) Whether to update pathThroughLink
The switch should update the
pathThroughLinkstored in its data structure if it receives a message with the same claimedrootvalue and the samedistance, but with a lowerpathThroughLinkUpdate related other fields in the data structure
Send new messages to neighbors for sync
7. Local Test
$ make test
$ make clean