Example #1
0
    def __init__(self, name, measurement_config, **kwargs):
        Node.__init__(self, name, measurement_config, **kwargs)
        self.dpid = abs(hash(name))
        self.dstmac_cache = {}
        self.pox_switch = PoxBridgeSoftwareSwitch(self.dpid, name=name, 
            ports=0, miss_send_len=2**16, max_buffers=2**8, features=None)
        self.pox_switch.set_connection(self)
        self.pox_switch.set_output_packet_callback(self. send_packet)
        self.controller_name = kwargs.get('controller', 'controller')
        self.autoack = bool(eval(kwargs.get('autoack', 'False')))
        self.controller_links = {}
        self.interface_to_port_map = {}
        self.trace = bool(eval(kwargs.get('trace', 'False')))

        self.ipdests = PyTricia()
        for prefix in kwargs.get('ipdests','').split():
            self.ipdests[prefix] = True

        # explicitly add a localhost link/interface
        ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ]
        remotemac = default_ip_to_macaddr(ipb)
        self.add_link(NullLink, ipa, ipb, 'remote', remotemac=remotemac)
        self.trafgen_ip = str(ipa)
        self.trafgen_mac = remotemac
        self.dstmac_cache[self.name] = remotemac
Example #2
0
 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
Example #3
0
def sidr_reject_by_peer(SDXes, sdxi):
    """
    Reject policy by checking peers
    """
    policy = SDXes[sdxi]['policy']
    reject_cnt = 0
    tp_reject_cnt = 0
    peer_policy = [SDXes[p]['policy'] for p in SDXes[sdxi]['peers']]
    merge_peer_policy = PyTricia()
    for p_policy in peer_policy:
        for p in p_policy.keys():
            if p not in merge_peer_policy.keys():
                merge_peer_policy[p] = {}
            merge_peer_policy[p].update(p_policy[p])
    rejected_prefixes = set(policy.keys()).intersection(
        merge_peer_policy.keys())

    fp_rejected_policy = PyTricia()

    for prefix in rejected_prefixes:
        deflect_port = {p for p in policy[prefix].keys()}
        peer_deflect_port = {p for p in merge_peer_policy[prefix].keys()}
        reject_cnt += len(deflect_port)
        tp_reject_cnt += len(deflect_port.intersection(peer_deflect_port))
        fp_rejected_policy[prefix] = {
            p: 1
            for p in deflect_port if p not in peer_deflect_port
        }
    return reject_cnt, tp_reject_cnt, fp_rejected_policy
Example #4
0
    def __init__(self):
        super(ForwardingTable, self).__init__()

        # initialize logging
        self.logger = get_logger('FECFinder', 'INFO')

        # init Trie
        self.fib = PyTricia()
Example #5
0
    def __configure_routing(self):
        for n in self.graph:
            self.routing[n] = single_source_dijkstra_path(self.graph, n)

        self.ipdestlpm = PyTricia()
        for n, d in self.graph.nodes_iter(data=True):
            dlist = d.get('ipdests', '').split()
            for destipstr in dlist:
                ipnet = ipaddr.IPNetwork(destipstr)
                xnode = {}
                self.ipdestlpm[str(ipnet)] = xnode
                if 'dests' in xnode:
                    xnode['dests'].append(n)
                else:
                    xnode['net'] = ipnet
                    xnode['dests'] = [n]

        # install static forwarding table entries to each node

        # FIXME: there's a problematic bit of code here that triggers
        # pytricia-related (iterator) core dump
        for nodename, nodeobj in self.nodes.iteritems():
            if isinstance(nodeobj, Router):
                for prefix in self.ipdestlpm.keys():
                    lpmnode = self.ipdestlpm.get(prefix)
                    if nodename not in lpmnode['dests']:
                        routes = self.routing[nodename]
                        for d in lpmnode['dests']:
                            try:
                                path = routes[d]
                            except KeyError:
                                self.logger.warn(
                                    "No route from {} to {}".format(
                                        nodename, d))
                                continue

                            nexthop = path[1]
                            nodeobj.addForwardingEntry(prefix, nexthop)

        self.owdhash = {}
        for a in self.graph:
            for b in self.graph:
                key = a + ':' + b

                rlist = [a]
                while rlist[-1] != b:
                    nh = self.nexthop(rlist[-1], b)
                    if not nh:
                        self.logger.debug(
                            'No route from %s to %s (in owd; ignoring)' %
                            (a, b))
                        return None
                    rlist.append(nh)

                owd = 0.0
                for i in xrange(len(rlist) - 1):
                    owd += self.delay(rlist[i], rlist[i + 1])
                self.owdhash[key] = owd
Example #6
0
def initiate_bubblecast(G):
    """
    Initiate bubblecast table for each network in G.
    """
    for n in G.nodes():
        G.node[n]['qcast'] = PyTricia()
        G.node[n]['qcast-in'] = PyTricia()
        G.node[n]['dcast'] = PyTricia()
        G.node[n]['dcast-in'] = PyTricia()
Example #7
0
    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 __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()
Example #9
0
def fp_affected_volume(G, fp_rejected_policy, srcs, flows):
    """
    """
    src_trie = PyTricia()
    volume = 0
    for src in srcs:
        node_prefixes = G.node[src]['ip-prefixes']
        for prefix in node_prefixes:
            src_trie[prefix] = 1
    for flow in flows:
        if src_trie.get(flow['src_ip'], 0) and \
           fp_rejected_policy.get(flow['dst_ip'], {}).get(flow['dst_port'], 0):
            volume += flow['volume']
    return volume
Example #10
0
def initiate_ribs(G, overwrite=True):
    """
    Initiate ribs for each network in G.
    """
    for n in G.nodes():
        G.node[n]['rib'] = PyTricia()
        for prefix in G.node[n]['ip-prefixes']:
            update_initial_rib(G.node[n]['rib'], prefix, overwrite)
            # out_ribs = G.node[n]['adj-ribs-out']
            # for d in out_ribs:
            #     update_initial_rib(out_ribs[d], prefix, n)
        if overwrite:
            G.node[n]['adj-ribs-in'] = {n: PyTricia() for n in G.neighbors(n)}
            G.node[n]['adj-ribs-out'] = {n: PyTricia() for n in G.neighbors(n)}
Example #11
0
    def __init__(self, name, measurement_config, **kwargs):
        Node.__init__(self, name, measurement_config, **kwargs)
        self.dpid = abs(hash(name))
        self.dstmac_cache = {}
        self.pox_switch = PoxBridgeSoftwareSwitch(self.dpid, name=name, 
            ports=0, miss_send_len=2**16, max_buffers=2**8, features=None)
        self.pox_switch.set_connection(self)
        self.pox_switch.set_output_packet_callback(self. send_packet)
        self.controller_name = kwargs.get('controller', 'controller')
        self.autoack = bool(eval(kwargs.get('autoack', 'False')))
        self.controller_links = {}
        self.interface_to_port_map = {}
        self.trace = bool(eval(kwargs.get('trace', 'False')))
	self.tracePkt = bool(eval(kwargs.get('tracePkt','False')))

        self.ipdests = PyTricia()
        for prefix in kwargs.get('ipdests','').split():
            self.ipdests[prefix] = True

        # explicitly add a localhost link/interface
        ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ]
        remotemac = default_ip_to_macaddr(ipb)
        self.add_link(NullLink, ipa, ipb, 'remote', remotemac=remotemac)
        self.trafgen_ip = str(ipa)
        self.trafgen_mac = remotemac
        self.dstmac_cache[self.name] = remotemac
