def handlePkt(pkt): if (TCP in pkt and len(pkt[TCP].payload) > 0): try: ofHeader = Header() ofHeader.unpack(bytes(pkt[TCP].payload)[:8]) print("[>] " + str(pkt[IP].src) + " -> ", end='') print("OFv" + str(ofHeader.version), end=' ') print(ofHeader.message_type, end=' -> ') print(str(pkt[IP].dst)) #ofBody = "" #try: ##TODO: Allow for detailed message information to be printed #ofBody = bytes(pkt[TCP].payload)[:(ofHeader.length-8)] #if((ofHeader.message_type & 0xFF) == 6): #ofFeatureReply = FeaturesReply() #ofFeatureReply.unpack(ofBody) #ofHelper.printFeatureReplyDetails(ofFeatureReply) #except Exception as e: #print(e) except: #Not an OF message pass
def activateRelaySocket(port): global ofSwitch hostname = socket.gethostbyname(socket.gethostname()) listenSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listenSock.bind(("0.0.0.0", port)) listenSock.listen(1) data = b'' sdnpwn.message("[Relay Socket] Relay port open on port " + str(port) + "", sdnpwn.NORMAL) while 1: try: conn, addr = listenSock.accept() msgHeader = conn.recv(8) header = Header() header.unpack(msgHeader) sdnpwn.message( "[Relay Socket] Got " + str(header.message_type) + " from " + str(addr), sdnpwn.NORMAL) msgBody = conn.recv(header.length - 8) msgFull = header.pack() + msgBody print(msgFull) ofSwitch.comm_sock.send(msgFull) except Exception as e: sdnpwn.message("[Relay socket] Error handling message", sdnpwn.WARNING) print(e) listenSock.close()
def unpack_message(buffer): """Unpack the whole buffer, including header pack.""" hdr_size = Header().get_size() hdr_buff, msg_buff = buffer[:hdr_size], buffer[hdr_size:] header = Header() header.unpack(hdr_buff) message = new_message_from_header(header) message.unpack(msg_buff) return message
def getResponse(sock): try: ofHeader = Header() replyHeader = sock.recv(8) ofHeader.unpack(replyHeader) replyBody = sock.recv(ofHeader.length - 8) return (ofHeader, replyBody) except Exception as e: if (verbose == True): print("Error: " + str(e)) return None
def _unpack_data(self): if self.data == b'': return BinaryData() # header unpacking header = Header() header_size = header.get_size() header_data = self.data.value[:header_size] header.unpack(header_data) # message unpacking msg = common.utils.new_message_from_header(header) msg_data = self.data.value[header_size:] msg.unpack(msg_data) return msg
class BarrierRequest(GenericMessage): """OpenFlow Barrier Request Message. This message does not contain a body in addition to the OpenFlow Header. """ header = Header(message_type=Type.OFPT_BARRIER_REQUEST)
class Hello(GenericMessage): """OpenFlow Hello Message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_HELLO, length=8)
class BarrierReply(GenericMessage): """OpenFlow Barrier Reply Message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_BARRIER_REPLY)
class FeaturesRequest(GenericMessage): """Features request message. This message does not contain a body in addition to the OpenFlow Header. """ header = Header(message_type=Type.OFPT_FEATURES_REQUEST)
class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" header = Header() flags = UBInt16(enum_ref=ConfigFlag) miss_send_len = UBInt16() def __init__(self, xid=None, flags=None, miss_send_len=None): """Create a SwitchConfig with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (ConfigFlag): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid) self.flags = flags self.miss_send_len = miss_send_len def __repr__(self): """Show a full representation of the object.""" return "%s(xid=%r, flags=%s, miss_send_len=%r)" \ % (self.__class__.__name__, self.header.xid, self.flags, self.miss_send_len)
class StatsRequest(GenericMessage): """Request statistics to switch.""" #: OpenFlow :class:`~pyof.v0x01.common.header.Header` header = Header(message_type=Type.OFPT_STATS_REQUEST) body_type = UBInt16(enum_ref=StatsType) flags = UBInt16() body = BinaryData() def __init__(self, xid=None, body_type=None, flags=0, body=b''): """Create a StatsRequest with the optional parameters below. Args: xid (int): xid to be used on the message header. body_type (StatsType): One of the OFPST_* constants. flags (int): OFPSF_REQ_* flags (none yet defined). body (BinaryData): Body of the request. """ super().__init__(xid) self.body_type = body_type self.flags = flags self.body = body def pack(self, value=None): """Pack according to :attr:`body_type`. Make `body` a binary pack before packing this object. Then, restore body. """ backup = self.body if not value: value = self.body if hasattr(value, 'pack'): self.body = value.pack() stats_request_packed = super().pack() self.body = backup return stats_request_packed def unpack(self, buff, offset=0): """Unpack according to :attr:`body_type`.""" super().unpack(buff) class_name = self._get_body_class() buff = self.body.value self.body = FixedTypeList(pyof_class=class_name) self.body.unpack(buff) def _get_body_class(self): if isinstance(self.body_type, (int, UBInt16)): self.body_type = self.body_type.enum_ref(self.body_type.value) module = import_module('pyof.v0x01.controller2switch.common') body_name = self.body_type.name.replace('OFPST_', '').title() for class_name in module.__all__: if 'Request' in class_name and body_name in class_name: return getattr(module, class_name) return None
class FlowRemoved(GenericMessage): """Flow removed (datapath -> controller).""" #: :class:`~.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_FLOW_REMOVED) #: :class:`~.flow_match.Match`: OpenFlow Header match = Match() cookie = UBInt64() priority = UBInt16() reason = UBInt8(enum_ref=FlowRemovedReason) #: Align to 32-bits. pad = Pad(1) duration_sec = UBInt32() duration_nsec = UBInt32() idle_timeout = UBInt16() #: Align to 64-bits. pad2 = Pad(2) packet_count = UBInt64() byte_count = UBInt64() def __init__(self, xid=None, match=None, cookie=None, priority=None, reason=None, duration_sec=None, duration_nsec=None, idle_timeout=None, packet_count=None, byte_count=None): """Assign parameters to object attributes. Args: xid (int): OpenFlow Header's xid. match (Match): Fields' description. cookie (int): Opaque controller-issued identifier. priority (int): Priority level of flow entry. reason (FlowRemovedReason): Why the flow was removed. duration_sec (int): Time the flow was alive in seconds. duration_nsec (int): Time the flow was alive in nanoseconds in addition to duration_sec. idle_timeout (int): Idle timeout from original flow mod. packet_count (int): Number of packets. byte_count (int): Byte count. """ super().__init__(xid) self.match = match self.cookie = cookie self.priority = priority self.reason = reason self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.idle_timeout = idle_timeout self.packet_count = packet_count self.byte_count = byte_count
def unpack_message(buffer): """Unpack the whole buffer, including header pack. Args: buffer (bytes): Bytes representation of a openflow message. Returns: object: Instance of openflow message. """ hdr_size = Header().get_size() hdr_buff, msg_buff = buffer[:hdr_size], buffer[hdr_size:] header = Header() header.unpack(hdr_buff) message = new_message_from_header(header) message.unpack(msg_buff) return message
class FlowMod(GenericMessage): """Modifies the flow table from the controller.""" header = Header(message_type=Type.OFPT_FLOW_MOD) match = Match() cookie = UBInt64() command = UBInt16(enum_ref=FlowModCommand) idle_timeout = UBInt16() hard_timeout = UBInt16() priority = UBInt16() buffer_id = UBInt32() out_port = UBInt16(enum_ref=Port) flags = UBInt16(enum_ref=FlowModFlags) actions = ListOfActions() def __init__(self, xid=None, match=None, cookie=0, command=None, idle_timeout=0, hard_timeout=0, priority=0, buffer_id=NO_BUFFER, out_port=Port.OFPP_NONE, flags=FlowModFlags.OFPFF_CHECK_OVERLAP, actions=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid to be used on the message header. match (Match): Fields to match. cookie (int): Opaque controller-issued identifier. command (FlowModCommand): One of OFPFC_*. idle_timeout (int): Idle time before discarding (seconds). hard_timeout (int): Max time before discarding (seconds). priority (int): Priority level of flow entry. buffer_idle (int): Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*. out_port (Port): For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. flags (FlowModFlags): One of OFPFF_*. actions (ListOfActions): The action length is inferred from the length field in the header. """ super().__init__(xid) self.match = match self.cookie = cookie self.command = command self.idle_timeout = idle_timeout self.hard_timeout = hard_timeout self.priority = priority self.buffer_id = buffer_id self.out_port = out_port self.flags = flags self.actions = [] if actions is None else actions
class TestHeader(unittest.TestCase): """Test the message Header.""" def setUp(self): """Setup the TestHeader Class instantiating a HELLO header.""" self.message = Header() self.message.message_type = Type.OFPT_HELLO self.message.xid = 1 self.message.length = 0 def test_size(self): """[Common/Header] - size 8.""" self.assertEqual(self.message.get_size(), 8) @unittest.expectedFailure def test_pack_empty(self): """[Common/Header] - packing empty header.""" self.assertRaises(TypeError, Header().pack()) def test_pack(self): """[Common/Header] - packing Hello.""" packed_header = b'\x01\x00\x00\x00\x00\x00\x00\x01' self.assertEqual(self.message.pack(), packed_header) def test_unpack(self): """[Common/Header] - unpacking Hello.""" filename = os.path.join(os.path.dirname(os.path.realpath('__file__')), 'raw/v0x01/ofpt_hello.dat') f = open(filename, 'rb') self.message.unpack(f.read(8)) self.assertEqual(self.message.length, 8) self.assertEqual(self.message.xid, 1) self.assertEqual(self.message.message_type, Type.OFPT_HELLO) self.assertEqual(self.message.version, 1) f.close() @patch('pyof.v0x01.common.header.randint') def test_random_xid(self, m): """Each Header instantiations without xid should call randint.""" Header(), Header() # noqa self.assertEqual(m.call_count, 2)
class StatsRequest(GenericMessage): """Request statistics to switch.""" #: OpenFlow :class:`.Header` header = Header(message_type=Type.OFPT_STATS_REQUEST) body_type = UBInt16(enum_ref=StatsTypes) flags = UBInt16() body = BinaryData() def __init__(self, xid=None, body_type=None, flags=0, body=b''): """The constructor just assings parameters to object attributes. Args: body_type (StatsTypes): One of the OFPST_* constants. flags (int): OFPSF_REQ_* flags (none yet defined). body (BinaryData): Body of the request. """ super().__init__(xid) self.body_type = body_type self.flags = flags self.body = body def pack(self): """Pack according to :attr:`body_type`. Make `body` a binary pack before packing this object. Then, restore body. """ if self.body_type == StatsTypes.OFPST_PORT or \ self.body_type == StatsTypes.OFPST_FLOW or \ self.body_type == StatsTypes.OFPST_AGGREGATE: backup = self.body self.body = self.body.pack() pack = super().pack() self.body = backup return pack else: return super().pack() def unpack(self, buff): """Unpack according to :attr:`body_type`.""" super().unpack(buff) if self.body_type == StatsTypes.OFPST_PORT: buff = self.body.value self.body = PortStatsRequest() self.body.unpack(buff) elif self.body_type == StatsTypes.OFPST_FLOW: buff = self.body.value self.body = FlowStatsRequest() self.body.unpack(buff) elif self.body_type == StatsTypes.OFPST_AGGREGATE: buff = self.body.value self.body = AggregateStatsRequest() self.body.unpack(buff)
class StatsReply(GenericMessage): """Class implements the response to the stats request.""" #: OpenFlow :class:`.Header` header = Header(message_type=Type.OFPT_STATS_REPLY) body_type = UBInt16(enum_ref=StatsTypes) flags = UBInt16() body = BinaryData() def __init__(self, xid=None, body_type=None, flags=None, body=b''): """The constructor just assings parameters to object attributes. Args: body_type (StatsTypes): One of the OFPST_* constants. flags (int): OFPSF_REQ_* flags (none yet defined). body (BinaryData): Body of the request. """ super().__init__(xid) self.body_type = body_type self.flags = flags self.body = body def unpack(self, buff): """Unpack a binary message into this object's attributes. Unpack the binary value *buff* and update this object attributes based on the results. It is an inplace method and it receives the binary data of the message **without the header**. This class' unpack method is like the :meth:`.GenericMessage.unpack` one, except for the ``body`` attribute which has its type determined by the ``body_type`` attribute. Args: buff (bytes): Binary data package to be unpacked, without the header. """ super().unpack(buff) if self.body_type == StatsTypes.OFPST_PORT: self._unpack_body(FixedTypeList(pyof_class=PortStats)) elif self.body_type == StatsTypes.OFPST_AGGREGATE: self._unpack_body(FixedTypeList(pyof_class=AggregateStatsReply)) elif self.body_type == StatsTypes.OFPST_FLOW: self._unpack_body(FixedTypeList(pyof_class=FlowStats)) elif self.body_type == StatsTypes.OFPST_DESC: self._unpack_body(DescStats()) def _unpack_body(self, obj): """Unpack `body` using `obj` and replace it by the result.""" obj.unpack(self.body.value) self.body = obj
class TestHeader(unittest.TestCase): """Test the message Header.""" def setUp(self): """Setup the TestHeader Class instantiating a HELLO header.""" self.message = Header() self.message.message_type = Type.OFPT_HELLO self.message.xid = 1 self.message.length = 0 def test_size(self): """[Common/Header] - size 8.""" self.assertEqual(self.message.get_size(), 8) @unittest.expectedFailure def test_pack_empty(self): """[Common/Header] - packing empty header.""" self.assertRaises(TypeError, Header().pack()) def test_pack(self): """[Common/Header] - packing Hello.""" packed_header = b'\x01\x00\x00\x00\x00\x00\x00\x01' self.assertEqual(self.message.pack(), packed_header) def test_unpack(self): """[Common/Header] - unpacking Hello.""" filename = os.path.join(os.path.dirname(os.path.realpath('__file__')), 'raw/v0x01/ofpt_hello.dat') f = open(filename, 'rb') self.message.unpack(f.read(8)) self.assertEqual(self.message.length, 8) self.assertEqual(self.message.xid, 1) self.assertEqual(self.message.message_type, Type.OFPT_HELLO) self.assertEqual(self.message.version, 1) f.close()
class GetConfigRequest(GenericMessage): """Get Config Request message.""" header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) def __init__(self, xid=None): """The constructor just assings parameters to object attributes. This message does not contain a body beyond the OpenFlow Header. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class FeaturesRequest(GenericMessage): """Features request message. This message does not contain a body in addition to the OpenFlow Header. """ header = Header(message_type=Type.OFPT_FEATURES_REQUEST) def __init__(self, xid=None): """The constructor takes the optional parameter below. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class BarrierReply(GenericMessage): """OpenFlow Barrier Reply Message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_BARRIER_REPLY) def __init__(self, xid=None): """The constructor just assings parameters to object attributes. Args: xid (int): Header's xid. """ super().__init__(xid)
class Hello(GenericMessage): """OpenFlow Hello Message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_HELLO, length=8) def __init__(self, xid=None): """The constructor takes the parameters below. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class BarrierRequest(GenericMessage): """OpenFlow Barrier Request Message. This message does not contain a body in addition to the OpenFlow Header. """ header = Header(message_type=Type.OFPT_BARRIER_REQUEST) def __init__(self, xid=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class EchoRequest(GenericMessage): """OpenFlow Reply message. This message does not contain a body after the OpenFlow Header. """ header = Header(message_type=Type.OFPT_ECHO_REQUEST, length=8) def __init__(self, xid=None): """The constructor takes the parameters below. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class QueueGetConfigRequest(GenericMessage): """Query structure for configured queues on a port.""" header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) port = UBInt16(enum_ref=Port) #: Pad to 64-bits pad = Pad(2) def __init__(self, xid=None, port=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid of OpenFlow header port (Port): Target port for the query """ super().__init__(xid) self.port = port
class QueueGetConfigRequest(GenericMessage): """Query structure for configured queues on a port.""" header = Header(message_type=Type.OFPT_QUEUE_GET_CONFIG_REQUEST) port = UBInt16(enum_ref=Port) #: Pad to 64-bits pad = Pad(2) def __init__(self, xid=None, port=None): """Create a QueueGetConfigRequest with the optional parameters below. Args: xid (int): xid of OpenFlow header port (~pyof.v0x01.common.phy_port.Port): Target port for the query """ super().__init__(xid) self.port = port
class EchoReply(GenericMessage): """OpenFlow Reply message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_ECHO_REPLY, length=8) data = BinaryData() def __init__(self, xid=None, data=None): """Create an EchoReply with the optional parameters below. Args: xid (int): xid to be used on the message header. data (bytes): arbitrary-length data field. """ super().__init__(xid) self.data = data
def do_handshake(client): """Get a client (socket) and do the handshake of it. This method receives a client (socket) that simulates a switch on the network and do the OpenFlow handshake process with a running controller on the network. Args: client (socket): a socket object connected on the controller. Returns: The client with the handshake process done. """ # -- STEP 1: Sending Hello message client.send(Hello(xid=3).pack()) # -- STEP 2: Whait for Hello response binary_packet = b'' while len(binary_packet) < 8: binary_packet = client.recv(8) header = Header() header.unpack(binary_packet) # -- STEP 3: Wait for features_request message binary_packet = b'' # len() < 8 here because we just expect a Hello as response while len(binary_packet) < 8: binary_packet = client.recv(8) header = Header() header.unpack(binary_packet) # -- STEP 4: Send features_reply to the controller basedir = os.path.dirname(os.path.abspath(__file__)) raw_dir = os.path.join(basedir, 'raw') message = None with open(os.path.join(raw_dir, 'features_reply.cap'), 'rb') as file: message = file.read() client.send(message) return client
class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" header = Header() flags = UBInt16(enum_ref=ConfigFlag) miss_send_len = UBInt16() def __init__(self, xid=None, flags=None, miss_send_len=None): """Create a SwitchConfig with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (ConfigFlag): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid) self.flags = flags self.miss_send_len = miss_send_len
class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" header = Header() flags = UBInt16(enum_ref=ConfigFlags) miss_send_len = UBInt16() def __init__(self, xid=None, flags=None, miss_send_len=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid to be used on the message header. flags (ConfigFlags): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid) self.flags = flags self.miss_send_len = miss_send_len
class VendorHeader(GenericMessage): """OpenFlow Vendor message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_VENDOR) vendor = UBInt32() def __init__(self, xid=None, vendor=None): """The constructor takes the parameters below. Args: xid (int): xid to be used on the message header. vendor (int): Vendor ID: MSB 0: low-order bytes are IEEE OUI. MSB != 0: defined by OpenFlow consortium. """ super().__init__(xid) self.vendor = vendor
def do_handshake(client: socket): """Get a client socket and do the handshake of it. This method receives a client socket that simulates a switch on the network and does the OpenFlow handshake process with a running controller on the network. Args: client (socket): a socket object connected to the controller. Returns: The client with the handshake process done. """ # -- STEP 1: Send Hello message client.send(Hello(xid=3).pack()) # -- STEP 2: Wait for Hello response binary_packet = b'' while len(binary_packet) < 8: binary_packet = client.recv(8) header = Header() header.unpack(binary_packet) # -- STEP 3: Wait for features_request message binary_packet = b'' # len() < 8 here because we just expect a Hello as response while len(binary_packet) < 8: binary_packet = client.recv(8) header = Header() header.unpack(binary_packet) # -- STEP 4: Send features_reply to the controller basedir = os.path.dirname(os.path.abspath(__file__)) raw_dir = os.path.join(basedir, 'raw') message = None with open(os.path.join(raw_dir, 'features_reply.cap'), 'rb') as file: message = file.read() client.send(message) return client
def setUp(self): """Setup the TestHeader Class instantiating a HELLO header.""" self.message = Header() self.message.message_type = Type.OFPT_HELLO self.message.xid = 1 self.message.length = 0