コード例 #1
0
ファイル: flow_table.py プロジェクト: ARCCN/elt
 def __init__(self, dpid=None):
     FlowTable.__init__(self)
     self._table = set()
     self.dpid = dpid
     self.fields = {}
     for f in self.dict_field_names:
         self.fields[f] = {}
     for f in self.trie_field_names:
         self.fields[f] = Trie()
コード例 #2
0
  def __init__ (self, switch=None, **kw):
    EventMixin.__init__(self)
    self.flow_table = FlowTable()
    self.switch = switch

    # a list of pending flow table entries : tuples (ADD|REMOVE, entry)
    self._pending = []

    # a map of pending barriers barrier_xid-> ([entry1,entry2])
    self._pending_barrier_to_ops = {}
    # a map of pending barriers per request entry -> (barrier_xid, time)
    self._pending_op_to_barrier = {}

    self.listenTo(switch)
コード例 #3
0
ファイル: testData.py プロジェクト: niuqg/pox
def GenerateACL():
    acl_rules = open(r'..\Data\acl3_4k_rules.txt')
    table = FlowTable()
    i = 3874
    while True:
        r = acl_rules.readline().split()
        if not r: break
        table.add_entry(
            TableEntry(priority=i,
                       cookie=0x1,
                       match=ofp_match(dl_src=EthAddr("00:00:00:00:00:01"),
                                       nw_src=r[0],
                                       nw_dst=r[1]),
                       actions=[ofp_action_output(port=5)]))
        #table.add_entry(TableEntry(priority=i, cookie=0x1, match=ofp_match(dl_src=EthAddr("00:00:00:00:00:01"),nw_src="53.45.14.183/32",nw_dst="18.184.25.126/32"), actions=[ofp_action_output(port=5)]))
        i = i - 1
    return table
コード例 #4
0
ファイル: topology.py プロジェクト: 14gr1010/software
  def __init__ (self, switch=None, **kw):
    EventMixin.__init__(self)
    self.flow_table = FlowTable()
    self.switch = switch

    # a list of pending flow table entries : tuples (ADD|REMOVE, entry)
    self._pending = []

    # a map of pending barriers barrier_xid-> ([entry1,entry2])
    self._pending_barrier_to_ops = {}
    # a map of pending barriers per request entry -> (barrier_xid, time)
    self._pending_op_to_barrier = {}

    self.listenTo(switch)
コード例 #5
0
 def table():
     t = FlowTable()
     t.add_entry(
         TableEntry(priority=6,
                    cookie=0x1,
                    match=ofp_match(dl_src=EthAddr("00:00:00:00:00:01"),
                                    nw_src="1.2.3.4"),
                    actions=[ofp_action_output(port=5)]))
     t.add_entry(
         TableEntry(priority=5,
                    cookie=0x2,
                    match=ofp_match(dl_src=EthAddr("00:00:00:00:00:02"),
                                    nw_src="1.2.3.0/24"),
                    actions=[ofp_action_output(port=6)]))
     t.add_entry(
         TableEntry(priority=1,
                    cookie=0x3,
                    match=ofp_match(),
                    actions=[]))
     return t
コード例 #6
0
ファイル: snapshot.py プロジェクト: StonyBrookUniversity/sts
 def decode_flow_table(self, json):
   ft = FlowTable()
   for e in json["entries"]:
     ft.add_entry(self.decode_entry(e))
   return ft
コード例 #7
0
class OFSyncFlowTable (EventMixin):
  _eventMixin_events = set([FlowTableModification])
  """
  A flow table that keeps in sync with a switch
  """
  ADD = of.OFPFC_ADD
  REMOVE = of.OFPFC_DELETE
  REMOVE_STRICT = of.OFPFC_DELETE_STRICT
  TIME_OUT = 2

  def __init__ (self, switch=None, **kw):
    EventMixin.__init__(self)
    self.flow_table = FlowTable()
    self.switch = switch

    # a list of pending flow table entries : tuples (ADD|REMOVE, entry)
    self._pending = []

    # a map of pending barriers barrier_xid-> ([entry1,entry2])
    self._pending_barrier_to_ops = {}
    # a map of pending barriers per request entry -> (barrier_xid, time)
    self._pending_op_to_barrier = {}

    self.listenTo(switch)

  def install (self, entries=[]):
    """
    asynchronously install entries in the flow table

    will raise a FlowTableModification event when the change has been
    processed by the switch
    """
    self._mod(entries, OFSyncFlowTable.ADD)

  def remove_with_wildcards (self, entries=[]):
    """
    asynchronously remove entries in the flow table

    will raise a FlowTableModification event when the change has been
    processed by the switch
    """
    self._mod(entries, OFSyncFlowTable.REMOVE)

  def remove_strict (self, entries=[]):
    """
    asynchronously remove entries in the flow table.

    will raise a FlowTableModification event when the change has been
    processed by the switch
    """
    self._mod(entries, OFSyncFlowTable.REMOVE_STRICT)

  @property
  def entries (self):
    return self.flow_table.entries

  @property
  def num_pending (self):
    return len(self._pending)

  def __len__ (self):
    return len(self.flow_table)

  def _mod (self, entries, command):
    if isinstance(entries, TableEntry):
      entries = [ entries ]

    for entry in entries:
      if(command == OFSyncFlowTable.REMOVE):
        self._pending = [(cmd,pentry) for cmd,pentry in self._pending
                         if not (cmd == OFSyncFlowTable.ADD
                                 and entry.matches_with_wildcards(pentry))]
      elif(command == OFSyncFlowTable.REMOVE_STRICT):
        self._pending = [(cmd,pentry) for cmd,pentry in self._pending
                         if not (cmd == OFSyncFlowTable.ADD
                                 and entry == pentry)]

      self._pending.append( (command, entry) )

    self._sync_pending()

  def _sync_pending (self, clear=False):
    if not self.switch.connected:
      return False

    # resync the switch
    if clear:
      self._pending_barrier_to_ops = {}
      self._pending_op_to_barrier = {}
      self._pending = filter(lambda(op): op[0] == OFSyncFlowTable.ADD,
                             self._pending)

      self.switch.send(of.ofp_flow_mod(command=of.OFPFC_DELETE,
                                       match=of.ofp_match()))
      self.switch.send(of.ofp_barrier_request())

      todo = map(lambda(e): (OFSyncFlowTable.ADD, e),
                 self.flow_table.entries) + self._pending
    else:
      todo = [op for op in self._pending
              if op not in self._pending_op_to_barrier
              or (self._pending_op_to_barrier[op][1]
                  + OFSyncFlowTable.TIME_OUT) < time.time() ]

    for op in todo:
      fmod_xid = self.switch._xid_generator()
      flow_mod = op[1].to_flow_mod(xid=fmod_xid, command=op[0],
                                   flags=op[1].flags | of.OFPFF_SEND_FLOW_REM)
      self.switch.send(flow_mod)

    barrier_xid = self.switch._xid_generator()
    self.switch.send(of.ofp_barrier_request(xid=barrier_xid))
    now = time.time()
    self._pending_barrier_to_ops[barrier_xid] = todo

    for op in todo:
      self._pending_op_to_barrier[op] = (barrier_xid, now)

  def _handle_SwitchConnectionUp (self, event):
    # sync all_flows
    self._sync_pending(clear=True)

  def _handle_SwitchConnectionDown (self, event):
    # connection down. too bad for our unconfirmed entries
    self._pending_barrier_to_ops = {}
    self._pending_op_to_barrier = {}

  def _handle_BarrierIn (self, barrier):
    # yeah. barrier in. time to sync some of these flows
    if barrier.xid in self._pending_barrier_to_ops:
      added = []
      removed = []
      #print "barrier in: pending for barrier: %d: %s" % (barrier.xid,
      #    self._pending_barrier_to_ops[barrier.xid])
      for op in self._pending_barrier_to_ops[barrier.xid]:
        (command, entry) = op
        if(command == OFSyncFlowTable.ADD):
          self.flow_table.add_entry(entry)
          added.append(entry)
        else:
          removed.extend(self.flow_table.remove_matching_entries(entry.match,
              entry.priority, strict=command == OFSyncFlowTable.REMOVE_STRICT))
        #print "op: %s, pending: %s" % (op, self._pending)
        if op in self._pending: self._pending.remove(op)
        self._pending_op_to_barrier.pop(op, None)
      del self._pending_barrier_to_ops[barrier.xid]
      self.raiseEvent(FlowTableModification(added = added, removed=removed))
      return EventHalt
    else:
      return EventContinue

  def _handle_FlowRemoved (self, event):
    """
    process a flow removed event -- remove the matching flow from the table.
    """
    flow_removed = event.ofp
    for entry in self.flow_table.entries:
      if (flow_removed.match == entry.match
          and flow_removed.priority == entry.priority):
        self.flow_table.remove_entry(entry)
        self.raiseEvent(FlowTableModification(removed=[entry]))
        return EventHalt
    return EventContinue
コード例 #8
0
    def __init__(self,
                 dpid,
                 name=None,
                 ports=4,
                 miss_send_len=128,
                 max_buffers=100,
                 max_entries=0x7fFFffFF,
                 features=None):
        """
    Initialize switch
     - ports is a list of ofp_phy_ports or a number of ports
     - miss_send_len is number of bytes to send to controller on table miss
     - max_buffers is number of buffered packets to store
     - max_entries is max flows entries per table
    """
        if name is None: name = dpid_to_str(dpid)
        self.name = name

        if isinstance(ports, int):
            ports = _generate_ports(num_ports=ports, dpid=dpid)

        self.dpid = dpid
        self.max_buffers = max_buffers
        self.max_entries = max_entries
        self.miss_send_len = miss_send_len
        self._has_sent_hello = False

        self.table = FlowTable()
        self.table.addListeners(self)

        self._lookup_count = 0
        self._matched_count = 0

        self.log = logging.getLogger(self.name)
        self._connection = None

        # buffer for packets during packet_in
        self._packet_buffer = []

        # Map port_no -> openflow.pylibopenflow_01.ofp_phy_ports
        self.ports = {}
        self.port_stats = {}
        for port in ports:
            self.ports[port.port_no] = port
            self.port_stats[port.port_no] = ofp_port_stats(
                port_no=port.port_no)

        if features is not None:
            self.features = features
        else:
            # Set up default features

            self.features = SwitchFeatures()
            self.features.cap_flow_stats = True
            self.features.cap_table_stats = True
            self.features.cap_port_stats = True
            #self.features.cap_stp = True
            #self.features.cap_ip_reasm = True
            #self.features.cap_queue_stats = True
            #self.features.cap_arp_match_ip = True

            self.features.act_output = True
            self.features.act_enqueue = True
            self.features.act_strip_vlan = True
            self.features.act_set_vlan_vid = True
            self.features.act_set_vlan_pcp = True
            self.features.act_set_dl_dst = True
            self.features.act_set_dl_src = True
            self.features.act_set_nw_dst = True
            self.features.act_set_nw_src = True
            self.features.act_set_nw_tos = True
            self.features.act_set_tp_dst = True
            self.features.act_set_tp_src = True
            #self.features.act_vendor = True

        # Set up handlers for incoming OpenFlow messages
        # That is, self.ofp_handlers[OFPT_FOO] = self._rx_foo
        self.ofp_handlers = {}
        for value, name in ofp_type_map.iteritems():
            name = name.split("OFPT_", 1)[-1].lower()
            h = getattr(self, "_rx_" + name, None)
            if not h: continue
            assert of._message_type_to_class[value]._from_controller, name
            self.ofp_handlers[value] = h

        # Set up handlers for actions
        # That is, self.action_handlers[OFPAT_FOO] = self._action_foo
        #TODO: Refactor this with above
        self.action_handlers = {}
        for value, name in ofp_action_type_map.iteritems():
            name = name.split("OFPAT_", 1)[-1].lower()
            h = getattr(self, "_action_" + name, None)
            if not h: continue
            if getattr(self.features, "act_" + name) is False: continue
            self.action_handlers[value] = h

        # Set up handlers for stats handlers
        # That is, self.stats_handlers[OFPST_FOO] = self._stats_foo
        #TODO: Refactor this with above
        self.stats_handlers = {}
        for value, name in ofp_stats_type_map.iteritems():
            name = name.split("OFPST_", 1)[-1].lower()
            h = getattr(self, "_stats_" + name, None)
            if not h: continue
            self.stats_handlers[value] = h

        # Set up handlers for flow mod handlers
        # That is, self.flow_mod_handlers[OFPFC_FOO] = self._flow_mod_foo
        #TODO: Refactor this with above
        self.flow_mod_handlers = {}
        for name, value in ofp_flow_mod_command_rev_map.iteritems():
            name = name.split("OFPFC_", 1)[-1].lower()
            h = getattr(self, "_flow_mod_" + name, None)
            if not h: continue
            self.flow_mod_handlers[value] = h
