def test_unpack_weird_header(self): """ Test the unpacking of a header we don't have a class for """ # Make a weird header... class nxm_weird(nx._nxm_maskable, nx._nxm_numeric_entry): _nxm_type = nx._make_type(0xdead, 0x42) _nxm_length = 4 original = nx.nx_reg_load(dst=nxm_weird, value=42, nbits=32) original_packed = original.pack() # Currently, the action unpacking API still sucks... unoriginal = nx.nx_reg_load() offset = unoriginal.unpack(original_packed, 0) self.assertEqual(offset, len(original_packed), "Didn't unpack entire entry") unoriginal_packed = unoriginal.pack() self.assertEqual(unoriginal.dst.__name__, "NXM_UNKNOWN_dead_42", "Didn't generate new class correctly?") self.assertEqual(original_packed, unoriginal_packed, "Pack/Unpack failed")
def vlan_load_reg(): if vlan_removed: of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=0, nbits=16)) else: """The load/unload operations are complicated to simplify the intermediate masked write operations. This is really helpful with multiple stages: it's easier to do a single masked write per stage; there are more stages which do the former than load/unload. Basically what is happening is the following mapping of different parts of the VLAN_TCI field into the register (typically NXM_NX_REG3) as follows: REG3: CFI PCP2 PCP1 PCP0 ID11 ID10 ... ID1 ID0 VLAN_TCI: PCP2 PCP1 PCP0 CFI ID11 ID10 ... ID1 ID0 This enables masked writes considering the VLAN as one *contiguous* 15 bit field, instead of breaking the masked write into two writes, one for the ID part and one for the PCP part (yuck!) """ of_actions.append(nx.nx_reg_move(dst=vlan_reg, src=nx.NXM_OF_VLAN_TCI, dst_ofs=0, src_ofs=0, nbits=12)) of_actions.append(nx.nx_reg_move(dst=vlan_reg, src=nx.NXM_OF_VLAN_TCI, dst_ofs=12, src_ofs=13, nbits=3)) of_actions.append(nx.nx_reg_move(dst=vlan_reg, src=nx.NXM_OF_VLAN_TCI, dst_ofs=15, src_ofs=12, nbits=1))
def ofp_action_set_in_port (in_port = 0): """ Creates an action that modifies a packet's in_port Use like: ofp_action_set_in_port(in_port = 42) If in_port is not given, defaults to 0 (a port never used by OpenFlow) """ # Return action that loads the OF_IN_PORT register with the given value return nxt.nx_reg_load(dst=nxt.NXM_OF_IN_PORT, value=in_port)
def vlan_masked_write(): if debug: print "pox_client: build_nx_actions: in rewrite function:" print vlan_removed, vlan_written if vlan_removed: if debug: print "pox_client: build_nx_actions: VLAN was removed" of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=0, nbits=16)) elif vlan_written: vlan_16bit = ((int(vlan_written['pcp']) << 12) | (int(vlan_written['id']))) load_value = ((vlan_16bit >> vlan_written['offset']) & ((1 << vlan_written['nbits'])-1)) of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=load_value, offset=vlan_written['offset'], nbits=vlan_written['nbits'])) of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=1, offset=15, nbits=1))
def test_unpack_weird_header (self): """ Test the unpacking of a header we don't have a class for """ # Make a weird header... class nxm_weird (nx._nxm_maskable, nx._nxm_numeric_entry): _nxm_type = nx._make_type(0xdead,0x42) _nxm_length = 4 original = nx.nx_reg_load(dst=nxm_weird,value=42,nbits=32) original_packed = original.pack() # Currently, the action unpacking API still sucks... unoriginal = nx.nx_reg_load() offset = unoriginal.unpack(original_packed, 0) self.assertEqual(offset, len(original_packed), "Didn't unpack entire entry") unoriginal_packed = unoriginal.pack() self.assertEqual(unoriginal.dst.__name__, "NXM_UNKNOWN_dead_42", "Didn't generate new class correctly?") self.assertEqual(original_packed, unoriginal_packed, "Pack/Unpack failed")
def _handle_ConnectionUp(self, event): # Initialize Nicira msg = nx.nx_flow_mod() event.connection.send(msg) # Signal Table use msg = nx.nx_flow_mod_table_id() event.connection.send(msg) #Table 1 -> TCP Table 2 -> ARP for temp_table_id in range(1, 5): msg = nx.nx_flow_mod(command=of.OFPFC_DELETE, table_id=temp_table_id) event.connection.send(msg) #Table 0 rule: Selection of tables #IP Packet Handling / TCP msg = nx.nx_flow_mod() msg.table_id = 0 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.priority = 65000 msg.actions.append(nx.nx_multipath(dst=nx.NXM_NX_REG2)) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=1)) event.connection.send(msg) #ARP Packet Handling msg = nx.nx_flow_mod() msg.table_id = 0 msg.priority = 65001 msg.match.eth_type = pkt.ethernet.ARP_TYPE msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=4)) event.connection.send(msg) log.info("Table 0 done") #Table 1 Rules # TBD: State Machine and Hash value # New flow function msg = nx.nx_flow_mod() msg.table_id = 1 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.priority = 65004 # Signifying New flow msg.actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG0, value=0x0)) # currently learning based on eth address # no hash msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=2)) event.connection.send(msg) #Table 2 Rules #1. Sync (Should be a Sync) msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.NXM_NX_REG0 = 0 msg.match.tcp_flags = 2 msg.priority = 65001 # learn function for table 1 learn = nx.nx_action_learn(table_id=1, priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append( fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 1))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True)) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #2. Sync Ack (Should be after sync) msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x12 msg.priority = 65002 msg.match.NXM_NX_REG0 = 1 # learn function for table 1 learn = nx.nx_action_learn(table_id=1, priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append( fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 2))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True)) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #3. Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 2 # learn function for table 1 learn = nx.nx_action_learn(table_id=1, priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append( fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 3))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True)) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #4. Fin msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 3 # learn function for table 1 learn = nx.nx_action_learn(table_id=1, priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append( fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 4))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True)) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #5. Fin-Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 4 # learn function for table 1 learn = nx.nx_action_learn(table_id=1, priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append( fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 5))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True)) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #7. Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 5 # learn function for table 1 learn = nx.nx_action_learn(table_id=1, priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append( fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 6))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True)) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #8. RST msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x14 msg.priority = 65003 msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #9. PSH-Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x18 msg.priority = 65003 msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #9. Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x10 msg.priority = 65003 msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) #send to controller currently sending to the destination as no old state stored msg = nx.nx_flow_mod() msg.table_id = 2 msg.priority = 64999 msg.actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG1, value=int(1))) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table=3)) event.connection.send(msg) log.info("Table 2 done") #Table 3 Rules: Forward the packet to the Destination msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_dst = "10.0.0.1" msg.priority = 65001 msg.actions.append(of.ofp_action_output(port=1)) event.connection.send(msg) msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_dst = "10.0.0.2" msg.priority = 65001 msg.actions.append(of.ofp_action_output(port=2)) event.connection.send(msg) msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_dst = "10.0.0.3" msg.priority = 65001 msg.actions.append(of.ofp_action_output(port=3)) event.connection.send(msg) #send to controller msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.NXM_NX_REG1 = 1 msg.priority = 65005 msg.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) event.connection.send(msg) log.info("Table 3 done") #Table 4 Rules msg = nx.nx_flow_mod() msg.table_id = 4 msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) event.connection.send(msg) log.info("Table 4 done")
def build_nx_actions(self,inport,action_list,table_id,pipeline): ### BUILD NX ACTIONS of_actions = [] ctlr_outport = False # there is a controller outport action phys_outports = list() # list of physical outports to forward out of possibly_resubmit_next_table = False # should packet be passed on to next table? atleast_one_action = False for actions in action_list: atleast_one_action = True if 'srcmac' in actions: of_actions.append(of.ofp_action_dl_addr.set_src(actions['srcmac'])) if 'dstmac' in actions: of_actions.append(of.ofp_action_dl_addr.set_dst(actions['dstmac'])) if 'srcip' in actions: of_actions.append(of.ofp_action_nw_addr.set_src(actions['srcip'])) if 'dstip' in actions: of_actions.append(of.ofp_action_nw_addr.set_dst(actions['dstip'])) if 'srcport' in actions: of_actions.append(of.ofp_action_tp_port.set_src(actions['srcport'])) if 'dstport' in actions: of_actions.append(of.ofp_action_tp_port.set_dst(actions['dstport'])) if 'vlan_id' in actions: if actions['vlan_id'] is None: of_actions.append(of.ofp_action_strip_vlan()) else: of_actions.append(of.ofp_action_vlan_vid(vlan_vid=actions['vlan_id'])) if 'vlan_pcp' in actions: if actions['vlan_pcp'] is None: if not actions['vlan_id'] is None: raise RuntimeError("vlan_id and vlan_pcp must be set together!") pass else: of_actions.append(of.ofp_action_vlan_pcp(vlan_pcp=actions['vlan_pcp'])) assert 'port' in actions outport = actions['port'] if outport == of.OFPP_CONTROLLER: ctlr_outport = True else: """ There is either a physical output action (i.e., on a non-controller port), or a "send to next table" action.""" possibly_resubmit_next_table = True if outport != CUSTOM_NEXT_TABLE_PORT: phys_outports.append(outport) """ Otherwise there are no physical outports; just a possibility of resubmitting to the next table. Pass. """ """In general, actual packet forwarding may have to wait until the final table in the pipeline. This means we must determine if there is a "next" table that processes the packet from here, or if this is the last one. But first, the easy part. There are exactly three cases where a forwarding table *will* in fact "immediately forward" a packet according to the current rule (and all previous table stages that processed the packet), without waiting for any other further processing: (1) if the packet is dropped by the current rule, (2) if the packet is forwarded to the controller port, or (3) if this is the last stage of the pipeline. In the case of (1) and (2), packet forwarding may happen immediately and only depend on the current rule. But in (3), the forwarding decision must take the current rule as well as previous port changes into account, as follows: (a) if the current rule specifies an output port, forward the packet out of that port. (b) if the current rule does not specify an outport, then forward the packet out of the port using the value stored in the dedicated per-packet port register. If neither of (1)-(3) above is true, then we take the following approach: (a) if there is an outport set by this rule, write that value into the dedicated per-packet register that contains the current port the packet is in. (b) if there is no outport set by this rule, and if this is table id 0, move the value of the inport into the dedicated per-packet port register. This denotes that the packet is currently still on its inport. (c) resubmit the packet to the "next" table (according to the pipeline). """ exists_next_table = table_id in pipeline.edges # Decide first on "immediate forwarding" conditions: immediately_fwd = True if not atleast_one_action: # (1) drop of_actions = [] elif ctlr_outport: # (2) controller of_actions = [] of_actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) elif possibly_resubmit_next_table and (not exists_next_table): # (3) last stage of pipeline if len(phys_outports) > 0: # fwd out of latest assigned ports for p in phys_outports: of_actions.append(of.ofp_action_output(port=p)) else: # fwd out of stored port value of_actions.append(nx.nx_output_reg(reg=nx.NXM_NX_REG2, nbits=16)) elif (not exists_next_table) and (not possibly_resubmit_next_table): raise RuntimeError("Unexpected condition in multi-stage processing") else: # must resubmit packet to subsequent tables for processing immediately_fwd = False if immediately_fwd: return of_actions # Act on packet with knowledge that subsequent tables must process it assert (possibly_resubmit_next_table and exists_next_table and (not immediately_fwd)) next_table = pipeline.edges[table_id] if len(phys_outports) > 0: # move port register to latest assigned port values for p in phys_outports: of_actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG2, value=p, nbits=16)) of_actions.append(nx.nx_action_resubmit.resubmit_table( table=next_table)) elif table_id == 0: # move the inport value to reg2. of_actions.append(nx.nx_reg_move(src=nx.NXM_OF_IN_PORT, dst=nx.NXM_NX_REG2, nbits=16)) of_actions.append(nx.nx_action_resubmit.resubmit_table( table=next_table)) else: of_actions.append(nx.nx_action_resubmit.resubmit_table( table=next_table)) return of_actions
def sync_table(self): if not self._conn: return self._cur = {RIP_NET_TABLE: {}, RIP_PORT_TABLE: {}} cur = self._cur for e in self.table.values(): if e.metric >= INFINITY: continue fm = ovs.ofp_flow_mod_table_id() fm.xid = 0 fm.table_id = RIP_NET_TABLE fm.priority = e.size + 1 # +1 because 0 reserved for fallback fm.match.dl_type = pkt.ethernet.IP_TYPE fm.match.nw_dst = (e.ip, e.size) if e.dev is not None: # This is for a directly attached network. It'll be looked up in # the port table. fm.actions.append( ovs.nx_action_resubmit.resubmit_table(RIP_PORT_TABLE)) else: # This is for a remote network. # Load the gateway into the dst IP; it will be looked up in the port # table to find the right port. The real dst IP will get reloaded # from a register before egress. fm.actions.append(of.ofp_action_nw_addr.set_dst(e.next_hop)) fm.actions.append( ovs.nx_action_resubmit.resubmit_table(RIP_PORT_TABLE)) cur[RIP_NET_TABLE][(e.ip, e.size)] = fm for e in self.table.values(): if e.metric >= INFINITY: continue fm = ovs.ofp_flow_mod_table_id() fm.xid = 0 fm.table_id = RIP_PORT_TABLE fm.priority = e.size + 1 # +1 because 0 reserved for fallback fm.match.dl_type = pkt.ethernet.IP_TYPE fm.match.nw_dst = (e.ip, e.size) if e.dev is not None: # This is for a directly attached network. Look up the port. # Also, fix the dst IP address. port = self._conn.ports.get(e.dev) if port is None: continue fm.actions.append( ovs.nx_reg_load(dst=OUT_PORT_REGISTER, value=e.dev)) fm.actions.append(of.ofp_action_dl_addr.set_src(port.hw_addr)) fm.actions.append( ovs.nx_action_resubmit.resubmit_table(ARP_TABLE)) else: # If we get to this table and we don't have a direct entry that # matches, we have no working route! # Should we install something so that we generate an ICMP unreachable # or something? pass cur[RIP_PORT_TABLE][(e.ip, e.size)] = fm if self._conn: data1 = b''.join(x.pack() for x in self._cur[RIP_PORT_TABLE].itervalues()) data2 = b''.join(x.pack() for x in self._cur[RIP_NET_TABLE].itervalues()) data = data1 + data2 if data == self._prev: return # Nothing changed self._clear_table(RIP_NET_TABLE) self._clear_table(RIP_PORT_TABLE) self._init_rip_net_table() self._init_rip_port_table() self.log.debug("Syncing %s port and %s net table entries", len(cur[RIP_PORT_TABLE]), len(cur[RIP_NET_TABLE])) self._conn.send(data) self._prev = data
def build_nx_actions(self,inport,action_list,table_id,pipeline,debug=False): ### BUILD NX ACTIONS of_actions = [] ctlr_outport = False # there is a controller outport action phys_outports = list() # list of physical outports to forward out of possibly_resubmit_next_table = False # should packet be passed on to next table? atleast_one_action = False # vlan handling flags vlan_removed = False vlan_written = {} if debug: print "pox_client: build_nx_actions: Received actions:" print action_list for actions in action_list: atleast_one_action = True if 'srcmac' in actions: of_actions.append(of.ofp_action_dl_addr.set_src(actions['srcmac'])) if 'dstmac' in actions: of_actions.append(of.ofp_action_dl_addr.set_dst(actions['dstmac'])) if 'srcip' in actions: of_actions.append(of.ofp_action_nw_addr.set_src(actions['srcip'])) if 'dstip' in actions: of_actions.append(of.ofp_action_nw_addr.set_dst(actions['dstip'])) if 'srcport' in actions: of_actions.append(of.ofp_action_tp_port.set_src(actions['srcport'])) if 'dstport' in actions: of_actions.append(of.ofp_action_tp_port.set_dst(actions['dstport'])) if 'vlan_id' in actions: if actions['vlan_id'] is None: vlan_removed = True else: assert 'vlan_pcp' in actions assert 'vlan_offset' in actions assert 'vlan_nbits' in actions vlan_written = {k: actions['vlan_' + k] for k in ['pcp', 'offset', 'nbits', 'id']} if 'vlan_pcp' in actions: assert 'vlan_id' in actions, "vlan_id and vlan_pcp must be set together" assert 'port' in actions outport = actions['port'] if outport == of.OFPP_CONTROLLER: ctlr_outport = True else: """ There is either a physical output action (i.e., on a non-controller port), or a "send to next table" action.""" possibly_resubmit_next_table = True if outport != CUSTOM_NEXT_TABLE_PORT: phys_outports.append(outport) """ Otherwise there are no physical outports; just a possibility of resubmitting to the next table. Pass. """ """Construct routines to move packet VLAN into the register, do a masked write into the register, and to write back the register into the packet. """ vlan_reg = nx.NXM_NX_REG3 def vlan_load_reg(): if vlan_removed: of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=0, nbits=16)) else: """The load/unload operations are complicated to simplify the intermediate masked write operations. This is really helpful with multiple stages: it's easier to do a single masked write per stage; there are more stages which do the former than load/unload. Basically what is happening is the following mapping of different parts of the VLAN_TCI field into the register (typically NXM_NX_REG3) as follows: REG3: CFI PCP2 PCP1 PCP0 ID11 ID10 ... ID1 ID0 VLAN_TCI: PCP2 PCP1 PCP0 CFI ID11 ID10 ... ID1 ID0 This enables masked writes considering the VLAN as one *contiguous* 15 bit field, instead of breaking the masked write into two writes, one for the ID part and one for the PCP part (yuck!) """ of_actions.append(nx.nx_reg_move(dst=vlan_reg, src=nx.NXM_OF_VLAN_TCI, dst_ofs=0, src_ofs=0, nbits=12)) of_actions.append(nx.nx_reg_move(dst=vlan_reg, src=nx.NXM_OF_VLAN_TCI, dst_ofs=12, src_ofs=13, nbits=3)) of_actions.append(nx.nx_reg_move(dst=vlan_reg, src=nx.NXM_OF_VLAN_TCI, dst_ofs=15, src_ofs=12, nbits=1)) def vlan_masked_write(): if debug: print "pox_client: build_nx_actions: in rewrite function:" print vlan_removed, vlan_written if vlan_removed: if debug: print "pox_client: build_nx_actions: VLAN was removed" of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=0, nbits=16)) elif vlan_written: vlan_16bit = ((int(vlan_written['pcp']) << 12) | (int(vlan_written['id']))) load_value = ((vlan_16bit >> vlan_written['offset']) & ((1 << vlan_written['nbits'])-1)) of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=load_value, offset=vlan_written['offset'], nbits=vlan_written['nbits'])) of_actions.append(nx.nx_reg_load(dst=vlan_reg, value=1, offset=15, nbits=1)) def vlan_write_back(): """ The write back operation is slightly complicated for reasons described under `vlan_load_reg()`. """ if debug: print "pox_client: build_nx_actions: Writing back VLAN. The actions are:" print actions if table_id > 0: of_actions.append(nx.nx_reg_move(src=vlan_reg, dst=nx.NXM_OF_VLAN_TCI, src_ofs=0, dst_ofs=0, nbits=12)) of_actions.append(nx.nx_reg_move(src=vlan_reg, dst=nx.NXM_OF_VLAN_TCI, src_ofs=12, dst_ofs=13, nbits=3)) of_actions.append(nx.nx_reg_move(src=vlan_reg, dst=nx.NXM_OF_VLAN_TCI, src_ofs=15, dst_ofs=12, nbits=1)) """In general, actual packet forwarding may have to wait until the final table in the pipeline. This means we must determine if there is a "next" table that processes the packet from here, or if this is the last one. But first, the easy part. There are exactly three cases where a forwarding table *will* in fact "immediately forward" a packet according to the current rule (and all previous table stages that processed the packet), without waiting for any other further processing: (1) if the packet is dropped by the current rule, (2) if the packet is forwarded to the controller port, or (3) if this is the last stage of the pipeline. In the case of (1) and (2), packet forwarding may happen immediately and only depend on the current rule. But in (3), the forwarding decision must take the current rule as well as previous port changes into account, as follows: (a) if the current rule specifies an output port, forward the packet out of that port. (b) if the current rule does not specify an outport, then forward the packet out of the port using the value stored in the dedicated per-packet port register. If neither of (1)-(3) above is true, then we take the following approach: (a) if there is an outport set by this rule, write that value into the dedicated per-packet register that contains the current port the packet is in. (b) if there is no outport set by this rule, and if this is table id 0, move the value of the inport into the dedicated per-packet port register. This denotes that the packet is currently still on its inport. (c) resubmit the packet to the "next" table (according to the pipeline). """ exists_next_table = table_id in pipeline.edges # Decide first on "immediate forwarding" conditions: immediately_fwd = True if not atleast_one_action: # (1) drop of_actions = [] elif ctlr_outport: # (2) controller of_actions = [] vlan_write_back() of_actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) elif possibly_resubmit_next_table and (not exists_next_table): # (3) last stage of pipeline vlan_masked_write() vlan_write_back() if len(phys_outports) > 0: # fwd out of latest assigned ports for p in phys_outports: of_actions.append(of.ofp_action_output(port=p)) else: # fwd out of stored port value of_actions.append(nx.nx_output_reg(reg=nx.NXM_NX_REG2, nbits=16)) elif (not exists_next_table) and (not possibly_resubmit_next_table): raise RuntimeError("Unexpected condition in multi-stage processing") else: # must resubmit packet to subsequent tables for processing immediately_fwd = False if immediately_fwd: return of_actions if debug: print "pox_client: build_nx_actions: Not the last stage." # Act on packet with knowledge that subsequent tables must process it assert (possibly_resubmit_next_table and exists_next_table and (not immediately_fwd)) # 1. Handle VLAN writing for resubmitted packets if table_id == 0: vlan_load_reg() vlan_masked_write() # 2. Handle packet forwarding for resubmitted packets next_table = pipeline.edges[table_id] if len(phys_outports) > 0: # move port register to latest assigned port values for p in phys_outports: of_actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG2, value=p, nbits=16)) of_actions.append(nx.nx_action_resubmit.resubmit_table( table=next_table)) elif table_id == 0: # move the inport value to reg2. of_actions.append(nx.nx_reg_move(src=nx.NXM_OF_IN_PORT, dst=nx.NXM_NX_REG2, nbits=16)) of_actions.append(nx.nx_action_resubmit.resubmit_table( table=next_table)) else: of_actions.append(nx.nx_action_resubmit.resubmit_table( table=next_table)) return of_actions
def _handle_ConnectionUp(self, event): # Initialize Nicira msg = nx.nx_flow_mod() event.connection.send(msg) # Signal Table use msg = nx.nx_flow_mod_table_id() event.connection.send(msg) #Table 1 -> TCP Table 2 -> ARP for temp_table_id in range(1, 5): msg = nx.nx_flow_mod(command=of.OFPFC_DELETE, table_id = temp_table_id) event.connection.send(msg) #Table 0 rule: Selection of tables #IP Packet Handling / TCP msg = nx.nx_flow_mod() msg.table_id = 0 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.priority = 65000 msg.actions.append(nx.nx_multipath(dst = nx.NXM_NX_REG2)) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 1)) event.connection.send(msg) #ARP Packet Handling msg = nx.nx_flow_mod() msg.table_id = 0 msg.priority = 65001 msg.match.eth_type = pkt.ethernet.ARP_TYPE msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 4)) event.connection.send(msg) log.info("Table 0 done") #Table 1 Rules # TBD: State Machine and Hash value # New flow function msg = nx.nx_flow_mod() msg.table_id = 1 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.priority = 65004 # Signifying New flow msg.actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG0, value=0x0)) # currently learning based on eth address # no hash msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 2)) event.connection.send(msg) #Table 2 Rules #1. Sync (Should be a Sync) msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.NXM_NX_REG0 = 0 msg.match.tcp_flags = 2 msg.priority = 65001 # learn function for table 1 learn = nx.nx_action_learn(table_id=1,priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append(fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 1))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True )) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #2. Sync Ack (Should be after sync) msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x12 msg.priority = 65002 msg.match.NXM_NX_REG0 = 1 # learn function for table 1 learn = nx.nx_action_learn(table_id=1,priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append(fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 2))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True )) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #3. Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 2 # learn function for table 1 learn = nx.nx_action_learn(table_id=1,priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append(fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 3))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True )) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #4. Fin msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 3 # learn function for table 1 learn = nx.nx_action_learn(table_id=1,priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append(fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 4))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True )) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #5. Fin-Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 4 # learn function for table 1 learn = nx.nx_action_learn(table_id=1,priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append(fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 5))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True )) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #7. Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x010 msg.priority = 65003 msg.match.NXM_NX_REG0 = 5 # learn function for table 1 learn = nx.nx_action_learn(table_id=1,priority=65111) learn.spec = [ nx.flow_mod_spec(src=nx.nx_learn_src_field(nx.NXM_NX_REG2), dst=nx.nx_learn_dst_match(nx.NXM_NX_REG2)), ] fms = nx.flow_mod_spec.new learn.spec.append(fms(load=nx.NXM_NX_REG0, src=nx.nx_learn_src_immediate.u32(None, 6))) learn.spec.append(fms(field=nx.NXM_NX_REG0, reserved=True )) msg.actions.append(learn) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #8. RST msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x14 msg.priority = 65003 msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #9. PSH-Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x18 msg.priority = 65003 msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #9. Ack msg = nx.nx_flow_mod() msg.table_id = 2 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_proto = ipv4.TCP_PROTOCOL msg.match.tcp_flags = 0x10 msg.priority = 65003 msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) #send to controller currently sending to the destination as no old state stored msg = nx.nx_flow_mod() msg.table_id = 2 msg.priority = 64999 msg.actions.append(nx.nx_reg_load(dst=nx.NXM_NX_REG1, value=int(1))) msg.actions.append(nx.nx_action_resubmit.resubmit_table(table = 3)) event.connection.send(msg) log.info("Table 2 done") #Table 3 Rules: Forward the packet to the Destination msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_dst = "10.0.0.1" msg.priority = 65001 msg.actions.append(of.ofp_action_output(port = 1)) event.connection.send(msg) msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_dst = "10.0.0.2" msg.priority = 65001 msg.actions.append(of.ofp_action_output(port = 2)) event.connection.send(msg) msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.eth_type = pkt.ethernet.IP_TYPE msg.match.ip_dst = "10.0.0.3" msg.priority = 65001 msg.actions.append(of.ofp_action_output(port = 3)) event.connection.send(msg) #send to controller msg = nx.nx_flow_mod() msg.table_id = 3 msg.match.NXM_NX_REG1 = 1 msg.priority = 65005 msg.actions.append(of.ofp_action_output(port = of.OFPP_CONTROLLER)) event.connection.send(msg) log.info("Table 3 done") #Table 4 Rules msg = nx.nx_flow_mod() msg.table_id = 4 msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD)) event.connection.send(msg) log.info("Table 4 done")