Example #12
0
def sidr_deflection_sim(G,
                        flows,
                        SDXes,
                        service_types=DEFAULT_SERVICE_TYPES,
                        network_ratio=0.5,
                        prefix_ratio=0.2,
                        **kwargs):
    """
    Simulate false-positive cases of SIDR
        SDXes: [sdx]
        sdx: (sdxi, dests, peers)
    """
    for sdxi in SDXes.keys():
        policy = PyTricia()
        for dest in SDXes[sdxi]['dests']:
            node_prefixes = G.node[dest]['ip-prefixes']
            sidr_random_deflect(node_prefixes, policy, service_types,
                                network_ratio, prefix_ratio)
        SDXes[sdxi]['policy'] = policy

    total_reject_cnt = 0
    total_tp_reject_cnt = 0
    total_fp_volume = 0
    for sdxi in SDXes.keys():
        reject_cnt, tp_reject_cnt, fp_rejected_policy = sidr_reject_by_peer(
            SDXes, sdxi)
        fp_volume = fp_affected_volume(G, fp_rejected_policy,
                                       SDXes[sdxi]['srcs'], flows)
        print(sdxi, reject_cnt, tp_reject_cnt, fp_volume)
        total_reject_cnt += reject_cnt
        total_tp_reject_cnt += tp_reject_cnt
        total_fp_volume += fp_volume
    print(total_reject_cnt, total_reject_cnt - total_tp_reject_cnt,
          total_fp_volume)
Example #13
0
    def __init__(self, debug=False):
        super(NetworkTopology, self).__init__()

        # name
        self.name = 'unnamed'

        # initialize logging
        self.debug = debug
        self.logger = get_logger('NetworkTopology', 'DEBUG' if debug else 'INFO')

        # dataplane engine that computes forwarding and dominator graphs
        self.dp_engine = None

        # dict of router name to router
        self.routers = dict()

        # mapping of router and interface to the next hop router
        self.next_hops = defaultdict(dict)

        # dict of all subnets in network to router and interface name directly on that subnet
        self.subnets = defaultdict(PyTricia)

        # dict of dst subnet to edges which are blocked by simple ACLs
        self.simple_acls = PyTricia()

        self.links = list()
Example #14
0
def sidr_peer_deflection_sim(G,
                             dests,
                             service_types=DEFAULT_SERVICE_TYPES,
                             network_ratio=0.5,
                             prefix_ratio=0.2,
                             **kwargs):
    """
    Simulate false-positive cases of SIDR between peer SDXes
    """
    peer1 = PyTricia()
    peer2 = PyTricia()
    for dest in dests:
        node_prefixes = G.node[dest]['ip-prefixes']
        sidr_random_deflect(node_prefixes, peer1, service_types, network_ratio,
                            prefix_ratio)
        sidr_random_deflect(node_prefixes, peer2, service_types, network_ratio,
                            prefix_ratio)
    total_reject_cnt, tp_reject_cnt = sidr_reject(peer1, peer2)
    print(total_reject_cnt, tp_reject_cnt)
Example #15
0
    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)
Example #16
0
    def __configure_routing(self):
        for n in self.graph:
            self.routing[n] = single_source_dijkstra_path(self.graph, n)

        self.ipdestlpm = PyTricia()
        for n,d in self.graph.nodes_iter(data=True):
            dlist = d.get('ipdests','').split()
            for destipstr in dlist:
                ipnet = ipaddr.IPNetwork(destipstr)
                xnode = {}
                self.ipdestlpm[str(ipnet)] = xnode
                if 'dests' in xnode:
                    xnode['dests'].append(n)
                else:
                    xnode['net'] = ipnet
                    xnode['dests'] = [ n ]

        # install static forwarding table entries to each node

        # FIXME: there's a problematic bit of code here that triggers
        # pytricia-related (iterator) core dump
        for nodename,nodeobj in self.nodes.iteritems():
            if isinstance(nodeobj, Router):
                for prefix in self.ipdestlpm.keys():
                    lpmnode = self.ipdestlpm.get(prefix)
                    if nodename not in lpmnode['dests']:
                        routes = self.routing[nodename]
                        for d in lpmnode['dests']:
                            try:
                                path = routes[d]
                            except KeyError:
                                self.logger.warn("No route from {} to {}".format(nodename, d)) 
                                continue
                                
                            nexthop = path[1]
                            nodeobj.addForwardingEntry(prefix, nexthop)
                
        self.owdhash = {}
        for a in self.graph:
            for b in self.graph:
                key = a + ':' + b
                
                rlist = [ a ]
                while rlist[-1] != b:
                    nh = self.nexthop(rlist[-1], b)
                    if not nh:
                        self.logger.debug('No route from %s to %s (in owd; ignoring)' % (a,b))
                        return None
                    rlist.append(nh)

                owd = 0.0
                for i in xrange(len(rlist)-1):
                    owd += self.delay(rlist[i],rlist[i+1])
                self.owdhash[key] = owd
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 #18
0
def read_topo(filepath):
    # type: (str) -> networkx.Graph
    topo = yaml.load(open(filepath))
    nodes = topo["nodes"]
    links = topo["links"]
    G = networkx.DiGraph()
    G.ip_prefixes = PyTricia()
    for node_name in nodes:
        node = nodes[node_name]
        node_id = node['id']
        G.add_node(node_id)
        node_obj = G.nodes[node_id]
        node_obj['id'] = node_id
        node_obj['type'] = node['type']
        node_obj['asn'] = node.get('asn', None)
        node_obj['ip-prefixes'] = node.get('ip-prefixes', set()) or set()
        node_obj['ip'] = node.get('ip', None)
        node_obj['providers'] = set(node.get('providers', []))
        node_obj['customers'] = set(node.get('customers', []))
        node_obj['peers'] = set(node.get('peers', []))
        node_obj['name'] = node_name
        if 'topology-name' in node:
            node_obj['topology-name'] = node['topology-name']
        for prefix in node_obj['ip-prefixes']:
            G.ip_prefixes[prefix] = node_id
    for link in links:
        G.add_edge(*link)
        G.add_edge(*link[::-1])
    for node_id in G.nodes():
        node_obj = G.node[node_id]
        node_obj['adj-ribs-in'] = {n: PyTricia() for n in G.neighbors(node_id)}
        node_obj['rib'] = PyTricia()
        node_obj['adj-ribs-out'] = {
            n: PyTricia()
            for n in G.neighbors(node_id)
        }
        node_obj['local_policy'] = PyTricia()
    return G
