def connect_all(self): """ 尝试连接之前初始化好的所有接口 (因为初始化好的接口还没有得到socket相当于还没有连网线) 由于是初始化好的接口,因此默认假设一定全都能连上,只是时间早晚的问题 处理了:对方没有上线的情况 方法:晚一点再连 这里如果要优化,可以使用一个队列, 如果这个连接失败,放到队列末端,然后只要队列不空就继续循环取队头并尝试连接 直到队列为空。 """ # NOTICE: O(n^2) while len(self.hosts) != self.connected_cnt: for host in self.hosts: # is_connected = False # for subnet in self.subnets: # if subnet.prefix == Host.getSubnetPrefix(host): # is_connected = True # break if host.status == "down": ret = self.try_connect(host) # logger.debug("Trying connect %s -> %s", host.vip, host.counter_vip) if ret == 0: #TODO macro for SUCCESS ? self.connected_cnt = self.connected_cnt + 1 logger.debug("Interface %s is on, connected to %s", host.vip, host.counter_vip) host.status = "on"
def ask_for_global_table(): controller_index = config_data['controller_index'] src_ip = '' dst_ip = '' netmask = '' for interface in config_data['interfaces']: if interface['counter_index'] != controller_index: continue # now counter_index is controller_index src_ip = interface['vip'] dst_ip = interface['counter_vip'] netmask = interface['netmask'] logger.debug( 'going to send route table request msg\n, \ src_ip %s, dst_ip %s, netmask %s', src_ip, dst_ip, netmask) request_msg = { "code": 0, "msg": "request for route table", "src_index": ROUTER_INDEX } msg_bytes = utilities.objEncode(request_msg) pkg = route.IP_Package(src_ip, dst_ip, dst_ip, netmask, msg_bytes) pkg.protocol = 119 errno = route.link_layer.send(pkg.to_bytes()) if errno < 0: logger.warning('fail to send to link layer, errno: %d', errno)
def run(self) -> None: logger.debug('network layer listerner begin to work') while True: recv = self.network_layer.recv() if recv: logger.info('network layer pkg received\n{}'.format(recv)) time.sleep(0.1)
def broadcastMsg(self, data, protocol=0): interfaces = self.network_layer.interfaces for interface in interfaces: sip = interface.vip dip = interface.counter_vip logger.debug('broadcast to ip %s', dip) self.network_layer.send(sip, dip, utilities.objEncode(data), protocol)
def run_ping(self): logger.info("tracking direct router neighbor threading run...") for interface in self.network_layer.interfaces: src_ip = interface.vip dst_ip = interface.counter_vip netmask = interface.netmask msg = {'code': 0, 'msg': "are you still here?"} self.network_layer.send(src_ip, dst_ip, utilities.objEncode(msg), 100) logger.debug('send ping from %s to %s', src_ip, dst_ip)
def broadcast_logout(self, index): global msgID logger.debug('###########broadcasting!!!!!########') logger.debug('broadcast, id is %d, from index %d', msgID, ROUTER_INDEX) for interface in self.interfaces: src_ip, dst_ip = interface msg = {"type": "logout", "index": index, "id": msgID} msgID += V self.network_layer.send(src_ip, dst_ip, utilities.objEncode(msg), 119)
def run(self): """ 从队列中拿到一个包,做适当转换后,交给链路层 """ while True: # 如果队列为空,那就等直到队列中有包 while route_send_package.qsize() == 0: # NOTICE:此处循环可能会导致性能下降,CPU占用率100% time.sleep(0.2) continue # 从队列中获得一个包 ip_package = route_send_package.get() # DEBUG信息 logger.debug(' this package will be modiflied according to route table !') logger.debug(ip_package) # 使用成员函数处理IP包,修改其中的dest_ip字段,获得新的IP包 ret_ip_package = self.ip_package_modifier(ip_package) # type: Optional["IP_Package"] if ret_ip_package == None: logger.info('{} is unreachable. \nShow your route table by "show route table"'.format(ip_package.final_ip)) continue # DEBUG信息 logger.debug('ip pkg has been modified. now forwarding...\n modified package is below') logger.debug(ret_ip_package) # 发送IP包 link_layer.send(ret_ip_package.to_bytes())
def __init__(self, config) -> None: # NOTICE:这里有两种方案,一种是传json字符串,另一种是传文件名,然后就可以在路由器内部进行读取配置文件初始化 # 从配置文件中初始化各项数据 self.name = config['name'] self.index = config['index'] # 开启转发线程 logger.debug('my_package_forward_thread start!') my_package_forward_thread.start() # 开启链路层监听线程,用于从链路层得到包 my_monitor_link_layer.start() # 初始化转发表 self.init_route_table(config) # 初始化网线接口 self.interfaces = [] self.init_interfaces(config)
def __init__(self, hosts, subnets): threading.Thread.__init__(self) self.hosts = hosts self.subnets = subnets for host in self.hosts: subnet_prefix = host.getSubnetPrefix( ) # use member function host.getSubnetPrefix, which call utilities.getSubnet() new_subnet = Subnet(subnet_prefix) # 新的子网中加入这台主机 new_subnet.hosts.append(host) # 链路层子网列表中加入这个子网 self.subnets.append(new_subnet) # 这台主机(该子网第一台主机)的监听线程开始工作 host.start() logger.debug("Interface %s listening", host.vip) self.connected_cnt = 0
def init_global_route_table(config_file: str, src: int) -> None: """ 用SPFA算法,读取配置文件,更新路由表中的最短路信息 input: config_file: 储存有“所有route配置文件的文件名”文件名 """ interface2index.clear() index2interface.clear() graph_reset() logger.debug("[spfa] init graph\n %s", format(graph)) for filename in json_files['filenames']: f = open(CONFIG_ROOT + '/' + filename) #TODO:(YB) refactor. let it be path.resolve json_data = json.load(f) f.close() node = json_data['index'] # 当前配置文件/test/route*.json的路由标号 interfaces = json_data['interfaces'] for interface in interfaces: cvip = interface['counter_vip'] netmask = interface['netmask'] # interface2index 记录了所有的interfaces.它是一个从interface到所属路由的映射。 interface2index[(cvip, netmask)] = interface['counter_index'] if interface['counter_index'] == src: # index2interface, # idx -> intf # 记录了,站在本路由的角度,到达idx这个路由,需要通过的intf是什么 index2interface[node] = (interface['vip'], interface['netmask']) inf_node = interface['counter_index'] weight = 1 try: weight = interface['weight'] except KeyError: logger.warning('no weight info in %s, %s defaut to 1', filename, interface) graph[node][inf_node] = weight # C not in graph for i in range(V): graph[2][i] = -1 graph[i][2] = -1 logger.debug("[spfa] finished loading neighbour info into graph\n %s", format(graph))
def task(self): time.sleep(0.1) msg_queue = [] msg_queue.append(self.network_layer.recv_ospf()) msg_queue.append(self.network_layer.recv()) msg_queue.append(self.network_layer.recv_ping()) available_msg = [msg for msg in msg_queue if not msg is None] for msg in available_msg: if msg.protocol == 100: data = utilities.objDecode(msg.data) sip = msg.src_ip dip = msg.dest_ip netmask = msg.net_mask if data['code'] == 0: # get ping request from other logger.info('get ping from %s\n', sip) msg = {'code': 1, 'msg': 'I am here.'} self.network_layer.send(dip, sip, utilities.objEncode(msg), 100) if data['code'] == 1: # get ping response from it logger.info('ping response. I know %s reachable', sip) self.tracking_neighbour_alive.wakeup(dip, sip) elif msg.protocol == 119: data = utilities.objDecode(msg.data) msgid = data['id'] if msgid in known_msgid_list: # logger.debug('already know %d, continue', msgid) continue known_msgid_list.append(msgid) self.broadcastMsg(data, 119) msg_type = data['type'] if msg_type == 'logout': # pass #TODO: here, maybe bugs lgout_index = data['index'] logger.info( 'get logout broadcast. now refresh route table according to broadcast %d', lgout_index) logout_refresh_route_table(lgout_index) else: logger.debug('recv msg!!\n%s', msg)
def calculate_shortest_path(src: int): ret = [] dist, prev = shortestPath.SPFA(graph, src) logger.debug("[controller] finished run spfa\n dist %s \n prev %s\n", dist, prev) logger.debug("[controller, info] interface2index\n%s", interface2index) logger.debug("[controller, info] index2interface\n%s", index2interface) for ip, netmask in interface2index: # 自己的端口不需要作转发 if interface2index[(ip, netmask)] == src: continue logger.debug('[controller] dealing with %s, %s', ip, netmask) subnet = utilities.get_subnet(ip, netmask) index = interface2index[(ip, netmask)] prev_index = prev[index] if prev_index == -1: continue if prev_index == src: target_index = interface2index[(ip, netmask)] (dst_ip, dst_nm) = index2interface[target_index] # route.my_route_table.update_item(ip, netmask, dst_ip) ret.append((ip, netmask, dst_ip)) logger.info( '[1]add item into response msg\n \ %s, %s, %s', ip, netmask, dst_ip) else: try_get = index2interface.get(prev_index) while try_get is None: prev_index = prev[prev_index] try_get = index2interface.get(prev_index) prev_ip, prev_netmask = try_get # route.my_route_table.update_item(ip, netmask, prev_ip) ret.append((ip, netmask, prev_ip)) logger.info( '[2]add item into response msg\n \ %s, %s, %s', ip, netmask, prev_ip) logger.debug("calculation return value\n%s", ret) return ret
def main(): '''main''' # open file network_layer = route.NetworkLayer(config_data) if is_controller: logger.debug("I am controller. not calculate until asked") #init_global_route_table(GLOBAL_ROUTE_INFORMATIOIN_FILE, 2) #ret = calculate_shortest_path(2) #for item in ret: #route.my_route_table.update_item(*item) network_layer_listener = NetworkLayerListener(network_layer) network_layer_listener.start() if not is_controller: logger.debug("begin to ask for global table") ask_for_global_table() consoler = console.Console(network_layer, route) consoler.task()
def send(self, ip_pkg: bytes) -> int: """ 实现假设: 1. src这一台host自带的socket能够直接发送到dest 2. 两台host在同一个子网内 作用: 1. 将ip_pkg从src这个host发送到dest这个host 注意: 1. dest这个参数似乎没用上 """ logger.debug("in link, DataLinkLayer, sending\n%s", ip_pkg) pkg = IP_Package.bytes_package_to_object(ip_pkg) ip1 = pkg.dest_ip nm = pkg.net_mask # def send(self, src, dest, ip_pkg): # ip1, nm1 = src # _, nm2 = dest # if nm1 != nm2: # logger.error('{} and {} not in same subnet'.format(str(nm1), str(nm2))) # raise Exception("{} and {} not in same subnet".format(nm1, nm2)) subnet_prefix = utilities.get_subnet(ip1, nm) # 实现了:找到目的ip对应子网,再从子网中找到对应host,然后就直接调用这个host的send for subnet in self.subnets: if subnet.prefix == subnet_prefix: for host in subnet.hosts: if (host.counter_vip == ip1 or host.vip == ip1) and host.netmask == nm: if host.status == "offline" or host.status == "down": return -1 # TODO:允许以出端口作为dest_ip try: rsock = rdt_socket.rdt_socket(host.counter_socket) rsock.sendBytes(ip_pkg) return len(ip_pkg) except Exception as e: # 捕获所有异常,并且打印错误信息 logger.debug(traceback.format_exc()) return -1
def init_shortest_path_prerequisite(src: int) -> None: """ 用SPFA算法,读取配置文件,更新路由表中的最短路信息 input: """ logger.debug("[ospf.spfa] init graph\n %s", format(graph)) for filename in json_files['filenames']: f = open(CONFIG_ROOT + '/' + filename) #TODO:(YB) refactor. let it be path.resolve json_data = json.load(f) f.close() node = json_data['index'] # 当前配置文件/test/route*.json的路由标号 interfaces = json_data['interfaces'] for interface in interfaces: cvip = interface['counter_vip'] netmask = interface['netmask'] # interface2index 记录了所有的interfaces.它是一个从interface到所属路由的映射。 interface2index[(cvip, netmask)] = interface['counter_index'] if interface['counter_index'] == src: # index2interface, # idx -> intf # 记录了,站在本路由的角度,到达idx这个路由,需要通过的intf是什么 index2interface[node] = (interface['vip'], interface['netmask']) inf_node = interface['counter_index'] weight = 1 try: weight = interface['weight'] except KeyError: logger.warning('no weight info in %s, %s defaut to 1', filename, interface) graph[node][inf_node] = weight logger.debug("[spfa] finished loading neighbour info into graph\n %s", format(graph))
def run(self) -> None: logger.debug('network layer listener begin to work') while True: for protocol in ['rip', 'cost']: if protocol == 'rip': pkg = network_layer.recv_rip() if pkg is None: continue if(pkg.protocol == 120): rip_msg = utilities.objDecode(pkg.data) rip_worker.process(rip_msg) elif protocol == 'cost': pkg = network_layer.recv_cost() if pkg is None: continue if(pkg.protocol == 121): cost_msg = utilities.objDecode(pkg.data) _vip = cost_msg['vip'] _cost = cost_msg['cost'] for intf in rip_worker.interfaces: if intf.vip == _vip: logger.info('Cost %s -> %s changed to %d', rip_worker.route_name, intf.counter_name, _cost) rip_worker.dis_vec[intf.counter_name]['cost'] = _cost time.sleep(0.01)
def init_route_table(): init_shortest_path_prerequisite(ROUTER_INDEX) ret = calculate_shortest_path(ROUTER_INDEX) for dest_net, netmask, dest_ip in ret: route.my_route_table.update_item(dest_net, netmask, dest_ip) logger.debug("finish init route table.")
def portmapping(self, vmname, vmip, vmport, hostport, action): if action == 'a': iptables_cmd = '%s %s PREROUTING -p tcp --dport %s -j DNAT --to %s:%s' \ %(self.cmd_iptables, '-A', hostport, vmip, vmport) logger.info('Add port mapping for %s, from %s to %s on hostmachine'%(vmname, vmport, hostport)) logger.debug(iptables_cmd) elif action == 'd': iptables_cmd = '%s %s PREROUTING -p tcp --dport %s -j DNAT --to %s:%s' \ %(self.cmd_iptables, '-D', hostport, vmip, vmport) logger.info('Delete port mapping for %s, from %s to %s on hostmachine'%(vmname, vmport, hostport)) logger.debug(iptables_cmd) else : logger.error('Error argument!') return 0 (result, value) = pexpect.run(iptables_cmd, withexitstatus = 1) if value != 0 : logger.error(result) return 0 return 1 if connect: logger.info('Start.') logger.info('Database connected.') while 1: query_portreq = "SELECT * FROM %s WHERE state='adding' \ OR state='deleting 'ORDER BY hostport"%(porttb) db.query(query_portreq) req_res = db.store_result() fetched_req_data = req_res.fetch_row() while fetched_req_data: state = fetched_req_data[0][idx_state] vmname = fetched_req_data[0][idx_vmname] vmport = fetched_req_data[0][idx_vmport] oldhostport = fetched_req_data[0][idx_hostport] if (state == 'adding') : vmip = socket.gethostbyname(vmname) newport = oldhostport if (newport == '-1') : newport = get_port() if (newport != -1) : portmapping(vmname, vmip, vmport, newport, 'a') query = "UPDATE %s SET state='using', \ hostport='%s', ip='%s' \ WHERE hostport=%s"%(porttb, newport, vmip, oldhostport) db.query(query) else : logger.error('No more port!') elif (fetched_req_data[0][idx_state] == 'deleting') : #delete port vmip = fetched_req_data[0][idx_ip] portmapping(vmname, vmip, vmport, oldhostport, 'd') query = "DELETE FROM %s WHERE hostport=%s"%(porttb, oldhostport) db.query(query) else : logger.error("I don't know what's wrong!") fetched_req_data = req_res.fetch_row() time.sleep(sleep_time)
def portmapping(self, vmname, vmip, vmport, hostport, action): if action == 'a': iptables_cmd = '%s %s PREROUTING -p tcp --dport %s -j DNAT --to %s:%s' \ %(self.cmd_iptables, '-A', hostport, vmip, vmport) logger.info( 'Add port mapping for %s, from %s to %s on hostmachine' % (vmname, vmport, hostport)) logger.debug(iptables_cmd) elif action == 'd': iptables_cmd = '%s %s PREROUTING -p tcp --dport %s -j DNAT --to %s:%s' \ %(self.cmd_iptables, '-D', hostport, vmip, vmport) logger.info( 'Delete port mapping for %s, from %s to %s on hostmachine' % (vmname, vmport, hostport)) logger.debug(iptables_cmd) else: logger.error('Error argument!') return 0 (result, value) = pexpect.run(iptables_cmd, withexitstatus=1) if value != 0: logger.error(result) return 0 return 1 if connect: logger.info('Start.') logger.info('Database connected.') while 1: query_portreq = "SELECT * FROM %s WHERE state='adding' \ OR state='deleting 'ORDER BY hostport" % (porttb) db.query(query_portreq) req_res = db.store_result() fetched_req_data = req_res.fetch_row() while fetched_req_data: state = fetched_req_data[0][idx_state] vmname = fetched_req_data[0][idx_vmname] vmport = fetched_req_data[0][idx_vmport] oldhostport = fetched_req_data[0][idx_hostport] if (state == 'adding'): vmip = socket.gethostbyname(vmname) newport = oldhostport if (newport == '-1'): newport = get_port() if (newport != -1): portmapping(vmname, vmip, vmport, newport, 'a') query = "UPDATE %s SET state='using', \ hostport='%s', ip='%s' \ WHERE hostport=%s" % (porttb, newport, vmip, oldhostport) db.query(query) else: logger.error('No more port!') elif (fetched_req_data[0][idx_state] == 'deleting'): #delete port vmip = fetched_req_data[0][idx_ip] portmapping(vmname, vmip, vmport, oldhostport, 'd') query = "DELETE FROM %s WHERE hostport=%s" % ( porttb, oldhostport) db.query(query) else: logger.error("I don't know what's wrong!") fetched_req_data = req_res.fetch_row() time.sleep(sleep_time)
CONFIG_NAME = sys.argv[1] f = open(CONFIG_NAME, 'rt') config_data = json.load(f) f.close() ROUTER_INDEX = config_data['index'] disable_node = [] interface2index = {} # type: Dict[Tuple[str, str], int] index2interface = {} # type: Dict[int, Tuple[str, str]] # config_file中的内容是,所有test/route*.json的文件的文件名 f = open(GLOBAL_ROUTE_INFORMATIOIN_FILE, 'rt') json_files = json.load(f) f.close() logger.debug("[controller] read all files\n %s ", format(json_files)) V = len(json_files['filenames']) msgID = ROUTER_INDEX known_msgid_list = [] # graph 是用于求最短路的邻接矩阵 graph = [[-1 for i in range(V)] for j in range(V)] # type: List[List[int]] def logout_refresh_route_table(index): if index in disable_node: return disable_node.append(index) logger.info('refreshing router table ,logout index is %d', index) for i in range(V): graph[index][i] = -1
def process(self, rip_msg : dict): medium = rip_msg['from'] # 步骤一:阻止含有之前已经处理过的离线的路由器的信息的rip报文 if medium in self.tear_down: return for rname, detail in rip_msg['dv'].items(): for drname in self.tear_down: if drname in detail['path']: logger.debug("[RIP] [Dropped] DV of %s contain offline route %s", medium, drname) return for rname in rip_msg['topo'].keys(): if rname in self.tear_down: logger.debug("[RIP] [Dropped] TOPO of %s contain offline route %s", medium, rname) return logger.debug("[RIP] received rip package from %s", medium) new_tear_down = set(rip_msg['tear_down']) - set(self.tear_down) self.tear_down = list(set().union(self.tear_down, rip_msg['tear_down'])) # 步骤二:删除自身所有有关新得知的离线了的路由器的信息 # 删除距离向量中的这一行 if len(new_tear_down) != 0: to_del = [] for drname in new_tear_down: if drname in self.dis_vec.keys(): to_del.append(drname) for drname in to_del: del self.dis_vec[drname] # 删除距离向量中所有途径offline路由器的 to_del = [] for drname in new_tear_down: for rname, detail in self.dis_vec.items(): if drname in detail['path']: to_del.append(rname) for rname in to_del: del self.dis_vec[rname] for drname in new_tear_down: # 删除路由表中有关的 for vip, nm in self.topo[drname]: route.my_route_table.delete_item(vip, nm) route.my_route_table.delete_item(utilities.get_subnet(vip, nm), nm) # 网络层端口中将其设为offline for intf in network_layer.interfaces: if intf.counter_name == drname: intf.status = "offline" # 删除关于它的拓扑记录 del self.topo[drname] self.dis_mat = {} for intf in self.interfaces: self.dis_mat[intf.counter_name] = {} return for dest, intfs in rip_msg['topo'].items(): if dest not in self.topo: self.topo[dest] = intfs logger.info("[RIP] learned topo of {} : {}".format(dest, intfs)) if dest in self.direct_routes: for vip, netmask in intfs: logger.info("[RIP] Updating route table\n{} {} THROUTH {}".format(vip, netmask, self.next_hop[dest])) route.my_route_table.update_item(vip, netmask, self.next_hop[dest]) # 步骤三:更新自己的距离向量和拓扑图,因为可能学习到新的节点 # if medium not in self.topo.keys(): # self.topo[medium] = rip_msg['intfs'] if medium not in self.dis_vec.keys(): logger.critical("Unexpected rip msg from %s", rip_msg['from']) else: mininum = DV_INF for dest, detail in rip_msg['dv'].items(): if not dest in self.topo.keys(): continue cost = detail['cost'] newcost = self.dis_vec[medium]['cost'] + cost if dest == self.route_name: continue elif dest not in self.dis_vec.keys(): newpath = detail['path'] newpath.insert(0, self.route_name) self.dis_vec[dest] = \ { "cost": newcost, "path": newpath } logger.info("[RIP] New shortest path found\n{} -> {} cost {}, path: {}".format( self.route_name, dest, self.dis_vec[dest]['cost'], self.dis_vec[dest]['path'])) for vip, netmask in self.topo[dest]: logger.info("[RIP] Updating route table\n{} {} THROUTH {}".format(vip, netmask, self.next_hop[medium])) route.my_route_table.update_item(vip, netmask, self.next_hop[medium]) # 步骤四:更新距离矩阵中的一行 self.dis_mat[medium] = rip_msg['dv'] logger.debug('{}'.format(utilities.obj_to_beautiful_json(self.dis_mat))) # 步骤五:运行核心的dv算法 for dest, detail in self.dis_vec.items(): # 直连的链路cost不在这里更新cost,只在cost change protocol中更新 if dest in self.direct_routes: continue # 不需要对未知子网Interfaces情况的路由器计算距离,计算了也没用,也加不了路由表 if not dest in self.topo.keys(): continue mininum = DV_INF new_path = [] medium = '' for entry_route, dv in self.dis_mat.items(): # 去不到就continue,相当于设置了正无穷 if dest not in dv.keys(): continue new_cost = self.dis_vec[entry_route]['cost'] + dv[dest]['cost'] if new_cost < mininum: # 毒性逆转(poisoned reverse) # 如果可能的最短路径中包含目的节点自身 if dest in dv[dest]['path'] and dest != dv[dest]['path'][-1]: continue logger.debug('maybe shortest : {} to {}'.format(self.route_name, dest)) logger.debug('{} plus {}'.format(dest, dv[dest]['path'])) mininum = new_cost medium = entry_route new_path = dv[dest]['path'] if new_path[0] != self.route_name: new_path.insert(0, self.route_name) # 更新距离向量和路由表,并打印适量调试信息 old_cost = detail['cost'] detail['cost'] = mininum if mininum >= DV_INF: detail['path'] = [] else: logger.debug("{} to {} now walks {}".format(self.route_name, dest, new_path)) detail['path'] = new_path if medium != '': for vip, netmask in self.topo[dest]: route.my_route_table.update_item(vip, netmask, self.next_hop[medium]) if old_cost != detail['cost']: logger.info("[RIP] New shortest path found\n{} -> {} cost {}, path: {}".format( self.route_name, dest, self.dis_vec[dest]['cost'], self.dis_vec[dest]['path']))
def run(self): logger.debug("rip protocol is working") while True: logger.debug("[RIP] Broadcasting RIP msg") self.broadcast(self.interfaces) time.sleep(2)
def run(self): logger.debug("Trying to connect all counterpart interface") self.connect_all()