コード例 #9
0
class SoftwareSwitchBase(object):
    def __init__(self,
                 dpid,
                 name=None,
                 ports=4,
                 miss_send_len=128,
                 max_buffers=100,
                 max_entries=0x7fFFffFF,
                 features=None):
        """
    Initialize switch
     - ports is a list of ofp_phy_ports or a number of ports
     - miss_send_len is number of bytes to send to controller on table miss
     - max_buffers is number of buffered packets to store
     - max_entries is max flows entries per table
    """
        if name is None: name = dpid_to_str(dpid)
        self.name = name

        if isinstance(ports, int):
            ports = _generate_ports(num_ports=ports, dpid=dpid)

        self.dpid = dpid
        self.max_buffers = max_buffers
        self.max_entries = max_entries
        self.miss_send_len = miss_send_len
        self._has_sent_hello = False

        self.table = FlowTable()
        self.table.addListeners(self)

        self._lookup_count = 0
        self._matched_count = 0

        self.log = logging.getLogger(self.name)
        self._connection = None

        # buffer for packets during packet_in
        self._packet_buffer = []

        # Map port_no -> openflow.pylibopenflow_01.ofp_phy_ports
        self.ports = {}
        self.port_stats = {}
        for port in ports:
            self.ports[port.port_no] = port
            self.port_stats[port.port_no] = ofp_port_stats(
                port_no=port.port_no)

        if features is not None:
            self.features = features
        else:
            # Set up default features

            self.features = SwitchFeatures()
            self.features.cap_flow_stats = True
            self.features.cap_table_stats = True
            self.features.cap_port_stats = True
            #self.features.cap_stp = True
            #self.features.cap_ip_reasm = True
            #self.features.cap_queue_stats = True
            #self.features.cap_arp_match_ip = True

            self.features.act_output = True
            self.features.act_enqueue = True
            self.features.act_strip_vlan = True
            self.features.act_set_vlan_vid = True
            self.features.act_set_vlan_pcp = True
            self.features.act_set_dl_dst = True
            self.features.act_set_dl_src = True
            self.features.act_set_nw_dst = True
            self.features.act_set_nw_src = True
            self.features.act_set_nw_tos = True
            self.features.act_set_tp_dst = True
            self.features.act_set_tp_src = True
            #self.features.act_vendor = True

        # Set up handlers for incoming OpenFlow messages
        # That is, self.ofp_handlers[OFPT_FOO] = self._rx_foo
        self.ofp_handlers = {}
        for value, name in ofp_type_map.iteritems():
            name = name.split("OFPT_", 1)[-1].lower()
            h = getattr(self, "_rx_" + name, None)
            if not h: continue
            assert of._message_type_to_class[value]._from_controller, name
            self.ofp_handlers[value] = h

        # Set up handlers for actions
        # That is, self.action_handlers[OFPAT_FOO] = self._action_foo
        #TODO: Refactor this with above
        self.action_handlers = {}
        for value, name in ofp_action_type_map.iteritems():
            name = name.split("OFPAT_", 1)[-1].lower()
            h = getattr(self, "_action_" + name, None)
            if not h: continue
            if getattr(self.features, "act_" + name) is False: continue
            self.action_handlers[value] = h

        # Set up handlers for stats handlers
        # That is, self.stats_handlers[OFPST_FOO] = self._stats_foo
        #TODO: Refactor this with above
        self.stats_handlers = {}
        for value, name in ofp_stats_type_map.iteritems():
            name = name.split("OFPST_", 1)[-1].lower()
            h = getattr(self, "_stats_" + name, None)
            if not h: continue
            self.stats_handlers[value] = h

        # Set up handlers for flow mod handlers
        # That is, self.flow_mod_handlers[OFPFC_FOO] = self._flow_mod_foo
        #TODO: Refactor this with above
        self.flow_mod_handlers = {}
        for name, value in ofp_flow_mod_command_rev_map.iteritems():
            name = name.split("OFPFC_", 1)[-1].lower()
            h = getattr(self, "_flow_mod_" + name, None)
            if not h: continue
            self.flow_mod_handlers[value] = h

    @property
    def _time(self):
        """
    Get the current time

    This should be used for, e.g., calculating timeouts.  It currently isn't
    used everywhere it should be.

    Override this to change time behavior.
    """
        return time.time()

    def _handle_FlowTableModification(self, event):
        """
    Handle flow table modification events
    """
        # Currently, we only use this for sending flow_removed messages
        if not event.removed: return

        if event.reason in (OFPRR_IDLE_TIMEOUT, OFPRR_HARD_TIMEOUT,
                            OFPRR_DELETE):
            # These reasons may lead to a flow_removed
            count = 0
            for entry in event.removed:
                if entry.flags & OFPFF_SEND_FLOW_REM:
                    # Flow wants removal notification -- send it
                    fr = entry.to_flow_removed(self._time, reason=event.reason)
                    self.send(fr)
                    count += 1
            self.log.debug("%d flows removed (%d removal notifications)",
                           len(event.removed), count)

    def rx_message(self, connection, msg):
        """
    Handle an incoming OpenFlow message
    """
        ofp_type = msg.header_type
        h = self.ofp_handlers.get(ofp_type)
        if h is None:
            raise RuntimeError("No handler for ofp_type %s(%d)" %
                               (ofp_type_map.get(ofp_type), ofp_type))

        self.log.debug("Got %s with XID %s", ofp_type_map.get(ofp_type),
                       msg.xid)
        h(msg, connection=connection)

    def set_connection(self, connection):
        """
    Set this switch's connection.
    """
        self._has_sent_hello = False
        connection.set_message_handler(self.rx_message)
        self._connection = connection

    def send(self, message, connection=None):
        """
    Send a message to this switch's communication partner
    """
        if connection is None:
            connection = self._connection
        if connection:
            connection.send(message)
        else:
            self.log.debug("Asked to send message %s, but not connected",
                           message)

    def _rx_hello(self, ofp, connection):
        #FIXME: This isn't really how hello is supposed to work -- we're supposed
        #       to send it immediately on connection.  See _send_hello().
        self.send_hello()

    def _rx_echo_request(self, ofp, connection):
        """
    Handles echo requests
    """
        msg = ofp_echo_reply(xid=ofp.xid, body=ofp.body)
        self.send(msg)

    def _rx_features_request(self, ofp, connection):
        """
    Handles feature requests
    """
        self.log.debug("Send features reply")
        msg = ofp_features_reply(datapath_id=self.dpid,
                                 xid=ofp.xid,
                                 n_buffers=self.max_buffers,
                                 n_tables=1,
                                 capabilities=self.features.capability_bits,
                                 actions=self.features.action_bits,
                                 ports=self.ports.values())
        self.send(msg)

    def _rx_flow_mod(self, ofp, connection):
        """
    Handles flow mods
    """
        self.log.debug("Flow mod details: %s", ofp.show())

        #self.table.process_flow_mod(ofp)
        #self._process_flow_mod(ofp, connection=connection, table=self.table)
        handler = self.flow_mod_handlers.get(ofp.command)
        if handler is None:
            self.log.warn("Command not implemented: %s" % command)
            self.send_error(type=OFPET_FLOW_MOD_FAILED,
                            code=OFPFMFC_BAD_COMMAND,
                            ofp=ofp,
                            connection=connection)
            return
        handler(flow_mod=ofp, connection=connection, table=self.table)

        if ofp.buffer_id is not None:
            self._process_actions_for_packet_from_buffer(
                ofp.actions, ofp.buffer_id, ofp)

    def _rx_packet_out(self, packet_out, connection):
        """
    Handles packet_outs
    """
        self.log.debug("Packet out details: %s", packet_out.show())

        if packet_out.data:
            self._process_actions_for_packet(packet_out.actions,
                                             packet_out.data,
                                             packet_out.in_port, packet_out)
        elif packet_out.buffer_id is not None:
            self._process_actions_for_packet_from_buffer(
                packet_out.actions, packet_out.buffer_id, packet_out)
        else:
            self.log.warn("packet_out: No data and no buffer_id -- "
                          "don't know what to send")

    def _rx_echo_reply(self, ofp, connection):
        pass

    def _rx_barrier_request(self, ofp, connection):
        msg = ofp_barrier_reply(xid=ofp.xid)
        self.send(msg)

    def _rx_get_config_request(self, ofp, connection):
        msg = ofp_get_config_reply(xid=ofp.xid)
        #FIXME: Set attributes of reply!
        self.send(msg)

    def _rx_stats_request(self, ofp, connection):
        handler = self.stats_handlers.get(ofp.type)
        if handler is None:
            self.log.warning("Stats type %s not implemented", ofp.type)

            self.send_error(type=OFPET_BAD_REQUEST,
                            code=OFPBRC_BAD_STAT,
                            ofp=ofp,
                            connection=connection)
            return

        body = handler(ofp, connection=connection)
        if body is not None:
            reply = ofp_stats_reply(xid=ofp.xid, type=ofp.type, body=body)
            self.log.debug("Sending stats reply %s", reply)
            self.send(reply)

    def _rx_set_config(self, config, connection):
        #FIXME: Implement this!
        pass

    def _rx_port_mod(self, port_mod, connection):
        port_no = port_mod.port_no
        if port_no not in self.ports:
            self.send_error(type=OFPET_PORT_MOD_FAILED,
                            code=OFPPMFC_BAD_PORT,
                            ofp=port_mod,
                            connection=connection)
            return
        port = self.ports[port_no]
        if port.hw_addr != port_mod.hw_addr:
            self.send_error(type=OFPET_PORT_MOD_FAILED,
                            code=OFPPMFC_BAD_HW_ADDR,
                            ofp=port_mod,
                            connection=connection)
            return

        mask = port_mod.mask

        for bit in range(32):
            bit = 1 << bit
            if mask & bit:
                handled, r = self._set_port_config_bit(port, bit,
                                                       port_mod.config & bit)
                if not handled:
                    self.log.warn("Unsupported port config flag: %08x", bit)
                    continue
                if r is not None:
                    msg = "Port %s: " % (port.port_no, )
                    if isinstance(r, str):
                        msg += r
                    else:
                        msg += ofp_port_config_map.get(
                            bit, "config bit %x" % (bit, ))
                        msg += " set to "
                        msg += "true" if r else "false"
                    self.log.debug(msg)

    def _rx_vendor(self, vendor, connection):
        # We don't support vendor extensions, so send an OFP_ERROR, per
        # page 42 of spec
        self.send_error(type=OFPET_BAD_REQUEST,
                        code=OFPBRC_BAD_VENDOR,
                        ofp=vendor,
                        connection=connection)

    def send_hello(self, force=False):
        """
    Send hello (once)
    """
        #FIXME: This is wrong -- we should just send when connecting.
        if self._has_sent_hello and not force: return
        self._has_sent_hello = True
        self.log.debug("Sent hello")
        msg = ofp_hello(xid=0)
        self.send(msg)

    def send_packet_in(self,
                       in_port,
                       buffer_id=None,
                       packet=b'',
                       reason=None,
                       data_length=None):
        """
    Send PacketIn
    """
        if hasattr(packet, 'pack'):
            packet = packet.pack()
        assert assert_type("packet", packet, bytes)
        self.log.debug("Send PacketIn")
        if reason is None:
            reason = OFPR_NO_MATCH
        if data_length is not None and len(packet) > data_length:
            if buffer_id is not None:
                packet = packet[:data_length]

        msg = ofp_packet_in(xid=0,
                            in_port=in_port,
                            buffer_id=buffer_id,
                            reason=reason,
                            data=packet)

        self.send(msg)

    def send_port_status(self, port, reason):
        """
    Send port status

    port is an ofp_phy_port
    reason is one of OFPPR_xxx
    """
        assert assert_type("port", port, ofp_phy_port, none_ok=False)
        assert reason in ofp_port_reason_rev_map.values()
        msg = ofp_port_status(desc=port, reason=reason)
        self.send(msg)

    def send_error(self, type, code, ofp=None, data=None, connection=None):
        """
    Send an error

    If you pass ofp, it will be used as the source of the error's XID and
    data.
    You can override the data by also specifying data.
    """
        err = ofp_error(type=type, code=code)
        if ofp:
            err.xid = ofp.xid
            err.data = ofp.pack()
        else:
            err.xid = 0
        if data is not None:
            err.data = data
        self.send(err, connection=connection)

    def rx_packet(self, packet, in_port, packet_data=None):
        """
    process a dataplane packet

    packet: an instance of ethernet
    in_port: the integer port number
    packet_data: packed version of packet if available
    """
        assert assert_type("packet", packet, ethernet, none_ok=False)
        assert assert_type("in_port", in_port, int, none_ok=False)
        port = self.ports.get(in_port)
        if port is None:
            self.log.warn("Got packet on missing port %i", in_port)
            return

        is_stp = packet.dst == _STP_MAC

        if (port.config & OFPPC_NO_RECV) and not is_stp:
            # Drop all except STP
            return
        if (port.config & OFPPC_NO_RECV_STP) and is_stp:
            # Drop STP
            return

        self.port_stats[in_port].rx_packets += 1
        if packet_data is not None:
            self.port_stats[in_port].rx_bytes += len(packet_data)
        else:
            self.port_stats[in_port].rx_bytes += len(
                packet.pack())  # Expensive

        self._lookup_count += 1
        entry = self.table.entry_for_packet(packet, in_port)
        if entry is not None:
            self._matched_count += 1
            entry.touch_packet(len(packet))
            self._process_actions_for_packet(entry.actions, packet, in_port)
        else:
            # no matching entry
            if port.config & OFPPC_NO_PACKET_IN:
                return
            buffer_id = self._buffer_packet(packet, in_port)
            if packet_data is None:
                packet_data = packet.pack()
            self.send_packet_in(in_port,
                                buffer_id,
                                packet_data,
                                reason=OFPR_NO_MATCH,
                                data_length=self.miss_send_len)

    def delete_port(self, port):
        """
    Removes a port

    Sends a port_status message to the controller

    Returns the removed phy_port
    """
        try:
            port_no = port.port_no
            assert self.ports[port_no] is port
        except:
            port_no = port
            port = self.ports[port_no]
        if port_no not in self.ports:
            raise RuntimeError("Can't remove nonexistent port " + str(port_no))
        self.send_port_status(port, OFPPR_DELETE)
        del self.ports[port_no]
        return port

    def add_port(self, port):
        """
    Adds a port

    Sends a port_status message to the controller
    """
        try:
            port_no = port.port_no
        except:
            port_no = port
            port = _generate_port(port_no, self.dpid)
        if port_no in self.ports:
            raise RuntimeError("Port %s already exists" % (port_no, ))
        self.ports[port_no] = port
        self.send_port_status(port, OFPPR_ADD)

    def _set_port_config_bit(self, port, bit, value):
        """
    Set a port config bit

    This is called in response to port_mods.  It is passed the ofp_phy_port,
    the bit/mask, and the value of the bit (i.e., 0 if the flag is to be
    unset, or the same value as bit if it is to be set).

    The return value is a tuple (handled, msg).
    If bit is handled, then handled will be True, else False.
    if msg is a string, it will be used as part of a log message.
    If msg is None, there will be no log message.
    If msg is anything else "truthy", an "enabled" log message is generated.
    If msg is anything else "falsy", a "disabled" log message is generated.
    msg is only used when handled is True.
    """
        if bit == OFPPC_NO_STP:
            if value == 0:
                # we also might send OFPBRC_EPERM if trying to disable this bit
                self.log.warn("Port %s: Can't enable 802.1D STP", port.port_no)
            return (True, None)

        if bit not in (OFPPC_PORT_DOWN, OFPPC_NO_STP, OFPPC_NO_RECV,
                       OFPPC_NO_RECV_STP, OFPPC_NO_FLOOD, OFPPC_NO_FWD,
                       OFPPC_NO_PACKET_IN):
            return (False, None)

        if port.set_config(value, bit):
            if bit == OFPPC_PORT_DOWN:
                # Note (Peter Peresini): Although the spec is not clear about it,
                # we will assume that config.OFPPC_PORT_DOWN implies
                # state.OFPPS_LINK_DOWN. This is consistent with Open vSwitch.

                #TODO: for now, we assume that there is always physical link present
                # and that the link state depends only on the configuration.
                old_state = port.state & OFPPS_LINK_DOWN
                port.state = port.state & ~OFPPS_LINK_DOWN
                if port.config & OFPPC_PORT_DOWN:
                    port.state = port.state | OFPPS_LINK_DOWN
                new_state = port.state & OFPPS_LINK_DOWN
                if old_state != new_state:
                    self.send_port_status(port, OFPPR_MODIFY)

            # Do default log message.
            return (True, value)

        # No change -- no log message.
        return (True, None)

    def _output_packet_physical(self, packet, port_no):
        """
    send a packet out a single physical port

    This is called by the more general _output_packet().

    Override this.
    """
        self.log.info("Sending packet %s out port %s", str(packet), port_no)

    def _output_packet(self, packet, out_port, in_port, max_len=None):
        """
    send a packet out some port

    This handles virtual ports and does validation.

    packet: instance of ethernet
    out_port, in_port: the integer port number
    max_len: maximum packet payload length to send to controller
    """
        assert assert_type("packet", packet, ethernet, none_ok=False)

        def real_send(port_no, allow_in_port=False):
            if type(port_no) == ofp_phy_port:
                port_no = port_no.port_no
            if port_no == in_port and not allow_in_port:
                self.log.warn("Dropping packet sent on port %i: Input port",
                              port_no)
                return
            if port_no not in self.ports:
                self.log.warn("Dropping packet sent on port %i: Invalid port",
                              port_no)
                return
            if self.ports[port_no].config & OFPPC_NO_FWD:
                self.log.warn(
                    "Dropping packet sent on port %i: Forwarding disabled",
                    port_no)
                return
            if self.ports[port_no].config & OFPPC_PORT_DOWN:
                self.log.warn("Dropping packet sent on port %i: Port down",
                              port_no)
                return
            if self.ports[port_no].state & OFPPS_LINK_DOWN:
                self.log.debug("Dropping packet sent on port %i: Link down",
                               port_no)
                return
            self.port_stats[port_no].tx_packets += 1
            self.port_stats[port_no].tx_bytes += len(
                packet.pack())  #FIXME: Expensive
            self._output_packet_physical(packet, port_no)

        if out_port < OFPP_MAX:
            real_send(out_port)
        elif out_port == OFPP_IN_PORT:
            real_send(in_port, allow_in_port=True)
        elif out_port == OFPP_FLOOD:
            for no, port in self.ports.iteritems():
                if no == in_port: continue
                if port.config & OFPPC_NO_FLOOD: continue
                real_send(port)
        elif out_port == OFPP_ALL:
            for no, port in self.ports.iteritems():
                if no == in_port: continue
                real_send(port)
        elif out_port == OFPP_CONTROLLER:
            buffer_id = self._buffer_packet(packet, in_port)
            # Should we honor OFPPC_NO_PACKET_IN here?
            self.send_packet_in(in_port,
                                buffer_id,
                                packet,
                                reason=OFPR_ACTION,
                                data_length=max_len)
        elif out_port == OFPP_TABLE:
            # Do we disable send-to-controller when performing this?
            # (Currently, there's the possibility that a table miss from this
            # will result in a send-to-controller which may send back to table...)
            self.rx_packet(packet, in_port)
        else:
            self.log.warn("Unsupported virtual output port: %d", out_port)

    def _buffer_packet(self, packet, in_port=None):
        """
    Buffer packet and return buffer ID

    If no buffer is available, return None.
    """
        # Do we have an empty slot?
        for (i, value) in enumerate(self._packet_buffer):
            if value is None:
                # Yes -- use it
                self._packet_buffer[i] = (packet, in_port)
                return i + 1
        # No -- create a new slow
        if len(self._packet_buffer) >= self.max_buffers:
            # No buffers available!
            return None
        self._packet_buffer.append((packet, in_port))
        return len(self._packet_buffer)

    def _process_actions_for_packet_from_buffer(self,
                                                actions,
                                                buffer_id,
                                                ofp=None):
        """
    output and release a packet from the buffer

    ofp is the message which triggered this processing, if any (used for error
    generation)
    """
        buffer_id = buffer_id - 1
        if (buffer_id >= len(self._packet_buffer)) or (buffer_id < 0):
            self.log.warn("Invalid output buffer id: %d", buffer_id + 1)
            return
        if self._packet_buffer[buffer_id] is None:
            self.log.warn("Buffer %d has already been flushed", buffer_id + 1)
            return
        (packet, in_port) = self._packet_buffer[buffer_id]
        self._process_actions_for_packet(actions, packet, in_port, ofp)
        self._packet_buffer[buffer_id] = None

    def _process_actions_for_packet(self, actions, packet, in_port, ofp=None):
        """
    process the output actions for a packet

    ofp is the message which triggered this processing, if any (used for error
    generation)
    """
        assert assert_type("packet", packet, (ethernet, bytes), none_ok=False)
        if not isinstance(packet, ethernet):
            packet = ethernet.unpack(packet)

        for action in actions:
            #if action.type is ofp_action_resubmit:
            #  self.rx_packet(packet, in_port)
            #  return
            h = self.action_handlers.get(action.type)
            if h is None:
                self.log.warn("Unknown action type: %x " % (action.type, ))
                self.send_error(type=OFPET_BAD_ACTION,
                                code=OFPBAC_BAD_TYPE,
                                ofp=ofp)
                return
            packet = h(action, packet, in_port)

    def _flow_mod_add(self, flow_mod, connection, table):
        """
    Process an OFPFC_ADD flow mod sent to the switch.
    """
        match = flow_mod.match
        priority = flow_mod.priority

        new_entry = TableEntry.from_flow_mod(flow_mod)

        if flow_mod.flags & OFPFF_CHECK_OVERLAP:
            if table.check_for_overlapping_entry(new_entry):
                # Another entry overlaps. Do not add.
                self.send_error(type=OFPET_FLOW_MOD_FAILED,
                                code=OFPFMFC_OVERLAP,
                                ofp=flow_mod,
                                connection=connection)
                return

        if flow_mod.command == OFPFC_ADD:
            # Exactly matching entries have to be removed if OFPFC_ADD
            table.remove_matching_entries(match,
                                          priority=priority,
                                          strict=True)

        if len(table) >= self.max_entries:
            # Flow table is full. Respond with error message.
            self.send_error(type=OFPET_FLOW_MOD_FAILED,
                            code=OFPFMFC_ALL_TABLES_FULL,
                            ofp=flow_mod,
                            connection=connection)
            return

        table.add_entry(new_entry)

    def _flow_mod_modify(self, flow_mod, connection, table, strict=False):
        """
    Process an OFPFC_MODIFY flow mod sent to the switch.
    """
        match = flow_mod.match
        priority = flow_mod.priority

        modified = False
        for entry in table.entries:
            # update the actions field in the matching flows
            if entry.is_matched_by(match, priority=priority, strict=strict):
                entry.actions = flow_mod.actions
                modified = True

        if not modified:
            # if no matching entry is found, modify acts as add
            self._flow_mod_add(flow_mod, connection, table)

    def _flow_mod_modify_strict(self, flow_mod, connection, table):
        """
    Process an OFPFC_MODIFY_STRICT flow mod sent to the switch.
    """
        self._flow_mod_modify(flow_mod, connection, table, strict=True)

    def _flow_mod_delete(self, flow_mod, connection, table, strict=False):
        """
    Process an OFPFC_DELETE flow mod sent to the switch.
    """
        match = flow_mod.match
        priority = flow_mod.priority

        out_port = flow_mod.out_port
        if out_port == OFPP_NONE: out_port = None  # Don't filter
        table.remove_matching_entries(match,
                                      priority=priority,
                                      strict=strict,
                                      out_port=out_port,
                                      reason=OFPRR_DELETE)

    def _flow_mod_delete_strict(self, flow_mod, connection, table):
        """
    Process an OFPFC_DELETE_STRICT flow mod sent to the switch.
    """
        self._flow_mod_delete(flow_mod, connection, table, strict=True)

    def _action_output(self, action, packet, in_port):
        self._output_packet(packet, action.port, in_port, action.max_len)
        return packet

    def _action_set_vlan_vid(self, action, packet, in_port):
        if not isinstance(packet.payload, vlan):
            vl = vlan()
            vl.eth_type = packet.type
            vl.payload = packet.payload
            packet.type = ethernet.VLAN_TYPE
            packet.payload = vl
        packet.payload.id = action.vlan_vid
        return packet

    def _action_set_vlan_pcp(self, action, packet, in_port):
        if not isinstance(packet.payload, vlan):
            vl = vlan()
            vl.payload = packet.payload
            vl.eth_type = packet.type
            packet.payload = vl
            packet.type = ethernet.VLAN_TYPE
        packet.payload.pcp = action.vlan_pcp
        return packet

    def _action_strip_vlan(self, action, packet, in_port):
        if isinstance(packet.payload, vlan):
            packet.type = packet.payload.eth_type
            packet.payload = packet.payload.payload
        return packet

    def _action_set_dl_src(self, action, packet, in_port):
        packet.src = action.dl_addr
        return packet

    def _action_set_dl_dst(self, action, packet, in_port):
        packet.dst = action.dl_addr
        return packet

    def _action_set_nw_src(self, action, packet, in_port):
        nw = packet.payload
        if isinstance(nw, vlan):
            nw = nw.payload
        if isinstance(nw, ipv4):
            nw.srcip = action.nw_addr
        return packet

    def _action_set_nw_dst(self, action, packet, in_port):
        nw = packet.payload
        if isinstance(nw, vlan):
            nw = nw.payload
        if isinstance(nw, ipv4):
            nw.dstip = action.nw_addr
        return packet

    def _action_set_nw_tos(self, action, packet, in_port):
        nw = packet.payload
        if isinstance(nw, vlan):
            nw = nw.payload
        if isinstance(nw, ipv4):
            nw.tos = action.nw_tos
        return packet

    def _action_set_tp_src(self, action, packet, in_port):
        nw = packet.payload
        if isinstance(nw, vlan):
            nw = nw.payload
        if isinstance(nw, ipv4):
            tp = nw.payload
            if isinstance(tp, udp) or isinstance(tp, tcp):
                tp.srcport = action.tp_port
        return packet

    def _action_set_tp_dst(self, action, packet, in_port):
        nw = packet.payload
        if isinstance(nw, vlan):
            nw = nw.payload
        if isinstance(nw, ipv4):
            tp = nw.payload
            if isinstance(tp, udp) or isinstance(tp, tcp):
                tp.dstport = action.tp_port
        return packet

    def _action_enqueue(self, action, packet, in_port):
        self.log.warn("Enqueue not supported.  Performing regular output.")
        self._output_packet(packet, action.tp_port, in_port)
        return packet


