Exemplo n.º 1
0
  def __init__(self, dpid, name=None, ports=4, miss_send_len=128,
      n_buffers=100, n_tables=1, capabilities=None):
    """Initialize switch"""
    ##Datapath id of switch
    self.dpid = dpid
    ## Human-readable name of the switch
    self.name = name
    if self.name is None:
      self.name = str(dpid)
    self.log = logging.getLogger(self.name)
    ##Number of buffers
    self.n_buffers = n_buffers
    ##Number of tables
    self.n_tables= n_tables
    # Note that there is one switch table in the OpenFlow 1.0 world
    self.table = SwitchFlowTable()
    # buffer for packets during packet_in
    self.packet_buffer = []
    if(ports == None or isinstance(ports, int)):
      ports=_default_port_list(num_ports=ports, prefix=dpid)

    self.xid_count = xid_generator(1)

    ## Hash of 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)

    ## (OpenFlow Handler map)
    self.ofp_handlers = {
       # Reactive handlers
       ofp_type_rev_map['OFPT_HELLO'] : self._receive_hello,
       ofp_type_rev_map['OFPT_ECHO_REQUEST'] : self._receive_echo,
       ofp_type_rev_map['OFPT_FEATURES_REQUEST'] : self._receive_features_request,
       ofp_type_rev_map['OFPT_FLOW_MOD'] : self._receive_flow_mod,
       ofp_type_rev_map['OFPT_PACKET_OUT'] : self._receive_packet_out,
       ofp_type_rev_map['OFPT_BARRIER_REQUEST'] : self._receive_barrier_request,
       ofp_type_rev_map['OFPT_GET_CONFIG_REQUEST'] : self._receive_get_config_request,
       ofp_type_rev_map['OFPT_SET_CONFIG'] : self._receive_set_config,
       ofp_type_rev_map['OFPT_STATS_REQUEST'] : self._receive_stats_request,
       ofp_type_rev_map['OFPT_VENDOR'] : self._receive_vendor,
       ofp_type_rev_map['OFPT_PORT_MOD'] : self._receive_port_mod,
       # Proactive responses
       ofp_type_rev_map['OFPT_ECHO_REPLY'] : self._receive_echo_reply
       # TODO: many more packet types to process
    }
    
    self._connection = None

    ##Capabilities
    if (isinstance(capabilities, SwitchCapabilities)):
      self.capabilities = capabilities
    else:
      self.capabilities = SwitchCapabilities(miss_send_len)