Example #19
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 #20
0
def transform(g: Graph, c_as):
    g.ip_prefixes = PyTricia()
    for n in g.nodes():
        cust = set()
        prov = set()
        peer = set()
        for n1, n2 in g.edges(n):
            e = g.edges[n1, n2]
            if e['rel'] == 'pc':
                cust.add(n2)
            elif e['rel'] == 'cp':
                prov.add(n2)
            elif e['rel'] == 'pp':
                peer.add(n2)
        # g.node[n]['asn'] = n
        g.node[n]['customers'] = cust
        g.node[n]['providers'] = prov
        g.node[n]['peers'] = peer
        g.node[n]['ip-prefixes'] = set()
        # g.node[n]['ip'] = None
    g.node[c_as]['ip-prefixes'].add("1.1.1.1/24")

    g.ip_prefixes["1.1.1.1/24"] = c_as
    for node_id in g.nodes():
        node_obj = g.node[node_id]
        node_obj['adj-ribs-in'] = {n: PyTricia() for n in g.neighbors(node_id)}
        node_obj['rib'] = PyTricia()
        node_obj['adj-ribs-out'] = {
            n: PyTricia()
            for n in g.neighbors(node_id)
        }
        node_obj['local_policy'] = PyTricia()

    for n in g.nodes():
        g.node[n]['rib'] = PyTricia()
        for prefix in g.node[n]['ip-prefixes']:
            update_initial_rib(g.node[n]['rib'], prefix, True)
            # out_ribs = G.node[n]['adj-ribs-out']
            # for d in out_ribs:
            #     update_initial_rib(out_ribs[d], prefix, n)
        g.node[n]['adj-ribs-in'] = {n: PyTricia() for n in g.neighbors(n)}
        g.node[n]['adj-ribs-out'] = {n: PyTricia() for n in g.neighbors(n)}
Example #21
0
def generate_funny_block_policy(G,
                                service_types=DEFAULT_SERVICE_TYPES,
                                network_ratio=0.5,
                                prefix_ratio=0.2,
                                policy_type='both',
                                **kwargs):
    magic_ratio = 3  # Magic!
    funny_ports = [
        p for p in range(1, 65536) if p not in DEFAULT_SERVICE_TYPES.keys()
    ]
    # funny_ports = [21, 80, 22, 23, 24, 25, 32, 44, 98]
    edge_networks = [
        n for n in G.nodes() if G.node[n].get('type', '') == 'edge'
    ]
    transit_networks = [
        n for n in G.nodes()
        if G.node[n].get('type', '') == 'transit' and n != 1
    ]
    for d in random.sample(
            transit_networks,
            math.ceil(
                len(transit_networks) * float(network_ratio) / magic_ratio)):
        # print(d)
        if 'local_policy' not in G.node[d]:
            G.node[d]['local_policy'] = PyTricia()
        local_policy = G.node[d]['local_policy']
        for dest in random.sample(
            [n for n in edge_networks if n != d],
                math.ceil(len(edge_networks) * float(network_ratio))):
            node_prefixes = G.node[dest]['ip-prefixes']
            for prefix in random.sample(
                    node_prefixes,
                    math.ceil(len(node_prefixes) * float(prefix_ratio))):
                local_policy[prefix] = {
                    port: None
                    for port in random.sample(funny_ports, random.randint(
                        1, 4))
                }
    return G
Example #22
0
def generate_fully_random_policy(G,
                                 service_types=DEFAULT_SERVICE_TYPES,
                                 network_ratio=0.5,
                                 prefix_ratio=0.2,
                                 policy_place='transit',
                                 policy_type='both',
                                 **kwargs):
    edge_networks = [
        n for n in G.nodes() if G.node[n].get('type', '') == 'edge'
    ]
    transit_networks = [
        n for n in G.nodes() if G.node[n].get('type', '') == 'transit'
    ]
    if policy_place == 'transit':
        policy_networks = transit_networks
    elif policy_place == 'edge':
        policy_networks = edge_networks
    else:
        policy_networks = G.nodes()
    for d in policy_networks:
        if 'local_policy' not in G.node[d]:
            G.node[d]['local_policy'] = PyTricia()
        local_policy = G.node[d]['local_policy']
        for dest in random.sample(
            [n for n in edge_networks if n != d],
                math.ceil(len(edge_networks) * float(network_ratio))):
            node_prefixes = G.node[dest]['ip-prefixes']
            for prefix in random.sample(
                    node_prefixes,
                    math.ceil(len(node_prefixes) * float(prefix_ratio))):
                local_policy[prefix] = {
                    port: gen_single_policy(G, d, dest, policy_type)
                    for port in random.sample(service_types.keys(),
                                              random.randint(1, 4))
                }
    return G
Example #23
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)   
Example #24
0
from pytricia import PyTricia

# FIXME: They are global variable and need to be removed
ip_prefixes = PyTricia()
global_policy = dict()  # type: dict[str, PyTricia]


def update_initial_rib(rib, prefix, overwrite=True):
    """
    Update the initial the rib for a prefix
    """
    if overwrite or prefix not in rib:
        rib[prefix] = {0: []}
    else:
        rib[prefix].update({0: []})


def initiate_ribs(G, overwrite=True):
    """
    Initiate ribs for each network in G.
    """
    for n in G.nodes():
        G.node[n]['rib'] = PyTricia()
        for prefix in G.node[n]['ip-prefixes']:
            update_initial_rib(G.node[n]['rib'], prefix, overwrite)
            # out_ribs = G.node[n]['adj-ribs-out']
            # for d in out_ribs:
            #     update_initial_rib(out_ribs[d], prefix, n)
        if overwrite:
            G.node[n]['adj-ribs-in'] = {n: PyTricia() for n in G.neighbors(n)}
            G.node[n]['adj-ribs-out'] = {n: PyTricia() for n in G.neighbors(n)}