#  def _action_push_mpls_tag (self, action, packet, in_port):
#    bottom_of_stack = isinstance(packet.next, mpls)
#    packet.next = mpls(prev = packet.pack())
#    if bottom_of_stack:
#      packet.next.s = 1
#    packet.type = action.ethertype
#    return packet
#  def _action_pop_mpls_tag (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      return packet
#    if not isinstance(packet.next.next, str):
#      packet.next.next = packet.next.next.pack()
#    if action.ethertype in ethernet.type_parsers:
#      packet.next = ethernet.type_parsers[action.ethertype](packet.next.next)
#    else:
#      packet.next = packet.next.next
#    packet.ethertype = action.ethertype
#    return packet
#  def _action_set_mpls_label (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      mock = ofp_action_push_mpls()
#      packet = push_mpls_tag(mock, packet)
#    packet.next.label = action.mpls_label
#    return packet
#  def _action_set_mpls_tc (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      mock = ofp_action_push_mpls()
#      packet = push_mpls_tag(mock, packet)
#    packet.next.tc = action.mpls_tc
#    return packet
#  def _action_set_mpls_ttl (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      mock = ofp_action_push_mpls()
#      packet = push_mpls_tag(mock, packet)
#    packet.next.ttl = action.mpls_ttl
#    return packet
#  def _action_dec_mpls_ttl (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      return packet
#    packet.next.ttl = packet.next.ttl - 1
#    return packet

    def _stats_desc(self, ofp, connection):
        try:
            from pox.core import core
            return ofp_desc_stats(mfr_desc="POX",
                                  hw_desc=core._get_platform_info(),
                                  sw_desc=core.version_string,
                                  serial_num=str(self.dpid),
                                  dp_desc=type(self).__name__)
        except:
            return ofp_desc_stats(mfr_desc="POX",
                                  hw_desc="Unknown",
                                  sw_desc="Unknown",
                                  serial_num=str(self.dpid),
                                  dp_desc=type(self).__name__)

    def _stats_flow(self, ofp, connection):
        if ofp.body.table_id not in (TABLE_ALL, 0):
            return []  # No flows for other tables
        out_port = ofp.body.out_port
        if out_port == OFPP_NONE: out_port = None  # Don't filter
        return self.table.flow_stats(ofp.body.match, out_port)

    def _stats_aggregate(self, ofp, connection):
        if ofp.body.table_id not in (TABLE_ALL, 0):
            return []  # No flows for other tables
        out_port = ofp.body.out_port
        if out_port == OFPP_NONE: out_port = None  # Don't filter
        return self.table.aggregate_stats(ofp.body.match, out_port)

    def _stats_table(self, ofp, connection):
        # Some of these may come from the actual table(s) in the future...
        r = ofp_table_stats()
        r.table_id = 0
        r.name = "Default"
        r.wildcards = OFPFW_ALL
        r.max_entries = self.max_entries
        r.active_count = len(self.table)
        r.lookup_count = self._lookup_count
        r.matched_count = self._matched_count
        return r

    def _stats_port(self, ofp, connection):
        req = ofp.body
        if req.port_no == OFPP_NONE:
            return self.port_stats.values()
        else:
            return self.port_stats[req.port_no]

    def _stats_queue(self, ofp, connection):
        # We don't support queues whatsoever so either send an empty list or send
        # an OFP_ERROR if an actual queue is requested.
        req = ofp.body
        #if req.port_no != OFPP_ALL:
        #  self.send_error(type=OFPET_QUEUE_OP_FAILED, code=OFPQOFC_BAD_PORT,
        #                  ofp=ofp, connection=connection)
        # Note: We don't care about this case for now, even if port_no is bogus.
        if req.queue_id == OFPQ_ALL:
            return []
        else:
            self.send_error(type=OFPET_QUEUE_OP_FAILED,
                            code=OFPQOFC_BAD_QUEUE,
                            ofp=ofp,
                            connection=connection)

    def __repr__(self):
        return "%s(dpid=%s, num_ports=%d)" % (
            type(self).__name__, dpid_to_str(self.dpid), len(self.ports))