Exemplo n.º 2
0
class SwitchImpl(EventMixin):
  _eventMixin_events = set([DpPacketOut])

  # ports is a list of ofp_phy_ports
  def __init__(self, dpid, name=None, ports=4, miss_send_len=128,
      n_buffers=100, n_tables=1, capabilities=None):
    """Initialize switch"""
    ##Datapath id of switch
    self.dpid = dpid
    ## Human-readable name of the switch
    self.name = name
    if self.name is None:
      self.name = str(dpid)
    self.log = logging.getLogger(self.name)
    ##Number of buffers
    self.n_buffers = n_buffers
    ##Number of tables
    self.n_tables= n_tables
    # Note that there is one switch table in the OpenFlow 1.0 world
    self.table = SwitchFlowTable()
    # buffer for packets during packet_in
    self.packet_buffer = []
    if(ports == None or isinstance(ports, int)):
      ports=_default_port_list(num_ports=ports, prefix=dpid)

    self.xid_count = xid_generator(1)

    ## Hash of 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)

    ## (OpenFlow Handler map)
    self.ofp_handlers = {
       # Reactive handlers
       ofp_type_rev_map['OFPT_HELLO'] : self._receive_hello,
       ofp_type_rev_map['OFPT_ECHO_REQUEST'] : self._receive_echo,
       ofp_type_rev_map['OFPT_FEATURES_REQUEST'] : self._receive_features_request,
       ofp_type_rev_map['OFPT_FLOW_MOD'] : self._receive_flow_mod,
       ofp_type_rev_map['OFPT_PACKET_OUT'] : self._receive_packet_out,
       ofp_type_rev_map['OFPT_BARRIER_REQUEST'] : self._receive_barrier_request,
       ofp_type_rev_map['OFPT_GET_CONFIG_REQUEST'] : self._receive_get_config_request,
       ofp_type_rev_map['OFPT_SET_CONFIG'] : self._receive_set_config,
       ofp_type_rev_map['OFPT_STATS_REQUEST'] : self._receive_stats_request,
       ofp_type_rev_map['OFPT_VENDOR'] : self._receive_vendor,
       ofp_type_rev_map['OFPT_PORT_MOD'] : self._receive_port_mod,
       # Proactive responses
       ofp_type_rev_map['OFPT_ECHO_REPLY'] : self._receive_echo_reply
       # TODO: many more packet types to process
    }
    
    self._connection = None

    ##Capabilities
    if (isinstance(capabilities, SwitchCapabilities)):
      self.capabilities = capabilities
    else:
      self.capabilities = SwitchCapabilities(miss_send_len)

  def set_io_worker(self, io_worker):
    self._connection = ControllerConnection(io_worker, self.ofp_handlers)
    return self._connection

  def set_connection(self, connection):
    connection.ofp_handlers = self.ofp_handlers
    self._connection = connection

  def send(self, message):
    """ Send a message to this switches communication partner. If the switch is not connected, the message is silently dropped. """

    if self._connection:
      self._connection.send(message)
    else:
      self.log.debug("Asked to send message %s, but not connected" % message)

  # ==================================== #
  #    Reactive OFP processing           #
  # ==================================== #
  def _receive_hello(self, ofp):
    self.log.debug("Receive hello %s" % self.name)
    # How does the OpenFlow protocol prevent an infinite loop of Hello messages?
    self.send_hello()

  def _receive_echo(self, ofp):
    """Reply to echo request
    """
    self.log.debug("Reply echo of xid: %s %s" % (str(ofp), self.name))
    msg = ofp_echo_reply(xid=ofp.xid)
    self.send(msg)

  def _receive_features_request(self, ofp):
    """Reply to feature request
    """
    self.log.debug("Reply features request of xid %s %s" % (str(ofp), self.name))
    msg = ofp_features_reply(datapath_id = self.dpid, xid = ofp.xid, n_buffers = self.n_buffers,
                             n_tables = self.n_tables,
                             capabilities = self.capabilities.get_capabilities(),
                             actions = self.capabilities.get_actions(),
                             ports = self.ports.values())
    self.send(msg)

  def _receive_flow_mod(self, ofp):
    """Handle flow mod: just print it here
    """
    self.log.debug("Flow mod %s: %s" % (self.name, ofp.show()))
    self.table.process_flow_mod(ofp)
    if(ofp.buffer_id > 0):
      self._process_actions_for_packet_from_buffer(ofp.actions, ofp.buffer_id)

  def _receive_packet_out(self, packet_out):
    """
    Send the packet out the given port
    """
    self.log.debug("Packet out: %s" % packet_out.show())

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

  def _receive_echo_reply(self, ofp):
    self.log.debug("Echo reply: %s %s" % (str(ofp), self.name))

  def _receive_barrier_request(self, ofp):
    self.log.debug("Barrier request %s %s" % (self.name, str(ofp)))
    msg = ofp_barrier_reply(xid = ofp.xid)
    self.send(msg)

  def _receive_get_config_request(self, ofp):
    self.log.debug("Get config request %s %s " % (self.name, str(ofp)))
    msg = ofp_get_config_reply(xid = ofp.xid)
    self.send(msg)

  def _receive_stats_request(self, ofp):
    self.log.debug("Get stats request %s %s " % (self.name, str(ofp)))
 
    def desc_stats(ofp):
      return ofp_desc_stats(mfr_desc="BadAssEmulatedPoxSwitch(TM)",
                            hw_desc="your finest emulated asics",
                            sw_desc="pox. reliable, fast, stable. Choose 0 (or more?)",
                            serial_num=str(self.dpid),
                            dp_desc="high performance emuswitch. Several packets per second have been observed (but not by reliable witnesses)")

    def flow_stats(ofp):
      req = ofp_flow_stats_request().unpack(ofp.body)
      assert(self.table_id == TABLE_ALL)
      return self.table.flow_stats(req.match, req.out_port)

    def aggregate_stats(ofp):
      req = ofp_aggregate_stats_request().unpack(ofp.body)
      assert(self.table_id == TABLE_ALL)
      return self.table.aggregate_stats(req.match, out_port)

    def table_stats(ofp):
      return self.table.table_stats()

    def port_stats(ofp):
      req = ofp_port_stats_request().unpack(ofp.body)
      if req.port_no == OFPP_NONE:
        res = ofp_port_stats(port_no=OFPP_NONE)
        for stats in self.port_stats.values():
          res += stats
        return res
      else:
        return self.port_stats[req.port_no]

    def queue_stats(ofp):
      raise AttributeError("not implemented")

    stats_handlers = {
        OFPST_DESC: desc_stats,
        OFPST_FLOW: flow_stats,
        OFPST_AGGREGATE: aggregate_stats,
        OFPST_TABLE: table_stats,
        OFPST_PORT: port_stats,
        OFPST_QUEUE: queue_stats
    }

    if ofp.type in stats_handlers:
      handler = stats_handlers[ofp.type]
    else:
      raise AttributeError("Unsupported stats request type %d" % ofp.type)

    reply = ofp_stats_reply(xid=ofp.xid, body=handler(ofp))
    self.log.debug("Sending stats reply %s %s" % (self.name, str(reply)))
    self.send(reply)

  def _receive_set_config(self, config):
    self.log.debug("Set config %s %s" % (self.name, str(config)))

  def _receive_vendor(self, vendor):
    self.log.debug("Vendor %s %s" % (self.name, str(vendor)))
    # We don't support vendor extensions, so send an OFP_ERROR, per page 42 of spec
    err = ofp_error(type=OFPET_BAD_REQUEST, code=OFPBRC_BAD_VENDOR)
    self.send(err)

  def _receive_port_mod(self, ofp):
    self.log.debug("Get port modification request %s %s " % (self.name, str(ofp)))
    port_no = ofp.port_no
    if port_no not in self.ports:
      err = ofp_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_PORT)
      self.send(err)
      return
    port = self.ports[port_no]
    if port.hw_addr != ofp.hw_addr:
      err = ofp_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_HW_ADDR)
      self.send(err)
      return

    supported = OFPPC_PORT_DOWN
    assert (ofp.mask | supported) == supported

    if ofp.mask & OFPPC_PORT_DOWN:
      port.config = (port.config & ~OFPPC_PORT_DOWN) | (ofp.config & 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 OpenVSwitch.

      # FIXME: for now, we assume that there is always physical link present
      # and that the link state depends only on the configuration.
      port.state = port.state & ~OFPPS_LINK_DOWN
      if port.config & OFPPC_PORT_DOWN:
        port.state = port.state | OFPPS_LINK_DOWN

    self.send_port_status(port, OFPPR_MODIFY)

  # ==================================== #
  #    Proactive OFP processing          #
  # ==================================== #
  def send_hello(self):
    """Send hello
    """
    self.log.debug("Send hello %s " % self.name)
    msg = ofp_hello()
    self.send(msg)

  def send_packet_in(self, in_port, buffer_id=None, packet="", xid=None, reason=None):
    """Send PacketIn
    Assume no match as reason, buffer_id = 0xFFFFFFFF,
    and empty packet by default
    """
    assert_type("packet", packet, ethernet)
    self.log.debug("Send PacketIn %s " % self.name)
    if (reason == None):
      reason = ofp_packet_in_reason_rev_map['OFPR_NO_MATCH']
    if (buffer_id == None):
      buffer_id = int("0xFFFFFFFF",16)

    if xid == None:
      xid = self.xid_count()
    msg = ofp_packet_in(xid=xid, in_port = in_port, buffer_id = buffer_id, reason = reason,
                        data = packet.pack())
    
    self.send(msg)

  def send_echo(self, xid=0):
    """Send echo request
    """
    self.log.debug("Send echo %s" % self.name)
    msg = ofp_echo_request()
    self.send(msg)
    
  def send_port_status(self, port, reason):
    '''
    port is an ofp_phy_port
    reason is one of 'OFPPR_ADD', 'OFPPR_DELETE', 'OFPPR_MODIFY'
    '''
    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)

  # ==================================== #
  #   Dataplane processing               #
  # ==================================== #

  def process_packet(self, packet, in_port):
    """ process a dataplane packet the way a real OpenFlow switch would.
        packet: an instance of ethernet
        in_port: the integer port number
    """
    assert_type("packet", packet, ethernet, none_ok=False)
    assert_type("in_port", in_port, int, none_ok=False)

    entry = self.table.entry_for_packet(packet, in_port)
    if(entry != None):
      entry.touch_packet(len(packet))
      self._process_actions_for_packet(entry.actions, packet, in_port)
    else:
      # no matching entry
      buffer_id = self._buffer_packet(packet, in_port)
      self.send_packet_in(in_port, buffer_id, packet, self.xid_count(), reason=OFPR_NO_MATCH)

  def take_port_down(self, port):
    ''' Take the given port down, and send a port_status message to the controller '''
    port_no = port.port_no
    if port_no not in self.ports:
      raise RuntimeError("port_no %d not in %s's ports" % (port_no, str(self)))
    # Hmmm, is deleting the port the correct behavior?
    del self.ports[port_no]
    self.send_port_status(port, OFPPR_DELETE)
    
  def bring_port_up(self, port):
    ''' Bring the given port up, and send a port_status message to the controller '''
    port_no = port.port_no
    if port_no in self.ports:
      raise RuntimeError("port_no %d already in %s's ports" % (port_no, str(self)))
    self.ports[port_no] = port
    self.send_port_status(port, OFPPR_ADD)

  # ==================================== #
  #    Helper Methods                    #
  # ==================================== #

  def _output_packet(self, packet, out_port, in_port):
    """ send a packet out some port.
        packet: instance of ethernet
        out_port, in_port: the integer port number """
    assert_type("packet", packet, ethernet, none_ok=False)
    def real_send(port_no):
      if type(port_no) == ofp_phy_port:
        port_no = port_no.port_no
      if port_no not in self.ports:
        raise RuntimeError("Invalid physical output port: %x" % port_no)
      if self.ports[port_no].state & OFPPS_LINK_DOWN:
        self.log.debug("Sending packet on a port which is down!")
      else:
        self.raiseEvent(DpPacketOut(self, packet, self.ports[port_no]))

    if out_port < OFPP_MAX:
      real_send(out_port)
    elif out_port == OFPP_IN_PORT:
      real_send(in_port)
    elif out_port == OFPP_FLOOD or out_port == OFPP_ALL:
      # no support for spanning tree yet -> flood=all
      for (no,port) in self.ports.iteritems():
        if no != in_port:
          real_send(port)
    elif out_port == OFPP_CONTROLLER:
      buffer_id = self._buffer_packet(packet, in_port)
      self.send_packet_in(in_port, buffer_id, packet, self.xid_count(), reason=OFPR_ACTION)
    else:
      raise("Unsupported virtual output port: %x" % out_port)


  def _buffer_packet(self, packet, in_port=None):
    """ Find a free buffer slot to buffer the packet in. """
    for (i, value) in enumerate(self.packet_buffer):
      if(value==None):
        self.packet_buffer[i] = (packet, in_port)
        return i + 1
    self.packet_buffer.append( (packet, in_port) )
    return len(self.packet_buffer)

  def _process_actions_for_packet_from_buffer(self, actions, buffer_id):
    """ output and release a packet from the buffer """
    buffer_id = buffer_id - 1
    if(buffer_id > len(self.packet_buffer) or self.packet_buffer[buffer_id] == None):
      self.log.warn("Invalid output buffer id: %x" % buffer_id)
      return
    (packet, in_port) = self.packet_buffer[buffer_id]
    self._process_actions_for_packet(actions, packet, in_port)
    self.packet_buffer[buffer_id] = None

  def _process_actions_for_packet(self, actions, packet, in_port):
    """ process the output actions for a packet """
    assert_type("packet", packet, [ethernet, str], none_ok=False)
    if not isinstance(packet, ethernet):
      packet = ethernet.unpack(packet)

    def output_packet(action, packet):
      self._output_packet(packet, action.port, in_port)
      return packet
    def set_vlan_id(action, packet):
      if not isinstance(packet.next, vlan): 
        packet.next = vlan(prev = packet.next)
        packet.next.eth_type = packet.type
        packet.type = ethernet.VLAN_TYPE
      packet.id = action.vlan_id
      return packet
    def set_vlan_pcp(action, packet):
      if not isinstance(packet.next, vlan):
        packet.next = vlan(prev = packet)
        packet.next.eth_type = packet.type
        packet.type = ethernet.VLAN_TYPE
      packet.pcp = action.vlan_pcp
      return packet
    def strip_vlan(action, packet):
      if isinstance(packet.next, vlan): 
        packet.type = packet.next.eth_type
        packet.next = packet.next.next
      return packet
    def set_dl_src(action, packet):
      packet.src = action.dl_addr
      return packet
    def set_dl_dst(action, packet):
      packet.dst = action.dl_addr
      return packet
    def set_nw_src(action, packet):
      if(isinstance(packet.next, ipv4)):
        packet.next.nw_src = action.nw_addr
      return packet
    def set_nw_dst(action, packet):
      if(isinstance(packet.next, ipv4)):
        packet.next.nw_dst = action.nw_addr
      return packet
    def set_nw_tos(action, packet):
      if(isinstance(packet.next, ipv4)):
        packet.next.tos = action.nw_tos
      return packet
    def set_tp_src(action, packet):
      if(isinstance(packet.next, udp) or isinstance(packet.next, tcp)):
        packet.next.srcport = action.tp_port
      return packet
    def set_tp_dst(action, packet):
      if(isinstance(packet.next, udp) or isinstance(packet.next, tcp)):
        packet.next.dstport = action.tp_port
      return packet
    def enqueue(action, packet):
      self.log.warn("output_enqueue not supported yet. Performing regular output")
      return output_packet(action.tp_port, packet)