Example #25
0
class Topology(NullTopology):
    def __init__(self, graph, nodes, links, traffic_modulators):
        self.logger = get_logger('fslib.config')
        self.__graph = graph
        self.nodes = nodes
        self.links = links
        self.traffic_modulators = traffic_modulators
        self.routing = {}
        self.ipdestlpm = None
        self.owdhash = {}
        self.__configure_routing()

        for a,b,d in self.graph.edges(data=True):
            if 'reliability' in d:
                self.__configure_edge_reliability(a,b,d['reliability'],d)

    @property
    def graph(self):
        return self.__graph

    def remove_node(self, name):
        self.__graph.remove_node(name)
        for n in self.graph:
            self.routing[n] = single_source_dijkstra_path(self.graph, n)

    def __configure_edge_reliability(self, a, b, relistr, edict):
        relidict = fsutil.mkdict(relistr)
        ttf = ttr = None
        for k,v in relidict.iteritems():
            if k == 'failureafter':
                ttf = eval(v)
                if isinstance(ttf, (int, float)):
                    ttf = modulation_generator([ttf])

            elif k == 'downfor':
                ttr = eval(v)
                if isinstance(ttr, (int, float)):
                    ttr = modulation_generator([ttr])

            elif k == 'mttf':
                ttf = eval(v)

            elif k == 'mttr':
                ttr = eval(v)

        if ttf or ttr:
            assert(ttf and ttr)
            xttf = next(ttf)
            fscore().after(xttf, 'link-failure-'+a+'-'+b, self.__linkdown, a, b, edict, ttf, ttr)

    def __configure_routing(self):
        for n in self.graph:
            self.routing[n] = single_source_dijkstra_path(self.graph, n)

        self.ipdestlpm = PyTricia()
        for n,d in self.graph.nodes_iter(data=True):
            dlist = d.get('ipdests','').split()
            for destipstr in dlist:
                ipnet = ipaddr.IPNetwork(destipstr)
                xnode = {}
                self.ipdestlpm[str(ipnet)] = xnode
                if 'dests' in xnode:
                    xnode['dests'].append(n)
                else:
                    xnode['net'] = ipnet
                    xnode['dests'] = [ n ]

        # install static forwarding table entries to each node

        # FIXME: there's a problematic bit of code here that triggers
        # pytricia-related (iterator) core dump
        for nodename,nodeobj in self.nodes.iteritems():
            if isinstance(nodeobj, Router):
                for prefix in self.ipdestlpm.keys():
                    lpmnode = self.ipdestlpm.get(prefix)
                    if nodename not in lpmnode['dests']:
                        routes = self.routing[nodename]
                        for d in lpmnode['dests']:
                            try:
                                path = routes[d]
                            except KeyError:
                                self.logger.warn("No route from {} to {}".format(nodename, d)) 
                                continue
                                
                            nexthop = path[1]
                            nodeobj.addForwardingEntry(prefix, nexthop)
                
        self.owdhash = {}
        for a in self.graph:
            for b in self.graph:
                key = a + ':' + b
                
                rlist = [ a ]
                while rlist[-1] != b:
                    nh = self.nexthop(rlist[-1], b)
                    if not nh:
                        self.logger.debug('No route from %s to %s (in owd; ignoring)' % (a,b))
                        return None
                    rlist.append(nh)

                owd = 0.0
                for i in xrange(len(rlist)-1):
                    owd += self.delay(rlist[i],rlist[i+1])
                self.owdhash[key] = owd


    def node(self, nname):
        '''get the node object corresponding to a name '''
        return self.nodes[nname]

    def start(self):
        for tm in self.traffic_modulators:
            tm.start()

        for nname,n in self.nodes.iteritems():
            n.start()

    def stop(self):
        for nname,n in self.nodes.iteritems():
            n.stop()     
            
    def __linkdown(self, a, b, edict, ttf, ttr):
        '''kill a link & recompute routing '''
        self.logger.info('Link failed %s - %s' % (a,b))
        self.graph.remove_edge(a,b)
        self.__configure_routing()

        uptime = None
        try:
            uptime = next(ttr)
        except:
            self.logger.info('Link %s-%s permanently taken down (no recovery time remains in generator)' % (a, b))
            return
        else:
            fscore().after(uptime, 'link-recovery-'+a+'-'+b, self.__linkup, a, b, edict, ttf, ttr)

        
    def __linkup(self, a, b, edict, ttf, ttr):
        '''revive a link & recompute routing '''
        self.logger.info('Link recovered %s - %s' % (a,b))
        self.graph.add_edge(a,b,weight=edict.get('weight',1),delay=edict.get('delay',0),capacity=edict.get('capacity',1000000))
        self.__configure_routing()

        downtime = None
        try:
            downtime = next(ttf)
        except:
            self.logger.info('Link %s-%s permanently going into service (no failure time remains in generator)' % (a, b))
            return
        else:
            fscore().after(downtime, 'link-failure-'+a+'-'+b, self.__linkdown, a, b, edict, ttf, ttr)


    def owd(self, a, b):
        '''get the raw one-way delay between a and b '''
        key = a + ':' + b
        rv = None
        if key in self.owdhash:
            rv = self.owdhash[key]
        return rv


    def delay(self, a, b):
        '''get the link delay between a and b '''
        d = self.graph.edge[a][b]
        if d is not None:
            return d.values()[0]['delay']
        return None

        
    def capacity(self, a, b):
        '''get the bandwidth between a and b '''
        d = self.graph.edge[a][b]
        if d is not None:
            return d.values()[0]['capacity']
        return None
        
    def nexthop(self, node, dest):
        '''
        return the next hop node for a given destination.
        node: current node
        dest: dest node name
        returns: next hop node name
        '''
        try:
            nlist = self.routing[node][dest]
        except:
            return None
        if len(nlist) == 1:
            return nlist[0]
        return nlist[1]

    def destnode(self, node, dest):
        '''
        return the destination node corresponding to a dest ip.
        node: current node
        dest: ipdest
        returns: destination node name
        '''
        # radix trie lpm lookup for destination IP prefix
        xnode = self.ipdestlpm.get(dest, None)

        if xnode:
            dlist = xnode['dests']
            best = None
            if len(dlist) > 1: 
                # in the case that there are multiple egress nodes
                # for the same IP destination, choose the closest egress
                best = None
                bestw = 10e6
                for d in dlist:
                    w = single_source_dijkstra_path_length(self.graph, node, d)
                    if w < bestw:
                        bestw = w
                        best = d
            else:
                best = dlist[0]

            return best
        else:
            raise InvalidRoutingConfiguration('No route for ' + dest)
Example #26
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 #27
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 #28
0
    def __configure_routing(self):
        for n in self.graph:
            self.routing[n] = single_source_dijkstra_path(self.graph, n)

        self.ipdestlpm = PyTricia()
        for n,d in self.graph.nodes_iter(data=True):
            dlist = d.get('ipdests','').split()
            for destipstr in dlist:
                ipnet = ipaddr.IPNetwork(destipstr)
                xnode = {}
                self.ipdestlpm[str(ipnet)] = xnode
                if 'dests' in xnode:
                    xnode['dests'].append(n)
                else:
                    xnode['net'] = ipnet
                    xnode['dests'] = [ n ]

        # install static forwarding table entries to each node
        #print "Routing Table"
        #for i in range(1,33):
        #   for j in range (1,33):
        #        if j!=i and j==20:
        #           print 'H'+str(i)+' H'+str(j),
        #           print networkControlLogic2object.RoutingTable[i-1][j-1][0].first+1

        # FIXME: there's a problematic bit of code here that triggers
        # pytricia-related (iterator) core dump
        for nodename,nodeobj in self.nodes.iteritems():
            if isinstance(nodeobj, Router):
                for prefix in self.ipdestlpm.keys():
                    lpmnode = self.ipdestlpm.get(prefix)
                    if nodename not in lpmnode['dests']:
                        routes = self.routing[nodename]
                        for d in lpmnode['dests']:
                            #path = routes[d]
                            nexthop = 'H1' #path[1]
                            #nexthop='H'+str(networkControlLogic2object.RoutingTable[int(nodename.strip('H'))-1][int(d.strip('H'))-1][0].first)
                            #flowhash=hash(nodename+d)
                            #print int(nodename.strip('H'))-1,
                            #print int(d.strip('H'))-1
                            #nexthopNum=networkControlLogic2module.vectorSizeCalc(networkControlLogic2object.RoutingTable[int(nodename.strip('H'))-1][int(d.strip('H'))-1])
                            #nexthopIndex=flowhash%nexthopNum
                            #nexthop='H'+str(networkControlLogic2object.RoutingTable[int(nodename.strip('H'))-1][int(d.strip('H'))-1][nexthopIndex].first+1)
                            #nodeobj.addForwardingEntry(prefix, nexthop)
                
        self.owdhash = {}
        for a in self.graph:
            for b in self.graph:
                key = a + ':' + b
                
                rlist = [ a ]
                while rlist[-1] != b:
                    nh = self.nexthop(rlist[-1], b)
                    if not nh:
                        self.logger.debug('No route from %s to %s (in owd; ignoring)' % (a,b))
                        return None
                    rlist.append(nh)

                owd = 0.0
                for i in xrange(len(rlist)-1):
                    owd += self.delay(rlist[i],rlist[i+1])
                self.owdhash[key] = owd
