def setUpClass(cls): """Setup TestStruct.""" reason = FlowRemovedReason.OFPRR_IDLE_TIMEOUT match = Match(in_port=80, dl_vlan=1, dl_vlan_pcp=1, dl_type=1, nw_tos=1, nw_proto=1, tp_src=80, tp_dst=80, dl_src=HWAddress('00:00:00:00:00:00'), dl_dst=HWAddress('00:00:00:00:00:00'), nw_src=IPAddress('192.168.0.1'), nw_dst=IPAddress('192.168.0.2')) super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_flow_removed') super().set_raw_dump_object(FlowRemoved, xid=12, match=match, cookie=0, priority=1, reason=reason, duration_sec=4, duration_nsec=23, idle_timeout=9, packet_count=10, byte_count=4) super().set_minimum_size(88)
def _get_ports(): return [ PhyPort(port_no=65534, hw_addr=HWAddress('0e:d3:98:a5:30:47'), name='s1', config=PortConfig.OFPPC_PORT_DOWN, state=PortState.OFPPS_LINK_DOWN, curr=0, advertised=0, supported=0, peer=0), PhyPort(port_no=1, hw_addr=HWAddress('0a:54:cf:fc:4e:6d'), name='s1-eth1', config=0, state=PortState.OFPPS_STP_LISTEN, curr=0x000000c0, advertised=0, supported=0, peer=0), PhyPort(port_no=2, hw_addr=HWAddress('f6:b6:ab:cc:f8:4f'), name='s1-eth2', config=0, state=PortState.OFPPS_STP_LISTEN, curr=0x000000c0, advertised=0, supported=0, peer=0) ]
class Ethernet(GenericStruct): destination = HWAddress() source = HWAddress() type = UBInt16() data = BinaryData() def __init__(self, destination=None, source=None, type=None, data=b''): super().__init__() self.destination = destination self.source = source self.type = type self.data = data def get_hash(self): return hash(self.pack())
class PortMod(GenericMessage): """Implement messages to modify the physical port behavior.""" header = Header(message_type=Type.OFPT_PORT_MOD) port_no = UBInt32() pad = Pad(4) hw_addr = HWAddress() pad2 = Pad(2) config = UBInt32(enum_ref=PortConfig) mask = UBInt32(enum_ref=PortConfig) advertise = UBInt32(enum_ref=PortFeatures) #: Pad to 64-bits. pad3 = Pad(4) def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, advertise=None): """The constructor just assings parameters to object attributes. Args: xid (int): OpenFlow xid to the header. port_no (int): Physical port number. hw_addr (HWAddress): The hardware address is not configurable. This is used to sanity-check the request, so it must be the same as returned in an ofp_phy_port struct. config (PortConfig): Bitmap of OFPPC_* flags mask (PortConfig): Bitmap of OFPPC_* flags to be changed advertise (PortFeatures): Bitmap of OFPPF_*. Zero all bits to prevent any action taking place. """ super().__init__(xid) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.advertise = advertise
def addPort(self, port_no=0, hw_addr="", port_name="", port_config=0, port_state=PortState.OFPPS_STP_LISTEN, port_curr=0, port_advertised=0, port_supported=0, port_peer=0): if(port_no == 0): port_no = randint(30000,65534) if(hw_addr == ""): hw_addr = sdnpwn.generateRandomMacAddress(self.switch_vendor_ouid) if(port_name == ""): port_name = "OF Port " + str(randint(1,10000)) initQueueID = randint(30000,65534) port = PhyPort(port_no=port_no, hw_addr=HWAddress(hw_addr), name=port_name, #config=PortConfig.OFPC_PORT_DOWN, #state=PortState.OFPPS_LINK_DOWN, config=port_config, state=port_state, curr=port_curr, advertised=port_advertised, supported=port_supported, peer=port_peer) self.switch_ports.append(port) self.switch_features["ports"] = self.switch_ports self.switch_stats["port"][str(port_no)] = PortStats(port_no=port_no, rx_packets=0, tx_packets=0, rx_bytes=0, tx_bytes=0, rx_dropped=0, tx_dropped=0, rx_errors=0, tx_errors=0, rx_frame_err=0, rx_over_err=0, rx_crc_err=0, collisions=0) self.switch_stats["queue"][str(port_no) + ":" + str(initQueueID)] = QueueStats(port_no=port_no, queue_id=initQueueID, tx_bytes=0, tx_packets=0, tx_errors=0)
class ActionDLAddr(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_DL_SRC` or _DST.""" dl_addr_type = UBInt16(enum_ref=ActionType) length = UBInt16(16) dl_addr = HWAddress() #: Pad for bit alignment. pad = Pad(6) def __init__(self, dl_addr_type=None, dl_addr=None): """The following constructor parameters are optional. Args: dl_addr_type (ActionType): :attr:`~ActionType.OFPAT_SET_DL_SRC` or :attr:`~ActionType.OFPAT_SET_DL_DST`. dl_addr (:class:`~.HWAddress`): Ethernet address. Defaults to None. """ super().__init__() self.dl_addr_type = dl_addr_type self.dl_addr = dl_addr def allowed_types(): """Return the Allowed Action Type Returns: action_types (list): list of allowed types """ return [ActionType.OFPAT_SET_DL_SRC, ActionType.OFPAT_SET_DL_DST]
class PhyPort(GenericStruct): """Description of a physical port. The port_no field is a value the datapath associates with a physical port. The hw_addr field typically is the MAC address for the port; :data:`.OFP_ETH_ALEN` is 6. The name field is a null-terminated string containing a human-readable name for the interface. The value of :data:`.OFP_MAX_PORT_NAME_LEN` is 16. :attr:`curr`, :attr:`advertised`, :attr:`supported` and :attr:`peer` are bitmaps of :class:`PortFeatures` enum values that describe features. If unsupported or unavailable, set all bits to zero. """ port_no = UBInt16() hw_addr = HWAddress() name = Char(length=OFP_MAX_PORT_NAME_LEN) config = UBInt32(enum_ref=PortConfig) state = UBInt32(enum_ref=PortState) curr = UBInt32(enum_ref=PortFeatures) advertised = UBInt32(enum_ref=PortFeatures) supported = UBInt32(enum_ref=PortFeatures) peer = UBInt32(enum_ref=PortFeatures) def __init__(self, port_no=None, hw_addr=None, name=None, config=None, state=None, curr=None, advertised=None, supported=None, peer=None): """The constructor takes the optional parameters below. Args: port_no (int): Port number. hw_addr (HWAddress): Hardware address. name(str): Null-terminated name. config (PortConfig): Bitmap of OFPPC* flags. state (PortState): Bitmap of OFPPS* flags. curr (PortFeatures): Current features. advertised (PortFeatures): Features being advertised by the port. supported (PortFeatures): Features supported by the port. peer (PortFeatures): Features advertised by peer. """ super().__init__() self.port_no = port_no self.hw_addr = hw_addr self.name = name self.config = config self.state = state self.curr = curr self.advertised = advertised self.supported = supported self.peer = peer
def setUp(self): """Basic setup for test.""" self.message = PhyPort() self.message.port_no = 1 self.message.hw_addr = HWAddress('9a:da:11:8a:f4:0c') self.message.name = 's1-eth1' self.message.state = PortState.OFPPS_STP_LISTEN self.message.curr = (PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER)
def from_of_tlv(cls, tlv): """Return an instance from a pyof OXM TLV.""" hw_address = HWAddress() hw_address.unpack(tlv.oxm_value) addr_str = str(hw_address) value = addr_str if tlv.oxm_hasmask: hw_mask = HWAddress() hw_mask.unpack(tlv.oxm_value[6:]) mask_str = str(hw_mask) value = f'{addr_str}/{mask_str}' return cls(value)
class Ethernet(GenericStruct): """Ethernet struct.""" destination = HWAddress() source = HWAddress() type = UBInt16() data = BinaryData() def __init__(self, destination=None, source=None, eth_type=None, data=b''): """Create an instance and set its attributes.""" super().__init__() self.destination = destination self.source = source self.type = eth_type self.data = data def get_hash(self): """Return a hash that identifies this instance.""" return hash(self.pack())
def as_of_tlv(self): """Return a pyof OXM TLV instance.""" if '/' in self.value: value, mask = self.value.split('/') mask = mask.upper() if mask == 'FF:FF:FF:FF:FF:FF': mask = None oxm_hasmask = False else: oxm_hasmask = True else: value = self.value mask = None oxm_hasmask = False value_bytes = HWAddress(value).pack() if mask: value_bytes += HWAddress(mask).pack() return OxmTLV(oxm_field=self.oxm_field, oxm_hasmask=oxm_hasmask, oxm_value=value_bytes)
def _new_portstatus(): """Crate new PortStatus and PhyPort instances.""" desc_name = 's1-eth1' desc = PhyPort(port_no=1, hw_addr=HWAddress('9a:da:11:8a:f4:0c'), name=desc_name, state=PortState.OFPPS_STP_LISTEN, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER) return PortStatus(xid=0, reason=PortReason.OFPPR_MODIFY, desc=desc)
def _new_portstatus(): """Crate new PortStatus and PhyPort instances.""" desc_name = 'X' * OFP_MAX_PORT_NAME_LEN desc = PhyPort(port_no=2, hw_addr=HWAddress('0a:0a:12:12:10:10'), name=desc_name, config=PortConfig.OFPPC_NO_STP, state=PortState.OFPPS_STP_FORWARD, curr=PortFeatures.OFPPF_10GB_FD, advertised=PortFeatures.OFPPF_PAUSE, supported=PortFeatures.OFPPF_AUTONEG, peer=PortFeatures.OFPPF_AUTONEG) return PortStatus(xid=1, reason=PortReason.OFPPR_ADD, desc=desc)
def _get_body(): """Return the body used by MultipartReply message.""" port1 = Port(port_no=PortNo.OFPP_LOCAL, hw_addr=HWAddress('5a:ee:a5:a0:62:4f'), name='s1', config=PortConfig.OFPPC_PORT_DOWN, state=PortState.OFPPS_LINK_DOWN, curr=0, advertised=0, supported=0, peer=0, curr_speed=0, max_speed=0) port2 = Port(port_no=1, hw_addr=HWAddress('4e:bf:ca:27:8e:ca'), name='s1-eth1', config=0, state=PortState.OFPPS_LIVE, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, advertised=0, supported=0, peer=0, curr_speed=10000000, max_speed=0) port3 = Port(port_no=2, hw_addr=HWAddress('26:1f:b9:5e:3c:c7'), name='s1-eth2', config=0, state=PortState.OFPPS_LIVE, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, advertised=0, supported=0, peer=0, curr_speed=10000000, max_speed=0) lop = ListOfPorts([port1, port2, port3]) return lop
def _new_portstatus(): """Crate new PortStatus and Port instances.""" desc_name = 's1-eth1' desc = Port(port_no=1, hw_addr=HWAddress('62:43:e5:db:35:0a'), name=desc_name, config=0, state=PortState.OFPPS_LIVE, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, advertised=0, supported=0, peer=0, curr_speed=10000000, max_speed=0) return PortStatus(xid=0, reason=PortReason.OFPPR_MODIFY, desc=desc)
class PortMod(GenericMessage): """Implement messages to modify the physical port behavior.""" header = Header(message_type=Type.OFPT_PORT_MOD) port_no = UBInt32() pad = Pad(4) #: The hardware address is not configurable. This is used to #: sanity-check the request, so it must be the same as returned in an port struct. hw_addr = HWAddress() #: Pad to 64 bits. pad2 = Pad(2) #: Bitmap of OFPPC_* flags. config = UBInt32(enum_ref=PortConfig) #: Bitmap of OFPPC_* flags to be changed. mask = UBInt32(enum_ref=PortConfig) #: Port mod property list - 0 or more properties properties = FixedTypeList(pyof_class=PortModPropHeader) def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, properties=FixedTypeList(pyof_class=PortModPropHeader)): """Create a PortMod with the optional parameters below. Args: xid (int): OpenFlow xid to the header. port_no (int): Physical port number. hw_addr (HWAddress): The hardware address is not configurable. This is used to sanity-check the request, so it must be the same as returned in an ofp_phy_port struct. config (~pyof.v0x05.common.port.PortConfig): Bitmap of OFPPC_* flags mask (~pyof.v0x05.common.port.PortConfig): Bitmap of OFPPC_* flags to be changed properties (FixedTypeList(pyof_class=PortModPropHeader)): Port mod property list - 0 or more properties """ super().__init__(xid) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.properties = properties
def __setattr__(self, name, value): # converts string ip_address to IPAddress if isinstance(getattr(Match, name), IPAddress) and \ not isinstance(value, IPAddress): if isinstance(value, list): value = ".".join(str(x) for x in value) value = IPAddress(value) # noqa # convertstring or list of hwaddress to HWAddress elif isinstance(getattr(Match, name), HWAddress) and \ not isinstance(value, HWAddress): if isinstance(value, list): values = ["{0:0{1}x}".format(x, 2) for x in value] value = ":".join(values) value = HWAddress(value) super().__setattr__(name, value) self.fill_wildcards(name, value)
def _match_from_dict(self, dictionary): known_fields = ((field, data) for field, data in dictionary.items() if field in self._match_names) for field_name, data in known_fields: tlv = OxmTLV() tlv.oxm_field = self._match_names[field_name] # set oxm_value if field_name in ('dl_vlan_pcp', 'nw_proto'): tlv.oxm_value = data.to_bytes(1, 'big') elif field_name == 'dl_vlan': vid = data | VlanId.OFPVID_PRESENT tlv.oxm_value = vid.to_bytes(2, 'big') elif field_name in ('dl_src', 'dl_dst'): tlv.oxm_value = HWAddress(data).pack() elif field_name in ('nw_src', 'nw_dst'): tlv.oxm_value = IPAddress(data).pack() elif field_name == 'in_port': tlv.oxm_value = data.to_bytes(4, 'big') else: tlv.oxm_value = data.to_bytes(2, 'big') yield tlv
class ActionDLAddr(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_DL_SRC` or _DST.""" dl_addr = HWAddress() #: Pad for bit alignment. pad = Pad(6) _allowed_types = (ActionType.OFPAT_SET_DL_SRC, ActionType.OFPAT_SET_DL_DST) def __init__(self, action_type=None, dl_addr=None): """Create an ActionDLAddr with the optional parameters below. Args: action_type (:class:`~pyof.v0x01.common.action.ActionType`): :attr:`~ActionType.OFPAT_SET_DL_SRC` or :attr:`~ActionType.OFPAT_SET_DL_DST`. dl_addr (:class:`~.HWAddress`): Ethernet address. Defaults to None. """ super().__init__(action_type, length=16) self.dl_addr = dl_addr
class PortMod(GenericMessage): """Implement messages to modify the physical port behavior.""" header = Header(message_type=Type.OFPT_PORT_MOD) port_no = UBInt16() hw_addr = HWAddress() config = UBInt32(enum_ref=PortConfig) mask = UBInt32(enum_ref=PortConfig) advertise = UBInt32(enum_ref=PortFeatures) #: Pad to 64-bits. pad = Pad(4) def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, advertise=None): """Create a PortMod with the optional parameters below. Args: xid (int): OpenFlow xid to the header. port_no (int): Physical port number. hw_addr (HWAddress): The hardware address is not configurable. This is used to sanity-check the request, so it must be the same as returned in an ofp_phy_port struct. config (~pyof.v0x01.common.phy_port.PortConfig): Bitmap of OFPPC_* flags mask (~pyof.v0x01.common.phy_port.PortConfig): Bitmap of OFPPC_* flags to be changed advertise (~pyof.v0x01.common.phy_port.PortFeatures): Bitmap of "ofp_port_features"s """ super().__init__(xid) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.advertise = advertise
def _match_to_dict(self, flow_stats): match_dict = {} fields = (field for field in flow_stats.match.oxm_match_fields if field.oxm_field in self._match_values) for field in fields: match_field = self._match_values[field.oxm_field] if match_field == 'dl_vlan': data = int.from_bytes(field.oxm_value, 'big') & 4095 elif match_field in ('dl_src', 'dl_dst'): addr = HWAddress() addr.unpack(field.oxm_value) data = str(addr) elif match_field in ('nw_src', 'nw_dst'): addr = IPAddress() addr.unpack(field.oxm_value) data = str(addr) else: data = int.from_bytes(field.oxm_value, 'big') match_dict[match_field] = data return match_dict
class Ethernet(GenericStruct): """Ethernet "struct". Objects of this class represents an ethernet packet. It contains the 'Ethernet header', composed by destination (MAC), source (MAC), type (EtherType)[1] and the payload of the packet, as binary data. This class does not consider the Ethernet 'Preamble' or the 'CRC'. There is also a get_hash method, that hashes the binary representation of the object so we can have a unique representation of the ethernet packet, so we can keep a track of ethernet packets being flooded over the network. [1] EtherTypes: http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1 """ destination = HWAddress() source = HWAddress() vlan = VLAN() ether_type = UBInt16() data = BinaryData() def __init__(self, destination=None, source=None, vlan=None, ether_type=None, data=b''): """Create an instance and set its attributes. Args: destination (:class:`~pyof.foundation.basic_types.HWAddress`): The final destination MAC address. source (:class:`~pyof.foundation.basic_types.HWAddress`): The source Mac address of the packet. ether_type (:class:`~pyof.foundation.basic_types.UBInt16`): The EtherType of packet. data (:class:`~pyof.foundation.basic_types.BinaryData`): The content of the packet in binary format. """ super().__init__() self.destination = destination self.source = source self.vlan = VLAN() if vlan is None else vlan self.ether_type = ether_type self.data = data def get_hash(self): """Calculate a hash and returns it. Returns: int: Integer value that identifies this instance. """ return hash(self.pack()) def unpack(self, buff, offset=0): """Unpack a binary message into this object's attributes. Unpack the binary value *buff* and update this object attributes based on the results. Ethernet headers may have VLAN tags. If no VLAN tag is found, a 'wildcard VLAN tag' is inserted to assure correct unpacking. Args: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: UnpackException: If there is a struct unpacking error. """ # Check if the EtherType bytes are actually equal to EtherType.VLAN, # indicating that the packet is tagged. If it is not, we insert the # equivalent to 'NULL VLAN data' (\x00\x00\x00\x00) to enable the # correct unpacking process. if buff[12:14] != EtherType.VLAN.to_bytes(2, 'big'): buff = buff[0:12] + b'\x00\x00\x00\x00' + buff[12:] super().unpack(buff, offset)
class ARP(GenericStruct): """ARP packet "struct". Contains fields for an ARP packet's header and data. Designed for Ethernet and IPv4 only: needs to have some attributes changed for other HTYPE and PTYPE implementations. Must be encapsulated inside an Ethernet frame. """ htype = UBInt16() ptype = UBInt16() hlen = UBInt8() plen = UBInt8() oper = UBInt16() sha = HWAddress() spa = IPAddress() tha = HWAddress() tpa = IPAddress() def __init__(self, htype=1, ptype=EtherType.IPV4, hlen=6, plen=4, oper=1, sha='00:00:00:00:00:00', spa='0.0.0.0', tha="00:00:00:00:00:00", tpa='0.0.0.0'): """Create an ARP with the parameters below. Args: htype (int): Hardware protocol type. Defaults to 1 for Ethernet. ptype (int): Network protocol type. Defaults to 0x800 for IPv4. hlen (int): Length of the hardware address. Defaults to 6 for MAC addresses. plen (int): Length of the networking protocol address. Defaults to 4 for IPv4 addresses. oper (int): Determines the operation for this ARP packet. Must be 1 for ARP request or 2 for ARP reply. Defaults to 1. sha (str): Sender hardware address. Defaults to '00:00:00:00:00:00'. spa (str): Sender protocol address. Defaults to '0.0.0.0'. tha (str): Target hardware address. Defaults to '00:00:00:00:00:00'. tpa (str): Target protocol address. Defaults to '0.0.0.0'. """ super().__init__() self.htype = htype self.ptype = ptype self.hlen = hlen self.plen = plen self.oper = oper self.sha = sha self.spa = spa self.tha = tha self.tpa = tpa def is_valid(self): """Assure the ARP contains Ethernet and IPv4 information.""" return self.htype == 1 and self.ptype == EtherType.IPV4 def unpack(self, buff, offset=0): """Unpack a binary struct into this object's attributes. Return the values instead of the lib's basic types. Check if the protocols involved are Ethernet and IPv4. Other protocols are currently not supported. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ super().unpack(buff, offset) if not self.is_valid(): raise UnpackException("Unsupported protocols in ARP packet")
class Ethernet(GenericStruct): """Ethernet "struct". Objects of this class represents an ethernet packet. It contains the 'Ethernet header', composed by destination (MAC), source (MAC), type (EtherType)[1] and the payload of the packet, as binary data. This class does not consider the Ethernet 'Preamble' or the 'CRC'. There is also a get_hash method, that hashes the binary representation of the object so we can have a unique representation of the ethernet packet, so we can keep a track of ethernet packets being flooded over the network. [1] EtherTypes: http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1 """ destination = HWAddress() source = HWAddress() vlans = ListOfVLAN() ether_type = UBInt16() data = BinaryData() def __init__(self, destination=None, source=None, vlans=None, ether_type=None, data=b''): """Create an instance and set its attributes. Args: destination (:class:`~pyof.foundation.basic_types.HWAddress`): The final destination MAC address. source (:class:`~pyof.foundation.basic_types.HWAddress`): The source Mac address of the packet. ether_type (:class:`~pyof.foundation.basic_types.UBInt16`): The EtherType of packet. data (:class:`~pyof.foundation.basic_types.BinaryData`): The content of the packet in binary format. """ super().__init__() self.destination = destination self.source = source self.vlans = ListOfVLAN() if vlans is None else vlans self.ether_type = ether_type self.data = data def get_hash(self): """Calculate a hash and returns it. Returns: int: Integer value that identifies this instance. """ return hash(self.pack()) @staticmethod def _get_vlan_length(buff): """Return the total length of VLAN tags in a given Ethernet buffer.""" length = 0 begin = 12 while (buff[begin:begin + 2] in (EtherType.VLAN.to_bytes(2, 'big'), EtherType.VLAN_QINQ.to_bytes(2, 'big'))): length += 4 begin += 4 return length def unpack(self, buff, offset=0): """Unpack a binary message into this object's attributes. Unpack the binary value *buff* and update this object attributes based on the results. Ethernet headers may have VLAN tags. If no VLAN tag is found, a 'wildcard VLAN tag' is inserted to assure correct unpacking. Args: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: UnpackException: If there is a struct unpacking error. """ begin = offset vlan_length = self._get_vlan_length(buff) for attribute_name, class_attribute in self.get_class_attributes(): attribute = deepcopy(class_attribute) if attribute_name == 'vlans': attribute.unpack(buff[begin:begin + vlan_length]) else: attribute.unpack(buff, begin) setattr(self, attribute_name, attribute) begin += attribute.get_size()
class Port(GenericStruct): """Description of a port. The port_no field uniquely identifies a port within a switch. The hw_addr field typically is the MAC address for the port; :data:`.OFP_MAX_ETH_ALEN` is 6. The name field is a null-terminated string containing a human-readable name for the interface. The value of :data:`.OFP_MAX_PORT_NAME_LEN` is 16. :attr:`curr`, :attr:`advertised`, :attr:`supported` and :attr:`peer` fields indicate link modes (speed and duplexity), link type (copper/fiber) and link features (autonegotiation and pause). They are bitmaps of :class:`PortFeatures` enum values that describe features. Multiple of these flags may be set simultaneously. If none of the port speed flags are set, the :attr:`max_speed` or :attr:`curr_speed` are used. """ port_no = UBInt32() pad = Pad(4) hw_addr = HWAddress() pad2 = Pad(2) name = Char(length=OFP_MAX_PORT_NAME_LEN) config = UBInt32(enum_ref=PortConfig) state = UBInt32(enum_ref=PortState) curr = UBInt32(enum_ref=PortFeatures) advertised = UBInt32(enum_ref=PortFeatures) supported = UBInt32(enum_ref=PortFeatures) peer = UBInt32(enum_ref=PortFeatures) curr_speed = UBInt32() max_speed = UBInt32() def __init__(self, port_no=None, hw_addr=None, name=None, config=None, state=None, curr=None, advertised=None, supported=None, peer=None, curr_speed=None, max_speed=None): """The constructor takes the optional parameters below. Args: port_no (int): Port number. hw_addr (HWAddress): Hardware address. name (str): Null-terminated name. config (PortConfig): Bitmap of OFPPC* flags. state (PortState): Bitmap of OFPPS* flags. curr (PortFeatures): Current features. advertised (PortFeatures): Features being advertised by the port. supported (PortFeatures): Features supported by the port. peer (PortFeatures): Features advertised by peer. curr_speed (int): Current port bitrate in kbps. max_speed (int): Max port bitrate in kbps. """ super().__init__() self.port_no = port_no self.hw_addr = hw_addr self.name = name self.config = config self.state = state self.curr = curr self.advertised = advertised self.supported = supported self.peer = peer self.curr_speed = curr_speed self.max_speed = max_speed
class Match(GenericStruct): """Describes a flow entry. Fields to match against flows.""" #: Wildcards fields. wildcards = UBInt32(value=FlowWildCards.OFPFW_ALL, enum_ref=FlowWildCards) #: Input switch port. in_port = UBInt16(0) #: Ethernet source address. (default: '00:00:00:00:00:00') dl_src = HWAddress() #: Ethernet destination address. (default: '00:00:00:00:00:00') dl_dst = HWAddress() #: Input VLAN id. (default: 0) dl_vlan = UBInt16(0) #: Input VLAN priority. (default: 0) dl_vlan_pcp = UBInt8(0) #: Align to 64-bits. pad1 = Pad(1) #: Ethernet frame type. (default: 0) dl_type = UBInt16(0) #: IP ToS (actually DSCP field, 6 bits). (default: 0) nw_tos = UBInt8(0) #: IP protocol or lower 8 bits of ARP opcode. (default: 0) nw_proto = UBInt8(0) #: Align to 64-bits. pad2 = Pad(2) #: IP source address. (default: '0.0.0.0/0') nw_src = IPAddress('0.0.0.0/0') #: IP destination address. (default: '0.0.0.0/0') nw_dst = IPAddress('0.0.0.0/0') #: TCP/UDP source port. (default: 0) tp_src = UBInt16(0) #: TCP/UDP destination port. (default: 0) tp_dst = UBInt16(0) def __init__(self, **kwargs): """All the constructor parameters below are optional. Args: wildcards (FlowWildCards): Wildcards fields. (Default: OFPFW_ALL) in_port (int): Input switch port. (default: 0) dl_src (HWAddress): Ethernet source address. (default: '00:00:00:00:00:00') dl_dst (HWAddress): Ethernet destination address. (default: '00:00:00:00:00:00') dl_vlan (int): Input VLAN id. (default: 0) dl_vlan_pcp (int): Input VLAN priority. (default: 0) dl_type (int): Ethernet frame type. (default: 0) nw_tos (int): IP ToS (actually DSCP field, 6 bits). (default: 0) nw_proto (int): IP protocol or lower 8 bits of ARP opcode. (default: 0) nw_src (IPAddress): IP source address. (default: '0.0.0.0/0') nw_dst (IPAddress): IP destination address. (default: '0.0.0.0/0') tp_src (int): TCP/UDP source port. (default: 0) tp_dst (int): TCP/UDP destination port. (default: 0) """ super().__init__() for field, value in kwargs.items(): setattr(self, field, value) def __setattr__(self, name, value): # converts string ip_address to IPAddress if isinstance(getattr(Match, name), IPAddress) and \ not isinstance(value, IPAddress): if isinstance(value, list): value = ".".join(str(x) for x in value) value = IPAddress(value) # noqa # convertstring or list of hwaddress to HWAddress elif isinstance(getattr(Match, name), HWAddress) and \ not isinstance(value, HWAddress): if isinstance(value, list): values = ["{0:0{1}x}".format(x, 2) for x in value] value = ":".join(values) value = HWAddress(value) super().__setattr__(name, value) self.fill_wildcards(name, value) def unpack(self, buff, offset=0): """Unpack *buff* into this object. Do nothing, since the _length is already defined and it is just a Pad. Keep buff and offset just for compability with other unpack methods. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ super().unpack(buff, offset) self.wildcards = UBInt32(value=FlowWildCards.OFPFW_ALL, enum_ref=FlowWildCards) self.wildcards.unpack(buff, offset) def fill_wildcards(self, field=None, value=0): """Update wildcards attribute. This method update a wildcards considering the attributes of the current instance. Args: field (str): Name of the updated field. value (GenericType): New value used in the field. """ if field in [None, 'wildcards'] or isinstance(value, Pad): return default_value = getattr(Match, field) if isinstance(default_value, IPAddress): if field == 'nw_dst': shift = FlowWildCards.OFPFW_NW_DST_SHIFT base_mask = FlowWildCards.OFPFW_NW_DST_MASK else: shift = FlowWildCards.OFPFW_NW_SRC_SHIFT base_mask = FlowWildCards.OFPFW_NW_SRC_MASK # First we clear the nw_dst/nw_src mask related bits on the current # wildcard by setting 0 on all of them while we keep all other bits # as they are. self.wildcards &= FlowWildCards.OFPFW_ALL ^ base_mask # nw_dst and nw_src wildcard fields have 6 bits each. # "base_mask" is the 'all ones' for those 6 bits. # Once we know the netmask, we can calculate the these 6 bits # wildcard value and reverse them in order to insert them at the # correct position in self.wildcards wildcard = (value.max_prefix - value.netmask) << shift self.wildcards |= wildcard else: wildcard_field = "OFPFW_{}".format(field.upper()) wildcard = getattr(FlowWildCards, wildcard_field) if value == default_value and not (self.wildcards & wildcard) or \ value != default_value and (self.wildcards & wildcard): self.wildcards ^= wildcard
class Port(GenericStruct): """Description of a port. The port_no field uniquely identifies a port within a switch. The hw_addr field typically is the MAC address for the port; :data:`.OFP_MAX_ETH_ALEN` is 6. The name field is a null-terminated string containing a human-readable name for the interface. The value of :data:`.OFP_MAX_PORT_NAME_LEN` is 16. :attr:`curr`, :attr:`advertised`, :attr:`supported` and :attr:`peer` fields indicate link modes (speed and duplexity), link type (copper/fiber) and link features (autonegotiation and pause). They are bitmaps of :class:`PortFeatures` enum values that describe features. Multiple of these flags may be set simultaneously. If none of the port speed flags are set, the :attr:`max_speed` or :attr:`curr_speed` are used. """ port_no = UBInt32() length = UBInt16() pad = Pad(2) hw_addr = HWAddress() pad2 = Pad(2) # Align to 64 bits name = Char(length=OFP_MAX_PORT_NAME_LEN) # Null terminated config = UBInt32(enum_ref=PortConfig) # Bitmap of OFPPC_* flags state = UBInt32(enum_ref=PortState) # Bitmap of OFPPS_* flags #pad = Pad(4) #hw_addr = HWAddress() #pad2 = Pad(2) """ These are not existed in version 1.4 specifications curr = UBInt32(enum_ref=PortFeatures) advertised = UBInt32(enum_ref=PortFeatures) supported = UBInt32(enum_ref=PortFeatures) peer = UBInt32(enum_ref=PortFeatures) curr_speed = UBInt32() max_speed = UBInt32() """ def __init__(self, port_no=None, hw_addr=None, name=None, config=None, state=None): """Create a Port with the optional parameters below. Args: port_no (int): Port number. hw_addr (HWAddress): Hardware address. name (str): Null-terminated name. config (~pyof.v0x05.common.port.PortConfig): Bitmap of OFPPC* flags. state (~pyof.v0x05.common.port.PortState): Bitmap of OFPPS* flags. """ super().__init__() self.port_no = port_no self.hw_addr = hw_addr self.name = name self.config = config self.state = state
def as_of_tlv(self): """Return a pyof OXM TLV instance.""" value_bytes = HWAddress(self.value).pack() return OxmTLV(oxm_field=self.oxm_field, oxm_value=value_bytes)
def from_of_tlv(cls, tlv): """Return an instance from a pyof OXM TLV.""" hw_address = HWAddress() hw_address.unpack(tlv.oxm_value) addr_str = str(hw_address) return cls(addr_str)
class Match(GenericStruct): """Describes a flow entry. Fields to match against flows.""" #: Wildcards fields. wildcards = UBInt32(value=FlowWildCards.OFPFW_ALL, enum_ref=FlowWildCards) #: Input switch port. in_port = UBInt16(0) #: Ethernet source address. (default: '00:00:00:00:00:00') dl_src = HWAddress() #: Ethernet destination address. (default: '00:00:00:00:00:00') dl_dst = HWAddress() #: Input VLAN id. (default: 0) dl_vlan = UBInt16(0) #: Input VLAN priority. (default: 0) dl_vlan_pcp = UBInt8(0) #: Align to 64-bits. pad1 = Pad(1) #: Ethernet frame type. (default: 0) dl_type = UBInt16(0) #: IP ToS (actually DSCP field, 6 bits). (default: 0) nw_tos = UBInt8(0) #: IP protocol or lower 8 bits of ARP opcode. (default: 0) nw_proto = UBInt8(0) #: Align to 64-bits. pad2 = Pad(2) #: IP source address. (default: '0.0.0.0/32') nw_src = IPAddress() #: IP destination address. (default: '0.0.0.0/32') nw_dst = IPAddress() #: TCP/UDP source port. (default: 0) tp_src = UBInt16(0) #: TCP/UDP destination port. (default: 0) tp_dst = UBInt16(0) def __init__(self, **kwargs): """All the constructor parameters below are optional. Args: wildcards (FlowWildCards): Wildcards fields. (Default: OFPFW_ALL) in_port (int): Input switch port. (default: 0) dl_src (HWAddress): Ethernet source address. (default: '00:00:00:00:00:00') dl_dst (HWAddress): Ethernet destination address. (default: '00:00:00:00:00:00') dl_vlan (int): Input VLAN id. (default: 0) dl_vlan_pcp (int): Input VLAN priority. (default: 0) dl_type (int): Ethernet frame type. (default: 0) nw_tos (int): IP ToS (actually DSCP field, 6 bits). (default: 0) nw_proto (int): IP protocol or lower 8 bits of ARP opcode. (default: 0) nw_src (IPAddress): IP source address. (default: '0.0.0.0/32') nw_dst (IPAddress): IP destination address. (default: '0.0.0.0/32') tp_src (int): TCP/UDP source port. (default: 0) tp_dst (int): TCP/UDP destination port. (default: 0) """ super().__init__() [setattr(self, field, value) for field, value in kwargs.items()] def __setattr__(self, name, value): # converts string ip_address to IPAddress if isinstance(getattr(Match, name), IPAddress) and \ not isinstance(value, IPAddress): if isinstance(value, list): value = ".".join(str(x) for x in value) value = IPAddress(value) # convertstring or list of hwaddress to HWAddress if isinstance(getattr(Match, name), HWAddress) and \ not isinstance(value, HWAddress): if isinstance(value, list): values = ["{0:0{1}x}".format(x, 2) for x in value] value = ":".join(values) value = HWAddress(value) super().__setattr__(name, value) self.fill_wildcards(name, value) def unpack(self, buff, offset=0): """Unpack *buff* into this object. Do nothing, since the _length is already defined and it is just a Pad. Keep buff and offset just for compability with other unpack methods. Args: buff: Buffer where data is located. offset (int): Where data stream begins. """ super().unpack(buff, offset) self.wildcards = UBInt32(value=FlowWildCards.OFPFW_ALL, enum_ref=FlowWildCards) self.wildcards.unpack(buff, offset) def fill_wildcards(self, field=None, value=0): """Update wildcards attribute. This method update a wildcards considering the attributes of the current instance. Args: field (str): Name of the updated field. value (GenericType): New value used in the field. """ if field in [None, 'wildcards'] or isinstance(value, Pad): return default_value = getattr(Match, field) if isinstance(default_value, IPAddress): if field is 'nw_dst': self.wildcards |= FlowWildCards.OFPFW_NW_DST_MASK shift = FlowWildCards.OFPFW_NW_DST_SHIFT else: self.wildcards |= FlowWildCards.OFPFW_NW_SRC_MASK shift = FlowWildCards.OFPFW_NW_SRC_SHIFT wildcard = (value.max_prefix - value.netmask) << shift self.wildcards -= wildcard else: wildcard_field = "OFPFW_{}".format(field.upper()) wildcard = getattr(FlowWildCards, wildcard_field) if value == default_value and not (self.wildcards & wildcard) or \ value != default_value and (self.wildcards & wildcard): self.wildcards ^= wildcard