#    def push_mpls_tag(action, packet):
#      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 pop_mpls_tag(action, packet):
#      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 set_mpls_label(action, packet):
#      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 set_mpls_tc(action, packet):
#      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 set_mpls_ttl(action, packet):
#      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 dec_mpls_ttl(action, packet):
#      if not isinstance(packet.next, mpls):
#        return packet
#      packet.next.ttl = packet.next.ttl - 1
#      return packet
    handler_map = {
        OFPAT_OUTPUT: output_packet,
        OFPAT_SET_VLAN_VID: set_vlan_id,
        OFPAT_SET_VLAN_PCP: set_vlan_pcp,
        OFPAT_STRIP_VLAN: strip_vlan,
        OFPAT_SET_DL_SRC: set_dl_src,
        OFPAT_SET_DL_DST: set_dl_dst,
        OFPAT_SET_NW_SRC: set_nw_src,
        OFPAT_SET_NW_DST: set_nw_dst,
        OFPAT_SET_NW_TOS: set_nw_tos,
        OFPAT_SET_TP_SRC: set_tp_src,
        OFPAT_SET_TP_DST: set_tp_dst,
        OFPAT_ENQUEUE: enqueue,
#        OFPAT_PUSH_MPLS: push_mpls_tag,
#        OFPAT_POP_MPLS: pop_mpls_tag,
#        OFPAT_SET_MPLS_LABEL: set_mpls_label,
#        OFPAT_SET_MPLS_TC: set_mpls_tc,
#        OFPAT_SET_MPLS_TTL: set_mpls_ttl,
#        OFPAT_DEC_MPLS_TTL: dec_mpls_ttl,
    }
    for action in actions:
#      if action.type is ofp_action_resubmit:
#        self.process_packet(packet, in_port)
#        return
      if(action.type not in handler_map):
        raise NotImplementedError("Unknown action type: %x " % type)
      packet = handler_map[action.type](action, packet)

  def __repr__(self):
    return "SwitchImpl(dpid=%d, num_ports=%d)" % (self.dpid, len(self.ports))