Example #29
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 #30
0
class OpenflowSwitch(Node):
    __slots__ = ['dpid', 'pox_switch', 'controller_name', 'controller_links', 'ipdests', 
                 'interface_to_port_map', 'trafgen_ip', 'autoack', 'trafgen_mac', 'dstmac_cache',
                 'trace','tracePkt']

    def __init__(self, name, measurement_config, **kwargs):
        Node.__init__(self, name, measurement_config, **kwargs)
        self.dpid = abs(hash(name))
        self.dstmac_cache = {}
        self.pox_switch = PoxBridgeSoftwareSwitch(self.dpid, name=name, 
            ports=0, miss_send_len=2**16, max_buffers=2**8, features=None)
        self.pox_switch.set_connection(self)
        self.pox_switch.set_output_packet_callback(self. send_packet)
        self.controller_name = kwargs.get('controller', 'controller')
        self.autoack = bool(eval(kwargs.get('autoack', 'False')))
        self.controller_links = {}
        self.interface_to_port_map = {}
        self.trace = bool(eval(kwargs.get('trace', 'False')))
	self.tracePkt = bool(eval(kwargs.get('tracePkt','False')))

        self.ipdests = PyTricia()
        for prefix in kwargs.get('ipdests','').split():
            self.ipdests[prefix] = True

        # explicitly add a localhost link/interface
        ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ]
        remotemac = default_ip_to_macaddr(ipb)
        self.add_link(NullLink, ipa, ipb, 'remote', remotemac=remotemac)
        self.trafgen_ip = str(ipa)
        self.trafgen_mac = remotemac
        self.dstmac_cache[self.name] = remotemac

    @property
    def remote_macaddr(self):
        return self.trafgen_mac

    def send_packet(self, packet, port_num):
        '''Forward a data plane packet out a given port'''
        flet = packet_to_flowlet(packet)

        # has packet reached destination?
        if flet is None or self.ipdests.get(flet.dstaddr, None):
            return

        pinfo = self.ports[port_num]

        # self.logger.debug("Switch sending translated packet {}->{} from {}->{} on port {} to {}".format(packet, flet, flet.srcmac, flet.dstmac, port_num, pinfo.link.egress_name))
        pinfo.link.flowlet_arrival(flet, self.name, pinfo.remoteip)

    def send(self, ofmessage):
        '''Callback function for POX SoftwareSwitch to send an outgoing OF message
        to controller.'''
        if not self.started:
            # self.logger.debug("OF switch-to-controller deferred message {}".format(ofmessage))
            evid = 'deferred switch->controller send'
            fscore().after(0.0, evid, self.send, ofmessage)
        else:
            # self.logger.debug("OF switch-to-controller {} - {}".format(str(self.controller_links[self.controller_name]), ofmessage))
            clink = self.controller_links[self.controller_name]
            self.controller_links[self.controller_name].flowlet_arrival(OpenflowMessage(FlowIdent(), ofmessage), self.name, self.controller_name)

    def set_message_handler(self, *args):
        '''Dummy callback function for POX SoftwareSwitchBase'''
        pass

    def process_packet(self, poxpkt, inputport):
        '''Process an incoming POX packet.  Mainly want to check whether
        it's an ARP and update our ARP "table" state'''
        # self.logger.debug("Switch {} processing packet: {}".format(self.name, str(poxpkt)))
        if poxpkt.type == poxpkt.ARP_TYPE:
            if poxpkt.payload.opcode == pktlib.arp.REQUEST:
                self.logger.debug("Got ARP request: {}".format(str(poxpkt.payload)))
                arp = poxpkt.payload
                dstip = str(IPv4Address(arp.protodst))
                srcip = str(IPv4Address(arp.protosrc))
                if dstip in self.interface_to_port_map:
                    portnum = self.interface_to_port_map[dstip]
                    pinfo = self.ports[portnum]
                    if pinfo.remotemac == "ff:ff:ff:ff:ff:ff":
                        pinfo = PortInfo(pinfo.link, pinfo.localip, pinfo.remoteip, pinfo.localmac, str(arp.hwsrc))
                        self.ports[portnum] = pinfo
                        self.logger.debug("Learned MAC/IP mapping {}->{}".format(arp.hwsrc,srcip))
                        # self.logger.debug("Updated {} -> {}".format(portnum, self.ports))

    def __traceit(self, flowlet, pkt, input_port):
        # total demeter violation :-(
        tableentry = self.pox_switch.table.entry_for_packet(pkt, input_port)
        if tableentry is None:
            tableentry = 'No Match'
        self.logger.info("Flow table match for flowlet {} {} {} (packet {}): {}".format(flowlet.srcmac, flowlet.dstmac, str(flowlet), str(pkt), tableentry))
	
    def packetInDebugger(self, flowlet, prevnode, destnode, input_intf):
	''' Packet trace logic to trace at the flowlet level '''
	# Needs to be changed if controller messages are to be ignored
	# Wrote this way because it will be easier to extend this to a replay capability
	input_port = None
	table_entry = 'No match'
	actions = None
	printString = None
	if isinstance(flowlet,PoxFlowlet):
	    input_port = self.interface_to_port_map[input_intf]
	    table_entry = self.pox_switch.table.entry_for_packet(flowlet.origpkt, input_port)
            printString = "'{}' to '{}' via '{}'. Flow table match for flowlet {} {} {} (packet {}): {}".format(prevnode, destnode, self.name, flowlet.srcmac, flowlet.dstmac, str(flowlet), flowlet.origpkt, table_entry)
	else:
	    printString = "'{}' to '{}' via '{}'.".format(prevnode, destnode, self.name)
	self.logger.info(printString)

	# from fsdb import pdb as bp
	# bp.set_trace()
	
    def flowlet_arrival(self, flowlet, prevnode, destnode, input_intf=None):
        '''Incoming flowlet: determine whether it's a data plane flowlet or whether it's an OF message
        coming back from the controller'''
        if input_intf is None:
            input_intf = self.trafgen_ip
	
	if self.tracePkt:
	    self.packetInDebugger(flowlet, prevnode, destnode, input_intf)
	
        if isinstance(flowlet, OpenflowMessage):
            self.logger.debug("Received from controller: {}".format(flowlet.ofmsg))
            ofmsg = None
            if isinstance(flowlet.ofmsg, oflib.ofp_base):
                ofmsg = flowlet.ofmsg
            elif isinstance(flowlet.ofmsg, str):
                ofhdr = oflib.ofp_header()
                ofhdr.unpack(flowlet.ofmsg)
                ofmsg = oflib._message_type_to_class[ofhdr.header_type]()
                ofmsg.unpack(flowlet.ofmsg)
            else:
                raise UnhandledPoxPacketFlowletTranslation("Not an openflow message from controller: {}".format(flowlet.ofmsg))
            self.pox_switch.rx_message(self, ofmsg)
            if self.trace:
                for i,entry in enumerate(self.pox_switch.table.entries):
                    actions = '::'.join([oflib.ofp_action_type_map[a.type] + ' ' + repr(a) for a in entry.actions])
                    self.logger.info("Flow table entry {}: {} (actions: {})".format(i+1, str(entry), actions))

        elif isinstance(flowlet, PoxFlowlet):
            self.logger.debug("Received PoxFlowlet: {}".format(str(flowlet.origpkt)))
            input_port = self.interface_to_port_map[input_intf]
            self.process_packet(flowlet.origpkt, input_port)
            if self.trace:
                self.__traceit(flowlet, flowlet.origpkt, input_port)
            self.pox_switch.rx_packet(flowlet.origpkt, input_port)

        elif isinstance(flowlet, Flowlet):
            input_port = self.interface_to_port_map[input_intf]
            portinfo = self.ports[input_port]
            # self.logger.info("Received flowlet in {} intf{} dstmac{} plocal{}  --  {}".format(self.name, input_intf, flowlet.dstmac, portinfo.localmac, type(flowlet)))

            if portinfo.link is NullLink:
                flowlet.srcmac = portinfo.remotemac
                dstmac = self.dstmac_cache.get(destnode, None)
                if dstmac is None:
                    self.dstmac_cache[destnode] = dstmac = fscore().topology.node(destnode).remote_macaddr
                flowlet.dstmac = dstmac
                # self.logger.debug("Local flowlet: setting MAC addrs as {}->{}".format(flowlet.srcmac, flowlet.dstmac))
            #else:
            #    # self.logger.debug("Non-local flowlet: MAC addrs {}->{}".format(flowlet.srcmac, flowlet.dstmac))

            self.measure_flow(flowlet, prevnode, input_intf)
            # assume this is an incoming flowlet on the dataplane.  
            # reformat it and inject it into the POX switch
            # self.logger.debug("Flowlet arrival in OF switch {} {} {} {} {}".format(self.name, flowlet.dstaddr, prevnode, destnode, input_intf))

            pkt = flowlet_to_packet(flowlet)
            pkt.flowlet = None

            if self.trace:
                self.__traceit(flowlet, pkt, input_port)

            self.pox_switch.rx_packet(pkt, input_port)

            if self.ipdests.get(flowlet.dstaddr, None):
                self.logger.debug("Flowlet arrived at destination {}".format(self.name))
                if self.autoack and not flowlet.ackflow:
                    self.send_ack_flow(flowlet, input_intf)
        else:
            raise UnhandledPoxPacketFlowletTranslation("Unexpected message in OF switch: {}".format(type(flowlet)))

    def send_ack_flow(self, flowlet, input_intf):
        # print "constructing ack flow:", self.name, flowlet, prevnode, destnode, input_intf
        revident = flowlet.ident.mkreverse()
        revflet = Flowlet(revident)
        revflet.srcmac,revflet.dstmac = flowlet.dstmac,flowlet.srcmac
        revflet.ackflow = True
        revflet.pkts = flowlet.pkts/2
        revflet.bytes = revflet.pkts * 40
        revflet.iptos = flowlet.iptos
        revflet.tcpflags = flowlet.tcpflags
        revflet.ingress_intf = input_intf
        revflet.flowstart = fscore().now
        revflet.flowend = revflet.flowstart
        destnode = fscore().topology.destnode(self.name, revflet.dstaddr)
        # self.logger.debug("Injecting reverse flow: {}->{}".format(revflet.srcmac, revflet.dstmac))
        self.flowlet_arrival(revflet, self.name, destnode)

    def add_link(self, link, localip, remoteip, next_node, remotemac='ff:ff:ff:ff:ff:ff'):
        localip = str(localip)
        remoteip = str(remoteip)
        if next_node == self.controller_name:
            self.logger.debug("Adding link to {}: {}".format(self.name, link))
            self.controller_links[self.controller_name] = link
        else:
            portnum = len(self.ports)+1
            self.pox_switch.add_port(portnum)
            ofport = self.pox_switch.ports[portnum]
            # let pox create local hw_addr and just use it
            localmac = str(ofport.hw_addr)
            pi = PortInfo(link, localip, remoteip, localmac, remotemac)
            self.ports[portnum] = pi
            self.node_to_port_map[next_node].append(portnum)
            self.interface_to_port_map[localip] = portnum
            self.logger.debug("New port in OF switch {}: {}".format(portnum, pi))

    def send_gratuitous_arps(self):
        '''Send ARPs for our own interfaces to each connected node'''
        for pnum,pinfo in self.ports.iteritems():
            # construct an ARP request for one of our known interfaces.
            # controller isn't included in any of these ports, so these
            # are only ports connected to other switches
            arp = pktlib.arp()
            arp.opcode = pktlib.arp.REQUEST 
            arp.hwsrc = EthAddr(pinfo.localmac)
            arp.protosrc = int(IPv4Address(pinfo.localip))
            arp.protodst = int(IPv4Address(pinfo.remoteip))
            ethernet = pktlib.ethernet()
            ethernet.dst = pktlib.ETHER_BROADCAST
            ethernet.src = EthAddr(pinfo.localmac)
            ethernet.payload = arp
            ethernet.type = ethernet.ARP_TYPE
            flet = packet_to_flowlet(ethernet)
            pinfo.link.flowlet_arrival(flet, self.name, pinfo.link.egress_node_name)

    def start(self):
        Node.start(self)
        fscore().after(0.010, "arp {}".format(self.name), self.send_gratuitous_arps)
        self.logger.debug("OF Switch Startup: {}".format(dpid_to_str(self.pox_switch.dpid)))
        for p in self.ports:
            self.logger.debug("\tSwitch port {}: {}, {}".format(p, self.ports[p], self.pox_switch.ports[p].show()))
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 __init__(self):
     self._routes = PyTricia()
