Repository navigation
Expand file tree
/
Copy pathnetwork_1.py
More file actions
296 lines (251 loc) · 11 KB
/
Copy pathnetwork_1.py
File metadata and controls
296 lines (251 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
import queue
import threading
from collections import OrderedDict, defaultdict
import re
## wrapper class for a queue of packets
class Interface:
## @param maxsize - the maximum size of the queue storing packets
def __init__(self, maxsize=0):
self.in_queue = queue.Queue(maxsize)
self.out_queue = queue.Queue(maxsize)
##get packet from the queue interface
# @param in_or_out - use 'in' or 'out' interface
def get(self, in_or_out):
try:
if in_or_out == 'in':
pkt_S = self.in_queue.get(False)
# if pkt_S is not None:
# print('getting packet from the IN queue')
return pkt_S
else:
pkt_S = self.out_queue.get(False)
# if pkt_S is not None:
# print('getting packet from the OUT queue')
return pkt_S
except queue.Empty:
return None
##put the packet into the interface queue
# @param pkt - Packet to be inserted into the queue
# @param in_or_out - use 'in' or 'out' interface
# @param block - if True, block until room in queue, if False may throw queue.Full exception
def put(self, pkt, in_or_out, block=False):
if in_or_out == 'out':
# print('putting packet in the OUT queue')
self.out_queue.put(pkt, block)
else:
# print('putting packet in the IN queue')
self.in_queue.put(pkt, block)
## Implements a network layer packet.
class NetworkPacket:
## packet encoding lengths
dst_S_length = 5
prot_S_length = 1
##@param dst: address of the destination host
# @param data_S: packet payload
# @param prot_S: upper layer protocol for the packet (data, or control)
def __init__(self, dst, prot_S, data_S):
self.dst = dst
self.data_S = data_S
self.prot_S = prot_S
## called when printing the object
def __str__(self):
return self.to_byte_S()
## convert packet to a byte string for transmission over links
def to_byte_S(self):
byte_S = str(self.dst).zfill(self.dst_S_length)
if self.prot_S == 'data':
byte_S += '1'
elif self.prot_S == 'control':
byte_S += '2'
else:
raise('%s: unknown prot_S option: %s' %(self, self.prot_S))
byte_S += self.data_S
return byte_S
## extract a packet object from a byte string
# @param byte_S: byte string representation of the packet
@classmethod
def from_byte_S(self, byte_S):
dst = byte_S[0 : NetworkPacket.dst_S_length].strip('0')
prot_S = byte_S[NetworkPacket.dst_S_length : NetworkPacket.dst_S_length + NetworkPacket.prot_S_length]
if prot_S == '1':
prot_S = 'data'
elif prot_S == '2':
prot_S = 'control'
else:
raise('%s: unknown prot_S field: %s' %(self, prot_S))
data_S = byte_S[NetworkPacket.dst_S_length + NetworkPacket.prot_S_length : ]
return self(dst, prot_S, data_S)
## Implements a network host for receiving and transmitting data
class Host:
##@param addr: address of this node represented as an integer
def __init__(self, addr):
self.addr = addr
self.intf_L = [Interface()]
self.stop = False #for thread termination
## called when printing the object
def __str__(self):
return self.addr
## create a packet and enqueue for transmission
# @param dst: destination address for the packet
# @param data_S: data being transmitted to the network layer
def udt_send(self, dst, data_S):
p = NetworkPacket(dst, 'data', data_S)
print('%s: SENDING PACKET "%s"' % (self, p))
self.intf_L[0].put(p.to_byte_S(), 'out') #send packets always enqueued successfully
## receive packet from the network layer
def udt_receive(self):
pkt_S = self.intf_L[0].get('in')
if pkt_S is not None:
print('%s: RECIEVED PACKET "%s"' % (self, pkt_S))
## thread target for the host to keep receiving data
def run(self):
print (threading.currentThread().getName() + ': Starting')
while True:
#receive data arriving to the in interface
self.udt_receive()
#terminate
if(self.stop):
print (threading.currentThread().getName() + ': Ending')
return
## Implements a multi-interface router
class Router:
##@param name: friendly router name for debugging
# @param cost_D: cost table to neighbors {neighbor: {interface: cost}}
# @param max_queue_size: max queue length (passed to Interface)
def __init__(self, name, cost_D, max_queue_size):
self.stop = False #for thread termination
self.name = name
#create a list of interfaces
self.intf_L = [Interface(max_queue_size) for _ in range(len(cost_D))]
#save neighbors and interfeces on which we connect to them
self.neighbors = cost_D # {neighbor: {interface: cost}}
self.rt_tbl_D = cost_D.copy() # {destination: {router: cost}}
self.total_rt = defaultdict(dict)
for neighbor_name, neighbor_info in self.neighbors.items():
for interface,cost in neighbor_info.items():
self.total_rt[name][neighbor_name] = [cost]
print('%s: Initialized routing table' % self)
self.print_routes()
## called when printing the object
def __str__(self):
return self.name
## look through the content of incoming interfaces and
# process data and control packets
def process_queues(self):
for i in range(len(self.intf_L)):
pkt_S = None
#get packet from interface i
pkt_S = self.intf_L[i].get('in')
#if packet exists make a forwarding decision
if pkt_S is not None:
p = NetworkPacket.from_byte_S(pkt_S) #parse a packet out
if p.prot_S == 'data':
self.forward_packet(p,i)
elif p.prot_S == 'control':
self.update_routes(p, i)
else:
raise Exception('%s: Unknown packet type in packet %s' % (self, p))
## forward the packet according to the routing table
# @param p Packet to forward
# @param i Incoming interface number for packet p
def forward_packet(self, p, i):
try:
route = self.rt_tbl_D.get(str(p.dst)) #look up destination in routing table
for interface, cost in route.items(): #look up which interface to forward to
j = int(interface)
break
self.intf_L[j].put(p.to_byte_S(), 'out', True)
print('%s: forwarding packet "%s" from interface %d to %d' % \
(self, p, i, j))
except queue.Full:
print('%s: packet "%s" lost on interface %d' % (self, p, i))
pass
## send out route update
# @param i Interface number on which to send out a routing update
def send_routes(self, i): #ENCODING: NAME///NODE/LINK/COST//NODE/LINK/COST//....
print('sending_routes')
contents = self.name + "->" # encode name of router
for k, j in self.rt_tbl_D.items():
for link, cost in j.items():
contents += str(k) + "_" + str(link) + "_" + str(
cost) + "#" # one '/' seperates route from link and cost, two '/' seperates route from other route
p = NetworkPacket(0, 'control', contents)
self.intf_L[i].put(p.to_byte_S(), 'out', True)
## forward the packet according to the routing table
# @param p Packet containing routing information
def update_routes(self, p, i):
updateFlag = 0
contents = p.data_S.split("->") #decode packet
name = contents[0]
routeList = contents[1].split("#")
for i in routeList: #for all routes in this routing table
if i != '':
nodeList = i.split("_")
curNode = nodeList[0]
nodePar = int(nodeList[2])
self.total_rt[name][curNode] = [nodePar]
if curNode == self.name:
neiKeyValue = int(list(self.neighbors[name].keys())[0])
self.rt_tbl_D[curNode] = {neiKeyValue:0}
self.total_rt[self.name][curNode] = [0]
continue
else:
if curNode not in self.neighbors and curNode not in self.rt_tbl_D:
neiNameValue = list(self.neighbors[name].values())[0]
neiKeyValue = int(list(self.neighbors[name].keys())[0])
self.rt_tbl_D[curNode] = {neiKeyValue:nodePar + neiNameValue}
self.total_rt[self.name][curNode] = [nodePar + neiNameValue]
updateFlag = 1
elif curNode not in self.neighbors and curNode in self.rt_tbl_D:
neiNameValue = list(self.neighbors[name].values())[0]
neiKeyValue= int(list(self.neighbors[name].keys())[0])
curNodeValue = list(self.rt_tbl_D[curNode].values())[0]
if curNodeValue > int(neiNameValue) + nodePar:
self.rt_tbl_D[curNode] = {neiKeyValue:nodePar + neiNameValue}
self.total_rt[self.name][curNode] = [nodePar + neiNameValue]
updateFlag = 1
if updateFlag == 1:
for neighbor_name, neighbor_info in self.neighbors.items():
for interface,cost in neighbor_info.items():
if 'R' in neighbor_name:
self.send_routes(interface)
## Print routing table
def print_routes(self):
costTable = []
print('cost table for %s: ' % self)
sort_tbl = OrderedDict(sorted(self.total_rt.items()))
interfaces = []
for i,j in sort_tbl.items():
for link,cost in j.items():
if not link in interfaces:
interfaces.append(link)
interfaces = sorted(interfaces)
for interface in interfaces:
print(str(interface) + "\t", end='')
for i, j in sort_tbl.items():
for link,cost in j.items():
if link == interface:
print(str(cost) + "\t", end='')
costTable +=[cost]
print("\n")
tablestr1 = ''
tablestr2 = ''
for i in range(int(len(costTable)/2+1)):
tablestr1+='| '
tablestr1+= str(costTable[(i-1)*2])
if i< len(costTable):
tablestr2 += '| '
tablestr2 += str(costTable[(i-1)*2+1])
tablestr1 += ' | '
tablestr2 += ' | '
print(tablestr1)
print(tablestr2)
print("\n")
## thread target for the host to keep forwarding data
def run(self):
print (threading.currentThread().getName() + ': Starting')
while True:
self.process_queues()
if self.stop:
print (threading.currentThread().getName() + ': Ending')
return