Exemplo n.º 3
0
class SoftwareSwitchBase (object):
  def __init__ (self, dpid, name=None, ports=4, miss_send_len=128,
                max_buffers=100, features=None):
    """
    Initialize switch
     - ports is a list of ofp_phy_ports
    """
    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.miss_send_len = miss_send_len
    self._has_sent_hello = False

    self.table = SwitchFlowTable()
    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.flow_stats = True
      self.features.table_stats = True
      self.features.port_stats = True
      #self.features.stp = True
      #self.features.ip_reasm = True
      #self.features.queue_stats = True
      #self.features.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

  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):
    """
    Send a message to this switch's communication partner
    """

    if self._connection:
      self._connection.send(message)
    else:
      self.log.debug("Asked to send message %s, but not connected", message)

  def _rx_hello (self, ofp, connection):
    self.send_hello()

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

  def _rx_features_request (self, ofp, connection):
    """
    Handles feature requests
    """
    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)
    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)
    self.send(msg)

  def _rx_stats_request (self, ofp, connection):
    def desc_stats (ofp):
      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 flow_stats (ofp):
      req = ofp_flow_stats_request().unpack(ofp.body)
      assert self.table_id in (TABLE_ALL, 0)
      return self.table.flow_stats(req.match, req.out_port)

    def aggregate_stats (ofp):
      req = ofp_aggregate_stats_request().unpack(ofp.body)
      assert self.table_id in (TABLE_ALL, 0)
      return self.table.aggregate_stats(req.match, out_port)

    def table_stats (ofp):
      # 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 = 0x7fFFffFF
      r.active_count = len(self.table)
      r.lookup_count = self._lookup_count
      r.matched_count = self._matched_count
      return r

    def port_stats (ofp):
      req = ofp_port_stats_request().unpack(ofp.body)
      if req.port_no == OFPP_NONE:
        res = ofp_port_stats(port_no=OFPP_NONE)
        for stats in self.port_stats.values():
          res += stats
        return res
      else:
        return self.port_stats[req.port_no]

    def queue_stats (ofp):
      raise AttributeError("not implemented")

    stats_handlers = {
        OFPST_DESC: desc_stats,
        OFPST_FLOW: flow_stats,
        OFPST_AGGREGATE: aggregate_stats,
        OFPST_TABLE: table_stats,
        OFPST_PORT: port_stats,
        OFPST_QUEUE: queue_stats
    }

    if ofp.type in stats_handlers:
      handler = stats_handlers[ofp.type]
    else:
      raise AttributeError("Unsupported stats request type %d" % (ofp.type,))

    reply = ofp_stats_reply(xid=ofp.xid, body=handler(ofp))
    self.log.debug("Sending stats reply %s", str(reply))
    self.send(reply)

  def _rx_set_config (self, config, connection):
    pass

  def _rx_port_mod (self, port_mod, connection):
    port_no = port_mod.port_no
    if port_no not in self.ports:
      err = ofp_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_PORT)
      err.xid = port_mod.xid
      err.data = port_mod.pack()
      self.send(err)
      return
    port = self.ports[port_no]
    if port.hw_addr != port_mod.hw_addr:
      err = ofp_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_HW_ADDR)
      err.xid = port_mod.xid
      err.data = port_mod.pack()
      self.send(err)
      return

    mask = port_mod.mask

    if mask & OFPPC_NO_FLOOD:
      mask ^= OFPPC_NO_FLOOD
      if port.set_config(port_mod.config, OFPPC_NO_FLOOD):
        if port.config & OFPPC_NO_FLOOD:
          self.log.debug("Disabling flooding on port %s", port)
        else:
          self.log.debug("Enabling flooding on port %s", port)

    if mask & OFPPC_PORT_DOWN:
      mask ^= OFPPC_PORT_DOWN
      if port.set_config(port_mod.config, OFPPC_PORT_DOWN):
        if port.config & OFPPC_PORT_DOWN:
          self.log.debug("Set port %s down", port)
        else:
          self.log.debug("Set port %s up", port)

      # 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)

    if mask != 0:
      self.log.warn("Unsupported PORT_MOD flags: %08x", mask)

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

  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 rx_packet (self, packet, in_port):
    """
    process a dataplane packet

    packet: an instance of ethernet
    in_port: the integer port number
    """
    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
    if port.config & OFPPC_NO_RECV:
      return

    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)
      self.send_packet_in(in_port, buffer_id, packet,
                          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 _output_packet_physical (self, packet, port_no):
    """
    send a packet out a single physical port

    This is called by the more general _output_packet().
    """
    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._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:
      # There better be a table entry there, else we get infinite recurision
      # between switch<->controller
      # Note that this isn't infinite recursion, since the table entry's
      # out_port will not be OFPP_TABLE
      self.rx_packet(packet, in_port)
    else:
      raise("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):
      self.log.warn("Invalid output buffer id: %d", buffer_id)
      return
    if self._packet_buffer[buffer_id] is None:
      self.log.warn("Buffer %d has already been flushed", buffer_id)
      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,))
        err = ofp_error(type=OFPET_BAD_ACTION, code=OFPBAC_BAD_TYPE)
        if ofp:
          err.xid = ofp.xid
          err.data = ofp.pack()
        else:
          err.xid = 0
        self.send(err)
        return
      packet = h(action, packet, in_port)

  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_id (self, action, packet, in_port):
    if not isinstance(packet.next, vlan):
      packet.next = vlan(prev = packet.next)
      packet.next.eth_type = packet.type
      packet.type = ethernet.VLAN_TYPE
    packet.id = action.vlan_id
    return packet
  def _action_set_vlan_pcp (self, action, packet, in_port):
    if not isinstance(packet.next, vlan):
      packet.next = vlan(prev = packet)
      packet.next.eth_type = packet.type
      packet.type = ethernet.VLAN_TYPE
    packet.pcp = action.vlan_pcp
    return packet
  def _action_strip_vlan (self, action, packet, in_port):
    if isinstance(packet.next, vlan):
      packet.type = packet.next.eth_type
      packet.next = packet.next.next
    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):
    if isinstance(packet.next, ipv4):
      packet.next.nw_src = action.nw_addr
    return packet
  def _action_set_nw_dst (self, action, packet, in_port):
    if isinstance(packet.next, ipv4):
      packet.next.nw_dst = action.nw_addr
    return packet
  def _action_set_nw_tos (self, action, packet, in_port):
    if isinstance(packet.next, ipv4):
      packet.next.tos = action.nw_tos
    return packet
  def _action_set_tp_src (self, action, packet, in_port):
    if isinstance(packet.next, udp) or isinstance(packet.next, tcp):
      packet.next.srcport = action.tp_port
    return packet
  def _action_set_tp_dst (self, action, packet, in_port):
    if isinstance(packet.next, udp) or isinstance(packet.next, tcp):
      packet.next.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 __repr__ (self):
    return "%s(dpid=%s, num_ports=%d)" % (type(self).__name__,
                                          dpid_to_str(self.dpid),
                                          len(self.ports))