Example #33
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)   
Example #34
0
def main() -> None:
    args = parser.parse_args()

    # Set source IP addresses
    addrs: Sequence[AnyIPAddress] = args.bind or list(_select_addrs())
    ipv4_src = next((a for a in addrs if isinstance(a, IPv4Address)), None)
    ipv6_src = next((a for a in addrs if isinstance(a, IPv6Address)), None)
    del addrs

    # Create blacklist patricia trees from multiple sources
    ipv4_bl: Optional["PyTricia[bool]"] = None if ipv4_src is None else PyTricia(32, socket.AF_INET)
    ipv6_bl: Optional["PyTricia[bool]"] = None if ipv6_src is None else PyTricia(128, socket.AF_INET6)
    for net in itertools.chain.from_iterable(args.blacklist):
        if isinstance(net, IPv4Network) and ipv4_bl is not None and net not in ipv4_bl:
            ipv4_bl[net] = True
        elif ipv6_bl is not None and isinstance(net, IPv6Network) and net not in ipv6_bl:
            ipv6_bl[net] = True

    duplicate_filter: Set[ScanHost] = set()
    output_mod = get_output_module(args.output)

    # Aggregate targets from multiple sources and filter them by IP version and blacklist
    # Discarded and filtered targets are output immediately to allow their memory to be freed
    def target_filter(tgt: ScanHost) -> bool:
        if isinstance(tgt.ip, IPv4Address):
            if ipv4_bl is None or any(tgt.ip in net for net in _IPV4_DISC_NETS) or tgt.ip.is_multicast:
                output_mod.discarded_target(tgt)
                return False
            elif tgt.ip in ipv4_bl or tgt in duplicate_filter:
                output_mod.filtered_target(tgt)
                return False
        elif isinstance(tgt.ip, IPv6Address):
            if ipv6_bl is None or any(tgt.ip in net for net in _IPV6_DISC_NETS) or tgt.ip.is_multicast:
                output_mod.discarded_target(tgt)
                return False
            elif tgt.ip in ipv6_bl or tgt in duplicate_filter:
                output_mod.filtered_target(tgt)
                return False
        else:
            raise ValueError("tgt is neither IPv4 nor IPv6")

        duplicate_filter.add(tgt)
        return True

    # Filtering happens lazily within this generator (whenever a new chunk is requested)
    tgt_gen: Iterable[ScanHost] = filter(target_filter, itertools.chain(
        args.target, itertools.chain.from_iterable(args.nmap),
        itertools.chain.from_iterable(args.zmap), itertools.chain.from_iterable(args.json)
    ))

    # Setup sockets/multiplexers
    # Both multiplexers share a token bucket with a precision of 1/8th of a second
    # and which allows bursts of up to half a second's worth of packets
    limiter = TokenBucket(args.rate // 8 or 1, 0.125, args.rate // 2 or 1)
    loop = asyncio.SelectorEventLoop()
    ipv4_plex = None if ipv4_src is None else IPv4TestMultiplexer(ipv4_src, limiter, loop=loop)
    ipv6_plex = None if ipv6_src is None else IPv6TestMultiplexer(ipv6_src, limiter, loop=loop)

    try:
        pairs: Iterable[Tuple[Type[BaseTest], Type[BaseTest]]] = zip(itertools.repeat(LivenessTest),
                                                                     parse_test_list(args.tests))
        active_tests = [test for pair in pairs for test in pair]
        active_tests.append(LivenessTest)  # Additional final liveness test
        del pairs
    except ValueError as e:
        parser.error(str(e))
        return  # not strictly necessary, but helps type checkers

    # Run tests sequentially for chunks of targets such that they don't
    # run into the packet rate limit. Since MAX_PACKET_RATE is generally
    # an overestimation, we do not include a safety buffer here.
    chunksize = round(args.rate / overall_packet_rate(active_tests))
    chunk_gen = (list(itertools.islice(tgt_gen, chunksize)) for _ in itertools.repeat(None))

    first = True
    for chunk in chunk_gen:
        output_mod.flush()  # chunk creation may have triggered new DESC/FLTR outputs
        if not chunk:
            if first:
                parser.error("at least one valid target is required")
            break  # chunk is empty -> tgt_gen is empty -> all targets have been processed

        first = False
        tgt_futs: Dict[ScanHost, List["asyncio.Future[TestResult]"]] = {tgt: [] for tgt in chunk}
        for test in active_tests:
            all_futs: List["asyncio.Future[TestResult]"] = []
            random.shuffle(chunk)
            src_port = next(_PORT_SEQ)
            ipv4_host = None if ipv4_src is None else ScanHost(ipv4_src, src_port)
            ipv6_host = None if ipv6_src is None else ScanHost(ipv6_src, src_port)

            # Passing the test as a default parameter to the lambda ensures
            # that the variable is not overwritten by further iterations of the loop
            for tgt in chunk:
                if isinstance(tgt.ip, IPv4Address):
                    t = test(ipv4_host, tgt, loop=loop)  # type: ignore
                    ipv4_plex.register_test(t)  # type: ignore
                    fut = loop.create_task(t.run())
                    fut.add_done_callback(lambda f, t=t: ipv4_plex.unregister_test(t))  # type: ignore
                else:
                    t = test(ipv6_host, tgt, loop=loop)  # type: ignore
                    ipv6_plex.register_test(t)  # type: ignore
                    fut = loop.create_task(t.run())
                    fut.add_done_callback(lambda f, t=t: ipv6_plex.unregister_test(t))  # type: ignore

                tgt_futs[tgt].append(fut)
                all_futs.append(fut)

            print("Running", test.__name__)
            loop.run_until_complete(asyncio.wait(all_futs, loop=loop))

            if test.FAIL_EARLY:
                # Skip further tests for targets with a failure in the current test
                chunk = [tgt for tgt, fut in zip(chunk, all_futs) if result_not_failed(fut)]
            time.sleep(3)

        for tgt, results in tgt_futs.items():
            output_mod(tgt, active_tests, results)
        output_mod.flush()
Example #35
0
class OpenflowSwitch(Node):
    __slots__ = ['dpid', 'pox_switch', 'controller_name', 'controller_links', 'ipdests', 
                 'interface_to_port_map', 'trafgen_ip', 'autoack', 'trafgen_mac', 'dstmac_cache',
                 'trace']

    def __init__(self, name, measurement_config, **kwargs):
        Node.__init__(self, name, measurement_config, **kwargs)
        self.dpid = abs(hash(name))
        self.dstmac_cache = {}
        self.pox_switch = PoxBridgeSoftwareSwitch(self.dpid, name=name, 
            ports=0, miss_send_len=2**16, max_buffers=2**8, features=None)
        self.pox_switch.set_connection(self)
        self.pox_switch.set_output_packet_callback(self. send_packet)
        self.controller_name = kwargs.get('controller', 'controller')
        self.autoack = bool(eval(kwargs.get('autoack', 'False')))
        self.controller_links = {}
        self.interface_to_port_map = {}
        self.trace = bool(eval(kwargs.get('trace', 'False')))

        self.ipdests = PyTricia()
        for prefix in kwargs.get('ipdests','').split():
            self.ipdests[prefix] = True

        # explicitly add a localhost link/interface
        ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ]
        remotemac = default_ip_to_macaddr(ipb)
        self.add_link(NullLink, ipa, ipb, 'remote', remotemac=remotemac)
        self.trafgen_ip = str(ipa)
        self.trafgen_mac = remotemac
        self.dstmac_cache[self.name] = remotemac

    @property
    def remote_macaddr(self):
        return self.trafgen_mac

    def send_packet(self, packet, port_num):
        '''Forward a data plane packet out a given port'''
        flet = packet_to_flowlet(packet)

        # has packet reached destination?
        if flet is None or self.ipdests.get(flet.dstaddr, None):
            return

        pinfo = self.ports[port_num]
        # self.logger.debug("Switch sending translated packet {}->{} from {}->{} on port {} to {}".format(packet, flet, flet.srcmac, flet.dstmac, port_num, pinfo.link.egress_name))
        pinfo.link.flowlet_arrival(flet, self.name, pinfo.remoteip)

    def send(self, ofmessage):
        '''Callback function for POX SoftwareSwitch to send an outgoing OF message
        to controller.'''
        if not self.started:
            # self.logger.debug("OF switch-to-controller deferred message {}".format(ofmessage))
            evid = 'deferred switch->controller send'
            fscore().after(0.0, evid, self.send, ofmessage)
        else:
            # self.logger.debug("OF switch-to-controller {} - {}".format(str(self.controller_links[self.controller_name]), ofmessage))
            clink = self.controller_links[self.controller_name]
            self.controller_links[self.controller_name].flowlet_arrival(OpenflowMessage(FlowIdent(), ofmessage), self.name, self.controller_name)

    def set_message_handler(self, *args):
        '''Dummy callback function for POX SoftwareSwitchBase'''
        pass

    def process_packet(self, poxpkt, inputport):
        '''Process an incoming POX packet.  Mainly want to check whether
        it's an ARP and update our ARP "table" state'''
        # self.logger.debug("Switch {} processing packet: {}".format(self.name, str(poxpkt)))
        if poxpkt.type == poxpkt.ARP_TYPE:
            if poxpkt.payload.opcode == pktlib.arp.REQUEST:
                self.logger.debug("Got ARP request: {}".format(str(poxpkt.payload)))
                arp = poxpkt.payload
                dstip = str(IPv4Address(arp.protodst))
                srcip = str(IPv4Address(arp.protosrc))
                if dstip in self.interface_to_port_map:
                    portnum = self.interface_to_port_map[dstip]
                    pinfo = self.ports[portnum]
                    if pinfo.remotemac == "ff:ff:ff:ff:ff:ff":
                        pinfo = PortInfo(pinfo.link, pinfo.localip, pinfo.remoteip, pinfo.localmac, str(arp.hwsrc))
                        self.ports[portnum] = pinfo
                        self.logger.debug("Learned MAC/IP mapping {}->{}".format(arp.hwsrc,srcip))
                        # self.logger.debug("Updated {} -> {}".format(portnum, self.ports))

    def __traceit(self, flowlet, pkt, input_port):
        # total demeter violation :-(
        tableentry = self.pox_switch.table.entry_for_packet(pkt, input_port)
        if tableentry is None:
            tableentry = 'No Match'
        self.logger.info("Flow table match for flowlet {} {} {} (packet {}): {}".format(flowlet.srcmac, flowlet.dstmac, str(flowlet), str(pkt), tableentry))

    def flowlet_arrival(self, flowlet, prevnode, destnode, input_intf=None):
        '''Incoming flowlet: determine whether it's a data plane flowlet or whether it's an OF message
        coming back from the controller'''
        if input_intf is None:
            input_intf = self.trafgen_ip

        if isinstance(flowlet, OpenflowMessage):
            self.logger.debug("Received from controller: {}".format(flowlet.ofmsg))
            ofmsg = None
            if isinstance(flowlet.ofmsg, oflib.ofp_base):
                ofmsg = flowlet.ofmsg
            elif isinstance(flowlet.ofmsg, str):
                ofhdr = oflib.ofp_header()
                ofhdr.unpack(flowlet.ofmsg)
                ofmsg = oflib._message_type_to_class[ofhdr.header_type]()
                ofmsg.unpack(flowlet.ofmsg)
            else:
                raise UnhandledPoxPacketFlowletTranslation("Not an openflow message from controller: {}".format(flowlet.ofmsg))
            self.pox_switch.rx_message(self, ofmsg)
            if self.trace:
                for i,entry in enumerate(self.pox_switch.table.entries):
                    actions = '::'.join([oflib.ofp_action_type_map[a.type] + ' ' + repr(a) for a in entry.actions])
                    self.logger.info("Flow table entry {}: {} (actions: {})".format(i+1, str(entry), actions))

        elif isinstance(flowlet, PoxFlowlet):
            self.logger.debug("Received PoxFlowlet: {}".format(str(flowlet.origpkt)))
            input_port = self.interface_to_port_map[input_intf]
            self.process_packet(flowlet.origpkt, input_port)
            if self.trace:
                self.__traceit(flowlet, flowlet.origpkt, input_port)
            self.pox_switch.rx_packet(flowlet.origpkt, input_port)

        elif isinstance(flowlet, Flowlet):
            input_port = self.interface_to_port_map[input_intf]
            portinfo = self.ports[input_port]
            # self.logger.info("Received flowlet in {} intf{} dstmac{} plocal{}  --  {}".format(self.name, input_intf, flowlet.dstmac, portinfo.localmac, type(flowlet)))

            if portinfo.link is NullLink:
                flowlet.srcmac = portinfo.remotemac
                dstmac = self.dstmac_cache.get(destnode, None)
                if dstmac is None:
                    self.dstmac_cache[destnode] = dstmac = fscore().topology.node(destnode).remote_macaddr
                flowlet.dstmac = dstmac
                # self.logger.debug("Local flowlet: setting MAC addrs as {}->{}".format(flowlet.srcmac, flowlet.dstmac))
            #else:
            #    # self.logger.debug("Non-local flowlet: MAC addrs {}->{}".format(flowlet.srcmac, flowlet.dstmac))

            self.measure_flow(flowlet, prevnode, input_intf)
            # assume this is an incoming flowlet on the dataplane.  
            # reformat it and inject it into the POX switch
            # self.logger.debug("Flowlet arrival in OF switch {} {} {} {} {}".format(self.name, flowlet.dstaddr, prevnode, destnode, input_intf))

            pkt = flowlet_to_packet(flowlet)
            pkt.flowlet = None

            if self.trace:
                self.__traceit(flowlet, pkt, input_port)

            self.pox_switch.rx_packet(pkt, input_port)

            if self.ipdests.get(flowlet.dstaddr, None):
                self.logger.debug("Flowlet arrived at destination {}".format(self.name))
                if self.autoack and not flowlet.ackflow:
                    self.send_ack_flow(flowlet, input_intf)
        else:
            raise UnhandledPoxPacketFlowletTranslation("Unexpected message in OF switch: {}".format(type(flowlet)))

    def send_ack_flow(self, flowlet, input_intf):
        # print "constructing ack flow:", self.name, flowlet, prevnode, destnode, input_intf
        revident = flowlet.ident.mkreverse()
        revflet = Flowlet(revident)
        revflet.srcmac,revflet.dstmac = flowlet.dstmac,flowlet.srcmac
        revflet.ackflow = True
        revflet.pkts = flowlet.pkts/2
        revflet.bytes = revflet.pkts * 40
        revflet.iptos = flowlet.iptos
        revflet.tcpflags = flowlet.tcpflags
        revflet.ingress_intf = input_intf
        revflet.flowstart = fscore().now
        revflet.flowend = revflet.flowstart
        destnode = fscore().topology.destnode(self.name, revflet.dstaddr)
        # self.logger.debug("Injecting reverse flow: {}->{}".format(revflet.srcmac, revflet.dstmac))
        self.flowlet_arrival(revflet, self.name, destnode)

    def add_link(self, link, localip, remoteip, next_node, remotemac='ff:ff:ff:ff:ff:ff'):
        localip = str(localip)
        remoteip = str(remoteip)
        if next_node == self.controller_name:
            self.logger.debug("Adding link to {}: {}".format(self.name, link))
            self.controller_links[self.controller_name] = link
        else:
            portnum = len(self.ports)+1
            self.pox_switch.add_port(portnum)
            ofport = self.pox_switch.ports[portnum]
            # let pox create local hw_addr and just use it
            localmac = str(ofport.hw_addr)
            pi = PortInfo(link, localip, remoteip, localmac, remotemac)
            self.ports[portnum] = pi
            self.node_to_port_map[next_node].append(portnum)
            self.interface_to_port_map[localip] = portnum
            self.logger.debug("New port in OF switch {}: {}".format(portnum, pi))

    def send_gratuitous_arps(self):
        '''Send ARPs for our own interfaces to each connected node'''
        for pnum,pinfo in self.ports.iteritems():
            # construct an ARP request for one of our known interfaces.
            # controller isn't included in any of these ports, so these
            # are only ports connected to other switches
            arp = pktlib.arp()
            arp.opcode = pktlib.arp.REQUEST 
            arp.hwsrc = EthAddr(pinfo.localmac)
            arp.protosrc = int(IPv4Address(pinfo.localip))
            arp.protodst = int(IPv4Address(pinfo.remoteip))
            ethernet = pktlib.ethernet()
            ethernet.dst = pktlib.ETHER_BROADCAST
            ethernet.src = EthAddr(pinfo.localmac)
            ethernet.payload = arp
            ethernet.type = ethernet.ARP_TYPE
            flet = packet_to_flowlet(ethernet)
            pinfo.link.flowlet_arrival(flet, self.name, pinfo.link.egress_node_name)

    def start(self):
        Node.start(self)
        fscore().after(0.010, "arp {}".format(self.name), self.send_gratuitous_arps)
        self.logger.debug("OF Switch Startup: {}".format(dpid_to_str(self.pox_switch.dpid)))
        for p in self.ports:
            self.logger.debug("\tSwitch port {}: {}, {}".format(p, self.ports[p], self.pox_switch.ports[p].show()))