コード例 #10
0
ファイル: topology.py プロジェクト: 14gr1010/software
class OFSyncFlowTable (EventMixin):
  _eventMixin_events = set([FlowTableModification])
  """
  A flow table that keeps in sync with a switch
  """
  ADD = of.OFPFC_ADD
  REMOVE = of.OFPFC_DELETE
  REMOVE_STRICT = of.OFPFC_DELETE_STRICT
  TIME_OUT = 2

  def __init__ (self, switch=None, **kw):
    EventMixin.__init__(self)
    self.flow_table = FlowTable()
    self.switch = switch

    # a list of pending flow table entries : tuples (ADD|REMOVE, entry)
    self._pending = []

    # a map of pending barriers barrier_xid-> ([entry1,entry2])
    self._pending_barrier_to_ops = {}
    # a map of pending barriers per request entry -> (barrier_xid, time)
    self._pending_op_to_barrier = {}

    self.listenTo(switch)

  def install (self, entries=[]):
    """
    asynchronously install entries in the flow table

    will raise a FlowTableModification event when the change has been
    processed by the switch
    """
    self._mod(entries, OFSyncFlowTable.ADD)

  def remove_with_wildcards (self, entries=[]):
    """
    asynchronously remove entries in the flow table

    will raise a FlowTableModification event when the change has been
    processed by the switch
    """
    self._mod(entries, OFSyncFlowTable.REMOVE)

  def remove_strict (self, entries=[]):
    """
    asynchronously remove entries in the flow table.

    will raise a FlowTableModification event when the change has been
    processed by the switch
    """
    self._mod(entries, OFSyncFlowTable.REMOVE_STRICT)

  @property
  def entries (self):
    return self.flow_table.entries

  @property
  def num_pending (self):
    return len(self._pending)

  def __len__ (self):
    return len(self.flow_table)

  def _mod (self, entries, command):
    if isinstance(entries, TableEntry):
      entries = [ entries ]

    for entry in entries:
      if(command == OFSyncFlowTable.REMOVE):
        self._pending = [(cmd,pentry) for cmd,pentry in self._pending
                         if not (cmd == OFSyncFlowTable.ADD
                                 and entry.matches_with_wildcards(pentry))]
      elif(command == OFSyncFlowTable.REMOVE_STRICT):
        self._pending = [(cmd,pentry) for cmd,pentry in self._pending
                         if not (cmd == OFSyncFlowTable.ADD
                                 and entry == pentry)]

      self._pending.append( (command, entry) )

    self._sync_pending()

  def _sync_pending (self, clear=False):
    if not self.switch.connected:
      return False

    # resync the switch
    if clear:
      self._pending_barrier_to_ops = {}
      self._pending_op_to_barrier = {}
      self._pending = filter(lambda(op): op[0] == OFSyncFlowTable.ADD,
                             self._pending)

      self.switch.send(of.ofp_flow_mod(command=of.OFPFC_DELETE,
                                       match=of.ofp_match()))
      self.switch.send(of.ofp_barrier_request())

      todo = map(lambda(e): (OFSyncFlowTable.ADD, e),
                 self.flow_table.entries) + self._pending
    else:
      todo = [op for op in self._pending
              if op not in self._pending_op_to_barrier
              or (self._pending_op_to_barrier[op][1]
                  + OFSyncFlowTable.TIME_OUT) < time.time() ]

    for op in todo:
      fmod_xid = self.switch._xid_generator()
      flow_mod = op[1].to_flow_mod(xid=fmod_xid, command=op[0],
                                   flags=op[1].flags | of.OFPFF_SEND_FLOW_REM)
      self.switch.send(flow_mod)

    barrier_xid = self.switch._xid_generator()
    self.switch.send(of.ofp_barrier_request(xid=barrier_xid))
    now = time.time()
    self._pending_barrier_to_ops[barrier_xid] = todo

    for op in todo:
      self._pending_op_to_barrier[op] = (barrier_xid, now)

  def _handle_SwitchConnectionUp (self, event):
    # sync all_flows
    self._sync_pending(clear=True)

  def _handle_SwitchConnectionDown (self, event):
    # connection down. too bad for our unconfirmed entries
    self._pending_barrier_to_ops = {}
    self._pending_op_to_barrier = {}

  def _handle_BarrierIn (self, barrier):
    # yeah. barrier in. time to sync some of these flows
    if barrier.xid in self._pending_barrier_to_ops:
      added = []
      removed = []
      #print "barrier in: pending for barrier: %d: %s" % (barrier.xid,
      #    self._pending_barrier_to_ops[barrier.xid])
      for op in self._pending_barrier_to_ops[barrier.xid]:
        (command, entry) = op
        if(command == OFSyncFlowTable.ADD):
          self.flow_table.add_entry(entry)
          added.append(entry)
        else:
          removed.extend(self.flow_table.remove_matching_entries(entry.match,
              entry.priority, strict=command == OFSyncFlowTable.REMOVE_STRICT))
        #print "op: %s, pending: %s" % (op, self._pending)
        if op in self._pending: self._pending.remove(op)
        self._pending_op_to_barrier.pop(op, None)
      del self._pending_barrier_to_ops[barrier.xid]
      self.raiseEvent(FlowTableModification(added = added, removed=removed))
      return EventHalt
    else:
      return EventContinue

  def _handle_FlowRemoved (self, event):
    """
    process a flow removed event -- remove the matching flow from the table.
    """
    flow_removed = event.ofp
    for entry in self.flow_table.entries:
      if (flow_removed.match == entry.match
          and flow_removed.priority == entry.priority):
        self.flow_table.remove_entry(entry)
        self.raiseEvent(FlowTableModification(removed=[entry]))
        return EventHalt
    return EventContinue