Exemplo n.º 4
0
  def __init__ (self, dpid, name=None, ports=4, miss_send_len=128,
                max_buffers=100, features=None):
    """
    Initialize switch
     - ports is a list of ofp_phy_ports
    """
    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.miss_send_len = miss_send_len
    self._has_sent_hello = False

    self.table = SwitchFlowTable()
    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.flow_stats = True
      self.features.table_stats = True
      self.features.port_stats = True
      #self.features.stp = True
      #self.features.ip_reasm = True
      #self.features.queue_stats = True
      #self.features.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
Exemplo n.º 5
0
  def __init__ (self, dpid, name=None, ports=4, miss_send_len=128,
                max_buffers=100, features=None):
    """
    Initialize switch
     - ports is a list of ofp_phy_ports
    """
    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.miss_send_len = miss_send_len
    self._has_sent_hello = False

    self.table = SwitchFlowTable()
    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.flow_stats = True
      self.features.table_stats = True
      self.features.port_stats = True
      #self.features.stp = True
      #self.features.ip_reasm = True
      #self.features.queue_stats = True
      #self.features.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
Exemplo n.º 6
0
class SoftwareSwitchBase (object):
  def __init__ (self, dpid, name=None, ports=4, miss_send_len=128,
                max_buffers=100, features=None):
    """
    Initialize switch
     - ports is a list of ofp_phy_ports
    """
    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.miss_send_len = miss_send_len
    self._has_sent_hello = False

    self.table = SwitchFlowTable()
    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.flow_stats = True
      self.features.table_stats = True
      self.features.port_stats = True
      #self.features.stp = True
      #self.features.ip_reasm = True
      #self.features.queue_stats = True
      #self.features.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

  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.
    """
    connection.set_message_handler(self.rx_message)
    self._connection = connection

  def send (self, message):
    """
    Send a message to this switch's communication partner
    """

    if self._connection:
      self._connection.send(message)
    else:
      self.log.debug("Asked to send message %s, but not connected", message)

  def _rx_hello (self, ofp, connection):
    self.send_hello()

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

  def _rx_features_request (self, ofp, connection):
    """
    Handles feature requests
    """
    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)
    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)
    self.send(msg)

  def _rx_stats_request (self, ofp, connection):
    def desc_stats (ofp):
      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__)

    def flow_stats (ofp):
      req = ofp_flow_stats_request().unpack(ofp.body)
      assert self.table_id in (TABLE_ALL, 0)
      return self.table.flow_stats(req.match, req.out_port)

    def aggregate_stats (ofp):
      req = ofp_aggregate_stats_request().unpack(ofp.body)
      assert self.table_id in (TABLE_ALL, 0)
      return self.table.aggregate_stats(req.match, out_port)

    def table_stats (ofp):
      # 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 = 0x7fFFffFF
      r.active_count = len(self.table)
      r.lookup_count = self._lookup_count
      r.matched_count = self._matched_count
      return r

    def port_stats (ofp):
      req = ofp_port_stats_request().unpack(ofp.body)
      if req.port_no == OFPP_NONE:
        res = ofp_port_stats(port_no=OFPP_NONE)
        for stats in self.port_stats.values():
          res += stats
        return res
      else:
        return self.port_stats[req.port_no]

    def queue_stats (ofp):
      raise AttributeError("not implemented")

    stats_handlers = {
        OFPST_DESC: desc_stats,
        OFPST_FLOW: flow_stats,
        OFPST_AGGREGATE: aggregate_stats,
        OFPST_TABLE: table_stats,
        OFPST_PORT: port_stats,
        OFPST_QUEUE: queue_stats
    }

    if ofp.type in stats_handlers:
      handler = stats_handlers[ofp.type]
    else:
      raise AttributeError("Unsupported stats request type %d" % (ofp.type,))

    reply = ofp_stats_reply(xid=ofp.xid, body=handler(ofp))
    self.log.debug("Sending stats reply %s", str(reply))
    self.send(reply)

  def _rx_set_config (self, config, connection):
    pass

  def _rx_port_mod (self, port_mod, connection):
    port_no = port_mod.port_no
    if port_no not in self.ports:
      err = ofp_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_PORT)
      err.xid = port_mod.xid
      err.data = port_mod.pack()
      self.send(err)
      return
    port = self.ports[port_no]
    if port.hw_addr != port_mod.hw_addr:
      err = ofp_error(type=OFPET_PORT_MOD_FAILED, code=OFPPMFC_BAD_HW_ADDR)
      err.xid = port_mod.xid
      err.data = port_mod.pack()
      self.send(err)
      return

    mask = port_mod.mask

    if mask & OFPPC_NO_FLOOD:
      mask ^= OFPPC_NO_FLOOD
      if port.set_config(port_mod.config, OFPPC_NO_FLOOD):
        if port.config & OFPPC_NO_FLOOD:
          self.log.debug("Disabling flooding on port %s", port)
        else:
          self.log.debug("Enabling flooding on port %s", port)

    if mask & OFPPC_PORT_DOWN:
      mask ^= OFPPC_PORT_DOWN
      if port.set_config(port_mod.config, OFPPC_PORT_DOWN):
        if port.config & OFPPC_PORT_DOWN:
          self.log.debug("Set port %s down", port)
        else:
          self.log.debug("Set port %s up", port)

      # 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)

    if mask != 0:
      self.log.warn("Unsupported PORT_MOD flags: %08x", mask)

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

  def send_hello (self, force = False):
    """
    Send hello (once)
    """
    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 rx_packet (self, packet, in_port):
    """
    process a dataplane packet

    packet: an instance of ethernet
    in_port: the integer port number
    """
    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
    if port.config & OFPPC_NO_RECV:
      return

    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)
      self.send_packet_in(in_port, buffer_id, packet,
                          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 _output_packet_physical (self, packet, port_no):
    """
    send a packet out a single physical port

    This is called by the more general _output_packet().
    """
    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._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:
      # There better be a table entry there, else we get infinite recurision
      # between switch<->controller
      # Note that this isn't infinite recursion, since the table entry's
      # out_port will not be OFPP_TABLE
      self.rx_packet(packet, in_port)
    else:
      raise("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):
      self.log.warn("Invalid output buffer id: %d", buffer_id)
      return
    if self._packet_buffer[buffer_id] is None:
      self.log.warn("Buffer %d has already been flushed", buffer_id)
      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,))
        err = ofp_error(type=OFPET_BAD_ACTION, code=OFPBAC_BAD_TYPE)
        if ofp:
          err.xid = ofp.xid
          err.data = ofp.pack()
        else:
          err.xid = 0
        self.send(err)
        return
      packet = h(action, packet, in_port)

  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_id (self, action, packet, in_port):
    if not isinstance(packet.next, vlan):
      packet.next = vlan(prev = packet.next)
      packet.next.eth_type = packet.type
      packet.type = ethernet.VLAN_TYPE
    packet.id = action.vlan_id
    return packet
  def _action_set_vlan_pcp (self, action, packet, in_port):
    if not isinstance(packet.next, vlan):
      packet.next = vlan(prev = packet)
      packet.next.eth_type = packet.type
      packet.type = ethernet.VLAN_TYPE
    packet.pcp = action.vlan_pcp
    return packet
  def _action_strip_vlan (self, action, packet, in_port):
    if isinstance(packet.next, vlan):
      packet.type = packet.next.eth_type
      packet.next = packet.next.next
    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):
    if isinstance(packet.next, ipv4):
      packet.next.nw_src = action.nw_addr
    return packet
  def _action_set_nw_dst (self, action, packet, in_port):
    if isinstance(packet.next, ipv4):
      packet.next.nw_dst = action.nw_addr
    return packet
  def _action_set_nw_tos (self, action, packet, in_port):
    if isinstance(packet.next, ipv4):
      packet.next.tos = action.nw_tos
    return packet
  def _action_set_tp_src (self, action, packet, in_port):
    if isinstance(packet.next, udp) or isinstance(packet.next, tcp):
      packet.next.srcport = action.tp_port
    return packet
  def _action_set_tp_dst (self, action, packet, in_port):
    if isinstance(packet.next, udp) or isinstance(packet.next, tcp):
      packet.next.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 __repr__ (self):
    return "%s(dpid=%s, num_ports=%d)" % (type(self).__name__,
                                          dpid_to_str(self.dpid),
                                          len(self.ports))
Exemplo n.º 7
0
class SwitchImpl(EventMixin):
  _eventMixin_events = set([DpPacketOut])

  # ports is a list of ofp_phy_ports
  def __init__(self, dpid, name=None, ports=4, miss_send_len=128,
      n_buffers=100, n_tables=1, capabilities=None):
    """Initialize switch"""
    ##Datapath id of switch
    self.dpid = dpid
    ## Human-readable name of the switch
    self.name = name
    if self.name is None:
      self.name = str(dpid)
    self.log = logging.getLogger(self.name)
    ##Number of buffers
    self.n_buffers = n_buffers
    ##Number of tables
    self.n_tables= n_tables
    # Note that there is one switch table in the OpenFlow 1.0 world
    self.table = SwitchFlowTable()
    # buffer for packets during packet_in
    self.packet_buffer = []
    if(ports == None or isinstance(ports, int)):
      ports=_default_port_list(num_ports=ports, prefix=dpid)

    self.xid_count = xid_generator(1)

    ## Hash of port_no -> openflow.pylibopenflow_01.ofp_phy_ports
    self.ports = {}
    for port in ports:
      self.ports[port.port_no] = port
    ## (OpenFlow Handler map)
    self.ofp_handlers = {
       # Reactive handlers
       ofp_type_rev_map['OFPT_HELLO'] : self._receive_hello,
       ofp_type_rev_map['OFPT_ECHO_REQUEST'] : self._receive_echo,
       ofp_type_rev_map['OFPT_FEATURES_REQUEST'] : self._receive_features_request,
       ofp_type_rev_map['OFPT_FLOW_MOD'] : self._receive_flow_mod,
       ofp_type_rev_map['OFPT_PACKET_OUT'] : self._receive_packet_out,
       ofp_type_rev_map['OFPT_BARRIER_REQUEST'] : self._receive_barrier_request,
       ofp_type_rev_map['OFPT_SET_CONFIG'] : self._receive_set_config,

       # Proactive responses
       ofp_type_rev_map['OFPT_ECHO_REPLY'] : self._receive_echo_reply
       # TODO: many more packet types to process
    }
    self._connection = None

    ##Capabilities
    if (isinstance(capabilities, SwitchCapabilities)):
      self.capabilities = capabilities
    else:
      self.capabilities = SwitchCapabilities(miss_send_len)

  def set_io_worker(self, io_worker):
    self._connection = ControllerConnection(io_worker, self.ofp_handlers)
    return self._connection

  def set_connection(self, connection):
    connection.ofp_handlers = self.ofp_handlers
    self._connection = connection

  def send(self, message):
    """ Send a message to this switches communication partner. If the switch is not connected, the message is silently dropped. """

    if self._connection:
      self._connection.send(message)
    else:
      self.log.debug("Asked to send message %s, but not connected" % message)

  # ==================================== #
  #    Reactive OFP processing           #
  # ==================================== #
  def _receive_hello(self, ofp):
    self.log.debug("Receive hello %s" % self.name)
    # How does the OpenFlow protocol prevent an infinite loop of Hello messages?
    self.send_hello()

  def _receive_echo(self, ofp):
    """Reply to echo request
    """
    self.log.debug("Reply echo of xid: %s %s" % (str(ofp), self.name))
    msg = ofp_echo_reply(xid=ofp.xid)
    self.send(msg)

  def _receive_features_request(self, ofp):
    """Reply to feature request
    """
    self.log.debug("Reply features request of xid %s %s" % (str(ofp), self.name))
    msg = ofp_features_reply(datapath_id = self.dpid, xid = ofp.xid, n_buffers = self.n_buffers,
                             n_tables = self.n_tables,
                             capabilities = self.capabilities.get_capabilities(),
                             actions = self.capabilities.get_actions(),
                             ports = self.ports.values())
    self.send(msg)

  def _receive_flow_mod(self, ofp):
    """Handle flow mod: just print it here
    """
    self.log.debug("Flow mod %s: %s" % (self.name, ofp.show()))
    self.table.process_flow_mod(ofp)
    if(ofp.buffer_id >=0 ):
      self._process_actions_for_packet_from_buffer(ofp.actions, ofp.buffer_id)

  def _receive_packet_out(self, packet_out):
    """
    Send the packet out the given port
    """
    self.log.debug("Packet out: %s" % packet_out.show())

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

  def _receive_echo_reply(self, ofp):
    self.log.debug("Echo reply: %s %s" % (str(ofp), self.name))

  def _receive_barrier_request(self, ofp):
    self.log.debug("Barrier request %s %s" % (self.name, str(ofp)))
    msg = ofp_barrier_reply(xid = ofp.xid)
    self.send(msg)

  def _receive_set_config(self, config):
    self.log.debug("Set  config %s %s" % (self.name, str(config)))

  # ==================================== #
  #    Proactive OFP processing          #
  # ==================================== #
  def send_hello(self):
    """Send hello
    """
    self.log.debug("Send hello %s " % self.name)
    msg = ofp_hello()
    self.send(msg)

  def send_packet_in(self, in_port, buffer_id=None, packet="", xid=None, reason=None):
    """Send PacketIn
    Assume no match as reason, buffer_id = 0xFFFFFFFF,
    and empty packet by default
    """
    assert_type("packet", packet, ethernet)
    self.log.debug("Send PacketIn %s " % self.name)
    if (reason == None):
      reason = ofp_packet_in_reason_rev_map['OFPR_NO_MATCH']
    if (buffer_id == None):
      buffer_id = int("0xFFFFFFFF",16)

    if xid == None:
      xid = self.xid_count.next()
    msg = ofp_packet_in(xid=xid, in_port = in_port, buffer_id = buffer_id, reason = reason,
                        data = packet.pack())
    self.send(msg)

  def send_echo(self, xid=0):
    """Send echo request
    """
    self.log.debug("Send echo %s" % self.name)
    msg = ofp_echo_request()
    self.send(msg)
    
  def send_port_status(self, port, reason):
    '''
    port is an ofp_phy_port
    reason is one of 'OFPPR_ADD', 'OFPPR_DELETE', 'OFPPR_MODIFY'
    '''
    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)

  # ==================================== #
  #   Dataplane processing               #
  # ==================================== #

  def process_packet(self, packet, in_port):
    """ process a dataplane packet the way a real OpenFlow switch would.
        packet: an instance of ethernet
        in_port: the integer port number
    """
    assert_type("packet", packet, ethernet, none_ok=False)

    entry = self.table.entry_for_packet(packet, in_port)
    if(entry != None):
      entry.touch_packet(len(packet))
      self._process_actions_for_packet(entry.actions, packet, in_port)
    else:
      # no matching entry
      buffer_id = self._buffer_packet(packet, in_port)
      self.send_packet_in(in_port, buffer_id, packet, self.xid_count.next(), reason=OFPR_NO_MATCH)
      
  def take_port_down(self, port):
    ''' Take the given port down, and send a port_status message to the controller '''
    port_no = port.port_no
    if port_no not in self.ports:
      raise RuntimeError("port_no %d not in %s's ports" % (port_no, str(self)))
    # Hmmm, is deleting the port the correct behavior?
    del self.ports[port_no]
    self.send_port_status(port, OFPPR_DELETE)
    
  def bring_port_up(self, port):
    ''' Bring the given port up, and send a port_status message to the controller '''
    port_no = port.port_no
    if port_no in self.ports:
      raise RuntimeError("port_no %d already in %s's ports" % (port_no, str(self)))
    self.ports[port_no] = port
    self.send_port_status(port, OFPPR_ADD)

  # ==================================== #
  #    Helper Methods                    #
  # ==================================== #

  def _output_packet(self, packet, out_port, in_port):
    """ send a packet out some port.
        packet: instance of ethernet
        out_port, in_port: the integer port number """
    assert_type("packet", packet, ethernet, none_ok=False)
    def real_send(port_no):
      if type(port_no) == ofp_phy_port:
        port_no = port_no.port_no
      if port_no not in self.ports:
        raise RuntimeError("Invalid physical output port: %x" % port_no)
      self.raiseEvent(DpPacketOut(self, packet, self.ports[port_no]))

    if out_port < OFPP_MAX:
      real_send(out_port)
    elif out_port == OFPP_IN_PORT:
      real_send(in_port)
    elif out_port == OFPP_FLOOD or out_port == OFPP_ALL:
      # no support for spanning tree yet -> flood=all
      for (no,port) in self.ports.iteritems():
        if no != in_port:
          real_send(port)
    elif out_port == OFPP_CONTROLLER:
      buffer_id = self._buffer_packet(packet, in_port)
      self.send_packet_in(in_port, buffer_id, packet, self.xid_count.next(), reason=OFPR_ACTION)
    else:
      raise("Unsupported virtual output port: %x" % out_port)


  def _buffer_packet(self, packet, in_port=None):
    """ Find a free buffer slot to buffer the packet in. """
    for (i, value) in enumerate(self.packet_buffer):
      if(value==None):
        self.packet_buffer[i] = (packet, in_port)
        return i + 1
    self.packet_buffer.append( (packet, in_port) )
    return len(self.packet_buffer)

  def _process_actions_for_packet_from_buffer(self, actions, buffer_id):
    """ output and release a packet from the buffer """
    buffer_id = buffer_id - 1
    if(buffer_id > len(self.packet_buffer) or self.packet_buffer[buffer_id] == None):
      self.log.warn("Invalid output buffer id: %x" % buffer_id)
      return
    (packet, in_port) = self.packet_buffer[buffer_id]
    self._process_actions_for_packet(actions, packet, in_port)
    self.packet_buffer[buffer_id] = None

  def _process_actions_for_packet(self, actions, packet, in_port):
    """ process the output actions for a packet """
    assert_type("packet", packet, [ethernet, str], none_ok=False)
    if not isinstance(packet, ethernet):
      packet = ethernet.unpack(packet)

    def output_packet(action, packet):
      self._output_packet(packet, action.port, in_port)
      return packet
    def set_vlan_id(action, packet):
      if not isinstance(packet.next, vlan): 
        packet.next = vlan(prev = packet.next)
        packet.next.eth_type = packet.type
        packet.type = ethernet.VLAN_TYPE
      packet.id = action.vlan_id
      return packet
    def set_vlan_pcp(action, packet):
      if not isinstance(packet.next, vlan):
        packet.next = vlan(prev = packet)
        packet.next.eth_type = packet.type
        packet.type = ethernet.VLAN_TYPE
      packet.pcp = action.vlan_pcp
      return packet
    def strip_vlan(action, packet):
      if isinstance(packet.next, vlan): 
        packet.type = packet.next.eth_type
        packet.next = packet.next.next
      return packet
    def set_dl_src(action, packet):
      packet.src = action.dl_addr
      return packet
    def set_dl_dst(action, packet):
      packet.dst = action.dl_addr
      return packet
    def set_nw_src(action, packet):
      if(isinstance(packet.next, ipv4)):
        packet.next.nw_src = action.nw_addr
      return packet
    def set_nw_dst(action, packet):
      if(isinstance(packet.next, ipv4)):
        packet.next.nw_dst = action.nw_addr
      return packet
    def set_nw_tos(action, packet):
      if(isinstance(packet.next, ipv4)):
        packet.next.tos = action.nw_tos
      return packet
    def set_tp_src(action, packet):
      if(isinstance(packet.next, udp) or isinstance(packet.next, tcp)):
        packet.next.srcport = action.tp_port
      return packet
    def set_tp_dst(action, packet):
      if(isinstance(packet.next, udp) or isinstance(packet.next, tcp)):
        packet.next.dstport = action.tp_port
      return packet
    def enqueue(action, packet):
      self.log.warn("output_enqueue not supported yet. Performing regular output")
      return output_packet(action.tp_port, packet)
    def push_mpls_tag(action, packet):
      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 pop_mpls_tag(action, packet):
      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 set_mpls_label(action, packet):
      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 set_mpls_tc(action, packet):
      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 set_mpls_ttl(action, packet):
      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 dec_mpls_ttl(action, packet):
      if not isinstance(packet.next, mpls):
        return packet
      packet.next.ttl = packet.next.ttl - 1
      return packet
    handler_map = {
        OFPAT_OUTPUT: output_packet,
        OFPAT_SET_VLAN_VID: set_vlan_id,
        OFPAT_SET_VLAN_PCP: set_vlan_pcp,
        OFPAT_STRIP_VLAN: strip_vlan,
        OFPAT_SET_DL_SRC: set_dl_src,
        OFPAT_SET_DL_DST: set_dl_dst,
        OFPAT_SET_NW_SRC: set_nw_src,
        OFPAT_SET_NW_DST: set_nw_dst,
        OFPAT_SET_NW_TOS: set_nw_tos,
        OFPAT_SET_TP_SRC: set_tp_src,
        OFPAT_SET_TP_DST: set_tp_dst,
        OFPAT_ENQUEUE: enqueue,
        OFPAT_PUSH_MPLS: push_mpls_tag, 
        OFPAT_POP_MPLS: pop_mpls_tag,
        OFPAT_SET_MPLS_LABEL: set_mpls_label,
        OFPAT_SET_MPLS_TC: set_mpls_tc,
        OFPAT_SET_MPLS_TTL: set_mpls_ttl,
        OFPAT_DEC_MPLS_TTL: dec_mpls_ttl,
    }
    for action in actions:
      if action.type is ofp_action_resubmit:
        self.process_packet(packet, in_port)
        return
      if(action.type not in handler_map):
        raise NotImplementedError("Unknown action type: %x " % type)
      packet = handler_map[action.type](action, packet)

  def __repr__(self):
    return "SwitchImpl(dpid=%d, num_ports=%d)" % (self.dpid, len(self.ports))
Exemplo n.º 8
0
 def __init__(self, sw, topo):
     OpenFlowSwitch.__init__(self, sw)
     self.topo = topo
     self.table = SwitchFlowTable()
     log.debug("ScnOpenFlowSwitch init : %s" % str(self))
Exemplo n.º 9
0
class ScnOpenFlowSwitch(OpenFlowSwitch):
    """describe OpenFlowSwitch on ScnOpenFlow space.
    """
    def __init__(self, sw, topo):
        OpenFlowSwitch.__init__(self, sw)
        self.topo = topo
        self.table = SwitchFlowTable()
        log.debug("ScnOpenFlowSwitch init : %s" % str(self))

    def _getIniOfpSpeed(self, target_port):
        """get ofp max spped setting.
        """
        ofs_ip = self.ipaddr
        for section in core.parser.getSwitchsSections():
            ip = core.parser.getValue(section, IP)
            if ip != ofs_ip:
                continue
            ports = core.parser.getPortsSections(section)
            for port in ports:
                #log.debug("port = %s" % port)
                name = core.parser.getValue(port, INTFNAME)
                if target_port.name != name:
                    continue
                speed = core.parser.getValue(port, SPEED)
                if not speed:
                    return None
                factor = 1.
                speed = speed.lower()
                if speed[-1] == "k":
                    factor = 10.**3
                    speed = speed[:-1]
                elif speed[-1] == "m":
                    factor = 10.**6
                    speed = speed[:-1]
                elif speed[-1] == "g":
                    factor = 10.**9
                    speed = speed[:-1]
                return float(speed) * factor

    def setConnection(self, connection, ofp = None):
        """set connection listener.
            ofp - a FeaturesReply message
        """
        if self._connection:
            self._connection.removeListeners(self._listeners)
        self._listeners = []
        self._connection = connection
        if self._reconnectTimeout is not None:
            self._reconnectTimeout.cancel()
            self._reconnectTimeout = None
        if connection is None:
            self._reconnectTimeout = Timer(RECONNECT_TIMEOUT,
                                         self._timer_ReconnectTimeout)
        if ofp is not None:
            # update capabilities
            self.capabilities = ofp.capabilities
            # update all ports
            untouched = set(self.ports.keys())
            for port in ofp.ports:
                if port.port_no in self.ports:
                    self.ports[port.port_no]._update(port) # _update method is protected.
                    untouched.remove(port.port_no)
                else:
                    speed = self._getIniOfpSpeed(port)
                    log.debug("New ScnOpenFlowPort: [%s] OFP = %s" % (str(port), str(ofp)))
                    self.ports[port.port_no] = ScnOpenFlowPort(port, self, speed = speed)
            for port in untouched:
                log.debug("remove ports: [%s]" % str(port))
                self.ports[port].exists = False
                del self.ports[port]
        if connection is not None:
            self._listeners = self.listenTo(connection, prefix = "con")
            self.raiseEvent(SwitchConnectionUp(switch = self, connection = connection))
        else:
            self.raiseEvent(SwitchConnectionDown(switch = self))

    def _handle_con_PortStatus(self, event):
        port = event.ofp.desc
        if event.ofp.reason == of.ofp_port_reason_rev_map["OFPPR_DELETE"]:
            if port.port_no in self.ports:
                self.ports[port.port_no].exists = False
                del self.ports[port.port_no]
        elif event.ofp.reason == of.ofp_port_reason_rev_map["OFPPR_MODIFY"]:
            self.ports[port.port_no]._update(port) # _update method is protected.
        else:
            assert event.ofp.reason == of.ofp_port_reason_rev_map["OFPPR_ADD"]
            assert port.port_no not in self.ports
            speed = self._getIniOfpSpeed(port)
            log.debug("New ScnOpenFlowPort: [%s] OFP = %s" % (str(port), str(event.ofp)))
            self.ports[port.port_no] = ScnOpenFlowPort(port, self, speed = speed)
        self.raiseEvent(event)
        event.halt = False

    @property
    def ipaddr(self):
        """IP Address attribute.(getter)
        """
        ip = IPAddr(self._connection.sock.getpeername()[0])
        log.debug("[IP] get IP: %s" % str(ip))
        return ip

    def getPorts(self):
        """get all ScnOpenFlowPort.
        """
        return self.ports.values()

    def getOFPort(self, number):
        """get a ScnOpenFlowPort specified by number.
        """
        for port in self.ports.values():
            if port.number == number:
                return port
        return None

    def getHost(self, locator):
        """get a ScnOpenFlowHost specified by locator.
            use ScnOpenFlowPort.getHost
        """
        log.debug("getHost self=[%s], o=[%s]" % (str(self), str(locator)))
        assert isinstance(locator, EthAddr) or isinstance(locator, IPAddr)
        for ofp in self.ports.values():
            host = ofp.getHost(locator)
            if host:
                return host

    def __receive_flow_mod__(self, ofp):
        """Handle flow mod: just print it here
        """
        log.debug("Flow mod %s: %s" % (self.name, ofp.show()))
        self.table.process_flow_mod(ofp)

    def installFlow(self, msg):
        """@override
            because base class has bug. no import TableEntry...
            and extend input struct to flow_mod object.
        """
        tabEntry = TableEntry.from_flow_mod(msg)
        log.info("apply entry - %s" % str(tabEntry))
        self.flow_table.install(tabEntry)

    def removeFlow(self, msg):
        """remove flow entry.
            msg[flow_mod] -- entry msg object.
        """
        tabEntry = TableEntry.from_flow_mod(msg)
        log.info("remove entry - %s" % str(tabEntry))
        self.flow_table.remove_with_wildcards(tabEntry)