├── .gitignore ├── DFS-based path planning algorithm ├── DFLSPathPlan.py └── randomTopo.py ├── Euler trail-based path planning algorithm ├── algorithm │ ├── optimal_find_path_balance.py │ └── optimal_find_path_unbalance.py └── figure_generation │ ├── random graph generator new.py │ ├── randomTopo.py │ └── specialTopo.py ├── README.md ├── infocom19_INT_path_cr.pdf └── system ├── controller ├── app.py ├── dBParser.py ├── device.py ├── p4_mininet.py ├── switchRuntime.py └── topoMaker.py ├── p4app ├── app.json ├── app.p4 ├── header.p4 └── parser.p4 └── packet ├── int_data.sql ├── receiveint.c └── sendint.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /DFS-based path planning algorithm/DFLSPathPlan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import random 3 | import sys 4 | import copy 5 | import randomTopo 6 | 7 | def DFLSPathPlan(topoMatrix, sNum): 8 | sys.setrecursionlimit(1000000) 9 | linkState = copy.deepcopy(topoMatrix) 10 | pathCount = [0] 11 | #DFLS 12 | def DFLS(v, isNewPath): 13 | if isNewPath == 1: 14 | # print " " 15 | # print v, " " 16 | pathCount[0] += 1 17 | isNewPath = 0 18 | # else: 19 | # print v, " " 20 | for j in range(sNum): 21 | if linkState[v][j] == 1: 22 | linkState[v][j] = 0 23 | linkState[j][v] = 0 24 | isNewPath = DFLS(j, isNewPath) 25 | return 1 26 | DFLS(0,1) 27 | return pathCount[0] 28 | 29 | if __name__ == '__main__': 30 | sNum = 100 31 | topoMatrix = randomTopo.createRandomTopo(sNum) 32 | pathNum = DFLSPathPlan(topoMatrix, sNum) 33 | print pathNum 34 | -------------------------------------------------------------------------------- /DFS-based path planning algorithm/randomTopo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import random 3 | import sys 4 | import numpy as np 5 | 6 | def createRandomTopo(sNum): 7 | sys.setrecursionlimit(1000000) 8 | #create adjaMatrix 9 | topoMatrix = [[0 for i in range(sNum)] for i in range(sNum)] 10 | visited = [0 for i in range(sNum)] 11 | #create topo randomly 12 | for i in range(sNum): 13 | for j in range(i+1, sNum): 14 | link = random.randint(0,1) 15 | topoMatrix[i][j] = link 16 | topoMatrix[j][i] = link 17 | #DFS 18 | def DFS(v): 19 | visited[v] = 1 20 | for j in range(sNum): 21 | if topoMatrix[v][j] == 1 and visited[j] == 0: 22 | DFS(j) 23 | #check the network connectivity using DFS 24 | disconNode = [] 25 | for i in range(sNum): 26 | if visited[i] == 0: 27 | DFS(i) 28 | disconNode.append(i) 29 | #if the network is unconnected, connect each disconNode 30 | for i in range(len(disconNode)-1): 31 | topoMatrix[disconNode[i]][disconNode[i+1]] = 1 32 | topoMatrix[disconNode[i+1]][disconNode[i]] = 1 33 | 34 | oddCount = calOddNum(topoMatrix, sNum) 35 | 36 | return topoMatrix, oddCount 37 | 38 | def createRandomTopoWithFixedOdds(oddNum, maxSNum, step): 39 | sys.setrecursionlimit(1000000) 40 | sNum = 2*oddNum 41 | topoLists = [] 42 | 43 | while sNum <= maxSNum: 44 | flag = 0 45 | while flag != 1: 46 | #create adjaMatrix 47 | topoMatrix = g_generator_edge(10000, sNum) 48 | 49 | for i in topoMatrix: 50 | oddCount = calOddNum(i, sNum) 51 | if oddCount == oddNum: 52 | topoLists.append(i) 53 | flag = 1 54 | continue 55 | # print topoMatrix 56 | sNum += step 57 | 58 | return topoLists 59 | 60 | def calOddNum(topoMatrix, sNum): 61 | count = 0 62 | for i in range(sNum): 63 | degreeSum = 0 64 | for j in range(sNum): 65 | degreeSum += topoMatrix[i][j] 66 | if degreeSum%2 == 1: 67 | count += 1 68 | return count 69 | 70 | def g_generator_edge(NUM_GRAPHS,NUM_NODES,edge_incre=1): 71 | op=[] 72 | NUM_GRAPHS=min(NUM_GRAPHS,int(NUM_NODES*(NUM_NODES-1)/2-NUM_NODES)) 73 | for i in range(NUM_GRAPHS): 74 | if(i==0): 75 | network_matrix=np.zeros([NUM_NODES, NUM_NODES]) 76 | for j in range(NUM_NODES-1): 77 | network_matrix[j][j+1] = 1 78 | network_matrix[j+1][j]=1 79 | network_matrix[0][NUM_NODES-1]=1 80 | network_matrix[NUM_NODES-1][0]=1 81 | op.append(network_matrix) 82 | else: 83 | network_matrix=copy.deepcopy(op[i-1]) 84 | select=[] 85 | for j in range(NUM_NODES): 86 | for k in range(j+1,NUM_NODES): 87 | if(network_matrix[j][k]==0): 88 | select.append([j,k]) 89 | for l in range(edge_incre): 90 | temp=select.pop(random.randint(0,len(select)-1)) 91 | network_matrix[temp[0]][temp[1]]=1 92 | network_matrix[temp[1]][temp[0]]=1 93 | op.append(network_matrix) 94 | return op 95 | 96 | 97 | if __name__ == '__main__': 98 | sNum = 5 99 | topo, oddCount = createRandomTopo(sNum) 100 | print(topo) 101 | print(oddCount) 102 | topoList = createRandomTopoWithFixedOdds(4, 10, 2) #each sNum has five graphs 103 | print(topoList) -------------------------------------------------------------------------------- /Euler trail-based path planning algorithm/algorithm/optimal_find_path_balance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import matplotlib.pyplot as plt 3 | import copy 4 | import random 5 | import networkx as nx 6 | import numpy as np 7 | import time 8 | 9 | 10 | class Graph(object): 11 | def __init__(self, *args, **kwargs): 12 | self.node_neighbors = {} 13 | self.visited = {} 14 | self.A = [] # 为现存节点数组 15 | self.degree = [] # 为度数组 16 | 17 | def add_nodes(self, nodelist): 18 | for node in nodelist: 19 | self.add_node(node) 20 | 21 | def add_node(self, node): 22 | if not node in self.nodes(): 23 | self.node_neighbors[node] = [] 24 | 25 | def add_edge(self, edge): 26 | u, v = edge 27 | if (v not in self.node_neighbors[u]) and (u not in self.node_neighbors[v]): 28 | self.node_neighbors[u].append(v) 29 | 30 | if (u != v): 31 | self.node_neighbors[v].append(u) 32 | 33 | def nodes(self): 34 | return self.node_neighbors.keys() 35 | 36 | def not_null_node(self): 37 | self.A.clear() 38 | for node in self.nodes(): 39 | if len(self.node_neighbors[node]) > 0: 40 | self.A.append(node) 41 | 42 | def degrees(self): 43 | self.degree.clear() 44 | Node = self.nodes() 45 | for node in Node: 46 | self.degree.append(len(self.node_neighbors[node])) 47 | 48 | def set_diff(self, nodes): 49 | others = [] 50 | for node in self.A: 51 | if node not in nodes: 52 | others.append(node) 53 | 54 | return others 55 | 56 | def F1(self): # 求所有联通子图 57 | nodes = [] 58 | visited = [] 59 | subgraph = {} 60 | i = 1 61 | temp = self.set_diff(nodes) 62 | while len(temp) > 0: 63 | order = self.depth_first_search(temp[0]) 64 | subgraph[i] = order 65 | i += 1 66 | visited.extend(order) 67 | temp = self.set_diff(visited) 68 | return subgraph 69 | 70 | def judge(self, subgraph): 71 | for i in subgraph: 72 | t = 0 73 | temp = subgraph[i] 74 | for node in temp: 75 | if self.degree[node - 1] % 2 != 0: 76 | t = 1 # t=1说明有奇顶点 77 | break 78 | if t == 0: 79 | return i 80 | return 0 81 | 82 | def F2(self, gt): 83 | num = 0 84 | for node in gt: 85 | if self.degree[node - 1] % 2 != 0: 86 | num += 1 87 | return num 88 | 89 | def F3(self, path): 90 | for i in range(len(path) - 1): 91 | self.node_neighbors[path[i]].remove(path[i + 1]) 92 | self.node_neighbors[path[i + 1]].remove(path[i]) 93 | self.not_null_node() 94 | 95 | def depth_first_search(self, root=None): # 在连通的前提下进行深度优先搜索 96 | order = [] 97 | 98 | def dfs(node): 99 | self.visited[node] = True 100 | order.append(node) 101 | for n in self.node_neighbors[node]: 102 | if not n in self.visited: 103 | dfs(n) 104 | 105 | if root: 106 | dfs(root) 107 | 108 | for node in self.nodes(): 109 | if not node in self.visited: 110 | for v_node in self.visited: 111 | if node in self.node_neighbors[v_node]: 112 | dfs(node) 113 | self.visited.clear() 114 | return order 115 | 116 | def my_path(self, subgraph): 117 | odd_node = [] 118 | path_len={} 119 | for node in subgraph: 120 | if self.degree[node - 1] % 2 != 0: 121 | odd_node.append(node) 122 | distances = {} 123 | g_temp = dict_copy(self.node_neighbors, subgraph) 124 | for i in list(g_temp.keys()): 125 | temp = [] 126 | for j in g_temp[i]: 127 | temp.append((j, 1)) 128 | distances[i] = temp 129 | for node in odd_node: 130 | current = node 131 | dis=copy.deepcopy(distances) 132 | d, paths = dijkstra(dis, current) 133 | use_dict = dict_copy(paths, odd_node) 134 | d = dict_copy(d, odd_node) 135 | n_max = max(d.items(), key=lambda x: x[1])[0] 136 | path_len[d[n_max]]=use_dict[n_max] 137 | return max(path_len.items(), key=lambda x: x[0])[1] 138 | 139 | def dijkstra(G, s): 140 | d = {} # node distances from source 141 | predecessor = {} # node predecessor on the shortest path 142 | 143 | # initing distances to INF for all but source. 144 | for v in G: 145 | if v == s: 146 | d[v] = 0 147 | else: 148 | d[v] = float("inf") 149 | 150 | predecessor[s] = None 151 | 152 | Q = list(G.keys()) # contains all nodes to find shortest paths to, intially everything. 153 | while (Q): # until there is nothing left in Q 154 | u = min(Q, key=d.get) # get min distance node 155 | Q.remove(u) 156 | for v in G[u]: # relax all outgoing edges from it 157 | relax(u, v, d, predecessor) 158 | 159 | # print(d) 160 | # print(predecessor) 161 | paths = {} 162 | for v in predecessor: 163 | paths[v] = [v] 164 | p = predecessor[v] 165 | while p is not None: 166 | # paths[v] +=""+p 167 | paths[v].append(p) 168 | p = predecessor[p] 169 | # for v, path in paths.items(): 170 | # print(v, path) 171 | return d, paths 172 | 173 | 174 | def relax(u, v, d, predecessor): 175 | weight = v[1] 176 | v = v[0] 177 | if d[v] > d[u] + weight: 178 | d[v] = d[u] + weight 179 | predecessor[v] = u 180 | 181 | 182 | def add_path(p1, p2): 183 | k=1 184 | for i in range(len(p2)-1): 185 | p1.insert(p1.index(p2[0])+k,p2[i+1]) 186 | k+=1 187 | return p1 188 | 189 | 190 | def dict_copy(dict, a): 191 | temp = {} 192 | for i in a: 193 | temp[i] = dict[i] 194 | return temp 195 | 196 | 197 | ''' 198 | is_connected - Checks if a graph in the form of a dictionary is 199 | connected or not, using Breadth-First Search Algorithm (BFS) 200 | ''' 201 | 202 | 203 | def is_connected(G): 204 | start_node = list(G)[0] 205 | color = {v: 'white' for v in G} 206 | color[start_node] = 'gray' 207 | S = [start_node] 208 | while len(S) != 0: 209 | u = S.pop() 210 | for v in G[u]: 211 | if color[v] == 'white': 212 | color[v] = 'gray' 213 | S.append(v) 214 | color[u] = 'black' 215 | return list(color.values()).count('black') == len(G) 216 | 217 | 218 | ''' 219 | odd_degree_nodes - returns a list of all G odd degrees nodes 220 | ''' 221 | 222 | 223 | def odd_degree_nodes(G): 224 | odd_degree_nodes = [] 225 | for u in G: 226 | if len(G[u]) % 2 != 0: 227 | odd_degree_nodes.append(u) 228 | return odd_degree_nodes 229 | 230 | 231 | ''' 232 | from_dict - return a list of tuples links from a graph G in a 233 | dictionary format 234 | ''' 235 | 236 | 237 | def from_dict(G): 238 | links = [] 239 | for u in G: 240 | for v in G[u]: 241 | links.append((u, v)) 242 | return links 243 | 244 | 245 | ''' 246 | fleury(G) - return eulerian trail from graph G or a 247 | string 'Not Eulerian Graph' if it's not possible to trail a path 248 | ''' 249 | 250 | 251 | def fleury(G): 252 | ''' 253 | checks if G has eulerian cycle or trail 254 | ''' 255 | odn = odd_degree_nodes(G) 256 | if len(odn) > 2 or len(odn) == 1: 257 | return 'Not Eulerian Graph' 258 | else: 259 | g = copy.deepcopy(G) 260 | trail = [] 261 | if len(odn) == 2: 262 | u = odn[0] 263 | else: 264 | u = list(g)[0] 265 | while len(from_dict(g)) > 0: 266 | current_vertex = u 267 | for u in g[current_vertex]: 268 | g[current_vertex].remove(u) 269 | g[u].remove(current_vertex) 270 | bridge = not is_connected(g) 271 | if bridge: 272 | g[current_vertex].append(u) 273 | g[u].append(current_vertex) 274 | else: 275 | break 276 | if bridge: 277 | g[current_vertex].remove(u) 278 | g[u].remove(current_vertex) 279 | g.pop(current_vertex) 280 | if (len(trail) == 0): 281 | trail.append(current_vertex) 282 | trail.append(u) 283 | else: 284 | trail.append(u) 285 | #print(trail) 286 | return trail 287 | 288 | def euler(G,start=None): 289 | path = [] 290 | g=copy.deepcopy(G) 291 | def hierholzer(node): 292 | if (len(g[node]) == 0): 293 | path.append(node) 294 | return 295 | for n in g[node]: 296 | g[node].remove(n) 297 | g[n].remove(node) 298 | hierholzer(n) 299 | if (len(g[node]) == 0): 300 | path.append(node) 301 | return 302 | odn = odd_degree_nodes(g) 303 | if len(odn) > 2 or len(odn) == 1: 304 | return 'Not Eulerian Graph' 305 | else: 306 | if start: 307 | u=start 308 | else: 309 | if len(odn) == 2: 310 | u = odn[0] 311 | else: 312 | u = list(g)[0] 313 | hierholzer(u) 314 | path.reverse() 315 | #print(path) 316 | return path 317 | 318 | def path_iden(Queue,g): #找出Queue中哪条路径应该与g相连 319 | for n in g: 320 | for path in Queue: 321 | if(n in path): 322 | Queue.remove(path) 323 | return path,n 324 | 325 | def verification(Queue,testG): 326 | for path in Queue: 327 | for i in range(len(path)-1): 328 | testG[path[i]].remove(path[i+1]) 329 | testG[path[i+1]].remove(path[i]) 330 | i+=1 331 | #print('ok') 332 | 333 | def find_path(G): 334 | num_fleury = 0 335 | num_path = 0 336 | g = Graph() 337 | g.add_nodes([i + 1 for i in range(len(G))]) 338 | for i in range(len(G)): 339 | for j in range(len(G[i])): 340 | if G[i][j] != 0: 341 | g.add_edge((i + 1, j + 1)) 342 | g.not_null_node() 343 | g.F1() 344 | testG=copy.deepcopy(g.node_neighbors) 345 | Queue = [] 346 | while (len(g.A) > 0): 347 | g.degrees() 348 | subgraph = g.F1() 349 | n = len(subgraph) 350 | T = g.judge(subgraph) 351 | if (T > 0): 352 | if (len(Queue) > 0): 353 | t_path,start = path_iden(Queue,subgraph[T]) 354 | g_temp = dict_copy(g.node_neighbors, subgraph[T]) 355 | #result = fleury(g_temp) 356 | result =euler(g_temp,start) 357 | num_fleury += 1 358 | g.F3(result) 359 | #print('r',result) 360 | #print('p',t_path) 361 | #print('Q',Queue) 362 | result = add_path(t_path, result) 363 | #print('rs',result) 364 | Queue.append(result) 365 | else: 366 | g_temp = dict_copy(g.node_neighbors, subgraph[T]) 367 | #t_path = fleury(g_temp) 368 | t_path = euler(g_temp) 369 | num_fleury += 1 370 | Queue.append(t_path) 371 | g.F3(t_path) 372 | continue 373 | for i in range(1, n + 1): 374 | odd_num = g.F2(subgraph[i]) 375 | if odd_num == 2: 376 | g_temp = dict_copy(g.node_neighbors, subgraph[i]) 377 | #path = fleury(g_temp) 378 | path = euler(g_temp) 379 | num_fleury += 1 380 | elif odd_num > 2: 381 | path = g.my_path(subgraph[i]) 382 | else: 383 | break 384 | Queue.append(path) 385 | g.F3(path) 386 | num_path = len(Queue) 387 | #print('queue', Queue) 388 | verification(Queue,testG) 389 | return num_fleury, num_path,Queue 390 | 391 | 392 | def g_generator(NUM_GRAPHS, NUM_NODES): 393 | # NUM_GRAPHS = 25 394 | # NUM_NODES = 500 395 | op = [] 396 | 397 | for i in range(NUM_GRAPHS): 398 | while (True): 399 | WS = nx.random_graphs.connected_watts_strogatz_graph(NUM_NODES, 2, 400 | random.uniform(i * 2 / NUM_NODES - 0.1, 401 | i * 2 / NUM_NODES)) 402 | # spring layout 403 | 404 | count = 0 405 | for node in range(NUM_NODES): 406 | if WS.degree(node) % 2 != 0: 407 | count += 1 408 | 409 | if count == i * 2: 410 | # print(count) 411 | 412 | network_matrix = np.zeros([NUM_NODES, NUM_NODES]) 413 | 414 | for edge in WS.edges(): 415 | network_matrix[edge[0]][edge[1]] = 1 416 | 417 | op.append(network_matrix) 418 | 419 | break 420 | return op 421 | # print(op) 422 | # print(op.shape) 423 | ''' 424 | # G = nx.random_graphs.connected_watts_strogatz_graph(100,4,10) 425 | G = nx.random_graphs.barabasi_albert_graph(20, 3) 426 | 427 | for i in range(20): 428 | print(G.degree(i)) 429 | print(G.edges()) 430 | for i in G.edges(): 431 | network_matrix[i[0]][i[1]] = 1 432 | print(network_matrix) 433 | pos = nx.spring_layout(G) 434 | nx.draw(G, pos, with_labels = False, node_size = 30) 435 | plt.show() 436 | ''' 437 | 438 | def variance(Q): 439 | l=[] 440 | sum=0 441 | var=0 442 | for _ in Q: 443 | l.append(len(_)) 444 | sum+=len(_) 445 | average=sum/len(Q) 446 | for _ in l: 447 | var+=pow(_-average,2) 448 | var/=len(Q) 449 | return var 450 | 451 | if __name__ == '__main__': 452 | op = g_generator(10, 50) 453 | for _ in op: 454 | start = time.clock() 455 | num_fleury, num_path,q= find_path(_) 456 | end = time.clock() 457 | print(num_path) 458 | #print(end - start) -------------------------------------------------------------------------------- /Euler trail-based path planning algorithm/algorithm/optimal_find_path_unbalance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import matplotlib.pyplot as plt 3 | import copy 4 | import random 5 | import networkx as nx 6 | import numpy as np 7 | import time 8 | 9 | 10 | class Graph(object): 11 | def __init__(self, *args, **kwargs): 12 | self.node_neighbors = {} 13 | self.visited = {} 14 | self.A = [] # 为现存节点数组 15 | self.degree = [] # 为度数组 16 | 17 | def add_nodes(self, nodelist): 18 | for node in nodelist: 19 | self.add_node(node) 20 | 21 | def add_node(self, node): 22 | if not node in self.nodes(): 23 | self.node_neighbors[node] = [] 24 | 25 | def add_edge(self, edge): 26 | u, v = edge 27 | if (v not in self.node_neighbors[u]) and (u not in self.node_neighbors[v]): 28 | self.node_neighbors[u].append(v) 29 | 30 | if (u != v): 31 | self.node_neighbors[v].append(u) 32 | 33 | def nodes(self): 34 | return self.node_neighbors.keys() 35 | 36 | def not_null_node(self): 37 | self.A.clear() 38 | for node in self.nodes(): 39 | if len(self.node_neighbors[node]) > 0: 40 | self.A.append(node) 41 | 42 | def degrees(self): 43 | self.degree.clear() 44 | Node = self.nodes() 45 | for node in Node: 46 | self.degree.append(len(self.node_neighbors[node])) 47 | 48 | def set_diff(self, nodes): 49 | others = [] 50 | for node in self.A: 51 | if node not in nodes: 52 | others.append(node) 53 | 54 | return others 55 | 56 | def F1(self): # 求所有联通子图 57 | nodes = [] 58 | visited = [] 59 | subgraph = {} 60 | i = 1 61 | temp = self.set_diff(nodes) 62 | while len(temp) > 0: 63 | order = self.depth_first_search(temp[0]) 64 | subgraph[i] = order 65 | i += 1 66 | visited.extend(order) 67 | temp = self.set_diff(visited) 68 | return subgraph 69 | 70 | def judge(self, subgraph): 71 | for i in subgraph: 72 | t = 0 73 | temp = subgraph[i] 74 | for node in temp: 75 | if self.degree[node - 1] % 2 != 0: 76 | t = 1 # t=1说明有奇顶点 77 | break 78 | if t == 0: 79 | return i 80 | return 0 81 | 82 | def F2(self, gt): 83 | num = 0 84 | for node in gt: 85 | if self.degree[node - 1] % 2 != 0: 86 | num += 1 87 | return num 88 | 89 | def F3(self, path): 90 | for i in range(len(path) - 1): 91 | self.node_neighbors[path[i]].remove(path[i + 1]) 92 | self.node_neighbors[path[i + 1]].remove(path[i]) 93 | self.not_null_node() 94 | 95 | def depth_first_search(self, root=None): # 在连通的前提下进行深度优先搜索 96 | order = [] 97 | 98 | def dfs(node): 99 | self.visited[node] = True 100 | order.append(node) 101 | for n in self.node_neighbors[node]: 102 | if not n in self.visited: 103 | dfs(n) 104 | 105 | if root: 106 | dfs(root) 107 | 108 | for node in self.nodes(): 109 | if not node in self.visited: 110 | for v_node in self.visited: 111 | if node in self.node_neighbors[v_node]: 112 | dfs(node) 113 | self.visited.clear() 114 | return order 115 | 116 | def my_path(self, subgraph): 117 | odd_node = [] 118 | for node in subgraph: 119 | if self.degree[node - 1] % 2 != 0: 120 | odd_node.append(node) 121 | distances = {} 122 | g_temp = dict_copy(self.node_neighbors, subgraph) 123 | for i in list(g_temp.keys()): 124 | temp = [] 125 | for j in g_temp[i]: 126 | temp.append((j, 1)) 127 | distances[i] = temp 128 | current = odd_node[random.randint(1, len(odd_node)) - 1] 129 | d, paths = dijkstra(distances, current) 130 | use_dict = dict_copy(paths, odd_node) 131 | d = dict_copy(d, odd_node) 132 | n_max = max(d.items(), key=lambda x: x[1])[0] 133 | return use_dict[n_max] 134 | 135 | 136 | def dijkstra(G, s): 137 | d = {} # node distances from source 138 | predecessor = {} # node predecessor on the shortest path 139 | 140 | # initing distances to INF for all but source. 141 | for v in G: 142 | if v == s: 143 | d[v] = 0 144 | else: 145 | d[v] = float("inf") 146 | 147 | predecessor[s] = None 148 | 149 | Q = list(G.keys()) # contains all nodes to find shortest paths to, intially everything. 150 | while (Q): # until there is nothing left in Q 151 | u = min(Q, key=d.get) # get min distance node 152 | Q.remove(u) 153 | for v in G[u]: # relax all outgoing edges from it 154 | relax(u, v, d, predecessor) 155 | 156 | # print(d) 157 | # print(predecessor) 158 | paths = {} 159 | for v in predecessor: 160 | paths[v] = [v] 161 | p = predecessor[v] 162 | while p is not None: 163 | # paths[v] +=""+p 164 | paths[v].append(p) 165 | p = predecessor[p] 166 | # for v, path in paths.items(): 167 | # print(v, path) 168 | return d, paths 169 | 170 | 171 | def relax(u, v, d, predecessor): 172 | weight = v[1] 173 | v = v[0] 174 | if d[v] > d[u] + weight: 175 | d[v] = d[u] + weight 176 | predecessor[v] = u 177 | 178 | 179 | def add_path(p1, p2): 180 | k=1 181 | for i in range(len(p2)-1): 182 | p1.insert(p1.index(p2[0])+k,p2[i+1]) 183 | k+=1 184 | return p1 185 | 186 | 187 | def dict_copy(dict, a): 188 | temp = {} 189 | for i in a: 190 | temp[i] = dict[i] 191 | return temp 192 | 193 | 194 | ''' 195 | is_connected - Checks if a graph in the form of a dictionary is 196 | connected or not, using Breadth-First Search Algorithm (BFS) 197 | ''' 198 | 199 | 200 | def is_connected(G): 201 | start_node = list(G)[0] 202 | color = {v: 'white' for v in G} 203 | color[start_node] = 'gray' 204 | S = [start_node] 205 | while len(S) != 0: 206 | u = S.pop() 207 | for v in G[u]: 208 | if color[v] == 'white': 209 | color[v] = 'gray' 210 | S.append(v) 211 | color[u] = 'black' 212 | return list(color.values()).count('black') == len(G) 213 | 214 | 215 | ''' 216 | odd_degree_nodes - returns a list of all G odd degrees nodes 217 | ''' 218 | 219 | 220 | def odd_degree_nodes(G): 221 | odd_degree_nodes = [] 222 | for u in G: 223 | if len(G[u]) % 2 != 0: 224 | odd_degree_nodes.append(u) 225 | return odd_degree_nodes 226 | 227 | 228 | ''' 229 | from_dict - return a list of tuples links from a graph G in a 230 | dictionary format 231 | ''' 232 | 233 | 234 | def from_dict(G): 235 | links = [] 236 | for u in G: 237 | for v in G[u]: 238 | links.append((u, v)) 239 | return links 240 | 241 | 242 | ''' 243 | fleury(G) - return eulerian trail from graph G or a 244 | string 'Not Eulerian Graph' if it's not possible to trail a path 245 | ''' 246 | 247 | 248 | def fleury(G): 249 | ''' 250 | checks if G has eulerian cycle or trail 251 | ''' 252 | odn = odd_degree_nodes(G) 253 | if len(odn) > 2 or len(odn) == 1: 254 | return 'Not Eulerian Graph' 255 | else: 256 | g = copy.deepcopy(G) 257 | trail = [] 258 | if len(odn) == 2: 259 | u = odn[0] 260 | else: 261 | u = list(g)[0] 262 | while len(from_dict(g)) > 0: 263 | current_vertex = u 264 | for u in g[current_vertex]: 265 | g[current_vertex].remove(u) 266 | g[u].remove(current_vertex) 267 | bridge = not is_connected(g) 268 | if bridge: 269 | g[current_vertex].append(u) 270 | g[u].append(current_vertex) 271 | else: 272 | break 273 | if bridge: 274 | g[current_vertex].remove(u) 275 | g[u].remove(current_vertex) 276 | g.pop(current_vertex) 277 | if (len(trail) == 0): 278 | trail.append(current_vertex) 279 | trail.append(u) 280 | else: 281 | trail.append(u) 282 | #print(trail) 283 | return trail 284 | 285 | def euler(G,start=None): 286 | path = [] 287 | g=copy.deepcopy(G) 288 | def hierholzer(node): 289 | if (len(g[node]) == 0): 290 | path.append(node) 291 | return 292 | for n in g[node]: 293 | g[node].remove(n) 294 | g[n].remove(node) 295 | hierholzer(n) 296 | if (len(g[node]) == 0): 297 | path.append(node) 298 | return 299 | odn = odd_degree_nodes(g) 300 | if len(odn) > 2 or len(odn) == 1: 301 | return 'Not Eulerian Graph' 302 | else: 303 | if start: 304 | u=start 305 | else: 306 | if len(odn) == 2: 307 | u = odn[0] 308 | else: 309 | u = list(g)[0] 310 | hierholzer(u) 311 | path.reverse() 312 | #print(path) 313 | return path 314 | 315 | def path_iden(Queue,g): #找出Queue中哪条路径应该与g相连 316 | for n in g: 317 | for path in Queue: 318 | if(n in path): 319 | Queue.remove(path) 320 | return path,n 321 | 322 | def verification(Queue,testG): 323 | for path in Queue: 324 | for i in range(len(path)-1): 325 | testG[path[i]].remove(path[i+1]) 326 | testG[path[i+1]].remove(path[i]) 327 | i+=1 328 | #print('ok') 329 | 330 | def find_path(G): 331 | num_fleury = 0 332 | num_path = 0 333 | g = Graph() 334 | g.add_nodes([i + 1 for i in range(len(G))]) 335 | for i in range(len(G)): 336 | for j in range(len(G[i])): 337 | if G[i][j] != 0: 338 | g.add_edge((i + 1, j + 1)) 339 | g.not_null_node() 340 | g.F1() 341 | testG=copy.deepcopy(g.node_neighbors) 342 | Queue = [] 343 | while (len(g.A) > 0): 344 | g.degrees() 345 | subgraph = g.F1() 346 | n = len(subgraph) 347 | T = g.judge(subgraph) 348 | if (T > 0): 349 | if (len(Queue) > 0): 350 | t_path,start = path_iden(Queue,subgraph[T]) 351 | g_temp = dict_copy(g.node_neighbors, subgraph[T]) 352 | #result = fleury(g_temp) 353 | result =euler(g_temp,start) 354 | num_fleury += 1 355 | g.F3(result) 356 | #print('r',result) 357 | #print('p',t_path) 358 | #print('Q',Queue) 359 | result = add_path(t_path, result) 360 | #print('rs',result) 361 | Queue.append(result) 362 | else: 363 | g_temp = dict_copy(g.node_neighbors, subgraph[T]) 364 | #t_path = fleury(g_temp) 365 | t_path = euler(g_temp) 366 | num_fleury += 1 367 | Queue.append(t_path) 368 | g.F3(t_path) 369 | continue 370 | for i in range(1, n + 1): 371 | odd_num = g.F2(subgraph[i]) 372 | if odd_num == 2: 373 | g_temp = dict_copy(g.node_neighbors, subgraph[i]) 374 | #path = fleury(g_temp) 375 | path = euler(g_temp) 376 | num_fleury += 1 377 | elif odd_num > 2: 378 | path = g.my_path(subgraph[i]) 379 | else: 380 | break 381 | Queue.append(path) 382 | g.F3(path) 383 | num_path = len(Queue) 384 | #print('queue', Queue) 385 | verification(Queue,testG) 386 | return num_fleury, num_path,Queue 387 | 388 | 389 | def g_generator(NUM_GRAPHS, NUM_NODES): 390 | # NUM_GRAPHS = 25 391 | # NUM_NODES = 500 392 | op = [] 393 | 394 | for i in range(NUM_GRAPHS): 395 | while (True): 396 | WS = nx.random_graphs.connected_watts_strogatz_graph(NUM_NODES, 2, 397 | random.uniform(i * 2 / NUM_NODES - 0.1, 398 | i * 2 / NUM_NODES)) 399 | # spring layout 400 | 401 | count = 0 402 | for node in range(NUM_NODES): 403 | if WS.degree(node) % 2 != 0: 404 | count += 1 405 | 406 | if count == i * 2: 407 | # print(count) 408 | 409 | network_matrix = np.zeros([NUM_NODES, NUM_NODES]) 410 | 411 | for edge in WS.edges(): 412 | network_matrix[edge[0]][edge[1]] = 1 413 | 414 | op.append(network_matrix) 415 | 416 | break 417 | return op 418 | # print(op) 419 | # print(op.shape) 420 | ''' 421 | # G = nx.random_graphs.connected_watts_strogatz_graph(100,4,10) 422 | G = nx.random_graphs.barabasi_albert_graph(20, 3) 423 | 424 | for i in range(20): 425 | print(G.degree(i)) 426 | print(G.edges()) 427 | for i in G.edges(): 428 | network_matrix[i[0]][i[1]] = 1 429 | print(network_matrix) 430 | pos = nx.spring_layout(G) 431 | nx.draw(G, pos, with_labels = False, node_size = 30) 432 | plt.show() 433 | ''' 434 | 435 | def g_generator_edge(NUM_GRAPHS,NUM_NODES,edge_incre=1): 436 | op=[] 437 | NUM_GRAPHS=min(NUM_GRAPHS,int(NUM_NODES*(NUM_NODES-1)/2-NUM_NODES)) 438 | for i in range(NUM_GRAPHS): 439 | if(i==0): 440 | network_matrix=np.zeros([NUM_NODES, NUM_NODES]) 441 | for j in range(NUM_NODES-1): 442 | network_matrix[j][j+1] = 1 443 | network_matrix[j+1][j]=1 444 | network_matrix[0][NUM_NODES-1]=1 445 | network_matrix[NUM_NODES-1][0]=1 446 | op.append(network_matrix) 447 | else: 448 | network_matrix=copy.deepcopy(op[i-1]) 449 | select=[] 450 | for j in range(NUM_NODES): 451 | for k in range(j+1,NUM_NODES): 452 | if(network_matrix[j][k]==0): 453 | select.append([j,k]) 454 | for l in range(edge_incre): 455 | temp=select.pop(random.randint(0,len(select)-1)) 456 | network_matrix[temp[0]][temp[1]]=1 457 | network_matrix[temp[1]][temp[0]]=1 458 | op.append(network_matrix) 459 | return op 460 | 461 | if __name__ == '__main__': 462 | op = g_generator(1, 500) 463 | for _ in op: 464 | start = time.clock() 465 | num_fleury, num_path,q= find_path(_) 466 | end = time.clock() 467 | #print(num_path) 468 | #print(end - start) -------------------------------------------------------------------------------- /Euler trail-based path planning algorithm/figure_generation/random graph generator new.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import random 5 | 6 | NUM_NODES = 20 7 | NUM_ODD_DEGREE = 10 8 | 9 | while(True): 10 | WS = nx.random_graphs.connected_watts_strogatz_graph(NUM_NODES,2,NUM_ODD_DEGREE/NUM_NODES) 11 | # spring layout 12 | 13 | count = 0 14 | for node in range(NUM_NODES): 15 | if WS.degree(node)%2!=0: 16 | count+=1 17 | # print(count) 18 | 19 | if count==NUM_ODD_DEGREE: 20 | 21 | network_matrix = np.zeros([NUM_NODES,NUM_NODES]) 22 | 23 | for edge in WS.edges(): 24 | # print(edge) 25 | network_matrix[edge[0]][edge[1]] = 1 26 | network_matrix[edge[1]][edge[0]] = 1 27 | 28 | # print(network_matrix) 29 | # nx.draw(WS) 30 | # plt.show() 31 | 32 | break 33 | 34 | 35 | ''' 36 | op.append(network_matrix) 37 | 38 | break 39 | 40 | print(op) 41 | 42 | # G = nx.random_graphs.connected_watts_strogatz_graph(100,4,10) 43 | G = nx.random_graphs.barabasi_albert_graph(20, 3) 44 | 45 | for i in range(20): 46 | print(G.degree(i)) 47 | print(G.edges()) 48 | for i in G.edges(): 49 | network_matrix[i[0]][i[1]] = 1 50 | print(network_matrix) 51 | pos = nx.spring_layout(G) 52 | nx.draw(G, pos, with_labels = False, node_size = 30) 53 | plt.show() 54 | ''' 55 | -------------------------------------------------------------------------------- /Euler trail-based path planning algorithm/figure_generation/randomTopo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import random 3 | import sys 4 | import numpy as np 5 | import copy 6 | 7 | def createRandomTopo(sNum): 8 | sys.setrecursionlimit(1000000) 9 | #create adjaMatrix 10 | topoMatrix = [[0 for i in range(sNum)] for i in range(sNum)] 11 | visited = [0 for i in range(sNum)] 12 | #create topo randomly 13 | for i in range(sNum): 14 | for j in range(i+1, sNum): 15 | link = random.randint(0,1) 16 | topoMatrix[i][j] = link 17 | topoMatrix[j][i] = link 18 | #DFS 19 | def DFS(v): 20 | visited[v] = 1 21 | for j in range(sNum): 22 | if topoMatrix[v][j] == 1 and visited[j] == 0: 23 | DFS(j) 24 | #check the network connectivity using DFS 25 | disconNode = [] 26 | for i in range(sNum): 27 | if visited[i] == 0: 28 | DFS(i) 29 | disconNode.append(i) 30 | #if the network is unconnected, connect each disconNode 31 | for i in range(len(disconNode)-1): 32 | topoMatrix[disconNode[i]][disconNode[i+1]] = 1 33 | topoMatrix[disconNode[i+1]][disconNode[i]] = 1 34 | 35 | oddCount = calOddNum(topoMatrix, sNum) 36 | 37 | return topoMatrix, oddCount 38 | 39 | def createRandomTopoWithFixedOdds(oddNum, maxSNum, step=1): 40 | sys.setrecursionlimit(1000000) 41 | sNum = 2*oddNum 42 | topoLists = [] 43 | 44 | while sNum <= maxSNum: 45 | flag = 0 46 | while flag != 1: 47 | #create adjaMatrix 48 | topoMatrix = g_generator_edge(10000, sNum) 49 | for i in topoMatrix: 50 | oddCount = calOddNum(i, sNum) 51 | if oddCount == oddNum: 52 | topoLists.append(i) 53 | print("sNum ", sNum) 54 | flag = 1 55 | break 56 | sNum += step 57 | 58 | return topoLists 59 | 60 | def calOddNum(topoMatrix, sNum): 61 | count = 0 62 | for i in range(sNum): 63 | degreeSum = 0 64 | for j in range(sNum): 65 | degreeSum += topoMatrix[i][j] 66 | if degreeSum%2 == 1: 67 | count += 1 68 | return count 69 | 70 | def g_generator_edge(NUM_GRAPHS,NUM_NODES,edge_incre=1): 71 | op=[] 72 | NUM_GRAPHS=min(NUM_GRAPHS,int(NUM_NODES*(NUM_NODES-1)/2-NUM_NODES)) 73 | for i in range(NUM_GRAPHS): 74 | if(i==0): 75 | network_matrix=np.zeros([NUM_NODES, NUM_NODES]) 76 | for j in range(NUM_NODES-1): 77 | network_matrix[j][j+1] = 1 78 | network_matrix[j+1][j]=1 79 | network_matrix[0][NUM_NODES-1]=1 80 | network_matrix[NUM_NODES-1][0]=1 81 | op.append(network_matrix) 82 | else: 83 | network_matrix=copy.deepcopy(op[i-1]) 84 | select=[] 85 | for j in range(NUM_NODES): 86 | for k in range(j+1,NUM_NODES): 87 | if(network_matrix[j][k]==0): 88 | select.append([j,k]) 89 | for l in range(edge_incre): 90 | temp=select.pop(random.randint(0,len(select)-1)) 91 | network_matrix[temp[0]][temp[1]]=1 92 | network_matrix[temp[1]][temp[0]]=1 93 | op.append(network_matrix) 94 | return op 95 | 96 | 97 | if __name__ == '__main__': 98 | sNum = 5 99 | topo, oddCount = createRandomTopo(sNum) 100 | print(topo) 101 | print(oddCount) 102 | topoList = createRandomTopoWithFixedOdds(20,200,5) #each sNum has five graphs 103 | print(topoList) -------------------------------------------------------------------------------- /Euler trail-based path planning algorithm/figure_generation/specialTopo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | 4 | def genFatTree(maxSNum): #SNum must be 10,15,20,25,30... 5 | sys.setrecursionlimit(1000000) 6 | swSum = 10 7 | topoLists = [] 8 | 9 | while swSum <= maxSNum: 10 | L1 = int(swSum/5) 11 | L2 = L1*2 12 | L3 = L2 13 | 14 | topoList = [[0 for i in range(swSum)] for i in range(swSum)] 15 | hostList = [0 for i in range(swSum)] 16 | linkNum = 0 17 | 18 | core = [0 for i in range(L1)] 19 | agg = [0 for i in range(L2)] 20 | edg = [0 for i in range(L3)] 21 | 22 | # add core switches 23 | for i in range(L1): 24 | core[i] = i 25 | 26 | # add aggregation switches 27 | for i in range(L2): 28 | agg[i] = L1 + i 29 | 30 | # add edge switches 31 | for i in range(L3): 32 | edg[i] = L1 + L2 + i 33 | 34 | # add links between core and aggregation switches 35 | for i in range(L1): 36 | for j in agg[:]: 37 | topoList[core[i]][j] = 1 38 | topoList[j][core[i]] = 1 39 | linkNum += 2 40 | 41 | # add links between aggregation and edge switches 42 | for step in range(0, L2, 2): 43 | for i in agg[step:step+2]: 44 | for j in edg[step:step+2]: 45 | topoList[i][j] = 1 46 | topoList[j][i] = 1 47 | linkNum += 2 48 | # hostList 49 | for i in range((L1+L2), swSum): 50 | hostList[i] = 1 51 | 52 | topoLists.append(topoList) 53 | 54 | swSum += 5 55 | 56 | return topoLists 57 | 58 | def genSpineLeaf(maxSNum): #SNum must be 3,6,9,12,15... 59 | sys.setrecursionlimit(1000000) 60 | swSum = 3 61 | topoLists = [] 62 | 63 | while swSum <= maxSNum: 64 | L1 = int(swSum/3) 65 | L2 = L1*2 66 | 67 | topoList = [[0 for i in range(swSum)] for i in range(swSum)] 68 | 69 | for i in range(L1): 70 | for j in range(L1, swSum): 71 | topoList[i][j] = 1 72 | topoList[j][i] = 1 73 | 74 | topoLists.append(topoList) 75 | 76 | swSum += 3 77 | 78 | return topoLists 79 | 80 | def calOddNum(topoMatrix, sNum): 81 | count = 0 82 | for i in range(sNum): 83 | degreeSum = 0 84 | for j in range(sNum): 85 | degreeSum += topoMatrix[i][j] 86 | if degreeSum%2 == 1: 87 | count += 1 88 | return count 89 | 90 | if __name__ == '__main__': 91 | topoList1 = genFatTree(200) #maxSNum must be larger than 10 92 | print(len(topoList1)) 93 | print(topoList1[2]) 94 | topoList2 = genSpineLeaf(200) #SNum must be larger than 3 95 | print(len(topoList2)) 96 | print(topoList2[65]) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Towards Optimal Path Planning for In-band Network-Wide Telemetry 2 | 3 | Today's data center networks have become mega-scale with increasing deployment of diverse cloud services. With the continuous expansion of network size, fine-grained network monitoring becomes the prerequisite for better network reliability and closed-loop traffic control. In traditional network monitoring, management protocols, such as SNMP, are widely adopted to constantly poll the router/switch CPU for collecting device-internal states every few seconds or minutes. However, due to the frequent interaction between the control plane and the data plane as well as the limited CPU capability, such monitoring mechanism is coarse-grained and involves a large query latency, which cannot scale well in today's high-density data center networks with drastic traffic dynamics. 4 | 5 | To ameliorate the scalability issue, In-band Network Telemetry (INT) is proposed by the P4 Language Consortium (P4.org) to achieve fine-grained real-time data plane monitoring. INT allows packets to query device-internal states such as queue depth, queuing latency when they pass through the data plane pipeline, without requiring additional intervention by the control plane CPU. Typically, INT relies on a \emph{probe packet} with a variable-length label stack reserved in the packet header. The probe packets are periodically generated from an INT agent and injected into the network, where the probe packets will be queued and forwarded with the ordinary traffic. In each router/switch along the forwarding path, the probe packet will extract device-internal states and push them into the INT label stack. At the last hop, the probe packet containing the end-to-end monitoring data will be sent to the remote controller for further analysis. 6 | 7 | INT is essentially an underlying primitive that need the support of special hardware for internal state exposure. With such data extraction interface, network operators can easily obtain the real-time traffic status of a single device or a device chain along the probing path. However, to improve network management, INT further requires a high-level mechanism built upon it to efficiently extract the network-wide traffic status. More specifically, as Software-Defined Networking (SDN) is widely deployed, the controller always expects a global view (i.e., network-wide visibility) to make the optimal traffic control decisions. Besides, network management automation via machine learning also requires timely feedback from the environment as the fed-in training data. 8 | 9 | In this work, we raise the concept of "In-band Network-Wide Telemetry" and propose "INT-path", a telemetry framework to achieve network-wide traffic monitoring. We tackle the problem using the divide-and-conquer design philosophy by decoupling the solution into a routing mechanism and a routing path generation policy. Specifically, we embed source routing into INT probes to allow specifying the route the probe packet takes through the network. Based on the routing mechanism, we design two INT path planning policies to generate multiple non-overlapped INT paths that cover the entire network. The first is based on depth-first search (DFS) which is straightforward but time-efficient. The second is an Euler trail-based algorithm that optimally generates non-overlapped INT paths with a minimum path number. 10 | 11 | Based on INT, our approach can "encode" the network-wide traffic status into a series of "bitmap images", which further allows using advanced techniques, such as pattern recognition, to automate network monitoring and troubleshooting. Such transformation from traffic status to bitmap images will have profound significance because when the network becomes mega-scale, automated approaches are more efficient than traffic analysis purely by human efforts. 12 | 13 | # System 14 | 15 | The system include three modules:p4app, packet and controller. 16 | 17 | ## p4app 18 | 19 | Include p4 source code, implemented source-based INT functrion. 20 | 21 | header.p4, parser.p4, app.p4: p4 source code 22 | 23 | ### header.p4 24 | 25 | Including Headers and Metadatas 26 | 27 | ### parser.p4 28 | 29 | Including parser, deparser and checksum calculator. 30 | 31 | ### app.p4 32 | 33 | The main part of the p4 program. Including SR/INT/ARPProxy/UDP/ECMP tables. 34 | 35 | ### app.json 36 | 37 | The json file that compiled from app.p4 by p4c compiler. 38 | 39 | ## packet: 40 | 41 | Send & receive INT packet module which run in the hosts and the database config. 42 | 43 | ### int_data.sql 44 | 45 | The SQL Table which is used to initalize the database. 46 | 47 | ### receiveint.c 48 | 49 | Used to receive int packet, and parse them, then put them into database. 50 | 51 | ### sendint.py 52 | 53 | Use the given traverse path generate the INT packet and encode SR info. 54 | 55 | ## controller 56 | 57 | Generate Mininet network, send SR & INT command to hosts and get result from database. 58 | 59 | ### app.py 60 | 61 | The main controller. Use topoGraph and hostList generate netwrok, then use path to traverse the netwrok and collect INT info. 62 | 63 | ### dbParser.py 64 | 65 | The module which is used in app.py to get INT info from database. 66 | 67 | ### device.py 68 | 69 | The module which is used in app.py to generate the virtual devices(Hosts/Switches). 70 | 71 | ### p4_mininet.py 72 | 73 | The module which is used in mininiet to generate P4 devices. 74 | 75 | ### switchRuntime.py 76 | 77 | The module which is used in app.py to down tables using thrift RPC. 78 | 79 | ### topoMaker.py 80 | 81 | The module which is used in app.py to generate network topo. 82 | 83 | # DFS-based path planning algorithm 84 | 85 | ## DFLSPathPlan.py 86 | 87 | INT path planning algorithm based on DFS. 88 | 89 | ## randomTopo.py 90 | 91 | _createRandomTopo(sNum)_ can create topologies randomly. 92 | 93 | _createRandomTopoWithFixedOdds(oddNum, maxSNum, step)_ can create topologies randomly with fixed numbers of odd vertices. 94 | 95 | # Euler trail-based path planning algorithm 96 | 97 | ## algorithm 98 | 99 | ### optimal_find_path_balance.py 100 | 101 | Heuristic algorithm for balance task. 102 | 103 | ### optimal_find_path_unbalance.py 104 | 105 | Heuristic algorithm for unbalance task. 106 | 107 | ## figure_generation 108 | 109 | ### random graph generator new.py 110 | 111 | Heuristic algorithm for generating graph. 112 | 113 | ### randomTopo.py 114 | 115 | Heuristic algorithm for generating graph. 116 | 117 | ### specialTopo.py 118 | 119 | Heuristic algorithm for generating special graph, such as FatTree and SpineLeaf topo. 120 | 121 | # How to run 122 | 123 | If you installed the dependencies and configured the database(mysql,in`/system/packet/int_data.sql`) successfully, then you can run the system with commands below: 124 | 125 | ``` 126 | cd controller/ 127 | python app.py 128 | ``` 129 | 130 | You can change `graph`,`hostList` and `paths` in `app.py` to test your own network and you own INT path. 131 | -------------------------------------------------------------------------------- /infocom19_INT_path_cr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graytower/INT_PATH/b23baed512d826fbb3139620c35a72fef776ebfa/infocom19_INT_path_cr.pdf -------------------------------------------------------------------------------- /system/controller/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding:utf-8 -*- 3 | 4 | from device import Switch, Host 5 | from routeFinding import RouteFinding 6 | from switchRuntime import SwitchRuntime 7 | from topoMaker import TopoMaker 8 | from dBParser import DBParser 9 | 10 | import copy 11 | import socket 12 | import json 13 | import time 14 | 15 | 16 | _ = float('inf') 17 | 18 | 19 | class Ctrl(object): 20 | """ 21 | The controller's main control flow class. 22 | """ 23 | 24 | def __init__(self, switchGraph, hostList): 25 | """ 26 | Constructor, use switch graph and host list generate or initialize some instance variables or algorithm, and calculate some variables 27 | varibles list 28 | switchGraph: a two-dimension graph to describe the origin network 29 | graph: a two-dimension graph to describe the current network situation 30 | vertexNum: the switch num in the network 31 | routeFinding: initial the route finding algorithm with switch graph 32 | hostList: the list of which switch has a host 33 | 34 | hosts: hosts in network 35 | switches: switches in network 36 | oldGraph: never used 37 | oldSwitchList: never used 38 | socketList: socket links to each host in network 39 | socketPort: the specific port num to generate the link hosts in netwrok 40 | nowActId: the number of actions from AI 41 | dbInfo: the database connection information 42 | dbParser: initial the database parser with switch number and database connection information 43 | 44 | qdepth: the queue depth in network 45 | qrate: the queue rate in network 46 | qratesmall: (temporatory) a smaller queue rate in netwrok 47 | 48 | :param switchGraph: a two-dimension graph to describe a network 49 | :param hostList: a one-dimension list to describe which switch in network has a host connection 50 | """ 51 | 52 | self.switchGraph = copy.deepcopy(switchGraph) 53 | self.graph = switchGraph 54 | self.vertexNum = len(switchGraph) 55 | self.hostList = hostList 56 | self.switchList = [1] * self.vertexNum 57 | self.hosts = [] 58 | self.switches = [] 59 | self.oldGraph = [] 60 | self.oldSwitchList = [] 61 | self.socketList = [] 62 | self.nowActId = 0 63 | self.dbParser = DBParser(self.vertexNum, self.dbInfo, self.switches) 64 | 65 | def getSwitchByName(self, name): 66 | """ 67 | Get a switch in switch list by its name 68 | 69 | :param name: switch name, such as s1 70 | :returns: a switch instance 71 | """ 72 | return self.switches[int(name[1:])] 73 | 74 | def getHostByName(self, name): 75 | """ 76 | Get a host in host list by its name 77 | 78 | :param name: host name, such as h1 79 | :returns: a host instance 80 | """ 81 | return self.hosts[int(name[1:])] 82 | 83 | def genMac(self, id): 84 | """ 85 | Generate a MAC address by device id 86 | 87 | only support host id and must form 0 to 255 now 88 | 89 | :param id: device id 90 | :returns: a MAC address calculated by device id 91 | """ 92 | # only support id form 0 to 255 now 93 | macPrefix = '00:01:00:00:00:' 94 | hexId = hex(id)[2:].upper() 95 | if len(hexId) == 1: 96 | hexId = '0' + hexId 97 | mac = macPrefix + hexId 98 | return mac 99 | 100 | def genIp(self, id, isOvs=False): 101 | """ 102 | Generate a IP address by device id 103 | 104 | only support host id and must form 0 to 255 now 105 | 106 | :param id: device id 107 | :param isOvs: indicate the device is OpenVSwitch or P4 host 108 | :returns: a IP address calculated by device id 109 | """ 110 | # only support id form 0 to 255 now 111 | if isOvs: 112 | ipPrefix = '192.168.8.' 113 | else: 114 | ipPrefix = '10.0.0.' 115 | ip = ipPrefix + str(id + 100) 116 | return ip 117 | 118 | def genDevice(self): 119 | """ 120 | Generate logical device instance and add thrift link to host 121 | """ 122 | for i in range(self.vertexNum): 123 | thriftPort = 9090 + i 124 | self.switches.append( 125 | Switch('s' + str(i), thriftPort, SwitchRuntime(thriftPort=thriftPort))) 126 | if self.hostList[i] == 1: 127 | self.hosts.append( 128 | Host('h' + str(i), self.genMac(i), self.genIp(i), self.genIp(i, True))) 129 | else: 130 | self.hosts.append(None) 131 | 132 | def genLinks(self): 133 | """ 134 | Generate logical links to devices 135 | """ 136 | for i in range(self.vertexNum): 137 | for j in range(i + 1, self.vertexNum): 138 | if self.graph[i][j] != _: 139 | self.switches[i].addLink('s' + str(j)) 140 | self.switches[j].addLink('s' + str(i)) 141 | 142 | for i in range(self.vertexNum): 143 | if self.hostList[i] == 1: 144 | self.hosts[i].addLink('s' + str(i)) 145 | self.switches[i].addLink('h' + str(i)) 146 | 147 | def genArpTable(self): 148 | """ 149 | Generate arp table and add to each switches logically 150 | """ 151 | arpList = [('doarp', 'arpreply', ('00:00:00:00:00:00', host.ipAddress), host.macAddress) 152 | for host in self.hosts if host is not None] 153 | for i in range(self.vertexNum): 154 | self.switches[i].tables = self.switches[i].tables + arpList 155 | 156 | def getDevPortByName(self, deviceName, nextDeviceName): 157 | """ 158 | Get a device port on one device which is linked to another specified device 159 | 160 | :param deviceName: the device name to calculate the port 161 | :param nextDeviceName the name of device which the port linked to 162 | """ 163 | devices = None 164 | deviceType = deviceName[0:1] 165 | deviceId = int(deviceName[1:]) 166 | if deviceType == 's': 167 | devices = self.switches 168 | elif deviceType == 'h': 169 | devices = self.hosts 170 | if devices: 171 | device = devices[deviceId] 172 | for port in device.ports: 173 | if port.deviceName == nextDeviceName: 174 | return port.portNum 175 | return None 176 | 177 | def genRouteInfoViaPort(self, portsList): 178 | """ 179 | Generate source route info string by a list of ports on each switch 180 | 181 | :param portsList: a route port list for source routeing 182 | :returns: a info string for source routing 183 | """ 184 | portStr = '' 185 | for port in portsList: 186 | binPort = bin(port)[2:] 187 | for i in range(4 - len(binPort) % 4): 188 | binPort = str(0) + binPort 189 | portStr = portStr + binPort 190 | portStr = hex(int(portStr, 2)) 191 | return portStr 192 | 193 | def genDeviceNoTable(self): 194 | """ 195 | Generate device number table to switches 196 | 197 | the device number is used to indicate the switch itself 198 | """ 199 | for id, switch in enumerate(self.switches): 200 | tableItem = ('setmetadata', 'setdeviceno', '', id) 201 | switch.tables.append(tableItem) 202 | 203 | def getTableInfo(self): 204 | """ 205 | Get all tables in all switches 206 | :returns: a table list for all tables 207 | """ 208 | tables = [] 209 | for switch in self.switches: 210 | tables.append(switch.tables) 211 | return tables 212 | 213 | def genTables(self, doClear=False): 214 | """ 215 | Generate trans table with route list and push arplist to all switches and send information to front end 216 | :param doClear: clear old tables in all switches logically or not 217 | """ 218 | if doClear: 219 | for switch in self.switches: 220 | switch.tables = [] 221 | self.genArpTable() 222 | self.genDeviceNoTable() 223 | 224 | def downTables(self, doClear=False): 225 | """ 226 | Down all tables to all the switched in the true network 227 | :param doClear: clear old tables in all switches truely or not 228 | """ 229 | for switch in self.switches: 230 | if doClear: 231 | switch.runtime.table_clear('dotrans') 232 | for table in switch.tables: 233 | tableName = table[0] 234 | actionName = table[1] 235 | keys = table[2] 236 | values = table[3] 237 | switch.runtime.table_add(tableName, actionName, keys, values) 238 | 239 | def makeTopo(self): 240 | """ 241 | Build the true netwrok using the informatin in controller and change queue depth/rate in switches 242 | """ 243 | switchPath = 'simple_switch' 244 | jsonPath = 'p4app/app.json' 245 | self.topoMaker = TopoMaker(switchPath, jsonPath, self) 246 | self.topoMaker.cleanMn() 247 | self.topoMaker.genMnTopo() 248 | for i, switch in enumerate(self.switches): 249 | switch.runtime.makeThriftLink(self.qdepth, self.qrate) 250 | 251 | def genHostLink(self, host): 252 | """ 253 | Make socket link to a specific host in the true network 254 | :param host: the host to make socket link 255 | """ 256 | try: 257 | print(host.ovsIpAddress, self.socketPort, 'connecting') 258 | socketLink = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 259 | socketLink.connect((host.ovsIpAddress, self.socketPort)) 260 | return socketLink 261 | except: 262 | print('socket gen failed', host.name) 263 | return None 264 | 265 | def genSocketLinkToHosts(self): 266 | """ 267 | Make socket links to all hosts in the network 268 | """ 269 | self.socketList = [] 270 | print('start connect') 271 | for host in self.hosts: 272 | if host: 273 | print('connecting ', host.ovsIpAddress) 274 | socketLink = self.genHostLink(host) 275 | print(host.ovsIpAddress, 'connected') 276 | self.socketList.append(socketLink) 277 | else: 278 | self.socketList.append(None) 279 | 280 | def sendInfoViaSocket(self, hostId, sendType, info): 281 | """ 282 | Send information to host in true network with the socket establish before 283 | :param hostId: the host to send information 284 | :param sendType: the information type 285 | :param info: the information content 286 | :returns: the send status (success=>True/failed=>False) 287 | """ 288 | def sendInfo(sendType, info): 289 | socketLink.send(json.dumps({ 290 | 'type': sendType, 291 | 'info': info 292 | })) 293 | 294 | socketLink = self.socketList[hostId] 295 | if socketLink: 296 | try: 297 | sendInfo(sendType, info) 298 | except: 299 | time.sleep(5) 300 | socketLink = self.genHostLink(self.hosts[hostId]) 301 | sendInfo(sendType, info) 302 | pass 303 | return True 304 | return False 305 | 306 | def traversePaths(self, paths, sendTimes=3): 307 | """ 308 | Traverse all path using source routing to get all information in network use INT 309 | generate traverse info and send it to host by socket link 310 | :param paths: traverse path from AI 311 | """ 312 | startNodeDict = {} 313 | for path in paths: 314 | startNodeDict[path[0]] = { 315 | 'portsLists': [], 316 | 'addressList': [] 317 | } 318 | for path in paths: 319 | portsList = [self.getDevPortByName( 320 | 's' + str(path[i]), 's' + str(path[i + 1])) for i in range(len(path) - 1)] 321 | portsList.append(self.getDevPortByName( 322 | 's' + str(path[len(path) - 1]), 'h' + str(path[len(path) - 1]))) 323 | # print('path', path) 324 | # print('portsList', portsList) 325 | address = self.hosts[path[len(path) - 1]].ipAddress 326 | startNode = path[0] 327 | startNodeDict[startNode]['portsLists'].append(portsList) 328 | startNodeDict[startNode]['addressList'].append(address) 329 | for key, sendObj in startNodeDict.items(): 330 | # print('portslists', key, sendObj['portsLists']) 331 | self.sendInfoViaSocket(key, 'TraversePath', { 332 | 'portsLists': sendObj['portsLists'], 333 | 'addressList': sendObj['addressList'], 334 | 'actId': self.nowActId, 335 | 'sendTimes': sendTimes 336 | }) 337 | 338 | def start(self): 339 | """ 340 | Use the given topo and hosts start the experiment envirnoment 341 | 342 | generate devices, links, tables 343 | then generate the mininet network, link host with socket, 344 | then send information to front end 345 | 346 | :returns: True 347 | """ 348 | self.genDevice() 349 | self.genLinks() 350 | self.genTables() 351 | self.makeTopo() 352 | self.downTables() 353 | self.genSocketLinkToHosts() 354 | 355 | return True 356 | 357 | def updateGraphByAction(self, action): 358 | """ 359 | Update the topology in network by switch ID 360 | 361 | :param action: action switch ID(positive means open a switch, negative means close a switch, zero means no action, id is larger than actual 1) 362 | :returns: Boolen (True means an action had done, False means no action been done) 363 | """ 364 | close = False 365 | if action == 0: 366 | return False 367 | elif action < 0: 368 | action = -action 369 | # xiaoyujie's start id is 1 370 | self.switchList[action - 1] = 0 371 | close = True 372 | else: 373 | self.switchList[action - 1] = 1 374 | 375 | for i in range(self.vertexNum): 376 | if i != action - 1: 377 | if close: 378 | self.graph[i][action - 1] = _ 379 | self.graph[action - 1][i] = _ 380 | else: 381 | self.graph[i][action - 1] = self.switchGraph[i][action - 1] 382 | self.graph[action - 1][i] = self.switchGraph[action - 1][i] 383 | 384 | return True 385 | 386 | def update(self, action=None, paths=None, times=None): 387 | """ 388 | Receive parameters from AI, then update topology and traverse all INT path, last, get the rewardMatrix and send it to AI and front end 389 | 390 | :param action: action switch ID 391 | :param paths: path switch ID lists (the switches who need to do INT) 392 | :returns: rewardMatrix (the matrix contains link queue depth per route) 393 | """ 394 | self.nowActId = self.nowActId + 1 395 | print('now act id ', self.nowActId) 396 | rewardMatrix = None 397 | # print('action', action) 398 | if action: 399 | if self.updateGraphByAction(action): 400 | self.genTables(True) 401 | self.downTables(True) 402 | time.sleep(1) 403 | if paths: 404 | if times: 405 | self.traversePaths(paths, times) 406 | else: 407 | self.traversePaths(paths) 408 | time.sleep(10) 409 | rewardMatrix = self.dbParser.parser(self.nowActId) 410 | 411 | return rewardMatrix 412 | 413 | 414 | if __name__ == '__main__': 415 | graph = [ 416 | [0, 1, 1, _, _, _], 417 | [1, 0, 1, 1, _, _], 418 | [1, 1, 0, 1, 1, _], 419 | [_, 1, 1, 0, 1, 1], 420 | [_, _, 1, 1, 0, 1], 421 | [_, _, _, 1, 1, 0], 422 | ] 423 | # only support one host per switch 424 | hostList = [1, 1, 1, 0, 1, 1] 425 | 426 | app = Ctrl(graph, hostList) 427 | app.start() 428 | paths = [[0, 1, 2], [0, 2, 3]] 429 | app.update(-1, paths) 430 | print('end') 431 | -------------------------------------------------------------------------------- /system/controller/dBParser.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | 3 | 4 | class DBParser(): 5 | """ 6 | Intereavtive with database, transfer function to SQL and get the result 7 | """ 8 | 9 | def __init__(self, switchSum, sqlInfo, switchesInfo): 10 | """ 11 | Initialize the database connection information and truncate the database 12 | 13 | :param switchSum: the number of switches 14 | :param sqlInfo: the database link info 15 | """ 16 | self.switchSum = switchSum 17 | self.sqlInfo = sqlInfo 18 | self.switchesInfo = switchesInfo 19 | self.traunce_db() 20 | 21 | def traunce_db(self): 22 | self.searchDB('truncate `fnl_int`') 23 | 24 | def searchDB(self, sql): 25 | """ 26 | Link to database and search database with SQL 27 | 28 | :param sql: SQL senence 29 | :returns: the result dict 30 | """ 31 | def doConn(): 32 | conn = pymysql.connect(host=self.sqlInfo['host'], 33 | user=self.sqlInfo['user'], 34 | passwd=self.sqlInfo['passwd'], 35 | db=self.sqlInfo['db'], 36 | port=self.sqlInfo['port'], 37 | charset=self.sqlInfo['charset']) 38 | return conn 39 | try: 40 | self.conn = doConn() 41 | except: 42 | self.conn = doConn() 43 | cursor = self.conn.cursor() 44 | cursor.execute(sql) 45 | values = cursor.fetchall() 46 | cursor.close() 47 | self.conn.close() 48 | return values 49 | 50 | def parser(self, actionID): 51 | """ 52 | Find the record by action id 53 | 54 | after got the raw result by SQL query, the program will calculate the meam queue depth for each link in all results 55 | 56 | :param actionID: action id given by controller 57 | :returns: rewatrd matrix which contains queue depth of each link 58 | """ 59 | rewardMatr = [[[] 60 | for i in range(self.switchSum)] for i in range(self.switchSum)] 61 | sql = "select device_no,egress_port,deq_timedelta,enq_qdepth from fnl.fnl_int where action_id=" + \ 62 | str(actionID) 63 | values = self.searchDB(sql) 64 | 65 | # append the queue_time_delta of every link to the reward_matrix 66 | for value in values: 67 | device_no = int(value[0]) 68 | egress_port = int(value[1]) 69 | deq_timedelta = int(value[2]) 70 | enq_qdepth = int(value[3]) 71 | next_device_no = int( 72 | self.switchesInfo[device_no].ports[egress_port - 1].deviceName[1:]) 73 | rewardMatr[device_no][next_device_no].append(deq_timedelta/100) 74 | # calculate the average of queue_depth 75 | for i in range(self.switchSum): 76 | for j in range(self.switchSum): 77 | if len(rewardMatr[i][j]): 78 | avg = sum(rewardMatr[i][j]) / \ 79 | len(rewardMatr[i][j]) 80 | rewardMatr[i][j] = avg 81 | else: 82 | rewardMatr[i][j] = 0 83 | 84 | return rewardMatr 85 | 86 | def __del__(self): 87 | self.conn.close() 88 | 89 | 90 | if __name__ == '__main__': 91 | pass 92 | -------------------------------------------------------------------------------- /system/controller/device.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | 4 | class Port(object): 5 | """ 6 | Describe a switch port 7 | 8 | it contains the num of this port and the name of device which contains this port 9 | """ 10 | 11 | def __init__(self, portNum, deviceName): 12 | self.portNum = portNum 13 | self.deviceName = deviceName 14 | 15 | 16 | class Table(object): 17 | """ 18 | Describe a flow table in a switch 19 | 20 | it contains a name ,an action, a key and a value 21 | """ 22 | def __init__(self, name, action, key, value): 23 | self.name = name 24 | self.action = action 25 | self.key = key 26 | self.value = value 27 | 28 | 29 | class Device(object): 30 | """ 31 | Describe a device in a network 32 | 33 | it contains a name, port list and the count of port 34 | """ 35 | 36 | def __init__(self, name): 37 | self.name = name 38 | self.ports = [] 39 | self.portSum = 0 40 | 41 | def addLink(self, deviceName): 42 | self.portSum = self.portSum + 1 43 | port = Port(self.portSum, deviceName) 44 | self.ports.append(port) 45 | 46 | 47 | class Switch(Device): 48 | """ 49 | Describe a switch in a network (inherit the Device class) 50 | 51 | it contains tables, thrift port and thrift Runtime 52 | it has 2 actions: add table and clear table 53 | """ 54 | 55 | def __init__(self, name, thriftPort=9090, runtime=None): 56 | super(Switch, self).__init__(name) 57 | self.tables = [] 58 | self.thriftPort = thriftPort 59 | self.runtime = runtime 60 | 61 | def addTable(self, name, action, key, value): 62 | self.tables.append(Table(name, action, key, value)) 63 | 64 | def clearTable(self): 65 | self.tables = [] 66 | 67 | 68 | class Host(Device): 69 | """ 70 | Describe a host in a netwrok (interit the Device class) 71 | 72 | it contains a MAC address, an IP address and an OpenVSwitch Ip address 73 | """ 74 | 75 | def __init__(self, name, mac='', ip='', ovsIp=''): 76 | super(Host, self).__init__(name) 77 | self.macAddress = mac 78 | self.ipAddress = ip 79 | self.ovsIpAddress = ovsIp 80 | 81 | 82 | if __name__ == '__main__': 83 | device = Host('hahah') 84 | print(device.name) 85 | -------------------------------------------------------------------------------- /system/controller/p4_mininet.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-present Barefoot Networks, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | from mininet.net import Mininet 17 | from mininet.node import Switch, Host 18 | from mininet.log import setLogLevel, info, error, debug 19 | from mininet.moduledeps import pathCheck 20 | from sys import exit 21 | import os 22 | import tempfile 23 | import socket 24 | 25 | 26 | class P4Host(Host): 27 | """ 28 | P4 specific switch 29 | """ 30 | 31 | def config(self, **params): 32 | r = super(Host, self).config(**params) 33 | 34 | self.defaultIntf().rename("eth0") 35 | 36 | for off in ["rx", "tx", "sg"]: 37 | cmd = "/sbin/ethtool --offload eth0 %s off" % off 38 | self.cmd(cmd) 39 | 40 | # disable IPv6 41 | self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") 42 | self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") 43 | self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") 44 | 45 | return r 46 | 47 | def describe(self): 48 | print("**********") 49 | print(self.name) 50 | print("default interface: %s\t%s\t%s" % ( 51 | self.defaultIntf().name, 52 | self.defaultIntf().IP(), 53 | self.defaultIntf().MAC() 54 | )) 55 | print("**********") 56 | 57 | 58 | class P4Switch(Switch): 59 | """ 60 | P4 virtual switch 61 | """ 62 | device_id = 0 63 | 64 | def __init__(self, name, sw_path=None, json_path=None, 65 | thrift_port=None, 66 | pcap_dump=True, 67 | log_console=True, 68 | verbose=True, 69 | device_id=None, 70 | enable_debugger=False, 71 | **kwargs): 72 | Switch.__init__(self, name, **kwargs) 73 | assert(sw_path) 74 | assert(json_path) 75 | # make sure that the provided sw_path is valid 76 | pathCheck(sw_path) 77 | # make sure that the provided JSON file exists 78 | if not os.path.isfile(json_path): 79 | error("Invalid JSON file.\n") 80 | exit(1) 81 | self.sw_path = sw_path 82 | self.json_path = json_path 83 | self.verbose = verbose 84 | logfile = "/tmp/p4s.{}.log".format(self.name) 85 | self.output = open(logfile, 'w') 86 | self.thrift_port = thrift_port 87 | self.pcap_dump = pcap_dump 88 | self.enable_debugger = enable_debugger 89 | self.log_console = log_console 90 | if device_id is not None: 91 | self.device_id = device_id 92 | P4Switch.device_id = max(P4Switch.device_id, device_id) 93 | else: 94 | self.device_id = P4Switch.device_id 95 | P4Switch.device_id += 1 96 | self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) 97 | 98 | @classmethod 99 | def setup(cls): 100 | pass 101 | 102 | def check_switch_started(self, pid): 103 | """ 104 | While the process is running (pid exists), we check if the Thrift 105 | server has been started. If the Thrift server is ready, we assume that 106 | the switch was started successfully. This is only reliable if the Thrift 107 | server is started at the end of the init process 108 | """ 109 | while True: 110 | if not os.path.exists(os.path.join("/proc", str(pid))): 111 | return False 112 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 113 | sock.settimeout(0.5) 114 | result = sock.connect_ex(("localhost", self.thrift_port)) 115 | if result == 0: 116 | return True 117 | 118 | def start(self, controllers): 119 | """ 120 | Start up a new P4 switch 121 | """ 122 | info("Starting P4 switch {}.\n".format(self.name)) 123 | args = [self.sw_path] 124 | for port, intf in self.intfs.items(): 125 | if not intf.IP(): 126 | args.extend(['-i', str(port) + "@" + intf.name]) 127 | if self.pcap_dump: 128 | args.append("--pcap") 129 | # args.append("--useFiles") 130 | if self.thrift_port: 131 | args.extend(['--thrift-port', str(self.thrift_port)]) 132 | if self.nanomsg: 133 | args.extend(['--nanolog', self.nanomsg]) 134 | args.extend(['--device-id', str(self.device_id)]) 135 | P4Switch.device_id += 1 136 | args.append(self.json_path) 137 | if self.enable_debugger: 138 | args.append("--debugger") 139 | if self.log_console: 140 | args.append("--log-console") 141 | logfile = " /dev/null " 142 | info(' '.join(args) + "\n") 143 | 144 | pid = None 145 | with tempfile.NamedTemporaryFile() as f: 146 | self.cmd(' '.join(args) + ' >' + logfile + 147 | ' 2>&1 & echo $! >> ' + f.name) 148 | pid = int(f.read()) 149 | debug("P4 switch {} PID is {}.\n".format(self.name, pid)) 150 | if not self.check_switch_started(pid): 151 | error("P4 switch {} did not start correctly.\n".format(self.name)) 152 | exit(1) 153 | info("P4 switch {} has been started.\n".format(self.name)) 154 | 155 | def stop(self): 156 | """ 157 | Terminate P4 switch. 158 | """ 159 | self.output.flush() 160 | self.cmd('kill %' + self.sw_path) 161 | self.cmd('wait') 162 | self.deleteIntfs() 163 | 164 | def attach(self, intf): 165 | """ 166 | Connect a data port 167 | """ 168 | assert(0) 169 | 170 | def detach(self, intf): 171 | """ 172 | Disconnect a data port 173 | """ 174 | assert(0) 175 | -------------------------------------------------------------------------------- /system/controller/switchRuntime.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import cmd 4 | import os 5 | import sys 6 | import struct 7 | import json 8 | from functools import wraps 9 | import bmpy_utils as utils 10 | 11 | from bm_runtime.standard import Standard 12 | from bm_runtime.standard.ttypes import * 13 | from sswitch_runtime import SimpleSwitch 14 | try: 15 | from bm_runtime.simple_pre import SimplePre 16 | except: 17 | pass 18 | try: 19 | from bm_runtime.simple_pre_lag import SimplePreLAG 20 | except: 21 | pass 22 | 23 | 24 | class SwitchRuntime(object): 25 | """ 26 | Rewrite based on runtime_CLI written by Barefoot 27 | 28 | Use Thrift API communicate with BMV2 P4 switch 29 | 30 | Only can down tables now 31 | """ 32 | 33 | class Table(object): 34 | def __init__(self, runtime, name, id_): 35 | self.name = name 36 | self.id_ = id_ 37 | self.match_type_ = None 38 | self.actions = {} 39 | self.key = [] 40 | self.default_action = None 41 | self.type_ = None 42 | self.support_timeout = False 43 | self.action_prof = None 44 | 45 | runtime.TABLES[name] = self 46 | 47 | def num_key_fields(self): 48 | return len(self.key) 49 | 50 | def key_str(self): 51 | return ",\t".join([name + "(" + MatchType.to_str(t) + ", " + str(bw) + ")" for name, t, bw in self.key]) 52 | 53 | def table_str(self): 54 | ap_str = "implementation={}".format( 55 | "None" if not self.action_prof else self.action_prof.name) 56 | return "{0:30} [{1}, mk={2}]".format(self.name, ap_str, self.key_str()) 57 | 58 | class ActionProf(object): 59 | def __init__(self, runtime, name, id_): 60 | self.name = name 61 | self.id_ = id_ 62 | self.with_selection = False 63 | self.actions = {} 64 | self.ref_cnt = 0 65 | 66 | runtime.ACTION_PROFS[name] = self 67 | 68 | def action_prof_str(self): 69 | return "{0:30} [{1}]".format(self.name, self.with_selection) 70 | 71 | class Action(object): 72 | def __init__(self, runtime, name, id_): 73 | self.name = name 74 | self.id_ = id_ 75 | self.runtime_data = [] 76 | 77 | runtime.ACTIONS[name] = self 78 | 79 | def num_params(self): 80 | return len(self.runtime_data) 81 | 82 | def runtime_data_str(self): 83 | return ",\t".join([name + "(" + str(bw) + ")" for name, bw in self.runtime_data]) 84 | 85 | def action_str(self): 86 | return "{0:30} [{1}]".format(self.name, self.runtime_data_str()) 87 | 88 | class MeterArray(object): 89 | def __init__(self, runtime, name, id_): 90 | self.name = name 91 | self.id_ = id_ 92 | self.type_ = None 93 | self.is_direct = None 94 | self.size = None 95 | self.binding = None 96 | self.rate_count = None 97 | 98 | runtime.METER_ARRAYS[name] = self 99 | 100 | def meter_str(self): 101 | return "{0:30} [{1}, {2}]".format(self.name, self.size, 102 | MeterType.to_str(self.type_)) 103 | 104 | class CounterArray(object): 105 | def __init__(self, runtime, name, id_): 106 | self.name = name 107 | self.id_ = id_ 108 | self.is_direct = None 109 | self.size = None 110 | self.binding = None 111 | 112 | runtime.COUNTER_ARRAYS[name] = self 113 | 114 | def counter_str(self): 115 | return "{0:30} [{1}]".format(self.name, self.size) 116 | 117 | class RegisterArray(object): 118 | def __init__(self, runtime, name, id_): 119 | self.name = name 120 | self.id_ = id_ 121 | self.width = None 122 | self.size = None 123 | 124 | runtime.REGISTER_ARRAYS[name] = self 125 | 126 | def register_str(self): 127 | return "{0:30} [{1}]".format(self.name, self.size) 128 | 129 | class MatchType(object): 130 | EXACT = 0 131 | LPM = 1 132 | TERNARY = 2 133 | VALID = 3 134 | RANGE = 4 135 | 136 | @staticmethod 137 | def to_str(x): 138 | return {0: "exact", 1: "lpm", 2: "ternary", 3: "valid", 4: "range"}[x] 139 | 140 | @staticmethod 141 | def from_str(x): 142 | return {"exact": 0, "lpm": 1, "ternary": 2, "valid": 3, "range": 4}[x] 143 | 144 | def get_thrift_services(self, pre_type): 145 | services = [("standard", Standard.Client)] 146 | 147 | if pre_type == self.PreType.SimplePre: 148 | services += [("simple_pre", SimplePre.Client)] 149 | elif pre_type == self.PreType.SimplePreLAG: 150 | services += [("simple_pre_lag", SimplePreLAG.Client)] 151 | else: 152 | services += [(None, None)] 153 | 154 | return services 155 | 156 | def ssget_thrift_services(self): 157 | return [("simple_switch", SimpleSwitch.Client)] 158 | 159 | def __init__(self, thriftIp='localhost', thriftPort=9090, thriftPre='simplePre', jsonPath=None): 160 | 161 | self.thriftIp = thriftIp 162 | self.thriftPort = thriftPort 163 | self.thriftPre = thriftPre 164 | self.jsonPath = jsonPath 165 | 166 | self.TABLES = {} 167 | self.ACTION_PROFS = {} 168 | self.ACTIONS = {} 169 | self.METER_ARRAYS = {} 170 | self.COUNTER_ARRAYS = {} 171 | self.REGISTER_ARRAYS = {} 172 | self.CUSTOM_CRC_CALCS = {} 173 | 174 | self.PreType = self.enum( 175 | 'PreType', 'None', 'SimplePre', 'SimplePreLAG') 176 | self.MeterType = self.enum('MeterType', 'packets', 'bytes') 177 | self.TableType = self.enum( 178 | 'TableType', 'simple', 'indirect', 'indirect_ws') 179 | self._match_types_mapping = { 180 | self.MatchType.EXACT: BmMatchParamType.EXACT, 181 | self.MatchType.LPM: BmMatchParamType.LPM, 182 | self.MatchType.TERNARY: BmMatchParamType.TERNARY, 183 | self.MatchType.VALID: BmMatchParamType.VALID, 184 | self.MatchType.RANGE: BmMatchParamType.RANGE, 185 | } 186 | 187 | def makeThriftLink(self, qdepth, qrate): 188 | services = self.get_thrift_services(self.thriftPre) 189 | services.extend(self.ssget_thrift_services()) 190 | self.standard_client, self.mc_client, self.sswitch_client = utils.thrift_connect( 191 | self.thriftIp, self.thriftPort, services) 192 | # self.standard_client, self.mc_client = utils.thrift_connect( 193 | # self.thriftIp, self.thriftPort, self.get_thrift_services(self.thriftPre)) 194 | self.json_str = utils.get_json_config( 195 | self.standard_client, self.jsonPath) 196 | self.load_json_str() 197 | # self.sswitch_client.set_egress_queue_depth(port, qdepth) 198 | self.sswitch_client.set_all_egress_queue_depths(qdepth) 199 | self.sswitch_client.set_all_egress_queue_rates(qrate) 200 | 201 | def enum(self, type_name, *sequential, **named): 202 | enums = dict(zip(sequential, range(len(sequential))), **named) 203 | reverse = dict((value, key) for key, value in enums.iteritems()) 204 | 205 | @staticmethod 206 | def to_str(x): 207 | return reverse[x] 208 | enums['to_str'] = to_str 209 | 210 | @staticmethod 211 | def from_str(x): 212 | return enums[x] 213 | 214 | enums['from_str'] = from_str 215 | return type(type_name, (), enums) 216 | 217 | def bytes_to_string(self, byte_array): 218 | form = 'B' * len(byte_array) 219 | return struct.pack(form, *byte_array) 220 | 221 | def ipv4Addr_to_bytes(self, addr): 222 | s = addr.split('.') 223 | return [int(b) for b in s] 224 | 225 | def macAddr_to_bytes(self, addr): 226 | s = addr.split(':') 227 | return [int(b, 16) for b in s] 228 | 229 | def ipv6Addr_to_bytes(self, addr): 230 | from ipaddr import IPv6Address 231 | ip = IPv6Address(addr) 232 | return [ord(b) for b in ip.packed] 233 | 234 | def int_to_bytes(self, i, num): 235 | byte_array = [] 236 | while i > 0: 237 | byte_array.append(i % 256) 238 | i = i / 256 239 | num -= 1 240 | while num > 0: 241 | byte_array.append(0) 242 | num -= 1 243 | byte_array.reverse() 244 | return byte_array 245 | 246 | def parse_param(self, input_str, bitwidth): 247 | if bitwidth == 32: 248 | return self.ipv4Addr_to_bytes(input_str) 249 | elif bitwidth == 48: 250 | return self.macAddr_to_bytes(input_str) 251 | elif bitwidth == 128: 252 | return self.ipv6Addr_to_bytes(input_str) 253 | else: 254 | if isinstance(input_str, str): 255 | input_ = int(input_str, 0) 256 | else: 257 | input_ = input_str 258 | return self.int_to_bytes(input_, (bitwidth + 7) / 8) 259 | 260 | def reset_config(self): 261 | self.TABLES.clear() 262 | self.ACTION_PROFS.clear() 263 | self.ACTIONS.clear() 264 | self.METER_ARRAYS.clear() 265 | self.COUNTER_ARRAYS.clear() 266 | self.REGISTER_ARRAYS.clear() 267 | self.CUSTOM_CRC_CALCS.clear() 268 | 269 | def load_json_str(self): 270 | def get_header_type(header_name, j_headers): 271 | for h in j_headers: 272 | if h["name"] == header_name: 273 | return h["header_type"] 274 | assert(0) 275 | 276 | def get_field_bitwidth(header_type, field_name, j_header_types): 277 | for h in j_header_types: 278 | if h["name"] != header_type: 279 | continue 280 | for t in h["fields"]: 281 | # t can have a third element (field signedness) 282 | f, bw = t[0], t[1] 283 | if f == field_name: 284 | return bw 285 | assert(0) 286 | 287 | self.reset_config() 288 | json_ = json.loads(self.json_str) 289 | 290 | def get_json_key(key): 291 | return json_.get(key, []) 292 | 293 | for j_action in get_json_key("actions"): 294 | action = self.Action(self, j_action["name"], j_action["id"]) 295 | for j_param in j_action["runtime_data"]: 296 | action.runtime_data += [(j_param["name"], j_param["bitwidth"])] 297 | 298 | for j_pipeline in get_json_key("pipelines"): 299 | if "action_profiles" in j_pipeline: # new JSON format 300 | for j_aprof in j_pipeline["action_profiles"]: 301 | action_prof = self.ActionProf( 302 | self, j_aprof["name"], j_aprof["id"]) 303 | action_prof.with_selection = "selector" in j_aprof 304 | 305 | for j_table in j_pipeline["tables"]: 306 | table = self.Table(self, j_table["name"], j_table["id"]) 307 | table.match_type = self.MatchType.from_str( 308 | j_table["match_type"]) 309 | table.type_ = self.TableType.from_str(j_table["type"]) 310 | table.support_timeout = j_table["support_timeout"] 311 | for action in j_table["actions"]: 312 | table.actions[action] = self.ACTIONS[action] 313 | 314 | if table.type_ in {self.TableType.indirect, self.TableType.indirect_ws}: 315 | if "action_profile" in j_table: 316 | action_prof = self.ACTION_PROFS[j_table["action_profile"]] 317 | else: # for backward compatibility 318 | assert("act_prof_name" in j_table) 319 | action_prof = self.ActionProf(self, j_table["act_prof_name"], 320 | table.id_) 321 | action_prof.with_selection = "selector" in j_table 322 | action_prof.actions.update(table.actions) 323 | action_prof.ref_cnt += 1 324 | table.action_prof = action_prof 325 | 326 | for j_key in j_table["key"]: 327 | target = j_key["target"] 328 | match_type = self.MatchType.from_str(j_key["match_type"]) 329 | if match_type == self.MatchType.VALID: 330 | field_name = target + "_valid" 331 | bitwidth = 1 332 | elif target[1] == "$valid$": 333 | field_name = target[0] + "_valid" 334 | bitwidth = 1 335 | else: 336 | field_name = ".".join(target) 337 | header_type = get_header_type(target[0], 338 | json_["headers"]) 339 | bitwidth = get_field_bitwidth(header_type, target[1], 340 | json_["header_types"]) 341 | table.key += [(field_name, match_type, bitwidth)] 342 | 343 | for j_meter in get_json_key("meter_arrays"): 344 | meter_array = self.MeterArray(self, j_meter["name"], j_meter["id"]) 345 | if "is_direct" in j_meter and j_meter["is_direct"]: 346 | meter_array.is_direct = True 347 | meter_array.binding = j_meter["binding"] 348 | else: 349 | meter_array.is_direct = False 350 | meter_array.size = j_meter["size"] 351 | meter_array.type_ = self.MeterType.from_str(j_meter["type"]) 352 | meter_array.rate_count = j_meter["rate_count"] 353 | 354 | for j_counter in get_json_key("counter_arrays"): 355 | counter_array = self.CounterArray( 356 | self, j_counter["name"], j_counter["id"]) 357 | counter_array.is_direct = j_counter["is_direct"] 358 | if counter_array.is_direct: 359 | counter_array.binding = j_counter["binding"] 360 | else: 361 | counter_array.size = j_counter["size"] 362 | 363 | for j_register in get_json_key("register_arrays"): 364 | register_array = self.RegisterArray(self, 365 | j_register["name"], j_register["id"]) 366 | register_array.size = j_register["size"] 367 | register_array.width = j_register["bitwidth"] 368 | 369 | for j_calc in get_json_key("calculations"): 370 | calc_name = j_calc["name"] 371 | if j_calc["algo"] == "crc16_custom": 372 | self.CUSTOM_CRC_CALCS[calc_name] = 16 373 | elif j_calc["algo"] == "crc32_custom": 374 | self.CUSTOM_CRC_CALCS[calc_name] = 32 375 | 376 | def parse_runtime_data(self, action, params): 377 | bitwidths = [bw for(_, bw) in action.runtime_data] 378 | byte_array = [] 379 | for input_str, bitwidth in zip(params, bitwidths): 380 | byte_array += [self.bytes_to_string( 381 | self.parse_param(input_str, bitwidth))] 382 | return byte_array 383 | 384 | def parse_match_key(self, table, key_fields): 385 | 386 | def parse_param_(field, bw): 387 | return self.parse_param(field, bw) 388 | 389 | params = [] 390 | match_types = [t for (_, t, _) in table.key] 391 | bitwidths = [bw for (_, _, bw) in table.key] 392 | for idx, field in enumerate(key_fields): 393 | param_type = self._match_types_mapping[match_types[idx]] 394 | bw = bitwidths[idx] 395 | if param_type == BmMatchParamType.EXACT: 396 | key = self.bytes_to_string(parse_param_(field, bw)) 397 | param = BmMatchParam(type=param_type, 398 | exact=BmMatchParamExact(key)) 399 | elif param_type == BmMatchParamType.LPM: 400 | prefix, length = field.split("/") 401 | key = bytes_to_string(parse_param_(prefix, bw)) 402 | param = BmMatchParam(type=param_type, 403 | lpm=BmMatchParamLPM(key, int(length))) 404 | elif param_type == BmMatchParamType.TERNARY: 405 | key, mask = field.split("&&&") 406 | key = bytes_to_string(parse_param_(key, bw)) 407 | mask = bytes_to_string(parse_param_(mask, bw)) 408 | if len(mask) != len(key): 409 | pass 410 | param = BmMatchParam(type=param_type, 411 | ternary=BmMatchParamTernary(key, mask)) 412 | elif param_type == BmMatchParamType.VALID: 413 | key = bool(int(field)) 414 | param = BmMatchParam(type=param_type, 415 | valid=BmMatchParamValid(key)) 416 | elif param_type == BmMatchParamType.RANGE: 417 | start, end = field.split("->") 418 | start = bytes_to_string(parse_param_(start, bw)) 419 | end = bytes_to_string(parse_param_(end, bw)) 420 | if len(start) != len(end): 421 | pass 422 | if start > end: 423 | pass 424 | param = BmMatchParam(type=param_type, 425 | range=BmMatchParamRange(start, end)) 426 | else: 427 | assert(0) 428 | params.append(param) 429 | return params 430 | 431 | def table_clear(self, table_name): 432 | table = self.TABLES[table_name] 433 | self.standard_client.bm_mt_clear_entries(0, table_name, False) 434 | 435 | def table_add(self, table_name, action_name, match_key, action_params, priority=None): 436 | table = self.TABLES[table_name] 437 | if table.match_type in {self.MatchType.TERNARY, self.MatchType.RANGE}: 438 | try: 439 | priority = int(priority) 440 | except: 441 | # here need to throw a exception but I don't care 442 | pass 443 | else: 444 | priority = 0 445 | 446 | action = self.ACTIONS[action_name] 447 | if not isinstance(action_params, tuple): 448 | action_params = (action_params, ) 449 | runtime_data = self.parse_runtime_data(action, action_params) 450 | 451 | match_key = self.parse_match_key(table, match_key) 452 | 453 | entry_handle = self.standard_client.bm_mt_add_entry( 454 | 0, table_name, match_key, action_name, runtime_data, 455 | BmAddEntryOptions(priority=priority) 456 | ) 457 | 458 | def counter_read(self, counter_name, counter_index): 459 | value = self.standard_client.bm_counter_read( 460 | 0, counter_name, counter_index) 461 | return value 462 | 463 | def counter_reset(self, counter_name): 464 | value = self.standard_client.bm_counter_reset_all(0, counter_name) 465 | 466 | 467 | if __name__ == '__main__': 468 | sr = SwitchRuntime() 469 | sr.makeThriftLink() 470 | sr.table_add('doarp', 'arpreply', ('00:00:00:00:00:00', 471 | '10.0.0.101'), '00:01:00:00:00:01') 472 | 473 | # sr.table_clear('doarp') 474 | -------------------------------------------------------------------------------- /system/controller/topoMaker.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | from mininet.net import Mininet 4 | from mininet.topo import Topo 5 | from mininet.log import setLogLevel, info 6 | from mininet.cli import CLI 7 | from mininet.node import RemoteController, OVSSwitch 8 | from p4_mininet import P4Switch, P4Host 9 | 10 | import os 11 | import copy 12 | 13 | 14 | class MakeSwitchTopo(Topo): 15 | """ 16 | Mapping the topology in controller to Mininet 17 | """ 18 | 19 | def __init__(self, sw_path, json_path, app_topo, **opts): 20 | """ 21 | Make Mininet Host, Switch and Link entity 22 | 23 | :param sw_path: switch path (use bmv2's simple_switch target in this case) 24 | :param json_path: a compiled JSON file from P4 code 25 | :param app_topo: the Ctrl class instance to get informatin 26 | """ 27 | Topo.__init__(self, **opts) 28 | 29 | self.switchSum = len(app_topo.switches) 30 | self.hostSum = len(app_topo.hosts) 31 | 32 | self.mn_switches = [] 33 | self.mn_hosts = [] 34 | for i in range(self.switchSum): 35 | self.mn_switches.append(self.addSwitch(app_topo.switches[i].name, 36 | sw_path=sw_path, 37 | json_path=json_path, 38 | thrift_port=app_topo.switches[i].thriftPort, 39 | pcap_dump=False)) 40 | for i in range(self.hostSum): 41 | if app_topo.hosts[i] != None: 42 | self.mn_hosts.append(self.addHost(app_topo.hosts[i].name, 43 | ip=app_topo.hosts[i].ipAddress + "/24", 44 | mac=app_topo.hosts[i].macAddress)) 45 | else: 46 | self.mn_hosts.append(None) 47 | for i in range(self.switchSum): 48 | for j in range(app_topo.switches[i].portSum): 49 | deviceNum = int( 50 | app_topo.switches[i].ports[j].deviceName[1:]) 51 | if app_topo.switches[i].ports[j].deviceName.startswith('s'): 52 | if i < deviceNum: 53 | self.addLink( 54 | self.mn_switches[i], self.mn_switches[deviceNum]) 55 | else: 56 | self.addLink( 57 | self.mn_switches[i], self.mn_hosts[deviceNum]) 58 | 59 | 60 | class TopoMaker(object): 61 | """ 62 | Make topology in Mininet 63 | """ 64 | 65 | def __init__(self, switchPath, jsonPath, topoObj): 66 | """ 67 | Initial topology maker 68 | 69 | :param sw_path: switch path (use bmv2's simple_switch target in this case) 70 | :param json_path: a compiled JSON file from P4 code 71 | :param topoObj: the Ctrl class instance to get informatin 72 | """ 73 | self.topo = MakeSwitchTopo(switchPath, jsonPath, topoObj) 74 | self.topoObj = topoObj 75 | 76 | def genMnTopo(self): 77 | """ 78 | Launch Mininet topology and add some commands (like disable IPv6, open INT sender/packet client/INT parser) to host and switch & start OVS and ryu controller 79 | """ 80 | setLogLevel('debug') 81 | self.net = Mininet(topo=self.topo, 82 | host=P4Host, 83 | switch=P4Switch, 84 | controller=None) 85 | controller_list = [] 86 | c = self.net.addController('mycontroller', controller=RemoteController) 87 | controller_list.append(c) 88 | ovs = self.net.addSwitch('s999', cls=OVSSwitch) 89 | 90 | hostIpList = [ 91 | host.ipAddress for host in self.topoObj.hosts if host is not None] 92 | 93 | j = 0 94 | for i in range(self.topo.hostSum): 95 | if self.topo.mn_hosts[i] != None: 96 | self.net.addLink(self.net.hosts[j], ovs) 97 | self.net.hosts[j].cmd( 98 | "sysctl -w net.ipv6.conf.all.disable_ipv6=1") 99 | self.net.hosts[j].cmd( 100 | "sysctl -w net.ipv6.conf.default.disable_ipv6=1") 101 | self.net.hosts[j].cmd( 102 | "sysctl -w net.ipv6.conf.lo.disable_ipv6=1") 103 | name = self.topoObj.hosts[i].name 104 | ipAddr = self.topoObj.hosts[i].ovsIpAddress 105 | action = 'ip a a ' + ipAddr + '/24 dev ' + name + '-eth1' 106 | self.net.hosts[j].cmd(action) 107 | self.net.hosts[j].cmd('ifconfig') 108 | j = j + 1 109 | 110 | ovs.start(controller_list) 111 | 112 | self.net.start() 113 | os.popen('ovs-vsctl add-port s999 ens38') 114 | 115 | j = 0 116 | for i in range(self.topo.hostSum): 117 | if self.topo.mn_hosts[i] != None: 118 | 119 | packetSender = ' python ~/P4_DC/packet/sendint.py ' + \ 120 | str(i) + ' &' 121 | self.net.hosts[j].cmd(packetSender) 122 | 123 | intReceiver = '~/P4_DC/packet/receiveint ' + \ 124 | str(i) + ' >/dev/null &' 125 | self.net.hosts[j].cmd(intReceiver) 126 | j = j + 1 127 | 128 | def getCLI(self): 129 | """ 130 | Open Mininet CLI 131 | """ 132 | CLI(self.net) 133 | 134 | def stopMnTopo(self): 135 | """ 136 | Stop Mininet envirnoment 137 | """ 138 | self.net.stop() 139 | 140 | def cleanMn(self): 141 | """ 142 | Clean all mininet trace 143 | """ 144 | os.system('sudo mn -c') 145 | os.system( 146 | 'ryu-manager /usr/local/lib/python2.7/dist-packages/ryu/app/simple_switch.py 2>/dev/null >/dev/null &') 147 | pass 148 | 149 | 150 | if __name__ == '__main__': 151 | pass 152 | -------------------------------------------------------------------------------- /system/p4app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "program" : "app.p4", 3 | "__meta__" : null, 4 | "header_types" : [ 5 | { 6 | "name" : "scalars", 7 | "id" : 0, 8 | "fields" : [ 9 | ["tmp_1", 16, false], 10 | ["tmp", 32, false], 11 | ["tmp_0", 32, false], 12 | ["tmp_2", 32, false], 13 | ["tmp_3", 32, false], 14 | ["tmp_4", 32, false], 15 | ["result_1", 32, false], 16 | ["tempip", 32, false], 17 | ["port_3", 4, false], 18 | ["_padding_0", 4, false] 19 | ] 20 | }, 21 | { 22 | "name" : "ethernet_t", 23 | "id" : 1, 24 | "fields" : [ 25 | ["dstAddr", 48, false], 26 | ["srcAddr", 48, false], 27 | ["etherType", 16, false] 28 | ] 29 | }, 30 | { 31 | "name" : "arp_t", 32 | "id" : 2, 33 | "fields" : [ 34 | ["arpHdr", 16, false], 35 | ["arpPro", 16, false], 36 | ["arpHln", 8, false], 37 | ["arpPln", 8, false], 38 | ["arpOp", 16, false], 39 | ["arpSha", 48, false], 40 | ["arpSpa", 32, false], 41 | ["arpTha", 48, false], 42 | ["arpTpa", 32, false] 43 | ] 44 | }, 45 | { 46 | "name" : "ipv4_t", 47 | "id" : 3, 48 | "fields" : [ 49 | ["version", 4, false], 50 | ["ihl", 4, false], 51 | ["diffserv", 8, false], 52 | ["totalLen", 16, false], 53 | ["identification", 16, false], 54 | ["flags", 3, false], 55 | ["fragOffset", 13, false], 56 | ["ttl", 8, false], 57 | ["protocol", 8, false], 58 | ["hdrChecksum", 16, false], 59 | ["srcAddr", 32, false], 60 | ["dstAddr", 32, false] 61 | ] 62 | }, 63 | { 64 | "name" : "udp_t", 65 | "id" : 4, 66 | "fields" : [ 67 | ["srcPort", 16, false], 68 | ["dstPort", 16, false], 69 | ["len", 16, false], 70 | ["hdrChecksum", 16, false] 71 | ] 72 | }, 73 | { 74 | "name" : "sr_t", 75 | "id" : 5, 76 | "fields" : [ 77 | ["routingList", 512, false] 78 | ] 79 | }, 80 | { 81 | "name" : "inthdr_t", 82 | "id" : 6, 83 | "fields" : [ 84 | ["device_no", 8, false], 85 | ["ingress_port", 9, false], 86 | ["egress_port", 9, false], 87 | ["ingress_global_timestamp", 48, false], 88 | ["enq_timestamp", 32, false], 89 | ["enq_qdepth", 19, false], 90 | ["deq_timedelta", 32, false], 91 | ["deq_qdepth", 19, false] 92 | ] 93 | }, 94 | { 95 | "name" : "ingress_metadata_t", 96 | "id" : 7, 97 | "fields" : [ 98 | ["nhop_ipv4", 32, false] 99 | ] 100 | }, 101 | { 102 | "name" : "intrinsic_metadata_t", 103 | "id" : 8, 104 | "fields" : [ 105 | ["ingress_global_timestamp", 48, false], 106 | ["lf_field_list", 32, false], 107 | ["mcast_grp", 16, false], 108 | ["egress_rid", 16, false], 109 | ["resubmit_flag", 8, false], 110 | ["recirculate_flag", 8, false] 111 | ] 112 | }, 113 | { 114 | "name" : "queueing_metadata", 115 | "id" : 9, 116 | "fields" : [ 117 | ["enq_timestamp", 48, false], 118 | ["enq_qdepth", 16, false], 119 | ["deq_timedelta", 32, false], 120 | ["deq_qdepth", 16, false] 121 | ] 122 | }, 123 | { 124 | "name" : "int_metadata", 125 | "id" : 10, 126 | "fields" : [ 127 | ["device_no", 8, false] 128 | ] 129 | }, 130 | { 131 | "name" : "standard_metadata", 132 | "id" : 11, 133 | "fields" : [ 134 | ["ingress_port", 9, false], 135 | ["egress_spec", 9, false], 136 | ["egress_port", 9, false], 137 | ["clone_spec", 32, false], 138 | ["instance_type", 32, false], 139 | ["drop", 1, false], 140 | ["recirculate_port", 16, false], 141 | ["packet_length", 32, false], 142 | ["enq_timestamp", 32, false], 143 | ["enq_qdepth", 19, false], 144 | ["deq_timedelta", 32, false], 145 | ["deq_qdepth", 19, false], 146 | ["ingress_global_timestamp", 48, false], 147 | ["lf_field_list", 32, false], 148 | ["mcast_grp", 16, false], 149 | ["resubmit_flag", 1, false], 150 | ["egress_rid", 16, false], 151 | ["_padding", 5, false] 152 | ] 153 | } 154 | ], 155 | "headers" : [ 156 | { 157 | "name" : "scalars", 158 | "id" : 0, 159 | "header_type" : "scalars", 160 | "metadata" : true, 161 | "pi_omit" : true 162 | }, 163 | { 164 | "name" : "standard_metadata", 165 | "id" : 1, 166 | "header_type" : "standard_metadata", 167 | "metadata" : true, 168 | "pi_omit" : true 169 | }, 170 | { 171 | "name" : "ethernet", 172 | "id" : 2, 173 | "header_type" : "ethernet_t", 174 | "metadata" : false, 175 | "pi_omit" : true 176 | }, 177 | { 178 | "name" : "arp", 179 | "id" : 3, 180 | "header_type" : "arp_t", 181 | "metadata" : false, 182 | "pi_omit" : true 183 | }, 184 | { 185 | "name" : "ipv4", 186 | "id" : 4, 187 | "header_type" : "ipv4_t", 188 | "metadata" : false, 189 | "pi_omit" : true 190 | }, 191 | { 192 | "name" : "udp", 193 | "id" : 5, 194 | "header_type" : "udp_t", 195 | "metadata" : false, 196 | "pi_omit" : true 197 | }, 198 | { 199 | "name" : "sr", 200 | "id" : 6, 201 | "header_type" : "sr_t", 202 | "metadata" : false, 203 | "pi_omit" : true 204 | }, 205 | { 206 | "name" : "inthdr", 207 | "id" : 7, 208 | "header_type" : "inthdr_t", 209 | "metadata" : false, 210 | "pi_omit" : true 211 | }, 212 | { 213 | "name" : "ingress_metadata", 214 | "id" : 8, 215 | "header_type" : "ingress_metadata_t", 216 | "metadata" : true, 217 | "pi_omit" : true 218 | }, 219 | { 220 | "name" : "intrinsic_metadata", 221 | "id" : 9, 222 | "header_type" : "intrinsic_metadata_t", 223 | "metadata" : true, 224 | "pi_omit" : true 225 | }, 226 | { 227 | "name" : "queueing_metadata", 228 | "id" : 10, 229 | "header_type" : "queueing_metadata", 230 | "metadata" : true, 231 | "pi_omit" : true 232 | }, 233 | { 234 | "name" : "int_metadata", 235 | "id" : 11, 236 | "header_type" : "int_metadata", 237 | "metadata" : true, 238 | "pi_omit" : true 239 | } 240 | ], 241 | "header_stacks" : [], 242 | "field_lists" : [], 243 | "errors" : [ 244 | ["ParserTimeout", 6], 245 | ["HeaderTooShort", 5], 246 | ["NoError", 1], 247 | ["PacketTooShort", 2], 248 | ["NoMatch", 3], 249 | ["StackOutOfBounds", 4] 250 | ], 251 | "enums" : [], 252 | "parsers" : [ 253 | { 254 | "name" : "parser", 255 | "id" : 0, 256 | "init_state" : "start", 257 | "parse_states" : [ 258 | { 259 | "name" : "start", 260 | "id" : 0, 261 | "parser_ops" : [], 262 | "transitions" : [ 263 | { 264 | "value" : "default", 265 | "mask" : null, 266 | "next_state" : "parse_ethernet" 267 | } 268 | ], 269 | "transition_key" : [] 270 | }, 271 | { 272 | "name" : "parse_ethernet", 273 | "id" : 1, 274 | "parser_ops" : [ 275 | { 276 | "parameters" : [ 277 | { 278 | "type" : "regular", 279 | "value" : "ethernet" 280 | } 281 | ], 282 | "op" : "extract" 283 | } 284 | ], 285 | "transitions" : [ 286 | { 287 | "value" : "0x0800", 288 | "mask" : null, 289 | "next_state" : "parse_ipv4" 290 | }, 291 | { 292 | "value" : "0x0806", 293 | "mask" : null, 294 | "next_state" : "parse_arp" 295 | }, 296 | { 297 | "value" : "default", 298 | "mask" : null, 299 | "next_state" : null 300 | } 301 | ], 302 | "transition_key" : [ 303 | { 304 | "type" : "field", 305 | "value" : ["ethernet", "etherType"] 306 | } 307 | ] 308 | }, 309 | { 310 | "name" : "parse_arp", 311 | "id" : 2, 312 | "parser_ops" : [ 313 | { 314 | "parameters" : [ 315 | { 316 | "type" : "regular", 317 | "value" : "arp" 318 | } 319 | ], 320 | "op" : "extract" 321 | } 322 | ], 323 | "transitions" : [ 324 | { 325 | "value" : "default", 326 | "mask" : null, 327 | "next_state" : null 328 | } 329 | ], 330 | "transition_key" : [] 331 | }, 332 | { 333 | "name" : "parse_ipv4", 334 | "id" : 3, 335 | "parser_ops" : [ 336 | { 337 | "parameters" : [ 338 | { 339 | "type" : "regular", 340 | "value" : "ipv4" 341 | } 342 | ], 343 | "op" : "extract" 344 | } 345 | ], 346 | "transitions" : [ 347 | { 348 | "value" : "0x11", 349 | "mask" : null, 350 | "next_state" : "parse_udp" 351 | }, 352 | { 353 | "value" : "default", 354 | "mask" : null, 355 | "next_state" : null 356 | } 357 | ], 358 | "transition_key" : [ 359 | { 360 | "type" : "field", 361 | "value" : ["ipv4", "protocol"] 362 | } 363 | ] 364 | }, 365 | { 366 | "name" : "parse_udp", 367 | "id" : 4, 368 | "parser_ops" : [ 369 | { 370 | "parameters" : [ 371 | { 372 | "type" : "regular", 373 | "value" : "udp" 374 | } 375 | ], 376 | "op" : "extract" 377 | } 378 | ], 379 | "transitions" : [ 380 | { 381 | "value" : "0x08ae", 382 | "mask" : null, 383 | "next_state" : "parse_sr" 384 | }, 385 | { 386 | "value" : "default", 387 | "mask" : null, 388 | "next_state" : null 389 | } 390 | ], 391 | "transition_key" : [ 392 | { 393 | "type" : "field", 394 | "value" : ["udp", "dstPort"] 395 | } 396 | ] 397 | }, 398 | { 399 | "name" : "parse_sr", 400 | "id" : 5, 401 | "parser_ops" : [ 402 | { 403 | "parameters" : [ 404 | { 405 | "type" : "regular", 406 | "value" : "sr" 407 | } 408 | ], 409 | "op" : "extract" 410 | } 411 | ], 412 | "transitions" : [ 413 | { 414 | "value" : "default", 415 | "mask" : null, 416 | "next_state" : null 417 | } 418 | ], 419 | "transition_key" : [] 420 | } 421 | ] 422 | } 423 | ], 424 | "deparsers" : [ 425 | { 426 | "name" : "deparser", 427 | "id" : 0, 428 | "source_info" : { 429 | "filename" : "parser.p4", 430 | "line" : 43, 431 | "column" : 8, 432 | "source_fragment" : "DeparserImpl" 433 | }, 434 | "order" : ["ethernet", "arp", "ipv4", "udp", "sr", "inthdr"] 435 | } 436 | ], 437 | "meter_arrays" : [], 438 | "counter_arrays" : [ 439 | { 440 | "name" : "ingress_counter_2", 441 | "id" : 0, 442 | "source_info" : { 443 | "filename" : "app.p4", 444 | "line" : 54, 445 | "column" : 39, 446 | "source_fragment" : "ingress_counter_2" 447 | }, 448 | "size" : 16, 449 | "is_direct" : false 450 | }, 451 | { 452 | "name" : "egress_counter_2", 453 | "id" : 1, 454 | "source_info" : { 455 | "filename" : "app.p4", 456 | "line" : 55, 457 | "column" : 39, 458 | "source_fragment" : "egress_counter_2" 459 | }, 460 | "size" : 16, 461 | "is_direct" : false 462 | }, 463 | { 464 | "name" : "ingress_counter", 465 | "id" : 2, 466 | "source_info" : { 467 | "filename" : "app.p4", 468 | "line" : 11, 469 | "column" : 39, 470 | "source_fragment" : "ingress_counter" 471 | }, 472 | "size" : 16, 473 | "is_direct" : false 474 | }, 475 | { 476 | "name" : "egress_counter", 477 | "id" : 3, 478 | "source_info" : { 479 | "filename" : "app.p4", 480 | "line" : 12, 481 | "column" : 39, 482 | "source_fragment" : "egress_counter" 483 | }, 484 | "size" : 16, 485 | "is_direct" : false 486 | } 487 | ], 488 | "register_arrays" : [], 489 | "calculations" : [], 490 | "learn_lists" : [], 491 | "actions" : [ 492 | { 493 | "name" : "act", 494 | "id" : 0, 495 | "runtime_data" : [], 496 | "primitives" : [ 497 | { 498 | "op" : "assign", 499 | "parameters" : [ 500 | { 501 | "type" : "field", 502 | "value" : ["scalars", "tmp"] 503 | }, 504 | { 505 | "type" : "expression", 506 | "value" : { 507 | "type" : "expression", 508 | "value" : { 509 | "op" : "&", 510 | "left" : { 511 | "type" : "field", 512 | "value" : ["standard_metadata", "ingress_port"] 513 | }, 514 | "right" : { 515 | "type" : "hexstr", 516 | "value" : "0xffffffff" 517 | } 518 | } 519 | } 520 | } 521 | ] 522 | }, 523 | { 524 | "op" : "count", 525 | "parameters" : [ 526 | { 527 | "type" : "counter_array", 528 | "value" : "ingress_counter" 529 | }, 530 | { 531 | "type" : "field", 532 | "value" : ["scalars", "tmp"] 533 | } 534 | ], 535 | "source_info" : { 536 | "filename" : "app.p4", 537 | "line" : 44, 538 | "column" : 8, 539 | "source_fragment" : "ingress_counter.count((bit<32>)standard_metadata.ingress_port)" 540 | } 541 | }, 542 | { 543 | "op" : "assign", 544 | "parameters" : [ 545 | { 546 | "type" : "field", 547 | "value" : ["scalars", "tmp_0"] 548 | }, 549 | { 550 | "type" : "expression", 551 | "value" : { 552 | "type" : "expression", 553 | "value" : { 554 | "op" : "&", 555 | "left" : { 556 | "type" : "field", 557 | "value" : ["standard_metadata", "egress_port"] 558 | }, 559 | "right" : { 560 | "type" : "hexstr", 561 | "value" : "0xffffffff" 562 | } 563 | } 564 | } 565 | } 566 | ] 567 | }, 568 | { 569 | "op" : "count", 570 | "parameters" : [ 571 | { 572 | "type" : "counter_array", 573 | "value" : "egress_counter" 574 | }, 575 | { 576 | "type" : "field", 577 | "value" : ["scalars", "tmp_0"] 578 | } 579 | ], 580 | "source_info" : { 581 | "filename" : "app.p4", 582 | "line" : 45, 583 | "column" : 8, 584 | "source_fragment" : "egress_counter.count((bit<32>)standard_metadata.egress_port)" 585 | } 586 | } 587 | ] 588 | }, 589 | { 590 | "name" : "act_0", 591 | "id" : 1, 592 | "runtime_data" : [], 593 | "primitives" : [ 594 | { 595 | "op" : "assign", 596 | "parameters" : [ 597 | { 598 | "type" : "field", 599 | "value" : ["scalars", "tmp_3"] 600 | }, 601 | { 602 | "type" : "expression", 603 | "value" : { 604 | "type" : "expression", 605 | "value" : { 606 | "op" : "&", 607 | "left" : { 608 | "type" : "field", 609 | "value" : ["standard_metadata", "ingress_port"] 610 | }, 611 | "right" : { 612 | "type" : "hexstr", 613 | "value" : "0xffffffff" 614 | } 615 | } 616 | } 617 | } 618 | ] 619 | }, 620 | { 621 | "op" : "count", 622 | "parameters" : [ 623 | { 624 | "type" : "counter_array", 625 | "value" : "ingress_counter_2" 626 | }, 627 | { 628 | "type" : "field", 629 | "value" : ["scalars", "tmp_3"] 630 | } 631 | ], 632 | "source_info" : { 633 | "filename" : "app.p4", 634 | "line" : 180, 635 | "column" : 8, 636 | "source_fragment" : "ingress_counter_2.count((bit<32>)standard_metadata.ingress_port)" 637 | } 638 | }, 639 | { 640 | "op" : "assign", 641 | "parameters" : [ 642 | { 643 | "type" : "field", 644 | "value" : ["scalars", "tmp_4"] 645 | }, 646 | { 647 | "type" : "expression", 648 | "value" : { 649 | "type" : "expression", 650 | "value" : { 651 | "op" : "&", 652 | "left" : { 653 | "type" : "field", 654 | "value" : ["standard_metadata", "egress_port"] 655 | }, 656 | "right" : { 657 | "type" : "hexstr", 658 | "value" : "0xffffffff" 659 | } 660 | } 661 | } 662 | } 663 | ] 664 | }, 665 | { 666 | "op" : "count", 667 | "parameters" : [ 668 | { 669 | "type" : "counter_array", 670 | "value" : "egress_counter_2" 671 | }, 672 | { 673 | "type" : "field", 674 | "value" : ["scalars", "tmp_4"] 675 | } 676 | ], 677 | "source_info" : { 678 | "filename" : "app.p4", 679 | "line" : 181, 680 | "column" : 8, 681 | "source_fragment" : "egress_counter_2.count((bit<32>)standard_metadata.egress_port)" 682 | } 683 | } 684 | ] 685 | }, 686 | { 687 | "name" : "l2setmetadataecmp", 688 | "id" : 2, 689 | "runtime_data" : [ 690 | { 691 | "name" : "routeNum", 692 | "bitwidth" : 2 693 | }, 694 | { 695 | "name" : "portData", 696 | "bitwidth" : 16 697 | } 698 | ], 699 | "primitives" : [ 700 | { 701 | "op" : "assign", 702 | "parameters" : [ 703 | { 704 | "type" : "field", 705 | "value" : ["scalars", "tmp_2"] 706 | }, 707 | { 708 | "type" : "expression", 709 | "value" : { 710 | "type" : "expression", 711 | "value" : { 712 | "op" : "&", 713 | "left" : { 714 | "type" : "expression", 715 | "value" : { 716 | "op" : "&", 717 | "left" : { 718 | "type" : "expression", 719 | "value" : { 720 | "op" : "+", 721 | "left" : { 722 | "type" : "local", 723 | "value" : 0 724 | }, 725 | "right" : { 726 | "type" : "hexstr", 727 | "value" : "0x03" 728 | } 729 | } 730 | }, 731 | "right" : { 732 | "type" : "hexstr", 733 | "value" : "0x03" 734 | } 735 | } 736 | }, 737 | "right" : { 738 | "type" : "hexstr", 739 | "value" : "0xffffffff" 740 | } 741 | } 742 | } 743 | } 744 | ] 745 | }, 746 | { 747 | "op" : "modify_field_rng_uniform", 748 | "parameters" : [ 749 | { 750 | "type" : "field", 751 | "value" : ["scalars", "result_1"] 752 | }, 753 | { 754 | "type" : "hexstr", 755 | "value" : "0x00000000" 756 | }, 757 | { 758 | "type" : "field", 759 | "value" : ["scalars", "tmp_2"] 760 | } 761 | ] 762 | }, 763 | { 764 | "op" : "assign", 765 | "parameters" : [ 766 | { 767 | "type" : "field", 768 | "value" : ["standard_metadata", "egress_spec"] 769 | }, 770 | { 771 | "type" : "expression", 772 | "value" : { 773 | "type" : "expression", 774 | "value" : { 775 | "op" : "&", 776 | "left" : { 777 | "type" : "expression", 778 | "value" : { 779 | "op" : "&", 780 | "left" : { 781 | "type" : "expression", 782 | "value" : { 783 | "op" : "?", 784 | "left" : { 785 | "type" : "expression", 786 | "value" : { 787 | "op" : "&", 788 | "left" : { 789 | "type" : "expression", 790 | "value" : { 791 | "op" : ">>", 792 | "left" : { 793 | "type" : "local", 794 | "value" : 1 795 | }, 796 | "right" : { 797 | "type" : "hexstr", 798 | "value" : "0x8" 799 | } 800 | } 801 | }, 802 | "right" : { 803 | "type" : "hexstr", 804 | "value" : "0xffff" 805 | } 806 | } 807 | }, 808 | "right" : { 809 | "type" : "expression", 810 | "value" : { 811 | "op" : "?", 812 | "left" : { 813 | "type" : "expression", 814 | "value" : { 815 | "op" : "&", 816 | "left" : { 817 | "type" : "expression", 818 | "value" : { 819 | "op" : ">>", 820 | "left" : { 821 | "type" : "local", 822 | "value" : 1 823 | }, 824 | "right" : { 825 | "type" : "hexstr", 826 | "value" : "0x8" 827 | } 828 | } 829 | }, 830 | "right" : { 831 | "type" : "hexstr", 832 | "value" : "0xffff" 833 | } 834 | } 835 | }, 836 | "right" : { 837 | "type" : "expression", 838 | "value" : { 839 | "op" : "?", 840 | "left" : { 841 | "type" : "expression", 842 | "value" : { 843 | "op" : "&", 844 | "left" : { 845 | "type" : "expression", 846 | "value" : { 847 | "op" : ">>", 848 | "left" : { 849 | "type" : "local", 850 | "value" : 1 851 | }, 852 | "right" : { 853 | "type" : "hexstr", 854 | "value" : "0x4" 855 | } 856 | } 857 | }, 858 | "right" : { 859 | "type" : "hexstr", 860 | "value" : "0xffff" 861 | } 862 | } 863 | }, 864 | "right" : { 865 | "type" : "local", 866 | "value" : 1 867 | }, 868 | "cond" : { 869 | "type" : "expression", 870 | "value" : { 871 | "op" : "==", 872 | "left" : { 873 | "type" : "field", 874 | "value" : ["scalars", "result_1"] 875 | }, 876 | "right" : { 877 | "type" : "hexstr", 878 | "value" : "0x00000001" 879 | } 880 | } 881 | } 882 | } 883 | }, 884 | "cond" : { 885 | "type" : "expression", 886 | "value" : { 887 | "op" : "and", 888 | "left" : { 889 | "type" : "expression", 890 | "value" : { 891 | "op" : "not", 892 | "left" : null, 893 | "right" : { 894 | "type" : "expression", 895 | "value" : { 896 | "op" : "==", 897 | "left" : { 898 | "type" : "field", 899 | "value" : ["scalars", "result_1"] 900 | }, 901 | "right" : { 902 | "type" : "hexstr", 903 | "value" : "0x00000001" 904 | } 905 | } 906 | } 907 | } 908 | }, 909 | "right" : { 910 | "type" : "expression", 911 | "value" : { 912 | "op" : "==", 913 | "left" : { 914 | "type" : "field", 915 | "value" : ["scalars", "result_1"] 916 | }, 917 | "right" : { 918 | "type" : "hexstr", 919 | "value" : "0x00000002" 920 | } 921 | } 922 | } 923 | } 924 | } 925 | } 926 | }, 927 | "cond" : { 928 | "type" : "expression", 929 | "value" : { 930 | "op" : "and", 931 | "left" : { 932 | "type" : "expression", 933 | "value" : { 934 | "op" : "and", 935 | "left" : { 936 | "type" : "expression", 937 | "value" : { 938 | "op" : "not", 939 | "left" : null, 940 | "right" : { 941 | "type" : "expression", 942 | "value" : { 943 | "op" : "==", 944 | "left" : { 945 | "type" : "field", 946 | "value" : ["scalars", "result_1"] 947 | }, 948 | "right" : { 949 | "type" : "hexstr", 950 | "value" : "0x00000001" 951 | } 952 | } 953 | } 954 | } 955 | }, 956 | "right" : { 957 | "type" : "expression", 958 | "value" : { 959 | "op" : "not", 960 | "left" : null, 961 | "right" : { 962 | "type" : "expression", 963 | "value" : { 964 | "op" : "==", 965 | "left" : { 966 | "type" : "field", 967 | "value" : ["scalars", "result_1"] 968 | }, 969 | "right" : { 970 | "type" : "hexstr", 971 | "value" : "0x00000002" 972 | } 973 | } 974 | } 975 | } 976 | } 977 | } 978 | }, 979 | "right" : { 980 | "type" : "expression", 981 | "value" : { 982 | "op" : "==", 983 | "left" : { 984 | "type" : "field", 985 | "value" : ["scalars", "result_1"] 986 | }, 987 | "right" : { 988 | "type" : "hexstr", 989 | "value" : "0x00000003" 990 | } 991 | } 992 | } 993 | } 994 | } 995 | } 996 | }, 997 | "right" : { 998 | "type" : "hexstr", 999 | "value" : "0x0f" 1000 | } 1001 | } 1002 | }, 1003 | "right" : { 1004 | "type" : "hexstr", 1005 | "value" : "0x01ff" 1006 | } 1007 | } 1008 | } 1009 | } 1010 | ], 1011 | "source_info" : { 1012 | "filename" : "app.p4", 1013 | "line" : 79, 1014 | "column" : 20, 1015 | "source_fragment" : "(bit<9>)((bit<4>)data); ..." 1016 | } 1017 | }, 1018 | { 1019 | "op" : "assign", 1020 | "parameters" : [ 1021 | { 1022 | "type" : "field", 1023 | "value" : ["standard_metadata", "egress_port"] 1024 | }, 1025 | { 1026 | "type" : "expression", 1027 | "value" : { 1028 | "type" : "expression", 1029 | "value" : { 1030 | "op" : "&", 1031 | "left" : { 1032 | "type" : "expression", 1033 | "value" : { 1034 | "op" : "&", 1035 | "left" : { 1036 | "type" : "expression", 1037 | "value" : { 1038 | "op" : "?", 1039 | "left" : { 1040 | "type" : "expression", 1041 | "value" : { 1042 | "op" : "&", 1043 | "left" : { 1044 | "type" : "expression", 1045 | "value" : { 1046 | "op" : ">>", 1047 | "left" : { 1048 | "type" : "local", 1049 | "value" : 1 1050 | }, 1051 | "right" : { 1052 | "type" : "hexstr", 1053 | "value" : "0x8" 1054 | } 1055 | } 1056 | }, 1057 | "right" : { 1058 | "type" : "hexstr", 1059 | "value" : "0xffff" 1060 | } 1061 | } 1062 | }, 1063 | "right" : { 1064 | "type" : "expression", 1065 | "value" : { 1066 | "op" : "?", 1067 | "left" : { 1068 | "type" : "expression", 1069 | "value" : { 1070 | "op" : "&", 1071 | "left" : { 1072 | "type" : "expression", 1073 | "value" : { 1074 | "op" : ">>", 1075 | "left" : { 1076 | "type" : "local", 1077 | "value" : 1 1078 | }, 1079 | "right" : { 1080 | "type" : "hexstr", 1081 | "value" : "0x8" 1082 | } 1083 | } 1084 | }, 1085 | "right" : { 1086 | "type" : "hexstr", 1087 | "value" : "0xffff" 1088 | } 1089 | } 1090 | }, 1091 | "right" : { 1092 | "type" : "expression", 1093 | "value" : { 1094 | "op" : "?", 1095 | "left" : { 1096 | "type" : "expression", 1097 | "value" : { 1098 | "op" : "&", 1099 | "left" : { 1100 | "type" : "expression", 1101 | "value" : { 1102 | "op" : ">>", 1103 | "left" : { 1104 | "type" : "local", 1105 | "value" : 1 1106 | }, 1107 | "right" : { 1108 | "type" : "hexstr", 1109 | "value" : "0x4" 1110 | } 1111 | } 1112 | }, 1113 | "right" : { 1114 | "type" : "hexstr", 1115 | "value" : "0xffff" 1116 | } 1117 | } 1118 | }, 1119 | "right" : { 1120 | "type" : "local", 1121 | "value" : 1 1122 | }, 1123 | "cond" : { 1124 | "type" : "expression", 1125 | "value" : { 1126 | "op" : "==", 1127 | "left" : { 1128 | "type" : "field", 1129 | "value" : ["scalars", "result_1"] 1130 | }, 1131 | "right" : { 1132 | "type" : "hexstr", 1133 | "value" : "0x00000001" 1134 | } 1135 | } 1136 | } 1137 | } 1138 | }, 1139 | "cond" : { 1140 | "type" : "expression", 1141 | "value" : { 1142 | "op" : "and", 1143 | "left" : { 1144 | "type" : "expression", 1145 | "value" : { 1146 | "op" : "not", 1147 | "left" : null, 1148 | "right" : { 1149 | "type" : "expression", 1150 | "value" : { 1151 | "op" : "==", 1152 | "left" : { 1153 | "type" : "field", 1154 | "value" : ["scalars", "result_1"] 1155 | }, 1156 | "right" : { 1157 | "type" : "hexstr", 1158 | "value" : "0x00000001" 1159 | } 1160 | } 1161 | } 1162 | } 1163 | }, 1164 | "right" : { 1165 | "type" : "expression", 1166 | "value" : { 1167 | "op" : "==", 1168 | "left" : { 1169 | "type" : "field", 1170 | "value" : ["scalars", "result_1"] 1171 | }, 1172 | "right" : { 1173 | "type" : "hexstr", 1174 | "value" : "0x00000002" 1175 | } 1176 | } 1177 | } 1178 | } 1179 | } 1180 | } 1181 | }, 1182 | "cond" : { 1183 | "type" : "expression", 1184 | "value" : { 1185 | "op" : "and", 1186 | "left" : { 1187 | "type" : "expression", 1188 | "value" : { 1189 | "op" : "and", 1190 | "left" : { 1191 | "type" : "expression", 1192 | "value" : { 1193 | "op" : "not", 1194 | "left" : null, 1195 | "right" : { 1196 | "type" : "expression", 1197 | "value" : { 1198 | "op" : "==", 1199 | "left" : { 1200 | "type" : "field", 1201 | "value" : ["scalars", "result_1"] 1202 | }, 1203 | "right" : { 1204 | "type" : "hexstr", 1205 | "value" : "0x00000001" 1206 | } 1207 | } 1208 | } 1209 | } 1210 | }, 1211 | "right" : { 1212 | "type" : "expression", 1213 | "value" : { 1214 | "op" : "not", 1215 | "left" : null, 1216 | "right" : { 1217 | "type" : "expression", 1218 | "value" : { 1219 | "op" : "==", 1220 | "left" : { 1221 | "type" : "field", 1222 | "value" : ["scalars", "result_1"] 1223 | }, 1224 | "right" : { 1225 | "type" : "hexstr", 1226 | "value" : "0x00000002" 1227 | } 1228 | } 1229 | } 1230 | } 1231 | } 1232 | } 1233 | }, 1234 | "right" : { 1235 | "type" : "expression", 1236 | "value" : { 1237 | "op" : "==", 1238 | "left" : { 1239 | "type" : "field", 1240 | "value" : ["scalars", "result_1"] 1241 | }, 1242 | "right" : { 1243 | "type" : "hexstr", 1244 | "value" : "0x00000003" 1245 | } 1246 | } 1247 | } 1248 | } 1249 | } 1250 | } 1251 | }, 1252 | "right" : { 1253 | "type" : "hexstr", 1254 | "value" : "0x0f" 1255 | } 1256 | } 1257 | }, 1258 | "right" : { 1259 | "type" : "hexstr", 1260 | "value" : "0x01ff" 1261 | } 1262 | } 1263 | } 1264 | } 1265 | ], 1266 | "source_info" : { 1267 | "filename" : "app.p4", 1268 | "line" : 79, 1269 | "column" : 20, 1270 | "source_fragment" : "(bit<9>)((bit<4>)data); ..." 1271 | } 1272 | } 1273 | ] 1274 | }, 1275 | { 1276 | "name" : "setdeviceno", 1277 | "id" : 3, 1278 | "runtime_data" : [ 1279 | { 1280 | "name" : "device_no", 1281 | "bitwidth" : 8 1282 | } 1283 | ], 1284 | "primitives" : [ 1285 | { 1286 | "op" : "assign", 1287 | "parameters" : [ 1288 | { 1289 | "type" : "field", 1290 | "value" : ["int_metadata", "device_no"] 1291 | }, 1292 | { 1293 | "type" : "runtime_data", 1294 | "value" : 0 1295 | } 1296 | ], 1297 | "source_info" : { 1298 | "filename" : "app.p4", 1299 | "line" : 107, 1300 | "column" : 8, 1301 | "source_fragment" : "meta.int_metadata.device_no=device_no" 1302 | } 1303 | } 1304 | ] 1305 | }, 1306 | { 1307 | "name" : "srrouting", 1308 | "id" : 4, 1309 | "runtime_data" : [], 1310 | "primitives" : [ 1311 | { 1312 | "op" : "assign", 1313 | "parameters" : [ 1314 | { 1315 | "type" : "field", 1316 | "value" : ["scalars", "port_3"] 1317 | }, 1318 | { 1319 | "type" : "expression", 1320 | "value" : { 1321 | "type" : "expression", 1322 | "value" : { 1323 | "op" : "&", 1324 | "left" : { 1325 | "type" : "field", 1326 | "value" : ["sr", "routingList"] 1327 | }, 1328 | "right" : { 1329 | "type" : "hexstr", 1330 | "value" : "0x0f" 1331 | } 1332 | } 1333 | } 1334 | } 1335 | ], 1336 | "source_info" : { 1337 | "filename" : "app.p4", 1338 | "line" : 100, 1339 | "column" : 8, 1340 | "source_fragment" : "bit<4> port=(bit<4>)hdr.sr.routingList;" 1341 | } 1342 | }, 1343 | { 1344 | "op" : "assign", 1345 | "parameters" : [ 1346 | { 1347 | "type" : "field", 1348 | "value" : ["sr", "routingList"] 1349 | }, 1350 | { 1351 | "type" : "expression", 1352 | "value" : { 1353 | "type" : "expression", 1354 | "value" : { 1355 | "op" : "&", 1356 | "left" : { 1357 | "type" : "expression", 1358 | "value" : { 1359 | "op" : ">>", 1360 | "left" : { 1361 | "type" : "field", 1362 | "value" : ["sr", "routingList"] 1363 | }, 1364 | "right" : { 1365 | "type" : "hexstr", 1366 | "value" : "0x4" 1367 | } 1368 | } 1369 | }, 1370 | "right" : { 1371 | "type" : "hexstr", 1372 | "value" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1373 | } 1374 | } 1375 | } 1376 | } 1377 | ], 1378 | "source_info" : { 1379 | "filename" : "app.p4", 1380 | "line" : 101, 1381 | "column" : 8, 1382 | "source_fragment" : "hdr.sr.routingList=hdr.sr.routingList>>4" 1383 | } 1384 | }, 1385 | { 1386 | "op" : "assign", 1387 | "parameters" : [ 1388 | { 1389 | "type" : "field", 1390 | "value" : ["standard_metadata", "egress_spec"] 1391 | }, 1392 | { 1393 | "type" : "expression", 1394 | "value" : { 1395 | "type" : "expression", 1396 | "value" : { 1397 | "op" : "&", 1398 | "left" : { 1399 | "type" : "expression", 1400 | "value" : { 1401 | "op" : "+", 1402 | "left" : { 1403 | "type" : "expression", 1404 | "value" : { 1405 | "op" : "&", 1406 | "left" : { 1407 | "type" : "field", 1408 | "value" : ["scalars", "port_3"] 1409 | }, 1410 | "right" : { 1411 | "type" : "hexstr", 1412 | "value" : "0x01ff" 1413 | } 1414 | } 1415 | }, 1416 | "right" : { 1417 | "type" : "hexstr", 1418 | "value" : "0x0001" 1419 | } 1420 | } 1421 | }, 1422 | "right" : { 1423 | "type" : "hexstr", 1424 | "value" : "0x01ff" 1425 | } 1426 | } 1427 | } 1428 | } 1429 | ], 1430 | "source_info" : { 1431 | "filename" : "app.p4", 1432 | "line" : 102, 1433 | "column" : 8, 1434 | "source_fragment" : "standard_metadata.egress_spec = (bit<9>)port+9w1" 1435 | } 1436 | }, 1437 | { 1438 | "op" : "assign", 1439 | "parameters" : [ 1440 | { 1441 | "type" : "field", 1442 | "value" : ["standard_metadata", "egress_port"] 1443 | }, 1444 | { 1445 | "type" : "expression", 1446 | "value" : { 1447 | "type" : "expression", 1448 | "value" : { 1449 | "op" : "&", 1450 | "left" : { 1451 | "type" : "expression", 1452 | "value" : { 1453 | "op" : "+", 1454 | "left" : { 1455 | "type" : "expression", 1456 | "value" : { 1457 | "op" : "&", 1458 | "left" : { 1459 | "type" : "field", 1460 | "value" : ["scalars", "port_3"] 1461 | }, 1462 | "right" : { 1463 | "type" : "hexstr", 1464 | "value" : "0x01ff" 1465 | } 1466 | } 1467 | }, 1468 | "right" : { 1469 | "type" : "hexstr", 1470 | "value" : "0x0001" 1471 | } 1472 | } 1473 | }, 1474 | "right" : { 1475 | "type" : "hexstr", 1476 | "value" : "0x01ff" 1477 | } 1478 | } 1479 | } 1480 | } 1481 | ], 1482 | "source_info" : { 1483 | "filename" : "app.p4", 1484 | "line" : 103, 1485 | "column" : 8, 1486 | "source_fragment" : "standard_metadata.egress_port = (bit<9>)port+9w1" 1487 | } 1488 | } 1489 | ] 1490 | }, 1491 | { 1492 | "name" : "arpreply", 1493 | "id" : 5, 1494 | "runtime_data" : [ 1495 | { 1496 | "name" : "repmac", 1497 | "bitwidth" : 48 1498 | } 1499 | ], 1500 | "primitives" : [ 1501 | { 1502 | "op" : "assign", 1503 | "parameters" : [ 1504 | { 1505 | "type" : "field", 1506 | "value" : ["standard_metadata", "egress_spec"] 1507 | }, 1508 | { 1509 | "type" : "field", 1510 | "value" : ["standard_metadata", "ingress_port"] 1511 | } 1512 | ], 1513 | "source_info" : { 1514 | "filename" : "app.p4", 1515 | "line" : 85, 1516 | "column" : 8, 1517 | "source_fragment" : "standard_metadata.egress_spec = standard_metadata.ingress_port" 1518 | } 1519 | }, 1520 | { 1521 | "op" : "assign", 1522 | "parameters" : [ 1523 | { 1524 | "type" : "field", 1525 | "value" : ["standard_metadata", "egress_port"] 1526 | }, 1527 | { 1528 | "type" : "field", 1529 | "value" : ["standard_metadata", "ingress_port"] 1530 | } 1531 | ], 1532 | "source_info" : { 1533 | "filename" : "app.p4", 1534 | "line" : 86, 1535 | "column" : 8, 1536 | "source_fragment" : "standard_metadata.egress_port = standard_metadata.ingress_port" 1537 | } 1538 | }, 1539 | { 1540 | "op" : "assign", 1541 | "parameters" : [ 1542 | { 1543 | "type" : "field", 1544 | "value" : ["ethernet", "srcAddr"] 1545 | }, 1546 | { 1547 | "type" : "runtime_data", 1548 | "value" : 0 1549 | } 1550 | ], 1551 | "source_info" : { 1552 | "filename" : "app.p4", 1553 | "line" : 87, 1554 | "column" : 8, 1555 | "source_fragment" : "hdr.ethernet.srcAddr=repmac" 1556 | } 1557 | }, 1558 | { 1559 | "op" : "assign", 1560 | "parameters" : [ 1561 | { 1562 | "type" : "field", 1563 | "value" : ["ethernet", "dstAddr"] 1564 | }, 1565 | { 1566 | "type" : "field", 1567 | "value" : ["arp", "arpSha"] 1568 | } 1569 | ], 1570 | "source_info" : { 1571 | "filename" : "app.p4", 1572 | "line" : 88, 1573 | "column" : 8, 1574 | "source_fragment" : "hdr.ethernet.dstAddr=hdr.arp.arpSha" 1575 | } 1576 | }, 1577 | { 1578 | "op" : "assign", 1579 | "parameters" : [ 1580 | { 1581 | "type" : "field", 1582 | "value" : ["scalars", "tempip"] 1583 | }, 1584 | { 1585 | "type" : "field", 1586 | "value" : ["arp", "arpSpa"] 1587 | } 1588 | ], 1589 | "source_info" : { 1590 | "filename" : "app.p4", 1591 | "line" : 90, 1592 | "column" : 8, 1593 | "source_fragment" : "tempip=hdr.arp.arpSpa" 1594 | } 1595 | }, 1596 | { 1597 | "op" : "assign", 1598 | "parameters" : [ 1599 | { 1600 | "type" : "field", 1601 | "value" : ["arp", "arpSpa"] 1602 | }, 1603 | { 1604 | "type" : "field", 1605 | "value" : ["arp", "arpTpa"] 1606 | } 1607 | ], 1608 | "source_info" : { 1609 | "filename" : "app.p4", 1610 | "line" : 91, 1611 | "column" : 8, 1612 | "source_fragment" : "hdr.arp.arpSpa=hdr.arp.arpTpa" 1613 | } 1614 | }, 1615 | { 1616 | "op" : "assign", 1617 | "parameters" : [ 1618 | { 1619 | "type" : "field", 1620 | "value" : ["arp", "arpTpa"] 1621 | }, 1622 | { 1623 | "type" : "field", 1624 | "value" : ["scalars", "tempip"] 1625 | } 1626 | ], 1627 | "source_info" : { 1628 | "filename" : "app.p4", 1629 | "line" : 92, 1630 | "column" : 8, 1631 | "source_fragment" : "hdr.arp.arpTpa=tempip" 1632 | } 1633 | }, 1634 | { 1635 | "op" : "assign", 1636 | "parameters" : [ 1637 | { 1638 | "type" : "field", 1639 | "value" : ["arp", "arpTha"] 1640 | }, 1641 | { 1642 | "type" : "field", 1643 | "value" : ["arp", "arpSha"] 1644 | } 1645 | ], 1646 | "source_info" : { 1647 | "filename" : "app.p4", 1648 | "line" : 93, 1649 | "column" : 8, 1650 | "source_fragment" : "hdr.arp.arpTha=hdr.arp.arpSha" 1651 | } 1652 | }, 1653 | { 1654 | "op" : "assign", 1655 | "parameters" : [ 1656 | { 1657 | "type" : "field", 1658 | "value" : ["arp", "arpSha"] 1659 | }, 1660 | { 1661 | "type" : "runtime_data", 1662 | "value" : 0 1663 | } 1664 | ], 1665 | "source_info" : { 1666 | "filename" : "app.p4", 1667 | "line" : 94, 1668 | "column" : 8, 1669 | "source_fragment" : "hdr.arp.arpSha=repmac" 1670 | } 1671 | } 1672 | ] 1673 | }, 1674 | { 1675 | "name" : "l2setmetadata", 1676 | "id" : 6, 1677 | "runtime_data" : [ 1678 | { 1679 | "name" : "port", 1680 | "bitwidth" : 9 1681 | } 1682 | ], 1683 | "primitives" : [ 1684 | { 1685 | "op" : "assign", 1686 | "parameters" : [ 1687 | { 1688 | "type" : "field", 1689 | "value" : ["standard_metadata", "egress_spec"] 1690 | }, 1691 | { 1692 | "type" : "runtime_data", 1693 | "value" : 0 1694 | } 1695 | ], 1696 | "source_info" : { 1697 | "filename" : "app.p4", 1698 | "line" : 63, 1699 | "column" : 8, 1700 | "source_fragment" : "standard_metadata.egress_spec = port" 1701 | } 1702 | }, 1703 | { 1704 | "op" : "assign", 1705 | "parameters" : [ 1706 | { 1707 | "type" : "field", 1708 | "value" : ["standard_metadata", "egress_port"] 1709 | }, 1710 | { 1711 | "type" : "runtime_data", 1712 | "value" : 0 1713 | } 1714 | ], 1715 | "source_info" : { 1716 | "filename" : "app.p4", 1717 | "line" : 64, 1718 | "column" : 8, 1719 | "source_fragment" : "standard_metadata.egress_port = port" 1720 | } 1721 | } 1722 | ] 1723 | }, 1724 | { 1725 | "name" : "NoAction", 1726 | "id" : 7, 1727 | "runtime_data" : [], 1728 | "primitives" : [] 1729 | }, 1730 | { 1731 | "name" : "NoAction", 1732 | "id" : 8, 1733 | "runtime_data" : [], 1734 | "primitives" : [] 1735 | }, 1736 | { 1737 | "name" : "NoAction", 1738 | "id" : 9, 1739 | "runtime_data" : [], 1740 | "primitives" : [] 1741 | }, 1742 | { 1743 | "name" : "NoAction", 1744 | "id" : 10, 1745 | "runtime_data" : [], 1746 | "primitives" : [] 1747 | }, 1748 | { 1749 | "name" : "do_int", 1750 | "id" : 11, 1751 | "runtime_data" : [], 1752 | "primitives" : [ 1753 | { 1754 | "op" : "assign", 1755 | "parameters" : [ 1756 | { 1757 | "type" : "field", 1758 | "value" : ["udp", "len"] 1759 | }, 1760 | { 1761 | "type" : "expression", 1762 | "value" : { 1763 | "type" : "expression", 1764 | "value" : { 1765 | "op" : "&", 1766 | "left" : { 1767 | "type" : "expression", 1768 | "value" : { 1769 | "op" : "+", 1770 | "left" : { 1771 | "type" : "field", 1772 | "value" : ["udp", "len"] 1773 | }, 1774 | "right" : { 1775 | "type" : "hexstr", 1776 | "value" : "0x0016" 1777 | } 1778 | } 1779 | }, 1780 | "right" : { 1781 | "type" : "hexstr", 1782 | "value" : "0xffff" 1783 | } 1784 | } 1785 | } 1786 | } 1787 | ], 1788 | "source_info" : { 1789 | "filename" : "app.p4", 1790 | "line" : 20, 1791 | "column" : 8, 1792 | "source_fragment" : "hdr.udp.len = hdr.udp.len+16w22" 1793 | } 1794 | }, 1795 | { 1796 | "op" : "assign", 1797 | "parameters" : [ 1798 | { 1799 | "type" : "field", 1800 | "value" : ["udp", "hdrChecksum"] 1801 | }, 1802 | { 1803 | "type" : "hexstr", 1804 | "value" : "0x0000" 1805 | } 1806 | ], 1807 | "source_info" : { 1808 | "filename" : "app.p4", 1809 | "line" : 21, 1810 | "column" : 8, 1811 | "source_fragment" : "hdr.udp.hdrChecksum = 16w0" 1812 | } 1813 | }, 1814 | { 1815 | "op" : "assign", 1816 | "parameters" : [ 1817 | { 1818 | "type" : "field", 1819 | "value" : ["ipv4", "totalLen"] 1820 | }, 1821 | { 1822 | "type" : "expression", 1823 | "value" : { 1824 | "type" : "expression", 1825 | "value" : { 1826 | "op" : "&", 1827 | "left" : { 1828 | "type" : "expression", 1829 | "value" : { 1830 | "op" : "+", 1831 | "left" : { 1832 | "type" : "field", 1833 | "value" : ["ipv4", "totalLen"] 1834 | }, 1835 | "right" : { 1836 | "type" : "hexstr", 1837 | "value" : "0x0016" 1838 | } 1839 | } 1840 | }, 1841 | "right" : { 1842 | "type" : "hexstr", 1843 | "value" : "0xffff" 1844 | } 1845 | } 1846 | } 1847 | } 1848 | ], 1849 | "source_info" : { 1850 | "filename" : "app.p4", 1851 | "line" : 22, 1852 | "column" : 8, 1853 | "source_fragment" : "hdr.ipv4.totalLen=hdr.ipv4.totalLen+16w22" 1854 | } 1855 | }, 1856 | { 1857 | "op" : "assign", 1858 | "parameters" : [ 1859 | { 1860 | "type" : "field", 1861 | "value" : ["ipv4", "hdrChecksum"] 1862 | }, 1863 | { 1864 | "type" : "expression", 1865 | "value" : { 1866 | "type" : "expression", 1867 | "value" : { 1868 | "op" : "&", 1869 | "left" : { 1870 | "type" : "expression", 1871 | "value" : { 1872 | "op" : "+", 1873 | "left" : { 1874 | "type" : "field", 1875 | "value" : ["ipv4", "hdrChecksum"] 1876 | }, 1877 | "right" : { 1878 | "type" : "hexstr", 1879 | "value" : "0xffea" 1880 | } 1881 | } 1882 | }, 1883 | "right" : { 1884 | "type" : "hexstr", 1885 | "value" : "0xffff" 1886 | } 1887 | } 1888 | } 1889 | } 1890 | ], 1891 | "source_info" : { 1892 | "filename" : "app.p4", 1893 | "line" : 23, 1894 | "column" : 8, 1895 | "source_fragment" : "hdr.ipv4.hdrChecksum=hdr.ipv4.hdrChecksum-16w22" 1896 | } 1897 | }, 1898 | { 1899 | "op" : "add_header", 1900 | "parameters" : [ 1901 | { 1902 | "type" : "header", 1903 | "value" : "inthdr" 1904 | } 1905 | ], 1906 | "source_info" : { 1907 | "filename" : "app.p4", 1908 | "line" : 24, 1909 | "column" : 8, 1910 | "source_fragment" : "hdr.inthdr.setValid()" 1911 | } 1912 | }, 1913 | { 1914 | "op" : "assign", 1915 | "parameters" : [ 1916 | { 1917 | "type" : "field", 1918 | "value" : ["inthdr", "device_no"] 1919 | }, 1920 | { 1921 | "type" : "field", 1922 | "value" : ["int_metadata", "device_no"] 1923 | } 1924 | ], 1925 | "source_info" : { 1926 | "filename" : "app.p4", 1927 | "line" : 25, 1928 | "column" : 8, 1929 | "source_fragment" : "hdr.inthdr.device_no = meta.int_metadata.device_no" 1930 | } 1931 | }, 1932 | { 1933 | "op" : "assign", 1934 | "parameters" : [ 1935 | { 1936 | "type" : "field", 1937 | "value" : ["inthdr", "ingress_port"] 1938 | }, 1939 | { 1940 | "type" : "field", 1941 | "value" : ["standard_metadata", "ingress_port"] 1942 | } 1943 | ], 1944 | "source_info" : { 1945 | "filename" : "app.p4", 1946 | "line" : 26, 1947 | "column" : 8, 1948 | "source_fragment" : "hdr.inthdr.ingress_port = standard_metadata.ingress_port" 1949 | } 1950 | }, 1951 | { 1952 | "op" : "assign", 1953 | "parameters" : [ 1954 | { 1955 | "type" : "field", 1956 | "value" : ["inthdr", "egress_port"] 1957 | }, 1958 | { 1959 | "type" : "field", 1960 | "value" : ["standard_metadata", "egress_port"] 1961 | } 1962 | ], 1963 | "source_info" : { 1964 | "filename" : "app.p4", 1965 | "line" : 27, 1966 | "column" : 8, 1967 | "source_fragment" : "hdr.inthdr.egress_port = standard_metadata.egress_port" 1968 | } 1969 | }, 1970 | { 1971 | "op" : "assign", 1972 | "parameters" : [ 1973 | { 1974 | "type" : "field", 1975 | "value" : ["inthdr", "ingress_global_timestamp"] 1976 | }, 1977 | { 1978 | "type" : "field", 1979 | "value" : ["standard_metadata", "ingress_global_timestamp"] 1980 | } 1981 | ], 1982 | "source_info" : { 1983 | "filename" : "app.p4", 1984 | "line" : 28, 1985 | "column" : 8, 1986 | "source_fragment" : "hdr.inthdr.ingress_global_timestamp = standard_metadata.ingress_global_timestamp" 1987 | } 1988 | }, 1989 | { 1990 | "op" : "assign", 1991 | "parameters" : [ 1992 | { 1993 | "type" : "field", 1994 | "value" : ["inthdr", "enq_timestamp"] 1995 | }, 1996 | { 1997 | "type" : "field", 1998 | "value" : ["standard_metadata", "enq_timestamp"] 1999 | } 2000 | ], 2001 | "source_info" : { 2002 | "filename" : "app.p4", 2003 | "line" : 29, 2004 | "column" : 8, 2005 | "source_fragment" : "hdr.inthdr.enq_timestamp = standard_metadata.enq_timestamp" 2006 | } 2007 | }, 2008 | { 2009 | "op" : "assign", 2010 | "parameters" : [ 2011 | { 2012 | "type" : "field", 2013 | "value" : ["inthdr", "enq_qdepth"] 2014 | }, 2015 | { 2016 | "type" : "field", 2017 | "value" : ["standard_metadata", "enq_qdepth"] 2018 | } 2019 | ], 2020 | "source_info" : { 2021 | "filename" : "app.p4", 2022 | "line" : 30, 2023 | "column" : 8, 2024 | "source_fragment" : "hdr.inthdr.enq_qdepth = standard_metadata.enq_qdepth" 2025 | } 2026 | }, 2027 | { 2028 | "op" : "assign", 2029 | "parameters" : [ 2030 | { 2031 | "type" : "field", 2032 | "value" : ["inthdr", "deq_timedelta"] 2033 | }, 2034 | { 2035 | "type" : "field", 2036 | "value" : ["standard_metadata", "deq_timedelta"] 2037 | } 2038 | ], 2039 | "source_info" : { 2040 | "filename" : "app.p4", 2041 | "line" : 31, 2042 | "column" : 8, 2043 | "source_fragment" : "hdr.inthdr.deq_timedelta = standard_metadata.deq_timedelta" 2044 | } 2045 | }, 2046 | { 2047 | "op" : "assign", 2048 | "parameters" : [ 2049 | { 2050 | "type" : "field", 2051 | "value" : ["inthdr", "deq_qdepth"] 2052 | }, 2053 | { 2054 | "type" : "field", 2055 | "value" : ["standard_metadata", "deq_qdepth"] 2056 | } 2057 | ], 2058 | "source_info" : { 2059 | "filename" : "app.p4", 2060 | "line" : 32, 2061 | "column" : 8, 2062 | "source_fragment" : "hdr.inthdr.deq_qdepth = standard_metadata.deq_qdepth" 2063 | } 2064 | } 2065 | ] 2066 | } 2067 | ], 2068 | "pipelines" : [ 2069 | { 2070 | "name" : "ingress", 2071 | "id" : 0, 2072 | "source_info" : { 2073 | "filename" : "app.p4", 2074 | "line" : 52, 2075 | "column" : 8, 2076 | "source_fragment" : "ingress" 2077 | }, 2078 | "init_table" : "setmetadata", 2079 | "tables" : [ 2080 | { 2081 | "name" : "setmetadata", 2082 | "id" : 0, 2083 | "source_info" : { 2084 | "filename" : "app.p4", 2085 | "line" : 158, 2086 | "column" : 10, 2087 | "source_fragment" : "setmetadata" 2088 | }, 2089 | "key" : [], 2090 | "match_type" : "exact", 2091 | "type" : "simple", 2092 | "max_size" : 512, 2093 | "with_counters" : false, 2094 | "support_timeout" : false, 2095 | "direct_meters" : null, 2096 | "action_ids" : [3, 7], 2097 | "actions" : ["setdeviceno", "NoAction"], 2098 | "base_default_next" : "node_3", 2099 | "next_tables" : { 2100 | "setdeviceno" : "node_3", 2101 | "NoAction" : "node_3" 2102 | }, 2103 | "default_entry" : { 2104 | "action_id" : 7, 2105 | "action_const" : false, 2106 | "action_data" : [], 2107 | "action_entry_const" : false 2108 | } 2109 | }, 2110 | { 2111 | "name" : "dosr", 2112 | "id" : 1, 2113 | "source_info" : { 2114 | "filename" : "app.p4", 2115 | "line" : 149, 2116 | "column" : 10, 2117 | "source_fragment" : "dosr" 2118 | }, 2119 | "key" : [], 2120 | "match_type" : "exact", 2121 | "type" : "simple", 2122 | "max_size" : 512, 2123 | "with_counters" : false, 2124 | "support_timeout" : false, 2125 | "direct_meters" : null, 2126 | "action_ids" : [4], 2127 | "actions" : ["srrouting"], 2128 | "base_default_next" : "tbl_act_0", 2129 | "next_tables" : { 2130 | "srrouting" : "tbl_act_0" 2131 | }, 2132 | "default_entry" : { 2133 | "action_id" : 4, 2134 | "action_const" : false, 2135 | "action_data" : [], 2136 | "action_entry_const" : false 2137 | } 2138 | }, 2139 | { 2140 | "name" : "dotrans", 2141 | "id" : 2, 2142 | "source_info" : { 2143 | "filename" : "app.p4", 2144 | "line" : 111, 2145 | "column" : 10, 2146 | "source_fragment" : "dotrans" 2147 | }, 2148 | "key" : [ 2149 | { 2150 | "match_type" : "exact", 2151 | "target" : ["ethernet", "srcAddr"], 2152 | "mask" : null 2153 | }, 2154 | { 2155 | "match_type" : "exact", 2156 | "target" : ["ethernet", "dstAddr"], 2157 | "mask" : null 2158 | } 2159 | ], 2160 | "match_type" : "exact", 2161 | "type" : "simple", 2162 | "max_size" : 512, 2163 | "with_counters" : false, 2164 | "support_timeout" : false, 2165 | "direct_meters" : null, 2166 | "action_ids" : [2, 10], 2167 | "actions" : ["l2setmetadataecmp", "NoAction"], 2168 | "base_default_next" : "dosocket", 2169 | "next_tables" : { 2170 | "l2setmetadataecmp" : "dosocket", 2171 | "NoAction" : "dosocket" 2172 | }, 2173 | "default_entry" : { 2174 | "action_id" : 10, 2175 | "action_const" : false, 2176 | "action_data" : [], 2177 | "action_entry_const" : false 2178 | } 2179 | }, 2180 | { 2181 | "name" : "dosocket", 2182 | "id" : 3, 2183 | "source_info" : { 2184 | "filename" : "app.p4", 2185 | "line" : 124, 2186 | "column" : 10, 2187 | "source_fragment" : "dosocket" 2188 | }, 2189 | "key" : [ 2190 | { 2191 | "match_type" : "exact", 2192 | "target" : ["udp", "dstPort"], 2193 | "mask" : null 2194 | } 2195 | ], 2196 | "match_type" : "exact", 2197 | "type" : "simple", 2198 | "max_size" : 512, 2199 | "with_counters" : false, 2200 | "support_timeout" : false, 2201 | "direct_meters" : null, 2202 | "action_ids" : [6, 9], 2203 | "actions" : ["l2setmetadata", "NoAction"], 2204 | "base_default_next" : "tbl_act_0", 2205 | "next_tables" : { 2206 | "l2setmetadata" : "tbl_act_0", 2207 | "NoAction" : "tbl_act_0" 2208 | }, 2209 | "default_entry" : { 2210 | "action_id" : 9, 2211 | "action_const" : false, 2212 | "action_data" : [], 2213 | "action_entry_const" : false 2214 | } 2215 | }, 2216 | { 2217 | "name" : "doarp", 2218 | "id" : 4, 2219 | "source_info" : { 2220 | "filename" : "app.p4", 2221 | "line" : 136, 2222 | "column" : 10, 2223 | "source_fragment" : "doarp" 2224 | }, 2225 | "key" : [ 2226 | { 2227 | "match_type" : "exact", 2228 | "target" : ["arp", "arpTha"], 2229 | "mask" : null 2230 | }, 2231 | { 2232 | "match_type" : "exact", 2233 | "target" : ["arp", "arpTpa"], 2234 | "mask" : null 2235 | } 2236 | ], 2237 | "match_type" : "exact", 2238 | "type" : "simple", 2239 | "max_size" : 512, 2240 | "with_counters" : false, 2241 | "support_timeout" : false, 2242 | "direct_meters" : null, 2243 | "action_ids" : [5, 8], 2244 | "actions" : ["arpreply", "NoAction"], 2245 | "base_default_next" : "tbl_act_0", 2246 | "next_tables" : { 2247 | "arpreply" : "tbl_act_0", 2248 | "NoAction" : "tbl_act_0" 2249 | }, 2250 | "default_entry" : { 2251 | "action_id" : 8, 2252 | "action_const" : false, 2253 | "action_data" : [], 2254 | "action_entry_const" : false 2255 | } 2256 | }, 2257 | { 2258 | "name" : "tbl_act_0", 2259 | "id" : 5, 2260 | "key" : [], 2261 | "match_type" : "exact", 2262 | "type" : "simple", 2263 | "max_size" : 1024, 2264 | "with_counters" : false, 2265 | "support_timeout" : false, 2266 | "direct_meters" : null, 2267 | "action_ids" : [1], 2268 | "actions" : ["act_0"], 2269 | "base_default_next" : null, 2270 | "next_tables" : { 2271 | "act_0" : null 2272 | }, 2273 | "default_entry" : { 2274 | "action_id" : 1, 2275 | "action_const" : false, 2276 | "action_data" : [], 2277 | "action_entry_const" : true 2278 | } 2279 | } 2280 | ], 2281 | "action_profiles" : [], 2282 | "conditionals" : [ 2283 | { 2284 | "name" : "node_3", 2285 | "id" : 0, 2286 | "source_info" : { 2287 | "filename" : "app.p4", 2288 | "line" : 170, 2289 | "column" : 12, 2290 | "source_fragment" : "hdr.ipv4.isValid()" 2291 | }, 2292 | "expression" : { 2293 | "type" : "expression", 2294 | "value" : { 2295 | "op" : "valid", 2296 | "left" : null, 2297 | "right" : { 2298 | "type" : "header", 2299 | "value" : "ipv4" 2300 | } 2301 | } 2302 | }, 2303 | "true_next" : "node_4", 2304 | "false_next" : "node_8" 2305 | }, 2306 | { 2307 | "name" : "node_4", 2308 | "id" : 1, 2309 | "source_info" : { 2310 | "filename" : "app.p4", 2311 | "line" : 171, 2312 | "column" : 15, 2313 | "source_fragment" : "hdr.sr.isValid()" 2314 | }, 2315 | "expression" : { 2316 | "type" : "expression", 2317 | "value" : { 2318 | "op" : "valid", 2319 | "left" : null, 2320 | "right" : { 2321 | "type" : "header", 2322 | "value" : "sr" 2323 | } 2324 | } 2325 | }, 2326 | "true_next" : "dosr", 2327 | "false_next" : "dotrans" 2328 | }, 2329 | { 2330 | "name" : "node_8", 2331 | "id" : 2, 2332 | "source_info" : { 2333 | "filename" : "app.p4", 2334 | "line" : 177, 2335 | "column" : 18, 2336 | "source_fragment" : "hdr.arp.isValid()" 2337 | }, 2338 | "expression" : { 2339 | "type" : "expression", 2340 | "value" : { 2341 | "op" : "valid", 2342 | "left" : null, 2343 | "right" : { 2344 | "type" : "header", 2345 | "value" : "arp" 2346 | } 2347 | } 2348 | }, 2349 | "true_next" : "doarp", 2350 | "false_next" : "tbl_act_0" 2351 | } 2352 | ] 2353 | }, 2354 | { 2355 | "name" : "egress", 2356 | "id" : 1, 2357 | "source_info" : { 2358 | "filename" : "app.p4", 2359 | "line" : 9, 2360 | "column" : 8, 2361 | "source_fragment" : "egress" 2362 | }, 2363 | "init_table" : "tbl_act", 2364 | "tables" : [ 2365 | { 2366 | "name" : "tbl_act", 2367 | "id" : 6, 2368 | "key" : [], 2369 | "match_type" : "exact", 2370 | "type" : "simple", 2371 | "max_size" : 1024, 2372 | "with_counters" : false, 2373 | "support_timeout" : false, 2374 | "direct_meters" : null, 2375 | "action_ids" : [0], 2376 | "actions" : ["act"], 2377 | "base_default_next" : "node_14", 2378 | "next_tables" : { 2379 | "act" : "node_14" 2380 | }, 2381 | "default_entry" : { 2382 | "action_id" : 0, 2383 | "action_const" : false, 2384 | "action_data" : [], 2385 | "action_entry_const" : true 2386 | } 2387 | }, 2388 | { 2389 | "name" : "udp_int", 2390 | "id" : 7, 2391 | "source_info" : { 2392 | "filename" : "app.p4", 2393 | "line" : 35, 2394 | "column" : 10, 2395 | "source_fragment" : "udp_int" 2396 | }, 2397 | "key" : [], 2398 | "match_type" : "exact", 2399 | "type" : "simple", 2400 | "max_size" : 1024, 2401 | "with_counters" : false, 2402 | "support_timeout" : false, 2403 | "direct_meters" : null, 2404 | "action_ids" : [11], 2405 | "actions" : ["do_int"], 2406 | "base_default_next" : null, 2407 | "next_tables" : { 2408 | "do_int" : null 2409 | }, 2410 | "default_entry" : { 2411 | "action_id" : 11, 2412 | "action_const" : false, 2413 | "action_data" : [], 2414 | "action_entry_const" : false 2415 | } 2416 | } 2417 | ], 2418 | "action_profiles" : [], 2419 | "conditionals" : [ 2420 | { 2421 | "name" : "node_14", 2422 | "id" : 3, 2423 | "source_info" : { 2424 | "filename" : "app.p4", 2425 | "line" : 46, 2426 | "column" : 12, 2427 | "source_fragment" : "hdr.sr.isValid()" 2428 | }, 2429 | "expression" : { 2430 | "type" : "expression", 2431 | "value" : { 2432 | "op" : "valid", 2433 | "left" : null, 2434 | "right" : { 2435 | "type" : "header", 2436 | "value" : "sr" 2437 | } 2438 | } 2439 | }, 2440 | "false_next" : null, 2441 | "true_next" : "udp_int" 2442 | } 2443 | ] 2444 | } 2445 | ], 2446 | "checksums" : [], 2447 | "force_arith" : [], 2448 | "extern_instances" : [], 2449 | "field_aliases" : [ 2450 | [ 2451 | "queueing_metadata.enq_timestamp", 2452 | ["standard_metadata", "enq_timestamp"] 2453 | ], 2454 | [ 2455 | "queueing_metadata.enq_qdepth", 2456 | ["standard_metadata", "enq_qdepth"] 2457 | ], 2458 | [ 2459 | "queueing_metadata.deq_timedelta", 2460 | ["standard_metadata", "deq_timedelta"] 2461 | ], 2462 | [ 2463 | "queueing_metadata.deq_qdepth", 2464 | ["standard_metadata", "deq_qdepth"] 2465 | ], 2466 | [ 2467 | "intrinsic_metadata.ingress_global_timestamp", 2468 | ["standard_metadata", "ingress_global_timestamp"] 2469 | ], 2470 | [ 2471 | "intrinsic_metadata.lf_field_list", 2472 | ["standard_metadata", "lf_field_list"] 2473 | ], 2474 | [ 2475 | "intrinsic_metadata.mcast_grp", 2476 | ["standard_metadata", "mcast_grp"] 2477 | ], 2478 | [ 2479 | "intrinsic_metadata.resubmit_flag", 2480 | ["standard_metadata", "resubmit_flag"] 2481 | ], 2482 | [ 2483 | "intrinsic_metadata.egress_rid", 2484 | ["standard_metadata", "egress_rid"] 2485 | ] 2486 | ] 2487 | } -------------------------------------------------------------------------------- /system/p4app/app.p4: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "header.p4" 5 | #include "parser.p4" 6 | 7 | #define COUNTER_SIZE 32w16 8 | 9 | control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { 10 | 11 | counter(COUNTER_SIZE,CounterType.packets) egress_counter; 12 | 13 | @name("_drop") 14 | action _drop() { 15 | mark_to_drop(); 16 | } 17 | @name("do_int") 18 | action do_int() { 19 | hdr.udp.len = hdr.udp.len+16w22; 20 | hdr.udp.hdrChecksum = 16w0; 21 | hdr.ipv4.totalLen=hdr.ipv4.totalLen+16w22; 22 | hdr.ipv4.hdrChecksum=hdr.ipv4.hdrChecksum-16w22; 23 | hdr.inthdr.setValid(); 24 | hdr.inthdr.device_no = meta.int_metadata.device_no; 25 | hdr.inthdr.ingress_port = standard_metadata.ingress_port; 26 | hdr.inthdr.egress_port = standard_metadata.egress_port; 27 | hdr.inthdr.ingress_global_timestamp = standard_metadata.ingress_global_timestamp; 28 | hdr.inthdr.enq_timestamp = standard_metadata.enq_timestamp; 29 | hdr.inthdr.enq_qdepth = standard_metadata.enq_qdepth; 30 | hdr.inthdr.deq_timedelta = standard_metadata.deq_timedelta; 31 | hdr.inthdr.deq_qdepth = standard_metadata.deq_qdepth; 32 | } 33 | @name("udp_int") 34 | table udp_int { 35 | actions = { 36 | do_int; 37 | } 38 | key = {} 39 | size = 1024; 40 | default_action = do_int(); 41 | } 42 | apply { 43 | egress_counter.count((bit<32>)standard_metadata.egress_port); 44 | if (hdr.sr.isValid()) { 45 | udp_int.apply(); 46 | } 47 | } 48 | } 49 | 50 | control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { 51 | 52 | counter(COUNTER_SIZE,CounterType.packets) ingress_counter; 53 | 54 | @name("_drop") 55 | action _drop() { 56 | mark_to_drop(); 57 | } 58 | @name("l2setmetadata") 59 | action l2setmetadata(bit<9> port) { 60 | standard_metadata.egress_spec = port; 61 | standard_metadata.egress_port = port; 62 | } 63 | @name("l2setmetadataecmp") 64 | action l2setmetadataecmp(bit<2> routeNum, bit<16> portData) { 65 | bit<32> result=32w0; 66 | random(result,32w0,(bit<32>)(routeNum-2w1)); 67 | bit<16> data=portData; 68 | if (result == 32w1) { 69 | data=portData>>4; 70 | }else if(result == 32w2){ 71 | data=portData>>8; 72 | }else if(result==32w3){ 73 | data=portData>>4; 74 | data=portData>>8; 75 | } 76 | bit<9> port=(bit<9>)((bit<4>)data); 77 | standard_metadata.egress_spec = port; 78 | standard_metadata.egress_port = port; 79 | } 80 | @name("arpreply") 81 | action arpreply(bit<48>repmac) { 82 | standard_metadata.egress_spec = standard_metadata.ingress_port; 83 | standard_metadata.egress_port = standard_metadata.ingress_port; 84 | hdr.ethernet.srcAddr=repmac; 85 | hdr.ethernet.dstAddr=hdr.arp.arpSha; 86 | bit<32> tempip; 87 | tempip=hdr.arp.arpSpa; 88 | hdr.arp.arpSpa=hdr.arp.arpTpa; 89 | hdr.arp.arpTpa=tempip; 90 | hdr.arp.arpTha=hdr.arp.arpSha; 91 | hdr.arp.arpSha=repmac; 92 | } 93 | @name("srrouting") 94 | action srrouting() { 95 | // read 4 bit from routingList use listPosition 96 | // and set it to egress metadata 97 | bit<4> port=(bit<4>)hdr.sr.routingList; 98 | hdr.sr.routingList=hdr.sr.routingList>>4; 99 | standard_metadata.egress_spec = (bit<9>)port+9w1; 100 | standard_metadata.egress_port = (bit<9>)port+9w1; 101 | } 102 | @name("setdeviceno") 103 | action setdeviceno(bit<8> device_no) { 104 | meta.int_metadata.device_no=device_no; 105 | } 106 | 107 | @name("dotrans") 108 | table dotrans { 109 | actions = { 110 | l2setmetadataecmp; 111 | NoAction; 112 | } 113 | key = { 114 | hdr.ethernet.srcAddr:exact; 115 | hdr.ethernet.dstAddr:exact; 116 | } 117 | size=512; 118 | default_action=NoAction(); 119 | } 120 | @name("dosocket") 121 | table dosocket { 122 | actions = { 123 | l2setmetadata; 124 | NoAction; 125 | } 126 | key = { 127 | hdr.udp.dstPort:exact; 128 | } 129 | size=512; 130 | default_action=NoAction(); 131 | } 132 | @name("doarp") 133 | table doarp { 134 | actions = { 135 | arpreply; 136 | NoAction; 137 | } 138 | key = { 139 | hdr.arp.arpTha:exact; 140 | hdr.arp.arpTpa:exact; 141 | } 142 | size=512; 143 | default_action=NoAction(); 144 | } 145 | @name("dosr") 146 | table dosr { 147 | actions = { 148 | srrouting; 149 | } 150 | key={} 151 | size=512; 152 | default_action=srrouting(); 153 | } 154 | @name("setmetadata") 155 | table setmetadata { 156 | actions = { 157 | setdeviceno; 158 | NoAction; 159 | } 160 | key={} 161 | size=512; 162 | default_action=NoAction(); 163 | } 164 | apply { 165 | setmetadata.apply(); 166 | if (hdr.ipv4.isValid()) { 167 | if(hdr.sr.isValid()) { 168 | dosr.apply(); 169 | }else{ 170 | dotrans.apply(); 171 | dosocket.apply(); 172 | } 173 | } else if(hdr.arp.isValid()) { 174 | doarp.apply(); 175 | } 176 | ingress_counter.count((bit<32>)standard_metadata.ingress_port); 177 | } 178 | } 179 | 180 | V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main; -------------------------------------------------------------------------------- /system/p4app/header.p4: -------------------------------------------------------------------------------- 1 | #ifndef __HEADER_H__ 2 | #define __HEADER_H__ 1 3 | 4 | struct ingress_metadata_t { 5 | bit<32> nhop_ipv4; 6 | } 7 | 8 | @metadata 9 | struct intrinsic_metadata_t { 10 | bit<48> ingress_global_timestamp; 11 | bit<32> lf_field_list; 12 | bit<16> mcast_grp; 13 | bit<16> egress_rid; 14 | bit<8> resubmit_flag; 15 | bit<8> recirculate_flag; 16 | } 17 | 18 | @metadata @name("queueing_metadata") 19 | struct queueing_metadata_t { 20 | bit<48> enq_timestamp; 21 | bit<16> enq_qdepth; 22 | bit<32> deq_timedelta; 23 | bit<16> deq_qdepth; 24 | } 25 | 26 | @metadata @name("int_metadata") 27 | struct int_metadata_t { 28 | bit<8> device_no; 29 | } 30 | 31 | header ethernet_t { 32 | bit<48> dstAddr; 33 | bit<48> srcAddr; 34 | bit<16> etherType; 35 | } 36 | 37 | header arp_t { 38 | bit<16> arpHdr; /* format of hardware address */ 39 | bit<16> arpPro; /* format of protocol address */ 40 | bit<8> arpHln; /* length of hardware address */ 41 | bit<8> arpPln; /* length of protocol address */ 42 | bit<16> arpOp; /* ARP/RARP operation */ 43 | bit<48> arpSha; /* sender hardware address */ 44 | bit<32> arpSpa; /* sender protocol address */ 45 | bit<48> arpTha; /* target hardware address */ 46 | bit<32> arpTpa; /* target protocol address */ 47 | } 48 | 49 | header ipv4_t { 50 | bit<4> version; 51 | bit<4> ihl; 52 | bit<8> diffserv; 53 | bit<16> totalLen; 54 | bit<16> identification; 55 | bit<3> flags; 56 | bit<13> fragOffset; 57 | bit<8> ttl; 58 | bit<8> protocol; //udp 17, tcp 6 59 | bit<16> hdrChecksum; 60 | bit<32> srcAddr; 61 | bit<32> dstAddr; 62 | } 63 | 64 | header udp_t { 65 | bit<16> srcPort; 66 | bit<16> dstPort; 67 | bit<16> len; 68 | bit<16> hdrChecksum; 69 | } 70 | 71 | header sr_t { //source routing header 72 | bit<512> routingList; 73 | } 74 | 75 | header inthdr_t { 76 | bit<8> device_no; 77 | bit<9> ingress_port; 78 | bit<9> egress_port; 79 | bit<48> ingress_global_timestamp; 80 | bit<32> enq_timestamp; 81 | bit<19> enq_qdepth; 82 | bit<32> deq_timedelta; 83 | bit<19> deq_qdepth; 84 | } 85 | 86 | struct metadata { 87 | @name("ingress_metadata") 88 | ingress_metadata_t ingress_metadata; 89 | @name("intrinsic_metadata") 90 | intrinsic_metadata_t intrinsic_metadata; 91 | @name("queueing_metadata") 92 | queueing_metadata_t queueing_metadata; 93 | @name("int_metadata") 94 | int_metadata_t int_metadata; 95 | } 96 | 97 | struct headers { 98 | @name("ethernet") 99 | ethernet_t ethernet; 100 | @name("arp") 101 | arp_t arp; 102 | @name("ipv4") 103 | ipv4_t ipv4; 104 | @name("udp") 105 | udp_t udp; 106 | @name("sr") 107 | sr_t sr; 108 | @name("inthdr") 109 | inthdr_t inthdr; 110 | } 111 | 112 | #endif // __HEADER_H__ -------------------------------------------------------------------------------- /system/p4app/parser.p4: -------------------------------------------------------------------------------- 1 | parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { 2 | @name("start") 3 | state start { 4 | transition parse_ethernet; 5 | } 6 | @name("parse_ethernet") 7 | state parse_ethernet { 8 | packet.extract(hdr.ethernet); 9 | transition select(hdr.ethernet.etherType) { 10 | 16w0x0800: parse_ipv4; 11 | 16w0x0806: parse_arp; 12 | default: accept; 13 | } 14 | } 15 | @name("parse_arp") 16 | state parse_arp { 17 | packet.extract(hdr.arp); 18 | transition accept; 19 | } 20 | @name("parse_ipv4") 21 | state parse_ipv4 { 22 | packet.extract(hdr.ipv4); 23 | transition select(hdr.ipv4.protocol) { 24 | 8w0x11:parse_udp; 25 | default:accept; 26 | } 27 | } 28 | @name("parse_udp") 29 | state parse_udp { 30 | packet.extract(hdr.udp); 31 | transition select(hdr.udp.dstPort) { 32 | 16w0x8AE:parse_sr; //port 2222 33 | default:accept; 34 | } 35 | } 36 | @name("parse_sr") 37 | state parse_sr { 38 | packet.extract(hdr.sr); 39 | transition accept; 40 | } 41 | } 42 | 43 | control DeparserImpl(packet_out packet, in headers hdr) { 44 | apply { 45 | packet.emit(hdr.ethernet); 46 | packet.emit(hdr.arp); 47 | packet.emit(hdr.ipv4); 48 | packet.emit(hdr.udp); 49 | packet.emit(hdr.sr); 50 | packet.emit(hdr.inthdr); 51 | } 52 | } 53 | 54 | control verifyChecksum(in headers hdr, inout metadata meta) { 55 | } 56 | 57 | control computeChecksum(inout headers hdr, inout metadata meta) { 58 | } 59 | -------------------------------------------------------------------------------- /system/packet/int_data.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE IF NOT EXISTS `fnl_int` ( 3 | `id` int(11) NOT NULL AUTO_INCREMENT, 4 | `device_no` int(11) DEFAULT NULL, 5 | `ingress_port` int(11) DEFAULT NULL, 6 | `egress_port` int(11) DEFAULT NULL, 7 | `ingress_global_timestamp` bigint(20) DEFAULT NULL, 8 | `enq_timestamp` bigint(20) DEFAULT NULL, 9 | `enq_qdepth` int(11) DEFAULT NULL, 10 | `deq_timedelta` bigint(20) DEFAULT NULL, 11 | `deq_qdepth` int(11) DEFAULT NULL, 12 | `udp_port` int(11) DEFAULT NULL, 13 | `timestamp` int(11) DEFAULT NULL, 14 | `packet_id` text, 15 | `action_id` int(11) DEFAULT NULL, 16 | KEY `id` (`id`) 17 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; 18 | -------------------------------------------------------------------------------- /system/packet/receiveint.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | This is an INT packet parser, it can parse INT packet to integer or string and save it to database 16 | */ 17 | 18 | // byte 19 | #define headerSize 22 20 | #define fieldNum 8 21 | #define srHeaderSize 64 22 | #define idPayloadSize 4 23 | 24 | void doBinTrans(char message[], int startPos, int size, char binstr[]); 25 | void doBin2Str(char binarr[], char binstr[], int binLength); 26 | void doPayloadBin2Str(char binarr[], char binstr[], int binLength); 27 | long doBin2Int(char binarr[], int startPos, int endPos); 28 | void doFormatStr(char binstr[], char formattedStrArr[][fieldNum * fieldNum]); 29 | void doFormatInt(char binarr[], long formattedIntArr[fieldNum]); 30 | static int callback(void *NotUsed, int argc, char **argv, char **azColName); 31 | void mysqlConnection(const char *host, const char *user, const char *password, const char *database); 32 | 33 | MYSQL conn; 34 | 35 | void mysqlConnection(const char *host, const char *user, const char *password, const char *database) 36 | { 37 | mysql_init(&conn); 38 | 39 | my_bool my_true = 1; 40 | 41 | mysql_options(&conn, MYSQL_OPT_RECONNECT, &my_true); 42 | 43 | if (mysql_real_connect(&conn, host, user, password, database, 0, NULL, 0)) 44 | { 45 | printf("Connection success!\n"); 46 | } 47 | else 48 | { 49 | fprintf(stderr, "Connection failed!\n"); 50 | if (mysql_errno(&conn)) 51 | { 52 | fprintf(stderr, "Connection error %d: %s\n", mysql_errno(&conn), mysql_error(&conn)); 53 | } 54 | exit(EXIT_FAILURE); 55 | } 56 | } 57 | 58 | int main(int argc, char **argv) 59 | { 60 | char *host_id = argv[1]; 61 | int port = 2222; 62 | char *zErrMsg = 0; 63 | int rc; 64 | char sql[256]; 65 | 66 | int sin_len; 67 | char message[65535]; 68 | int socket_descriptor; 69 | struct sockaddr_in sin; 70 | 71 | mysqlConnection("localhost", "root", "fnl", "fnl"); 72 | 73 | printf("waiting for packet \n"); 74 | bzero(&sin, sizeof(sin)); 75 | sin.sin_family = AF_INET; 76 | sin.sin_addr.s_addr = inet_addr("0.0.0.0"); 77 | sin.sin_port = htons(port); 78 | sin_len = sizeof(sin); 79 | socket_descriptor = socket(AF_INET, SOCK_DGRAM, 0); 80 | bind(socket_descriptor, (struct sockaddr *)&sin, sizeof(sin)); 81 | 82 | time_t timep; 83 | uuid_t uuid; 84 | char packetId[36]; 85 | 86 | while (1) 87 | { 88 | time(&timep); 89 | srand((unsigned)timep); 90 | uuid_generate(uuid); 91 | uuid_unparse(uuid, packetId); 92 | 93 | // printf("time %d ,packetId %s \n", (int)timep, packetId); 94 | 95 | memset(message, 0, sizeof(message)); 96 | int rsize = recvfrom(socket_descriptor, message, sizeof(message), 0, (struct sockaddr *)&sin, &sin_len); 97 | printf("received %d byte data \n", rsize); 98 | 99 | int headerNum = (rsize - srHeaderSize - idPayloadSize) / headerSize; 100 | char binarr[headerSize * fieldNum]; 101 | char binstr[headerSize * fieldNum + 1]; 102 | char formattedStrArr[fieldNum][fieldNum * fieldNum]; 103 | long formattedIntArr[fieldNum * 8]; 104 | 105 | char binPayLoadArr[idPayloadSize * 8]; 106 | int payloadStartPos = headerSize * headerNum + srHeaderSize; 107 | doBinTrans(message, payloadStartPos, idPayloadSize, binPayLoadArr); 108 | long payLoadInt = doBin2Int(binPayLoadArr, 0, idPayloadSize * 8 - 1); 109 | 110 | int i; 111 | for (i = 0; i < headerNum; i++) 112 | { 113 | binstr[0] = '\0'; 114 | doBinTrans(message, i * headerSize + srHeaderSize, headerSize, binarr); 115 | doFormatInt(binarr, formattedIntArr); 116 | 117 | sprintf(sql, 118 | "insert into `fnl_int` (`device_no`,`ingress_port`,`egress_port`,`ingress_global_timestamp`,`enq_timestamp`,`enq_qdepth`,`deq_timedelta`,`deq_qdepth`,`udp_port`,`timestamp`,`packet_id`,`action_id`) values (%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%d,%d,'%s',%ld);", 119 | formattedIntArr[0], formattedIntArr[1], formattedIntArr[2], formattedIntArr[3], formattedIntArr[4], formattedIntArr[5], formattedIntArr[6], formattedIntArr[7], 120 | port, (int)timep, packetId, payLoadInt); 121 | int res = mysql_query(&conn, sql); 122 | if (!res) 123 | { 124 | printf("Inserted %lu rows actId %ld\n", (unsigned long)mysql_affected_rows(&conn), payLoadInt); 125 | } 126 | else 127 | { 128 | fprintf(stderr, "Insert error %d: %s\n", mysql_errno(&conn), mysql_error(&conn)); 129 | } 130 | } 131 | } 132 | exit(0); 133 | return (EXIT_SUCCESS); 134 | } 135 | 136 | void doBinTrans(char message[], int startPos, int size, char binarr[]) 137 | { 138 | int i; 139 | int m = 0; 140 | for (i = startPos; i < size + startPos; i++) 141 | { 142 | char j, k; 143 | for (j = 7; j >= 0; j--) 144 | { 145 | k = message[i] >> j; 146 | binarr[m] = k & 1; 147 | m++; 148 | } 149 | } 150 | } 151 | 152 | void doBin2Str(char binarr[], char binstr[], int binLength) 153 | { 154 | char st[2]; 155 | int i; 156 | for (i = 0; i < binLength; i++) 157 | { 158 | sprintf(st, "%d", binarr[i]); 159 | strcat(binstr, st); 160 | } 161 | } 162 | 163 | void doPayloadBin2Str(char binarr[], char binstr[], int binLength) 164 | { 165 | char st[2]; 166 | int i; 167 | for (i = 0; i < binLength; i++) 168 | { 169 | sprintf(st, "%d", binarr[i]); 170 | strcat(binstr, st); 171 | } 172 | binstr[binLength] = '\0'; 173 | } 174 | 175 | long doBin2Int(char binarr[], int startPos, int endPos) 176 | { 177 | int i; 178 | long binInt = 0; 179 | long binExp = 1; 180 | for (i = endPos; i >= startPos; i--) 181 | { 182 | binInt += binarr[i] * binExp; 183 | binExp *= 2; 184 | } 185 | return binInt; 186 | } 187 | 188 | void doFormatStr(char binstr[], char formattedStrArr[][fieldNum * fieldNum]) 189 | { 190 | int hdrLen[] = {8, 9, 9, 48, 32, 19, 32, 19}; 191 | char tmpFormat[fieldNum * fieldNum]; 192 | int position = 0; 193 | int i; 194 | for (i = 0; i < fieldNum; i++) 195 | { 196 | int j; 197 | for (j = 0; j < hdrLen[i]; j++) 198 | { 199 | tmpFormat[j] = binstr[position + j]; 200 | } 201 | tmpFormat[j] = '\0'; 202 | position += hdrLen[i]; 203 | strcpy(formattedStrArr[i], tmpFormat); 204 | } 205 | } 206 | void doFormatInt(char binarr[], long formattedIntArr[fieldNum]) 207 | { 208 | int hdrLen[] = {8, 9, 9, 48, 32, 19, 32, 19}; 209 | int i; 210 | int position = 0; 211 | for (i = 0; i < fieldNum; i++) 212 | { 213 | formattedIntArr[i] = doBin2Int(binarr, position, position + hdrLen[i] - 1); 214 | position += hdrLen[i]; 215 | } 216 | } -------------------------------------------------------------------------------- /system/packet/sendint.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import socket 4 | from sys import argv 5 | import json 6 | from bitstring import BitArray, BitStream 7 | import time 8 | import threading 9 | 10 | 11 | class PacketSender(object): 12 | """ 13 | INT Packet Sender 14 | """ 15 | 16 | def __init__(self, port): 17 | """ 18 | Initialization socket link to controller 19 | 20 | :param port: INT sending port 21 | """ 22 | self.port = port 23 | self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 24 | self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 25 | self.s.bind(('', self.port)) 26 | self.s.listen(10) 27 | self.typeDict = { 28 | 'TraversePath': self.doTraversePath, 29 | 'Test': self.doTest 30 | } 31 | 32 | def startSocket(self): 33 | """ 34 | start socket link to controller 35 | """ 36 | while True: 37 | conn, addr = self.s.accept() 38 | 39 | self.socketClient(conn, addr) 40 | 41 | def socketClient(self, conn, addr): 42 | """ 43 | Send data by type 44 | 45 | :param conn: receive data from controller circulate 46 | :param addr: not be used 47 | """ 48 | while True: 49 | data = conn.recv(4096).decode('utf-8') 50 | if data: 51 | dataJson = json.loads(data) 52 | dataType = dataJson.get('type') 53 | dataInfo = dataJson.get('info') 54 | 55 | self.typeDict.get(dataType, self.doDefault)(dataInfo) 56 | 57 | def doDefault(self, info): 58 | """ 59 | Default action 60 | """ 61 | 62 | def doTest(self, info): 63 | """ 64 | Test action 65 | """ 66 | pass 67 | 68 | def doTraversePath(self, info): 69 | """ 70 | Traverse INT using given path 71 | 72 | :param info: traverse path information 73 | """ 74 | def sendUDP(content, address): 75 | """ 76 | Send traverse path via UDP 77 | 78 | :param content: traverse route content 79 | :param address: traverse target address 80 | """ 81 | udpLink = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 82 | addr = (address, 2222) 83 | udpLink.sendto(content, addr) 84 | 85 | def byteDateToSend(byteRoute, actId): 86 | """ 87 | Convert traverse route byte info to a formatted byte info 88 | 89 | :param byteRoute: traverse route byte info 90 | :param actId: action id from controller 91 | :returns: a formatted byte info 92 | """ 93 | actIdBin = bin(actId)[2:] 94 | actIdBinStr = '0' * (32 - int(len(actIdBin)) % 32) + actIdBin 95 | byteRouteStr = '0' * \ 96 | (512 - int(len(byteRoute)) % 512) + byteRoute 97 | byteStr = byteRouteStr + actIdBinStr 98 | byteContent = BitArray('0b' + byteStr).bytes 99 | return byteContent 100 | 101 | def addRoute(port): 102 | """ 103 | Convert a route switch port to route byte info 104 | 105 | :param port: a switch port in route 106 | :returns: a prttied binary port string 107 | """ 108 | portOct = int(port) - 1 109 | portBin = bin(portOct)[2:] 110 | portBinPretty = '0' * ((4 - int(len(portBin))) % 4) + portBin 111 | return portBinPretty 112 | 113 | def sendPacketByTimes(sendTimes, byteContent, address): 114 | """ 115 | Send INT packet in the given number of times 116 | 117 | :param sendTimes: the number of times 118 | :param byteContent: the content to be send 119 | :param address: the target host IP address 120 | """ 121 | sleepTime = 10/sendTimes 122 | for i in range(10000): 123 | sendUDP(byteContent, address) 124 | time.sleep(sleepTime) 125 | 126 | def sendPacketByTime(sendTime, byteContent, address, actId): 127 | """ 128 | Send INT packet in the given time 129 | 130 | :param sendTimes: the time to send packet 131 | :param byteContent: the content to be send 132 | :param address: the target host IP address 133 | :param actId: the action ID receive from controller 134 | """ 135 | startTime = time.time() 136 | i = 0 137 | times = 0 138 | while time.time() - startTime < sendTime: 139 | sendUDP(byteContent, address) 140 | i = i + 1 141 | 142 | sleepTime = 0.1 143 | time.sleep(sleepTime) 144 | times = times + 1 145 | endTime = time.time() 146 | p_rate = i / (endTime - startTime) 147 | 148 | actId = info.get('actId') 149 | sendTimes = info.get('sendTimes') 150 | 151 | addressList = info.get('addressList') 152 | portsLists = info.get('portsLists') 153 | listLen = len(portsLists) 154 | for i in range(listLen): 155 | portsList = portsLists[i] 156 | address = addressList[i] 157 | byteRoute = '' 158 | portsList.reverse() 159 | for port in portsList: 160 | byteRoute = byteRoute + addRoute(port) 161 | byteContent = byteDateToSend(byteRoute, actId) 162 | 163 | # send packet async 164 | thread = threading.Thread(target=sendPacketByTime, args=( 165 | sendTimes, byteContent, address, actId)) 166 | thread.setDaemon(False) 167 | thread.start() 168 | 169 | def __del__(self): 170 | self.s.close() 171 | 172 | 173 | if __name__ == '__main__': 174 | logid = argv[1] 175 | ps = PacketSender(8888) 176 | ps.startSocket() 177 | --------------------------------------------------------------------------------