コード例 #11
0
ファイル: switch.py プロジェクト: caibitim/MyPOX
  def __init__ (self, dpid, name=None, ports=4, miss_send_len=128,
                max_buffers=100, max_entries=0x7fFFffFF, features=None):
    """
    Initialize switch
     - ports is a list of ofp_phy_ports or a number of ports
     - miss_send_len is number of bytes to send to controller on table miss
     - max_buffers is number of buffered packets to store
     - max_entries is max flows entries per table
    """
    if name is None: name = dpid_to_str(dpid)
    self.name = name

    self.dpid = dpid

    if isinstance(ports, int):
      ports = [self.generate_port(i) for i in range(1, ports+1)]

    self.max_buffers = max_buffers
    self.max_entries = max_entries
    self.miss_send_len = miss_send_len
    self.config_flags = 0
    self._has_sent_hello = False

    self.table = FlowTable()
    self.table.addListeners(self)

    self._lookup_count = 0
    self._matched_count = 0

    self.log = logging.getLogger(self.name)
    self._connection = None

    # buffer for packets during packet_in
    self._packet_buffer = []

    # Map port_no -> openflow.pylibopenflow_01.ofp_phy_ports
    self.ports = {}
    self.port_stats = {}

    for port in ports:
      self.add_port(port)

    if features is not None:
      self.features = features
    else:
      # Set up default features

      self.features = SwitchFeatures()
      self.features.cap_flow_stats = True
      self.features.cap_table_stats = True
      self.features.cap_port_stats = True
      #self.features.cap_stp = True
      #self.features.cap_ip_reasm = True
      #self.features.cap_queue_stats = True
      #self.features.cap_arp_match_ip = True

      self.features.act_output = True
      self.features.act_enqueue = True
      self.features.act_strip_vlan = True
      self.features.act_set_vlan_vid = True
      self.features.act_set_vlan_pcp = True
      self.features.act_set_dl_dst = True
      self.features.act_set_dl_src = True
      self.features.act_set_nw_dst = True
      self.features.act_set_nw_src = True
      self.features.act_set_nw_tos = True
      self.features.act_set_tp_dst = True
      self.features.act_set_tp_src = True
      #self.features.act_vendor = True

    # Set up handlers for incoming OpenFlow messages
    # That is, self.ofp_handlers[OFPT_FOO] = self._rx_foo
    self.ofp_handlers = {}
    for value,name in ofp_type_map.iteritems():
      name = name.split("OFPT_",1)[-1].lower()
      h = getattr(self, "_rx_" + name, None)
      if not h: continue
      assert of._message_type_to_class[value]._from_controller, name
      self.ofp_handlers[value] = h

    # Set up handlers for actions
    # That is, self.action_handlers[OFPAT_FOO] = self._action_foo
    #TODO: Refactor this with above
    self.action_handlers = {}
    for value,name in ofp_action_type_map.iteritems():
      name = name.split("OFPAT_",1)[-1].lower()
      h = getattr(self, "_action_" + name, None)
      if not h: continue
      if getattr(self.features, "act_" + name) is False: continue
      self.action_handlers[value] = h

    # Set up handlers for stats handlers
    # That is, self.stats_handlers[OFPST_FOO] = self._stats_foo
    #TODO: Refactor this with above
    self.stats_handlers = {}
    for value,name in ofp_stats_type_map.iteritems():
      name = name.split("OFPST_",1)[-1].lower()
      h = getattr(self, "_stats_" + name, None)
      if not h: continue
      self.stats_handlers[value] = h

    # Set up handlers for flow mod handlers
    # That is, self.flow_mod_handlers[OFPFC_FOO] = self._flow_mod_foo
    #TODO: Refactor this with above
    self.flow_mod_handlers = {}
    for name,value in ofp_flow_mod_command_rev_map.iteritems():
      name = name.split("OFPFC_",1)[-1].lower()
      h = getattr(self, "_flow_mod_" + name, None)
      if not h: continue
      self.flow_mod_handlers[value] = h
