def convert_topology(edges, hsf, portids): """ Convert a list of edges representing the topology into a topology transfer function. """ ttf = TF(hsf["length"] * 2) for (s1, p1, s2, p2) in edges: rule = TF.create_standard_rule([portids[(s1,p1)]], None, [portids[(s2,p2)]], None, None) ttf.add_link_rule(rule) rule = TF.create_standard_rule([portids[(s2,p2)]], None, [portids[(s1,p1)]], None, None) ttf.add_link_rule(rule) global TFS_FOLDER ttf.save_object_to_file("%s/topology.tf" % TFS_FOLDER)
def init_tfs(): """ Initialize one transfer function per device. """ tfs_map = {} for sw in sw_ports.keys(): tfs_map[sw] = TF(hsf["length"]) tfs_map[sw].set_prefix_id("s%d" % sw) tfs_map[sw].set_send_on_receiving_port(True) return tfs_map
def convert_classifier(classifier, hsf, portids, sw_ports): """Function to convert a classifier `classifier` into a header space given by a header space format dictionary `hsf`. """ hsalib_log = logging.getLogger('%s.convert_classifier' % __name__) def get_action_wc(acts): """Return a rewrite mask, rewrite HS, and the list of outports from the action list `acts` provided. For interpretation of these fields check the doctext of hsa.headerspace.tf create_standard_rule. In this function, we assume that the actions correspond to the actions of a single-stage table classifier, in addition to other restrictions. Specifically: - all non-`modify` actions are ignored - actions that do not set an output port are ignored - if there are multiple actions satisfying the above conditions, they may only simply output the packet via a port. i.e., No packet modifications. This is an artificial restriction because tf.create_standard_rule can only take a single rewrite wildcard for packet modification. """ new_acts = filter(lambda x: isinstance(x, modify) and 'port' in x.map, acts) outports = [] '''The current transfer function rule implementation can only take one new header space in rewrite rules if there are multiple outports involved. If there is only one action, multiple field rewrites are possible. A port must be set for rewrites to be active. ''' if len(new_acts) > 1: hsa_compilable = reduce(lambda acc, a: acc and a.map.keys() == ['port'], new_acts, True) outports = map(lambda x: x.map['port'], new_acts) if hsa_compilable: return (None, None, outports, False) else: raise RuntimeError("actions not HSA-compilable! %s" % acts) elif len(new_acts) == 1: mod_dict = copy.copy(new_acts[0].map) if 'port' in mod_dict: outports = [mod_dict['port']] del mod_dict['port'] else: return (None, None, [], False) if len(mod_dict.keys()) > 0: ''' There are rewritten packet header fields. ''' rw_rule = True mask = wildcard_create_bit_repeat(hsf["length"], 0x2) rewrite = wildcard_create_bit_repeat(hsf["length"], 0x1) for (field, val) in mod_dict.iteritems(): set_field_val(hsf, mask, field, 0, process_field=False) set_field_val(hsf, rewrite, field, val) return (mask, rewrite, outports, True) else: return (None, None, outports, False) else: ''' No modify actions ''' return (None, None, [], False) def process_outports(mat_map, acts_ports, sw, rule): ''' Return network-wide unique port identifiers from the port values present in classifier actions. ''' def warn_nonexistent_port(p): print sw_ports hsalib_log.warn("Non-existent port specified: " "(sw %d, p %d) in %s" % ( sw, p, str(rule))) out_ports = [] for p in acts_ports: if p in sw_ports[sw]: out_ports.append(portids[(sw,p)]) elif p == OFPP_IN_PORT: if 'port' in mat_map: out_ports.append(portids[(sw,mat_map['port'])]) else: warn_nonexistent_port(p) else: warn_nonexistent_port(p) return out_ports def init_tfs(): """ Initialize one transfer function per device. """ tfs_map = {} for sw in sw_ports.keys(): tfs_map[sw] = TF(hsf["length"]) tfs_map[sw].set_prefix_id("s%d" % sw) tfs_map[sw].set_send_on_receiving_port(True) return tfs_map def save_tfs(tfs_map): """ Save a dictionary of transfer functions to various files. """ global TFS_FOLDER for (sw, tf) in tfs_map.iteritems(): tf.save_object_to_file('%s/s%d.tf' % (TFS_FOLDER, sw)) ''' Classifier conversion core logic begins here. ''' tfs_map = init_tfs() for sw in sw_ports.keys(): tf = tfs_map[sw] for rule in classifier.rules: try: mat_map = {} if rule.match == identity else rule.match.map except: raise RuntimeError("unexpected rule match in classifier!") # determine first if this rule applicable to switch sw if (('switch' in mat_map and mat_map['switch'] == sw) or (not 'switch' in mat_map)): mat_wc = get_match_wc(hsf, mat_map) in_ports = get_ports(hsf, portids, sw_ports, mat_map, sw) (mask, rewrite, outports, rw) = get_action_wc(rule.actions) out_ports = process_outports(mat_map, outports, sw, rule) rule = TF.create_standard_rule(in_ports, mat_wc, out_ports, mask, rewrite) if rw: tf.add_rewrite_rule(rule) else: tf.add_fwd_rule(rule) save_tfs(tfs_map)