class Fib: def __init__(self): self._routes = PyTricia() def put_route(self, prefix, nexthops): route = FibRoute(prefix, nexthops) self._routes[prefix] = route def del_route(self, prefix): if self._routes.has_key(prefix): del self._routes[prefix] def get_route(self, prefix): if self._routes.has_key(prefix): return self._routes[prefix] return None def __str__(self): repr_str = "" for prefix in self._routes: repr_str += f"{self._routes[prefix]}\n" return repr_str
class ForwardingTable(object): """ Builds a trie with all supplied rule prefixes and provides all continuous equivalence classes. """ def __init__(self): super(ForwardingTable, self).__init__() # initialize logging self.logger = get_logger('FECFinder', 'INFO') # init Trie self.fib = PyTricia() def add_entry(self, prefix, interface, route_type, next_hop): assert isinstance(prefix, IPv4Network) assert next_hop is not None fwd_entry = ForwardingTableEntry(prefix, interface, route_type, next_hop) if self.fib.has_key(prefix): self.fib[prefix].append(fwd_entry) else: self.fib[prefix] = [fwd_entry] def get_next_hop(self, prefix): assert isinstance(prefix, IPv4Network) next_hops = list() if prefix in self.fib: entries = self.fib[prefix] for entry in entries: next_hops.append(entry.next_hop) return next_hops def get_entries(self, prefix): assert isinstance(prefix, IPv4Network) if prefix in self.fib: entries = self.fib[prefix] return entries return None
class RouteTable: """ The route table, also known as routing information base (RIB). """ def __init__(self, address_family, fib, log, log_id): assert fib.address_family == address_family self.address_family = address_family self.destinations = PyTricia() self.fib = fib self._log = log self._log_id = log_id def debug(self, msg, *args): if self._log: self._log.debug("[%s] %s" % (self._log_id, msg), *args) def get_route(self, prefix, owner): assert_prefix_address_family(prefix, self.address_family) prefix = ip_prefix_str(prefix) if self.destinations.has_key(prefix): return self.destinations.get(prefix).get_route(owner) return None def put_route(self, rib_route): assert_prefix_address_family(rib_route.prefix, self.address_family) rib_route.stale = False self.debug("Put %s", rib_route) prefix = rib_route.prefix prefix_str = ip_prefix_str(prefix) if not self.destinations.has_key(prefix_str): destination = Destination(self, prefix) self.destinations.insert(prefix_str, destination) else: destination = self.destinations.get(prefix_str) best_changed = destination.put_route(rib_route) if best_changed: child_prefix_strs = self.destinations.children(prefix_str) self.update_fib(prefix, child_prefix_strs) def del_route(self, prefix, owner): assert_prefix_address_family(prefix, self.address_family) prefix_str = ip_prefix_str(prefix) if self.destinations.has_key(prefix_str): destination = self.destinations.get(prefix_str) child_prefix_strs = self.destinations.children(prefix_str) deleted, best_changed = destination.del_route(owner) # If that was the last route for the destination, delete the destination itself if not destination.rib_routes: del self.destinations[prefix_str] else: deleted = False best_changed = False if deleted: self.debug("Delete %s", prefix) else: self.debug("Attempted delete %s (not present)", prefix) if best_changed: self.update_fib(prefix, child_prefix_strs) return deleted def update_fib(self, prefix, child_prefix_strs): # The child_prefix_strs have to be passed as a parameter, because they need to be collected # before the parent is potentially deleted by the calling function. # Locate the best RIB route for the prefix (None if there is no RIB route for the prefix). prefix_str = ip_prefix_str(prefix) if self.destinations.has_key(prefix_str): destination = self.destinations.get(prefix_str) rib_route = destination.best_route() else: rib_route = None # Determine the next-hops for the corresponding FIB route. If the next-hops is an empty # list [] it means it is a discard route. If the next-hops is None it means there should not # be a FIB route. if rib_route: fib_next_hops = rib_route.compute_fib_next_hops() else: fib_next_hops = None # Update the FIB route accordingly. if fib_next_hops is not None: fib_route = FibRoute(prefix, fib_next_hops) self.fib.put_route(fib_route) else: self.fib.del_route(prefix) # Recursively update the FIB for all child routes: their negative next-hops, if any, may # have to be recomputed if a parent route changed. for child_prefix_str in child_prefix_strs: child_destination = self.destinations[child_prefix_str] child_rib_route = child_destination.best_route() child_prefix = child_rib_route.prefix grand_child_prefix_strs = self.destinations.children( child_prefix_str) self.update_fib(child_prefix, grand_child_prefix_strs) def all_routes(self): for prefix in self.destinations: destination = self.destinations.get(prefix) for rib_route in destination.rib_routes: yield rib_route def all_prefix_routes(self, prefix): assert_prefix_address_family(prefix, self.address_family) prefix_str = ip_prefix_str(prefix) if self.destinations.has_key(prefix_str): destination = self.destinations[prefix_str] for rib_route in destination.rib_routes: yield rib_route def cli_table(self): table = Table() table.add_row(RibRoute.cli_summary_headers()) for rib_route in self.all_routes(): table.add_row(rib_route.cli_summary_attributes()) return table def mark_owner_routes_stale(self, owner): # Mark all routes of a given owner as stale. Returns number of routes marked. count = 0 for rib_route in self.all_routes(): if rib_route.owner == owner: rib_route.stale = True count += 1 return count def del_stale_routes(self): # Delete all routes still marked as stale. Returns number of deleted routes. # Cannot delete routes while iterating over routes, so prepare a delete list routes_to_delete = [] for rib_route in self.all_routes(): if rib_route.stale: routes_to_delete.append((rib_route.prefix, rib_route.owner)) # Now delete the routes in the prepared list count = len(routes_to_delete) if count > 0: self.debug("Delete %d remaining stale routes", count) for (prefix, owner) in routes_to_delete: self.del_route(prefix, owner) return count def nr_destinations(self): return len(self.destinations) def nr_routes(self): count = 0 for prefix in self.destinations: destination = self.destinations.get(prefix) count += len(destination.rib_routes) return count
class Router(Node): __slots__ = ['autoack', 'forwarding_table', 'default_link', 'trafgen_ip'] def __init__(self, name, measurement_config, **kwargs): Node.__init__(self, name, measurement_config, **kwargs) self.autoack=bool(eval(str(kwargs.get('autoack','False')))) self.forwarding_table = PyTricia(32) self.default_link = None from fslib.configurator import FsConfigurator ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ] self.add_link(NullLink, ipa, ipb, 'remote') self.trafgen_ip = str(ipa) def setDefaultNextHop(self, nexthop): '''Set up a default next hop route. Assumes that we just select the first link to the next hop node if there is more than one.''' self.logger.debug("Default: {}, {}".format(nexthop, str(self.node_to_port_map))) self.default_link = self.portFromNexthopNode(nexthop).link if not self.default_link: raise ForwardingFailure("Error setting default next hop: there's no static ARP entry to get interface") self.logger.debug("Setting default next hop for {} to {}".format(self.name, nexthop)) def addForwardingEntry(self, prefix, nexthop): '''Add new forwarding table entry to Node, given a destination prefix and a nexthop (node name)''' pstr = str(prefix) self.logger.debug("Adding forwarding table entry: {}->{}".format(pstr, nexthop)) xnode = self.forwarding_table.get(pstr, None) if not xnode: xnode = [] self.forwarding_table[pstr] = xnode xnode.append(nexthop) def removeForwardingEntry(self, prefix, nexthop): '''Remove an entry from the Node forwarding table.''' pstr = str(prefix) if not self.forwarding_table.has_key(pstr): return xnode = self.forwarding_table.get(pstr) xnode.remove(nexthop) if not xnode: del self.forwarding_table[pstr] def nextHop(self, destip): '''Return the next hop from the local forwarding table (next node, ipaddr), based on destination IP address (or prefix)''' xlist = self.forwarding_table.get(str(destip), None) if xlist: return xlist[hash(destip) % len(xlist)] raise ForwardingFailure() def flowlet_arrival(self, flowlet, prevnode, destnode, input_ip=None): if input_ip is None: input_ip = self.trafgen_ip input_port = self.ports[input_ip] if isinstance(flowlet, SubtractiveFlowlet): killlist = [] ok = [] self.unmeasure_flow(flowlet, prevnode) if destnode != self.name: self.forward(flowlet, destnode) return # a "normal" Flowlet object self.measure_flow(flowlet, prevnode, str(input_port.localip)) if flowlet.endofflow: self.unmeasure_flow(flowlet, prevnode) if destnode == self.name: if self.__should_make_acknowledgement_flow(flowlet): revflow = Flowlet(flowlet.flowident.mkreverse()) revflow.ackflow = True revflow.flowstart = revflow.flowend = fscore().now if flowlet.tcpflags & 0x04: # RST return if flowlet.tcpflags & 0x02: # SYN revflow.tcpflags = revflow.tcpflags | 0x10 # print 'setting syn/ack flags',revflow.tcpflagsstr if flowlet.tcpflags & 0x01: # FIN revflow.tcpflags = revflow.tcpflags | 0x10 # ack revflow.tcpflags = revflow.tcpflags | 0x01 # fin revflow.pkts = flowlet.pkts / 2 # brain-dead ack-every-other revflow.bytes = revflow.pkts * 40 self.measure_flow(revflow, self.name, input_port) # weird, but if reverse flow is short enough, it might only # stay in the flow cache for a very short period of time if revflow.endofflow: self.unmeasure_flow(revflow, prevnode) destnode = fscore().topology.destnode(self.name, revflow.dstaddr) # guard against case that we can't do the autoack due to # no "real" source (i.e., source was spoofed or source addr # has no route) if destnode and destnode != self.name: self.forward(revflow, destnode) else: self.forward(flowlet, destnode) def __should_make_acknowledgement_flow(self, flowlet): return self.autoack and flowlet.ipproto == IPPROTO_TCP and (not flowlet.ackflow) def forward(self, flowlet, destnode): nextnode = self.nextHop(flowlet.dstaddr) port = self.portFromNexthopNode(nextnode, flowkey=flowlet.key) link = port.link or self.default_link link.flowlet_arrival(flowlet, self.name, destnode)
class Rib: def __init__(self, fib): self._fib = fib # 1. The real production code for the RIB (but not the FIB) needs Destination objects and # Route objects to support multiple routes to the same destination (prefix): one per owner # (e.g. south-SPF and north-SPF). This prototype only supports a single route per # destination (prefix) to keep things simple and avoid distracting attention from the # negative disaggregation algorithm. # 2. The PyTricia code takes prefixes as strings. Not sure if this is the best choice # for production code. I suspect it is better to accept prefixes as Prefix objects that # have an internal binary representation. self._routes = PyTricia() def put_route(self, prefix, positive_nexthops, negative_nexthops=None): # Put the route in the RIB. if negative_nexthops is None: negative_nexthops = [] route = RibRoute(prefix, positive_nexthops, negative_nexthops) self._routes[prefix] = route # Gather the entire subtree of prefixes (children, grandchildren, ...) below the given # prefix (including the prefix itself). We rely on the fact that PyTricia.children always # returns parent aggregate routes before child more specific routes (although the PyTricia # documentation doesn't guarantee this). subtree_prefixes = [prefix] + self._routes.children(prefix) # Recompute the computed nexthops of child routes in the subtree and update the FIB # accordingly. self._recompute_subtree_nexthops(subtree_prefixes) def del_route(self, prefix): # If the route is not present in the RIB (this is legal), we have nothing to do. if not self._routes.has_key(prefix): return # Gather the entire subtree of prefixes (children, grandchildren, ...) below the given # prefix (excluding the prefix itself). We rely on the fact that PyTricia.children always # returns parent aggregate routes before child more specific routes (although the PyTricia # documentation doesn't guarantee this). subtree_prefixes = self._routes.children(prefix) # Delete the route from the RIB. del self._routes[prefix] # Delete corresponding route from the FIB. self._fib.del_route(prefix) # Recompute the computed nexthops of child routes in the subtree and update the FIB # accordingly. self._recompute_subtree_nexthops(subtree_prefixes) def get_route(self, prefix): if self._routes.has_key(prefix): return self._routes[prefix] return None def __str__(self): rep_str = "" for prefix in self._routes: rep_str += f"{self._routes[prefix]}\n" return rep_str def _recompute_subtree_nexthops(self, subtree_prefixes): for prefix in subtree_prefixes: route = self._routes[prefix] self._recompute_route_nexthops(route) self._fib.put_route(prefix, route.computed_nexthops) def _recompute_route_nexthops(self, route): # If the route does not have any negative nexthops, there is no disaggregation to be done. if not route.negative_nexthops: route.set_computed_nexthops(route.positive_nexthops) return # If the route does not have a parent route, there is no disaggregation to be done. parent_prefix = self._routes.parent(route.prefix) if parent_prefix is None: route.set_computed_nexthops(route.positive_nexthops) return # Compute the complementary nexthops of the negative nexthops. parent_route = self._routes[parent_prefix] complementary_nexthops = parent_route.computed_nexthops.difference( route.negative_nexthops) # Combine the complementary nexthops with the positive nexthops. computed_nexthops = route.positive_nexthops.union( complementary_nexthops) # Store the computed nexthops in the route route.set_computed_nexthops(computed_nexthops)
def get_routes(ifdict): def get_value(name, info, headers): if name == 'dst': dstip = info[headers.index('Destination')] if dstip == 'default': dstip = '0.0.0.0/0' if 'Genmask' in headers: dstip = "{}/{}".format(dstip, info[headers.index('Genmask')]) try: rv = IPv4Network(normalize_ipv4(dstip), strict=False) return rv except: return None elif name == 'gw': return info[headers.index('Gateway')] elif name == 'iface': if 'Iface' in headers: return info[headers.index('Iface')] elif 'Netif' in headers: return info[headers.index('Netif')] def is_ipaddr(s): return re.match(r'\d+(\.\d+){2,3}', s) def normalize_ipv4(s): if '/' in s: prefix, plen = s.split('/') while prefix.count('.') < 3: prefix += ".0" s = '/'.join((prefix, plen)) return s def is_ethaddr(s): return re.match(r'[0-9a-fA-F]{1,2}(:[0-9a-fA-F]{1,2}){5}', s) routes = PyTricia(32) gwips = defaultdict(list) cmd = "netstat -r -n" if sys.platform == 'darwin': cmd += ' -f inet' s = check_output(cmd, shell=True, universal_newlines=True) headers = [] for line in s.split('\n'): dst = gwip = iface = None if line.startswith('Destination'): headers = line.split() elif len(line) > 0 and \ (line[0].isdigit() or line.startswith('default')): info = line.split() dst = get_value('dst', info, headers) if dst is None: continue gwip = get_value('gw', info, headers) iface = get_value('iface', info, headers) # skip localhost and multicast if dst.is_multicast or dst.is_loopback: continue if iface not in ifdict: continue if is_ipaddr(gwip): gwip = IPv4Address(gwip) elif is_ethaddr(gwip): continue else: gwip = IPv4Address('0.0.0.0') # print(dst,gwip,iface) if routes.has_key(dst): # print("Already have nh for {}: {}".format(dst, routes[dst])) ii = routes[dst] else: nh = NextHop(dst, iface, gwip) routes[dst] = nh return routes
class Router(Node): __slots__ = ['autoack', 'forwarding_table', 'default_link', 'trafgen_ip'] def __init__(self, name, measurement_config, **kwargs): Node.__init__(self, name, measurement_config, **kwargs) self.autoack=bool(eval(str(kwargs.get('autoack','False')))) self.forwarding_table = PyTricia(32) self.default_link = None from fslib.configurator import FsConfigurator ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ] self.add_link(NullLink, ipa, ipb, 'remote') self.trafgen_ip = str(ipa) def setDefaultNextHop(self, nexthop): '''Set up a default next hop route. Assumes that we just select the first link to the next hop node if there is more than one.''' self.logger.debug("Default: {}, {}".format(nexthop, str(self.node_to_port_map))) self.default_link = self.portFromNexthopNode(nexthop).link if not self.default_link: raise ForwardingFailure("Error setting default next hop: there's no static ARP entry to get interface") self.logger.debug("Setting default next hop for {} to {}".format(self.name, nexthop)) def addForwardingEntry(self, prefix, nexthop): '''Add new forwarding table entry to Node, given a destination prefix and a nexthop (node name)''' pstr = str(prefix) self.logger.debug("Adding forwarding table entry: {}->{}".format(pstr, nexthop)) xnode = self.forwarding_table.get(pstr, None) if not xnode: xnode = [] self.forwarding_table[pstr] = xnode xnode.append(nexthop) def removeForwardingEntry(self, prefix, nexthop): '''Remove an entry from the Node forwarding table.''' pstr = str(prefix) if not self.forwarding_table.has_key(pstr): return xnode = self.forwarding_table.get(pstr) xnode.remove(nexthop) if not xnode: del self.forwarding_table[pstr] def nextHop(self, flowlet, destnode): '''Return the next hop from the local forwarding table (next node, ipaddr), based on destination IP address (or prefix)''' #if flowlet.fl flowID=str(flowlet.srcaddr)+' '+str(flowlet.dstaddr)+' '+str(flowlet.ipproto)+' '+str(flowlet.srcport)+' ' +str(flowlet.dstport) flowhash=hash(flowID) try: if flowlet.iselephant==False: nexthopNum=networkControlLogic2module.vectorSizeCalc(networkControlLogic2object.RoutingTable[int(self._Node__name.strip('H'))-1][int(destnode.strip('H'))-1]) nexthopIndex=flowhash%nexthopNum nexthop='H'+str(networkControlLogic2object.RoutingTable[int(self._Node__name.strip('H'))-1][int(destnode.strip('H'))-1][nexthopIndex].first+1) else: if networkControlLogic2object.get_FlowCount(int(self._Node__name.strip('H'))-1, flowID)==0: print "NO FLOW ID exists \n" print "flowID ", flowID print " Current Hop ", int(self._Node__name.strip('H'))-1 nexthopNum=networkControlLogic2module.vectorSizeCalc(networkControlLogic2object.RoutingTable[int(self._Node__name.strip('H'))-1][int(destnode.strip('H'))-1]) nexthopIndex=flowhash%nexthopNum nexthop='H'+str(networkControlLogic2object.RoutingTable[int(self._Node__name.strip('H'))-1][int(destnode.strip('H'))-1][nexthopIndex].first+1) else: #nexthop='H'+str(networkControlLogic2object.FlowRoutingTable[int(self._Node__name.strip('H'))-1][flowID].first+1) print "No FlowRouting Tables" except: print "Error finding Nexthop" pass print "Nexthop", nexthop return nexthop #raise ForwardingFailure() def flowlet_arrival(self, flowlet, prevnode, destnode, input_ip=None): if input_ip is None: input_ip = self.trafgen_ip input_port = self.ports[input_ip] if isinstance(flowlet, SubtractiveFlowlet): killlist = [] ok = [] for k,flet in self.flow_table.iteritems(): if next(flowlet.action) and (not flowlet.srcaddr or flet.srcaddr in flowlet.srcaddr) and (not flowlet.dstaddr or flet.dstaddr in flowlet.dstaddr) and (not flowlet.ipproto or flet.ipproto == flowlet.ipproto): killlist.append(k) else: ok.append(k) for kkey in killlist: del self.flow_table[kkey] if destnode != self.name: self.forward(flowlet, destnode) return # a "normal" Flowlet object self.measure_flow(flowlet, prevnode, str(input_port.localip)) if flowlet.endofflow: self.unmeasure_flow(flowlet, prevnode) if destnode==self._Node__name: #print "receivedtrafficvolume ",flowlet.size networkControlLogic2object.trafficVolumeInTransit[int(flowlet.srcaddr.split('.')[3])-1][int(flowlet.dstaddr.split('.')[3])-1]-=flowlet.size print "networkControlLogic2object.trafficVolumeInTransit[",int(flowlet.srcaddr.split('.')[3])-1,"][",int(flowlet.dstaddr.split('.')[3])-1,"]-=",flowlet.size,";" if flowlet.iselephant==True: networkControlLogic2object.elephentsVolumeInTransit[int(flowlet.srcaddr.split('.')[3])-1][int(flowlet.dstaddr.split('.')[3])-1]-=flowlet.size print "networkControlLogic2object.elephentsVolumeInTransit[",int(flowlet.srcaddr.split('.')[3])-1,"][",int(flowlet.dstaddr.split('.')[3])-1,"]-=",flowlet.size,";" if flowlet.endofflow and destnode==self._Node__name and flowlet.iselephant==True: #print "elephantFlowEnd" flowduration=flowlet.flowend-flowlet.flowstart flowID=str(flowlet.key[0])+' '+str(flowlet.key[1])+' '+str(flowlet.key[2])+' '+str(flowlet.key[3])+' '+str(flowlet.key[4]) status=networkControlLogic2object.elephantFlowEnd(flowID,int(flowlet.key[0].split('.')[3])-1,int(flowlet.key[1].split('.')[3])-1,long((fscore().now+flowlet.flowstarttime+flowduration)*(10**12))) print "networkControlLogic2object.elephantFlowEnd('",flowID,"',",int(flowlet.key[0].split('.')[3])-1,",",int(flowlet.key[1].split('.')[3])-1,",",long((fscore().now+flowlet.flowstarttime+flowduration)*(10**12)),");" if status==0: print "printf( \"elephantFlowEnd;Route table Not Changed\");" elif status==1: print "printf( \"elephantFlowEnd;Route table Changed\");" elif status==2: print "printf( \"elephantFlowEnd;Some Error\");" if destnode == self.name: if self.__should_make_acknowledgement_flow(flowlet): revflow = Flowlet(flowlet.flowident.mkreverse()) revflow.ackflow = True revflow.flowstart = revflow.flowend = fscore().now if flowlet.tcpflags & 0x04: # RST return if flowlet.tcpflags & 0x02: # SYN revflow.tcpflags = revflow.tcpflags | 0x10 # print 'setting syn/ack flags',revflow.tcpflagsstr if flowlet.tcpflags & 0x01: # FIN revflow.tcpflags = revflow.tcpflags | 0x10 # ack revflow.tcpflags = revflow.tcpflags | 0x01 # fin revflow.pkts = flowlet.pkts / 2 # brain-dead ack-every-other revflow.bytes = revflow.pkts * 40 self.measure_flow(revflow, self.name, input_port) # weird, but if reverse flow is short enough, it might only # stay in the flow cache for a very short period of time if revflow.endofflow: self.unmeasure_flow(revflow, prevnode) destnode = fscore().topology.destnode(self.name, revflow.dstaddr) # guard against case that we can't do the autoack due to # no "real" source (i.e., source was spoofed or source addr # has no route) if destnode and destnode != self.name: self.forward(revflow, destnode) else: self.forward(flowlet, destnode) def __should_make_acknowledgement_flow(self, flowlet): return self.autoack and flowlet.ipproto == IPPROTO_TCP and (not flowlet.ackflow) def forward(self, flowlet, destnode): nextnode="" try: nextnode = self.nextHop(flowlet,destnode) except: pass # if flowlet.iselephant==True and nextnode=='H'+str(flowlet.dstaddr.split('.')[3]) # status=networkControlLogic2object.elephantFlowEnd(flowID,int(flet.key[0].split('.')[3])-1,int(flet.key[1].split('.')[3])-1,long((fscore().now+flet.flowstarttime+flowduration)*(10**12))) # if status==0: # print "Route table Not Changed" # elif status==1: # print "Route table Changed" # elif status==2: # print "Some Error" port="" if nextnode!="": port = self.portFromNexthopNode(nextnode, flowkey=flowlet.key) if port!="": if port!=None: link = port.link or self.default_link link.flowlet_arrival(flowlet, self.name, destnode)
class Router(Node): __slots__ = ['autoack', 'forwarding_table', 'default_link', 'trafgen_ip'] def __init__(self, name, measurement_config, **kwargs): Node.__init__(self, name, measurement_config, **kwargs) self.autoack=bool(eval(str(kwargs.get('autoack','False')))) self.forwarding_table = PyTricia(32) self.default_link = None from fslib.configurator import FsConfigurator ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ] self.add_link(NullLink, ipa, ipb, 'remote') self.trafgen_ip = str(ipa) def setDefaultNextHop(self, nexthop): '''Set up a default next hop route. Assumes that we just select the first link to the next hop node if there is more than one.''' self.logger.debug("Default: {}, {}".format(nexthop, str(self.node_to_port_map))) self.default_link = self.portFromNexthopNode(nexthop).link if not self.default_link: raise ForwardingFailure("Error setting default next hop: there's no static ARP entry to get interface") self.logger.debug("Setting default next hop for {} to {}".format(self.name, nexthop)) def addForwardingEntry(self, prefix, nexthop): '''Add new forwarding table entry to Node, given a destination prefix and a nexthop (node name)''' pstr = str(prefix) self.logger.debug("Adding forwarding table entry: {}->{}".format(pstr, nexthop)) xnode = self.forwarding_table.get(pstr, None) if not xnode: xnode = [] self.forwarding_table[pstr] = xnode xnode.append(nexthop) def removeForwardingEntry(self, prefix, nexthop): '''Remove an entry from the Node forwarding table.''' pstr = str(prefix) if not self.forwarding_table.has_key(pstr): return xnode = self.forwarding_table.get(pstr) xnode.remove(nexthop) if not xnode: del self.forwarding_table[pstr] def nextHop(self, destip): '''Return the next hop from the local forwarding table (next node, ipaddr), based on destination IP address (or prefix)''' xlist = self.forwarding_table.get(str(destip), None) if xlist: return xlist[hash(destip) % len(xlist)] raise ForwardingFailure() def flowlet_arrival(self, flowlet, prevnode, destnode, input_ip=None): if input_ip is None: input_ip = self.trafgen_ip input_port = self.ports[input_ip] if isinstance(flowlet, SubtractiveFlowlet): killlist = [] ok = [] for k,flet in self.flow_table.iteritems(): if next(flowlet.action) and (not flowlet.srcaddr or flet.srcaddr in flowlet.srcaddr) and (not flowlet.dstaddr or flet.dstaddr in flowlet.dstaddr) and (not flowlet.ipproto or flet.ipproto == flowlet.ipproto): killlist.append(k) else: ok.append(k) for kkey in killlist: del self.flow_table[kkey] if destnode != self.name: self.forward(flowlet, destnode) return # a "normal" Flowlet object self.measure_flow(flowlet, prevnode, str(input_port.localip)) if flowlet.endofflow: self.unmeasure_flow(flowlet, prevnode) if destnode == self.name: if self.__should_make_acknowledgement_flow(flowlet): revflow = Flowlet(flowlet.flowident.mkreverse()) revflow.ackflow = True revflow.flowstart = revflow.flowend = fscore().now if flowlet.tcpflags & 0x04: # RST return if flowlet.tcpflags & 0x02: # SYN revflow.tcpflags = revflow.tcpflags | 0x10 # print 'setting syn/ack flags',revflow.tcpflagsstr if flowlet.tcpflags & 0x01: # FIN revflow.tcpflags = revflow.tcpflags | 0x10 # ack revflow.tcpflags = revflow.tcpflags | 0x01 # fin revflow.pkts = flowlet.pkts / 2 # brain-dead ack-every-other revflow.bytes = revflow.pkts * 40 self.measure_flow(revflow, self.name, input_port) # weird, but if reverse flow is short enough, it might only # stay in the flow cache for a very short period of time if revflow.endofflow: self.unmeasure_flow(revflow, prevnode) destnode = fscore().topology.destnode(self.name, revflow.dstaddr) # guard against case that we can't do the autoack due to # no "real" source (i.e., source was spoofed or source addr # has no route) if destnode and destnode != self.name: self.forward(revflow, destnode) else: self.forward(flowlet, destnode) def __should_make_acknowledgement_flow(self, flowlet): return self.autoack and flowlet.ipproto == IPPROTO_TCP and (not flowlet.ackflow) def forward(self, flowlet, destnode): nextnode = self.nextHop(flowlet.dstaddr) port = self.portFromNexthopNode(nextnode, flowkey=flowlet.key) link = port.link or self.default_link link.flowlet_arrival(flowlet, self.name, destnode)