コード例 #12
0
ファイル: switch.py プロジェクト: caibitim/MyPOX
class SoftwareSwitchBase (object):
  def __init__ (self, dpid, name=None, ports=4, miss_send_len=128,
                max_buffers=100, max_entries=0x7fFFffFF, features=None):
    """
    Initialize switch
     - ports is a list of ofp_phy_ports or a number of ports
     - miss_send_len is number of bytes to send to controller on table miss
     - max_buffers is number of buffered packets to store
     - max_entries is max flows entries per table
    """
    if name is None: name = dpid_to_str(dpid)
    self.name = name

    self.dpid = dpid

    if isinstance(ports, int):
      ports = [self.generate_port(i) for i in range(1, ports+1)]

    self.max_buffers = max_buffers
    self.max_entries = max_entries
    self.miss_send_len = miss_send_len
    self.config_flags = 0
    self._has_sent_hello = False

    self.table = FlowTable()
    self.table.addListeners(self)

    self._lookup_count = 0
    self._matched_count = 0

    self.log = logging.getLogger(self.name)
    self._connection = None

    # buffer for packets during packet_in
    self._packet_buffer = []

    # Map port_no -> openflow.pylibopenflow_01.ofp_phy_ports
    self.ports = {}
    self.port_stats = {}

    for port in ports:
      self.add_port(port)

    if features is not None:
      self.features = features
    else:
      # Set up default features

      self.features = SwitchFeatures()
      self.features.cap_flow_stats = True
      self.features.cap_table_stats = True
      self.features.cap_port_stats = True
      #self.features.cap_stp = True
      #self.features.cap_ip_reasm = True
      #self.features.cap_queue_stats = True
      #self.features.cap_arp_match_ip = True

      self.features.act_output = True
      self.features.act_enqueue = True
      self.features.act_strip_vlan = True
      self.features.act_set_vlan_vid = True
      self.features.act_set_vlan_pcp = True
      self.features.act_set_dl_dst = True
      self.features.act_set_dl_src = True
      self.features.act_set_nw_dst = True
      self.features.act_set_nw_src = True
      self.features.act_set_nw_tos = True
      self.features.act_set_tp_dst = True
      self.features.act_set_tp_src = True
      #self.features.act_vendor = True

    # Set up handlers for incoming OpenFlow messages
    # That is, self.ofp_handlers[OFPT_FOO] = self._rx_foo
    self.ofp_handlers = {}
    for value,name in ofp_type_map.iteritems():
      name = name.split("OFPT_",1)[-1].lower()
      h = getattr(self, "_rx_" + name, None)
      if not h: continue
      assert of._message_type_to_class[value]._from_controller, name
      self.ofp_handlers[value] = h

    # Set up handlers for actions
    # That is, self.action_handlers[OFPAT_FOO] = self._action_foo
    #TODO: Refactor this with above
    self.action_handlers = {}
    for value,name in ofp_action_type_map.iteritems():
      name = name.split("OFPAT_",1)[-1].lower()
      h = getattr(self, "_action_" + name, None)
      if not h: continue
      if getattr(self.features, "act_" + name) is False: continue
      self.action_handlers[value] = h

    # Set up handlers for stats handlers
    # That is, self.stats_handlers[OFPST_FOO] = self._stats_foo
    #TODO: Refactor this with above
    self.stats_handlers = {}
    for value,name in ofp_stats_type_map.iteritems():
      name = name.split("OFPST_",1)[-1].lower()
      h = getattr(self, "_stats_" + name, None)
      if not h: continue
      self.stats_handlers[value] = h

    # Set up handlers for flow mod handlers
    # That is, self.flow_mod_handlers[OFPFC_FOO] = self._flow_mod_foo
    #TODO: Refactor this with above
    self.flow_mod_handlers = {}
    for name,value in ofp_flow_mod_command_rev_map.iteritems():
      name = name.split("OFPFC_",1)[-1].lower()
      h = getattr(self, "_flow_mod_" + name, None)
      if not h: continue
      self.flow_mod_handlers[value] = h

  def _gen_port_name (self, port_no):
    return "%s.%s"%(dpid_to_str(self.dpid, True).replace('-','')[:12], port_no)

  def _gen_ethaddr (self, port_no):
    return EthAddr("02%06x%04x" % (self.dpid % 0x00FFff, port_no % 0xffFF))

  def generate_port (self, port_no, name = None, ethaddr = None):
    dpid = self.dpid
    p = ofp_phy_port()
    p.port_no = port_no
    if ethaddr is None:
      p.hw_addr = self._gen_ethaddr(p.port_no)
    else:
      p.hw_addr = EthAddr(ethaddr)
    if name is None:
      p.name = self._gen_port_name(p.port_no)
    else:
      p.name = name
    # Fill in features sort of arbitrarily
    p.config = OFPPC_NO_STP
    p.curr = OFPPF_10MB_HD
    p.advertised = OFPPF_10MB_HD
    p.supported = OFPPF_10MB_HD
    p.peer = OFPPF_10MB_HD
    return p

  @property
  def _time (self):
    """
    Get the current time

    This should be used for, e.g., calculating timeouts.  It currently isn't
    used everywhere it should be.

    Override this to change time behavior.
    """
    return time.time()

  def _handle_FlowTableModification (self, event):
    """
    Handle flow table modification events
    """
    # Currently, we only use this for sending flow_removed messages
    if not event.removed: return

    if event.reason in (OFPRR_IDLE_TIMEOUT,OFPRR_HARD_TIMEOUT,OFPRR_DELETE):
      # These reasons may lead to a flow_removed
      count = 0
      for entry in event.removed:
        if entry.flags & OFPFF_SEND_FLOW_REM and not entry.flags & OFPFF_EMERG:
          # Flow wants removal notification -- send it
          fr = entry.to_flow_removed(self._time, reason=event.reason)
          self.send(fr)
          count += 1
      self.log.debug("%d flows removed (%d removal notifications)",
          len(event.removed), count)

  def rx_message (self, connection, msg):
    """
    Handle an incoming OpenFlow message
    """
    ofp_type = msg.header_type
    h = self.ofp_handlers.get(ofp_type)
    if h is None:
      raise RuntimeError("No handler for ofp_type %s(%d)"
                         % (ofp_type_map.get(ofp_type), ofp_type))

    self.log.debug("Got %s with XID %s",ofp_type_map.get(ofp_type),msg.xid)
    h(msg, connection=connection)

  def set_connection (self, connection):
    """
    Set this switch's connection.
    """
    self._has_sent_hello = False
    connection.set_message_handler(self.rx_message)
    self._connection = connection

  def send (self, message, connection = None):
    """
    Send a message to this switch's communication partner
    """
    if connection is None:
      connection = self._connection
    if connection:
      connection.send(message)
    else:
      self.log.debug("Asked to send message %s, but not connected", message)

  def _rx_hello (self, ofp, connection):
    #FIXME: This isn't really how hello is supposed to work -- we're supposed
    #       to send it immediately on connection.  See _send_hello().
    self.send_hello()

  def _rx_echo_request (self, ofp, connection):
    """
    Handles echo requests
    """
    msg = ofp_echo_reply(xid=ofp.xid, body=ofp.body)
    self.send(msg)

  def _rx_features_request (self, ofp, connection):
    """
    Handles feature requests
    """
    self.log.debug("Send features reply")
    msg = ofp_features_reply(datapath_id = self.dpid,
                             xid = ofp.xid,
                             n_buffers = self.max_buffers,
                             n_tables = 1,
                             capabilities = self.features.capability_bits,
                             actions = self.features.action_bits,
                             ports = self.ports.values())
    self.send(msg)

  def _rx_flow_mod (self, ofp, connection):
    """
    Handles flow mods
    """
    self.log.debug("Flow mod details: %s", ofp.show())

    #self.table.process_flow_mod(ofp)
    #self._process_flow_mod(ofp, connection=connection, table=self.table)
    handler = self.flow_mod_handlers.get(ofp.command)
    if handler is None:
      self.log.warn("Command not implemented: %s" % command)
      self.send_error(type=OFPET_FLOW_MOD_FAILED, code=OFPFMFC_BAD_COMMAND,
                      ofp=ofp, connection=connection)
      return
    handler(flow_mod=ofp, connection=connection, table=self.table)

    if ofp.buffer_id is not None:
      self._process_actions_for_packet_from_buffer(ofp.actions, ofp.buffer_id,
                                                   ofp)

  def _rx_packet_out (self, packet_out, connection):
    """
    Handles packet_outs
    """
    self.log.debug("Packet out details: %s", packet_out.show())

    if packet_out.data:
      self._process_actions_for_packet(packet_out.actions, packet_out.data,
                                       packet_out.in_port, packet_out)
    elif packet_out.buffer_id is not None:
      self._process_actions_for_packet_from_buffer(packet_out.actions,
                                                   packet_out.buffer_id,
                                                   packet_out)
    else:
      self.log.warn("packet_out: No data and no buffer_id -- "
                    "don't know what to send")

  def _rx_echo_reply (self, ofp, connection):
    pass

  def _rx_barrier_request (self, ofp, connection):
    msg = ofp_barrier_reply(xid = ofp.xid)
    self.send(msg)

  def _rx_get_config_request (self, ofp, connection):
    msg = ofp_get_config_reply(xid = ofp.xid)
    msg.miss_send_len = self.miss_send_len
    msg.flags = self.config_flags
    self.log.debug("Sending switch config reply %s", msg)
    self.send(msg)

  def _rx_stats_request (self, ofp, connection):
    handler = self.stats_handlers.get(ofp.type)
    if handler is None:
      self.log.warning("Stats type %s not implemented", ofp.type)

      self.send_error(type=OFPET_BAD_REQUEST, code=OFPBRC_BAD_STAT,
                      ofp=ofp, connection=connection)
      return

    body = handler(ofp, connection=connection)
    if body is not None:
      reply = ofp_stats_reply(xid=ofp.xid, type=ofp.type, body=body)
      self.log.debug("Sending stats reply %s", reply)
      self.send(reply)

  def _rx_set_config (self, config, connection):
    self.miss_send_len = config.miss_send_len
    self.config_flags = config.flags

  def _rx_port_mod (self, port_mod, connection):
    port_no = port_mod.port_no
    if port_no not in self.ports:
      self.send_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_PORT,
                      ofp=port_mod, connection=connection)
      return
    port = self.ports[port_no]
    if port.hw_addr != port_mod.hw_addr:
      self.send_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_HW_ADDR,
                      ofp=port_mod, connection=connection)
      return

    mask = port_mod.mask

    for bit in range(32):
      bit = 1 << bit
      if mask & bit:
        handled,r = self._set_port_config_bit(port, bit, port_mod.config & bit)
        if not handled:
          self.log.warn("Unsupported port config flag: %08x", bit)
          continue
        if r is not None:
          msg = "Port %s: " % (port.port_no,)
          if isinstance(r, str):
            msg += r
          else:
            msg += ofp_port_config_map.get(bit, "config bit %x" % (bit,))
            msg += " set to "
            msg += "true" if r else "false"
          self.log.debug(msg)

  def _rx_vendor (self, vendor, connection):
    # We don't support vendor extensions, so send an OFP_ERROR, per
    # page 42 of spec
    self.send_error(type=OFPET_BAD_REQUEST, code=OFPBRC_BAD_VENDOR,
                    ofp=vendor, connection=connection)

  def _rx_queue_get_config_request (self, ofp, connection):
    """
    Handles an OFPT_QUEUE_GET_CONFIG_REQUEST message.
    """
    reply = ofp_queue_get_config_reply(xid=ofp.xid, port=ofp.port, queues=[])
    self.log.debug("Sending queue get config reply %s", reply)
    self.send(reply)

  def send_hello (self, force = False):
    """
    Send hello (once)
    """
    #FIXME: This is wrong -- we should just send when connecting.
    if self._has_sent_hello and not force: return
    self._has_sent_hello = True
    self.log.debug("Sent hello")
    msg = ofp_hello(xid=0)
    self.send(msg)

  def send_packet_in (self, in_port, buffer_id=None, packet=b'', reason=None,
                      data_length=None):
    """
    Send PacketIn
    """
    if hasattr(packet, 'pack'):
      packet = packet.pack()
    assert assert_type("packet", packet, bytes)
    self.log.debug("Send PacketIn")
    if reason is None:
      reason = OFPR_NO_MATCH
    if data_length is not None and len(packet) > data_length:
      if buffer_id is not None:
        packet = packet[:data_length]

    msg = ofp_packet_in(xid = 0, in_port = in_port, buffer_id = buffer_id,
                        reason = reason, data = packet)

    self.send(msg)

  def send_port_status (self, port, reason):
    """
    Send port status

    port is an ofp_phy_port
    reason is one of OFPPR_xxx
    """
    assert assert_type("port", port, ofp_phy_port, none_ok=False)
    assert reason in ofp_port_reason_rev_map.values()
    msg = ofp_port_status(desc=port, reason=reason)
    self.send(msg)

  def send_error (self, type, code, ofp=None, data=None, connection=None):
    """
    Send an error

    If you pass ofp, it will be used as the source of the error's XID and
    data.
    You can override the data by also specifying data.
    """
    err = ofp_error(type=type, code=code)
    if ofp:
      err.xid = ofp.xid
      err.data = ofp.pack()
    else:
      err.xid = 0
    if data is not None:
      err.data = data
    self.send(err, connection = connection)

  def rx_packet (self, packet, in_port, packet_data = None):
    """
    process a dataplane packet

    packet: an instance of ethernet
    in_port: the integer port number
    packet_data: packed version of packet if available
    """
    assert assert_type("packet", packet, ethernet, none_ok=False)
    assert assert_type("in_port", in_port, int, none_ok=False)
    port = self.ports.get(in_port)
    if port is None:
      self.log.warn("Got packet on missing port %i", in_port)
      return

    is_stp = packet.dst == _STP_MAC

    if (port.config & OFPPC_NO_RECV) and not is_stp:
      # Drop all except STP
      return
    if (port.config & OFPPC_NO_RECV_STP) and is_stp:
      # Drop STP
      return

    if self.config_flags & OFPC_FRAG_MASK:
      ipp = packet.find(ipv4)
      if ipp:
        if (ipp.flags & ipv4.MF_FLAG) or ipp.frag != 0:
          frag_mode = self.config_flags & OFPC_FRAG_MASK
          if frag_mode == OFPC_FRAG_DROP:
            # Drop fragment
            return
          elif frag_mode == OFPC_FRAG_REASM:
            if self.features.cap_ip_reasm:
              #TODO: Implement fragment reassembly
              self.log.info("Can't reassemble fragment: not implemented")
          else:
            self.log.warn("Illegal fragment processing mode: %i", frag_mode)

    self.port_stats[in_port].rx_packets += 1
    if packet_data is not None:
      self.port_stats[in_port].rx_bytes += len(packet_data)
    else:
      self.port_stats[in_port].rx_bytes += len(packet.pack()) # Expensive

    self._lookup_count += 1
    entry = self.table.entry_for_packet(packet, in_port)
    if entry is not None:
      self._matched_count += 1
      entry.touch_packet(len(packet))
      self._process_actions_for_packet(entry.actions, packet, in_port)
    else:
      # no matching entry
      if port.config & OFPPC_NO_PACKET_IN:
        return
      buffer_id = self._buffer_packet(packet, in_port)
      if packet_data is None:
        packet_data = packet.pack()
      self.send_packet_in(in_port, buffer_id, packet_data,
                          reason=OFPR_NO_MATCH, data_length=self.miss_send_len)

  def delete_port (self, port):
    """
    Removes a port

    Sends a port_status message to the controller

    Returns the removed phy_port
    """
    try:
      port_no = port.port_no
      assert self.ports[port_no] is port
    except:
      port_no = port
      port = self.ports[port_no]
    if port_no not in self.ports:
      raise RuntimeError("Can't remove nonexistent port " + str(port_no))
    self.send_port_status(port, OFPPR_DELETE)
    del self.ports[port_no]
    return port

  def add_port (self, port):
    """
    Adds a port

    Sends a port_status message to the controller
    """
    try:
      port_no = port.port_no
    except:
      port_no = port
      port = self.generate_port(port_no, self.dpid)
    if port_no in self.ports:
      raise RuntimeError("Port %s already exists" % (port_no,))
    self.ports[port_no] = port
    self.port_stats[port.port_no] = ofp_port_stats(port_no=port.port_no)
    self.send_port_status(port, OFPPR_ADD)

  def _set_port_config_bit (self, port, bit, value):
    """
    Set a port config bit

    This is called in response to port_mods.  It is passed the ofp_phy_port,
    the bit/mask, and the value of the bit (i.e., 0 if the flag is to be
    unset, or the same value as bit if it is to be set).

    The return value is a tuple (handled, msg).
    If bit is handled, then handled will be True, else False.
    if msg is a string, it will be used as part of a log message.
    If msg is None, there will be no log message.
    If msg is anything else "truthy", an "enabled" log message is generated.
    If msg is anything else "falsy", a "disabled" log message is generated.
    msg is only used when handled is True.
    """
    if bit == OFPPC_NO_STP:
      if value == 0:
        # we also might send OFPBRC_EPERM if trying to disable this bit
        self.log.warn("Port %s: Can't enable 802.1D STP", port.port_no)
      return (True, None)

    if bit not in (OFPPC_PORT_DOWN, OFPPC_NO_STP, OFPPC_NO_RECV, OFPPC_NO_RECV_STP,
                   OFPPC_NO_FLOOD, OFPPC_NO_FWD, OFPPC_NO_PACKET_IN):
      return (False, None)

    if port.set_config(value, bit):
      if bit == OFPPC_PORT_DOWN:
        # Note (Peter Peresini): Although the spec is not clear about it,
        # we will assume that config.OFPPC_PORT_DOWN implies
        # state.OFPPS_LINK_DOWN. This is consistent with Open vSwitch.

        #TODO: for now, we assume that there is always physical link present
        # and that the link state depends only on the configuration.
        old_state = port.state & OFPPS_LINK_DOWN
        port.state = port.state & ~OFPPS_LINK_DOWN
        if port.config & OFPPC_PORT_DOWN:
          port.state = port.state | OFPPS_LINK_DOWN
        new_state = port.state & OFPPS_LINK_DOWN
        if old_state != new_state:
          self.send_port_status(port, OFPPR_MODIFY)

      # Do default log message.
      return (True, value)

    # No change -- no log message.
    return (True, None)

  def _output_packet_physical (self, packet, port_no):
    """
    send a packet out a single physical port

    This is called by the more general _output_packet().

    Override this.
    """
    self.log.info("Sending packet %s out port %s", str(packet), port_no)

  def _output_packet (self, packet, out_port, in_port, max_len=None):
    """
    send a packet out some port

    This handles virtual ports and does validation.

    packet: instance of ethernet
    out_port, in_port: the integer port number
    max_len: maximum packet payload length to send to controller
    """
    assert assert_type("packet", packet, ethernet, none_ok=False)

    def real_send (port_no, allow_in_port=False):
      if type(port_no) == ofp_phy_port:
        port_no = port_no.port_no
      if port_no == in_port and not allow_in_port:
        self.log.warn("Dropping packet sent on port %i: Input port", port_no)
        return
      if port_no not in self.ports:
        self.log.warn("Dropping packet sent on port %i: Invalid port", port_no)
        return
      if self.ports[port_no].config & OFPPC_NO_FWD:
        self.log.warn("Dropping packet sent on port %i: Forwarding disabled",
                      port_no)
        return
      if self.ports[port_no].config & OFPPC_PORT_DOWN:
        self.log.warn("Dropping packet sent on port %i: Port down", port_no)
        return
      if self.ports[port_no].state & OFPPS_LINK_DOWN:
        self.log.debug("Dropping packet sent on port %i: Link down", port_no)
        return
      self.port_stats[port_no].tx_packets += 1
      self.port_stats[port_no].tx_bytes += len(packet.pack()) #FIXME: Expensive
      self._output_packet_physical(packet, port_no)

    if out_port < OFPP_MAX:
      real_send(out_port)
    elif out_port == OFPP_IN_PORT:
      real_send(in_port, allow_in_port=True)
    elif out_port == OFPP_FLOOD:
      for no,port in self.ports.iteritems():
        if no == in_port: continue
        if port.config & OFPPC_NO_FLOOD: continue
        real_send(port)
    elif out_port == OFPP_ALL:
      for no,port in self.ports.iteritems():
        if no == in_port: continue
        real_send(port)
    elif out_port == OFPP_CONTROLLER:
      buffer_id = self._buffer_packet(packet, in_port)
      # Should we honor OFPPC_NO_PACKET_IN here?
      self.send_packet_in(in_port, buffer_id, packet, reason=OFPR_ACTION,
                          data_length=max_len)
    elif out_port == OFPP_TABLE:
      # Do we disable send-to-controller when performing this?
      # (Currently, there's the possibility that a table miss from this
      # will result in a send-to-controller which may send back to table...)
      self.rx_packet(packet, in_port)
    else:
      self.log.warn("Unsupported virtual output port: %d", out_port)

  def _buffer_packet (self, packet, in_port=None):
    """
    Buffer packet and return buffer ID

    If no buffer is available, return None.
    """
    # Do we have an empty slot?
    for (i, value) in enumerate(self._packet_buffer):
      if value is None:
        # Yes -- use it
        self._packet_buffer[i] = (packet, in_port)
        return i + 1
    # No -- create a new slow
    if len(self._packet_buffer) >= self.max_buffers:
      # No buffers available!
      return None
    self._packet_buffer.append( (packet, in_port) )
    return len(self._packet_buffer)

  def _process_actions_for_packet_from_buffer (self, actions, buffer_id,
                                               ofp=None):
    """
    output and release a packet from the buffer

    ofp is the message which triggered this processing, if any (used for error
    generation)
    """
    buffer_id = buffer_id - 1
    if (buffer_id >= len(self._packet_buffer)) or (buffer_id < 0):
      self.log.warn("Invalid output buffer id: %d", buffer_id + 1)
      return
    if self._packet_buffer[buffer_id] is None:
      self.log.warn("Buffer %d has already been flushed", buffer_id + 1)
      return
    (packet, in_port) = self._packet_buffer[buffer_id]
    self._process_actions_for_packet(actions, packet, in_port, ofp)
    self._packet_buffer[buffer_id] = None

  def _process_actions_for_packet (self, actions, packet, in_port, ofp=None):
    """
    process the output actions for a packet

    ofp is the message which triggered this processing, if any (used for error
    generation)
    """
    assert assert_type("packet", packet, (ethernet, bytes), none_ok=False)
    if not isinstance(packet, ethernet):
      packet = ethernet.unpack(packet)

    for action in actions:
      #if action.type is ofp_action_resubmit:
      #  self.rx_packet(packet, in_port)
      #  return
      h = self.action_handlers.get(action.type)
      if h is None:
        self.log.warn("Unknown action type: %x " % (action.type,))
        self.send_error(type=OFPET_BAD_ACTION, code=OFPBAC_BAD_TYPE, ofp=ofp)
        return
      packet = h(action, packet, in_port)

  def _flow_mod_add (self, flow_mod, connection, table):
    """
    Process an OFPFC_ADD flow mod sent to the switch.
    """
    match = flow_mod.match
    priority = flow_mod.priority

    if flow_mod.flags & OFPFF_EMERG:
      if flow_mod.idle_timeout != 0 or flow_mod.hard_timeout != 0:
        # Emergency flow mod has non-zero timeouts. Do not add.
        self.log.warn("Rejecting emergency flow with nonzero timeout")
        self.send_error(type=OFPET_FLOW_MOD_FAILED,
                        code=OFPFMFC_BAD_EMERG_TIMEOUT,
                        ofp=flow_mod, connection=connection)
        return
      if flow_mod.flags & OFPFF_SEND_FLOW_REM:
        # Emergency flows can't send removal messages, we we might want to
        # reject this early.  Sadly, there's no error code for this, so we just
        # abuse EPERM.  If we eventually support Nicira extended error codes,
        # we should use one here.
        self.log.warn("Rejecting emergency flow with flow removal flag")
        self.send_error(type=OFPET_FLOW_MOD_FAILED,
                        code=OFPFMFC_EPERM,
                        ofp=flow_mod, connection=connection)
        return
      #NOTE: An error is sent anyways because the current implementation does
      #      not support emergency entries.
      self.log.warn("Rejecting emergency flow (not supported)")
      self.send_error(type=OFPET_FLOW_MOD_FAILED,
                      code=OFPFMFC_ALL_TABLES_FULL,
                      ofp=flow_mod, connection=connection)
      return

    new_entry = TableEntry.from_flow_mod(flow_mod)

    if flow_mod.flags & OFPFF_CHECK_OVERLAP:
      if table.check_for_overlapping_entry(new_entry):
        # Another entry overlaps. Do not add.
        self.send_error(type=OFPET_FLOW_MOD_FAILED, code=OFPFMFC_OVERLAP,
                        ofp=flow_mod, connection=connection)
        return

    if flow_mod.command == OFPFC_ADD:
      # Exactly matching entries have to be removed if OFPFC_ADD
      table.remove_matching_entries(match, priority=priority, strict=True)

    if len(table) >= self.max_entries:
      # Flow table is full. Respond with error message.
      self.send_error(type=OFPET_FLOW_MOD_FAILED,
                      code=OFPFMFC_ALL_TABLES_FULL,
                      ofp=flow_mod, connection=connection)
      return

    table.add_entry(new_entry)

  def _flow_mod_modify (self, flow_mod, connection, table, strict=False):
    """
    Process an OFPFC_MODIFY flow mod sent to the switch.
    """
    match = flow_mod.match
    priority = flow_mod.priority

    modified = False
    for entry in table.entries:
      # update the actions field in the matching flows
      if entry.is_matched_by(match, priority=priority, strict=strict):
        entry.actions = flow_mod.actions
        modified = True

    if not modified:
      # if no matching entry is found, modify acts as add
      self._flow_mod_add(flow_mod, connection, table)

  def _flow_mod_modify_strict (self, flow_mod, connection, table):
    """
    Process an OFPFC_MODIFY_STRICT flow mod sent to the switch.
    """
    self._flow_mod_modify(flow_mod, connection, table, strict=True)

  def _flow_mod_delete (self, flow_mod, connection, table, strict=False):
    """
    Process an OFPFC_DELETE flow mod sent to the switch.
    """
    match = flow_mod.match
    priority = flow_mod.priority

    out_port = flow_mod.out_port
    if out_port == OFPP_NONE: out_port = None # Don't filter
    table.remove_matching_entries(match, priority=priority, strict=strict,
                                  out_port=out_port, reason=OFPRR_DELETE)

  def _flow_mod_delete_strict (self, flow_mod, connection, table):
    """
    Process an OFPFC_DELETE_STRICT flow mod sent to the switch.
    """
    self._flow_mod_delete(flow_mod, connection, table, strict=True)

  def _action_output (self, action, packet, in_port):
    self._output_packet(packet, action.port, in_port, action.max_len)
    return packet
  def _action_set_vlan_vid (self, action, packet, in_port):
    if not isinstance(packet.payload, vlan):
      vl = vlan()
      vl.eth_type = packet.type
      vl.payload = packet.payload
      packet.type = ethernet.VLAN_TYPE
      packet.payload = vl
    packet.payload.id = action.vlan_vid
    return packet
  def _action_set_vlan_pcp (self, action, packet, in_port):
    if not isinstance(packet.payload, vlan):
      vl = vlan()
      vl.payload = packet.payload
      vl.eth_type = packet.type
      packet.payload = vl
      packet.type = ethernet.VLAN_TYPE
    packet.payload.pcp = action.vlan_pcp
    return packet
  def _action_strip_vlan (self, action, packet, in_port):
    if isinstance(packet.payload, vlan):
      packet.type = packet.payload.eth_type
      packet.payload = packet.payload.payload
    return packet
  def _action_set_dl_src (self, action, packet, in_port):
    packet.src = action.dl_addr
    return packet
  def _action_set_dl_dst (self, action, packet, in_port):
    packet.dst = action.dl_addr
    return packet
  def _action_set_nw_src (self, action, packet, in_port):
    nw = packet.payload
    if isinstance(nw, vlan):
      nw = nw.payload
    if isinstance(nw, ipv4):
      nw.srcip = action.nw_addr
    return packet
  def _action_set_nw_dst (self, action, packet, in_port):
    nw = packet.payload
    if isinstance(nw, vlan):
      nw = nw.payload
    if isinstance(nw, ipv4):
      nw.dstip = action.nw_addr
    return packet
  def _action_set_nw_tos (self, action, packet, in_port):
    nw = packet.payload
    if isinstance(nw, vlan):
      nw = nw.payload
    if isinstance(nw, ipv4):
      nw.tos = action.nw_tos
    return packet
  def _action_set_tp_src (self, action, packet, in_port):
    nw = packet.payload
    if isinstance(nw, vlan):
      nw = nw.payload
    if isinstance(nw, ipv4):
      tp = nw.payload
      if isinstance(tp, udp) or isinstance(tp, tcp):
        tp.srcport = action.tp_port
    return packet
  def _action_set_tp_dst (self, action, packet, in_port):
    nw = packet.payload
    if isinstance(nw, vlan):
      nw = nw.payload
    if isinstance(nw, ipv4):
      tp = nw.payload
      if isinstance(tp, udp) or isinstance(tp, tcp):
        tp.dstport = action.tp_port
    return packet
  def _action_enqueue (self, action, packet, in_port):
    self.log.warn("Enqueue not supported.  Performing regular output.")
    self._output_packet(packet, action.tp_port, in_port)
    return packet
