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
Example #2
0
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
Example #3
0
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
Example #4
0
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)
Example #6
0
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
Example #7
0
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)
Example #8
0
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)