#  def _action_push_mpls_tag (self, action, packet, in_port):
#    bottom_of_stack = isinstance(packet.next, mpls)
#    packet.next = mpls(prev = packet.pack())
#    if bottom_of_stack:
#      packet.next.s = 1
#    packet.type = action.ethertype
#    return packet
#  def _action_pop_mpls_tag (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      return packet
#    if not isinstance(packet.next.next, str):
#      packet.next.next = packet.next.next.pack()
#    if action.ethertype in ethernet.type_parsers:
#      packet.next = ethernet.type_parsers[action.ethertype](packet.next.next)
#    else:
#      packet.next = packet.next.next
#    packet.ethertype = action.ethertype
#    return packet
#  def _action_set_mpls_label (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      mock = ofp_action_push_mpls()
#      packet = push_mpls_tag(mock, packet)
#    packet.next.label = action.mpls_label
#    return packet
#  def _action_set_mpls_tc (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      mock = ofp_action_push_mpls()
#      packet = push_mpls_tag(mock, packet)
#    packet.next.tc = action.mpls_tc
#    return packet
#  def _action_set_mpls_ttl (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      mock = ofp_action_push_mpls()
#      packet = push_mpls_tag(mock, packet)
#    packet.next.ttl = action.mpls_ttl
#    return packet
#  def _action_dec_mpls_ttl (self, action, packet, in_port):
#    if not isinstance(packet.next, mpls):
#      return packet
#    packet.next.ttl = packet.next.ttl - 1
#    return packet


  def _stats_desc (self, ofp, connection):
    try:
      from pox.core import core
      return ofp_desc_stats(mfr_desc="POX",
                            hw_desc=core._get_platform_info(),
                            sw_desc=core.version_string,
                            serial_num=str(self.dpid),
                            dp_desc=type(self).__name__)
    except:
      return ofp_desc_stats(mfr_desc="POX",
                            hw_desc="Unknown",
                            sw_desc="Unknown",
                            serial_num=str(self.dpid),
                            dp_desc=type(self).__name__)


  def _stats_flow (self, ofp, connection):
    if ofp.body.table_id not in (TABLE_ALL, 0):
      return [] # No flows for other tables
    out_port = ofp.body.out_port
    if out_port == OFPP_NONE: out_port = None # Don't filter
    return self.table.flow_stats(ofp.body.match, out_port)

  def _stats_aggregate (self, ofp, connection):
    if ofp.body.table_id not in (TABLE_ALL, 0):
      return [] # No flows for other tables
    out_port = ofp.body.out_port
    if out_port == OFPP_NONE: out_port = None # Don't filter
    return self.table.aggregate_stats(ofp.body.match, out_port)

  def _stats_table (self, ofp, connection):
    # Some of these may come from the actual table(s) in the future...
    r = ofp_table_stats()
    r.table_id = 0
    r.name = "Default"
    r.wildcards = OFPFW_ALL
    r.max_entries = self.max_entries
    r.active_count = len(self.table)
    r.lookup_count = self._lookup_count
    r.matched_count = self._matched_count
    return r

  def _stats_port (self, ofp, connection):
    req = ofp.body
    if req.port_no == OFPP_NONE:
      return self.port_stats.values()
    else:
      return self.port_stats[req.port_no]

  def _stats_queue (self, ofp, connection):
    # We don't support queues whatsoever so either send an empty list or send
    # an OFP_ERROR if an actual queue is requested.
    req = ofp.body
    #if req.port_no != OFPP_ALL:
    #  self.send_error(type=OFPET_QUEUE_OP_FAILED, code=OFPQOFC_BAD_PORT,
    #                  ofp=ofp, connection=connection)
    # Note: We don't care about this case for now, even if port_no is bogus.
    if req.queue_id == OFPQ_ALL:
      return []
    else:
      self.send_error(type=OFPET_QUEUE_OP_FAILED, code=OFPQOFC_BAD_QUEUE,
                      ofp=ofp, connection=connection)


  def __repr__ (self):
    return "%s(dpid=%s, num_ports=%d)" % (type(self).__name__,
                                          dpid_to_str(self.dpid),
                                          len(self.ports))
コード例 #13
0
ファイル: snapshot.py プロジェクト: raunaks42/SDNRacer
 def decode_flow_table(self, json):
     ft = FlowTable()
     for e in json["entries"]:
         ft.add_entry(self.decode_entry(e))
     return ft
コード例 #14
0
ファイル: switch_test.py プロジェクト: 14gr1010/software
 def table():
   t = FlowTable()
   t.add_entry(TableEntry(priority=6, cookie=0x1, match=ofp_match(dl_src=EthAddr("00:00:00:00:00:01"),nw_src="1.2.3.4"), actions=[ofp_action_output(port=5)]))
   t.add_entry(TableEntry(priority=5, cookie=0x2, match=ofp_match(dl_src=EthAddr("00:00:00:00:00:02"), nw_src="1.2.3.0/24"), actions=[ofp_action_output(port=6)]))
   t.add_entry(TableEntry(priority=1, cookie=0x3, match=ofp_match(), actions=[]))
   return t