diff options
-rw-r--r-- | ryu/controller/controller.py | 4 | ||||
-rw-r--r-- | ryu/ofproto/ofproto_v1_4.py | 13 | ||||
-rw-r--r-- | ryu/ofproto/ofproto_v1_4_parser.py | 1452 | ||||
-rw-r--r-- | ryu/tests/unit/ofproto/test_ofproto.py | 5 |
4 files changed, 1473 insertions, 1 deletions
diff --git a/ryu/controller/controller.py b/ryu/controller/controller.py index f9b592f9..20a9c1d4 100644 --- a/ryu/controller/controller.py +++ b/ryu/controller/controller.py @@ -33,6 +33,8 @@ from ryu.ofproto import ofproto_v1_2 from ryu.ofproto import ofproto_v1_2_parser from ryu.ofproto import ofproto_v1_3 from ryu.ofproto import ofproto_v1_3_parser +from ryu.ofproto import ofproto_v1_4 +from ryu.ofproto import ofproto_v1_4_parser from ryu.ofproto import nx_match from ryu.controller import handler @@ -108,6 +110,8 @@ class Datapath(object): ofproto_v1_2_parser), ofproto_v1_3.OFP_VERSION: (ofproto_v1_3, ofproto_v1_3_parser), + ofproto_v1_4.OFP_VERSION: (ofproto_v1_4, + ofproto_v1_4_parser), } def __init__(self, socket, address): diff --git a/ryu/ofproto/ofproto_v1_4.py b/ryu/ofproto/ofproto_v1_4.py index a3063cfe..11408a70 100644 --- a/ryu/ofproto/ofproto_v1_4.py +++ b/ryu/ofproto/ofproto_v1_4.py @@ -944,6 +944,12 @@ assert (calcsize(OFP_MULTIPART_REQUEST_PACK_STR) + OFP_HEADER_SIZE == # enum ofp_multipart_reply_flags OFPMPF_REPLY_MORE = 1 << 0 # More requests to follow. +# struct ofp_multipart_reply +OFP_MULTIPART_REPLY_PACK_STR = '!HH4x' +OFP_MULTIPART_REPLY_SIZE = 16 +assert (calcsize(OFP_MULTIPART_REPLY_PACK_STR) + OFP_HEADER_SIZE == + OFP_MULTIPART_REPLY_SIZE) + DESC_STR_LEN = 256 DESC_STR_LEN_STR = str(DESC_STR_LEN) SERIAL_NUM_LEN = 32 @@ -970,7 +976,7 @@ assert (calcsize(OFP_FLOW_STATS_REQUEST_PACK_STR) == OFP_FLOW_STATS_REQUEST_SIZE) # struct ofp_flow_stats -_OFP_FLOW_STATS_0_PACK_STR = 'HBxIIHHHH4xQQQ' +_OFP_FLOW_STATS_0_PACK_STR = 'HBxIIHHHHH2xQQQ' OFP_FLOW_STATS_0_PACK_STR = '!' + _OFP_FLOW_STATS_0_PACK_STR OFP_FLOW_STATS_0_SIZE = 48 assert calcsize(OFP_FLOW_STATS_0_PACK_STR) == OFP_FLOW_STATS_0_SIZE @@ -1405,3 +1411,8 @@ OFP_BUNDLE_ADD_MSG_PACK_STR = '!IHH' + _OFP_HEADER_PACK_STR OFP_BUNDLE_ADD_MSG_SIZE = 24 assert (calcsize(OFP_BUNDLE_ADD_MSG_PACK_STR) + OFP_HEADER_SIZE == OFP_BUNDLE_ADD_MSG_SIZE) + +# define constants +OFP_VERSION = 0x05 +OFP_TCP_PORT = 6633 +MAX_XID = 0xffffffff diff --git a/ryu/ofproto/ofproto_v1_4_parser.py b/ryu/ofproto/ofproto_v1_4_parser.py new file mode 100644 index 00000000..616e0f52 --- /dev/null +++ b/ryu/ofproto/ofproto_v1_4_parser.py @@ -0,0 +1,1452 @@ +# Copyright (C) 2012, 2013, 2014 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2012, 2013 Isaku Yamahata <yamahata at valinux co jp> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import struct +import itertools + +from ryu.lib import addrconv +from ryu.lib import mac +from ryu import utils +from ofproto_parser import StringifyMixin, MsgBase, msg_pack_into, msg_str_attr +from . import ofproto_parser +from . import ofproto_common +from . import ofproto_v1_4 as ofproto + +_MSG_PARSERS = {} + + +def _set_msg_type(msg_type): + def _set_cls_msg_type(cls): + cls.cls_msg_type = msg_type + return cls + return _set_cls_msg_type + + +def _register_parser(cls): + '''class decorator to register msg parser''' + assert cls.cls_msg_type is not None + assert cls.cls_msg_type not in _MSG_PARSERS + _MSG_PARSERS[cls.cls_msg_type] = cls.parser + return cls + + +@ofproto_parser.register_msg_parser(ofproto.OFP_VERSION) +def msg_parser(datapath, version, msg_type, msg_len, xid, buf): + parser = _MSG_PARSERS.get(msg_type) + return parser(datapath, version, msg_type, msg_len, xid, buf) + + +@_register_parser +@_set_msg_type(ofproto.OFPT_HELLO) +class OFPHello(MsgBase): + """ + Hello message + + When connection is started, the hello message is exchanged between a + switch and a controller. + + This message is handled by the Ryu framework, so the Ryu application + do not need to process this typically. + + ========== ========================================================= + Attribute Description + ========== ========================================================= + elements list of ``OFPHelloElemVersionBitmap`` instance + ========== ========================================================= + """ + def __init__(self, datapath, elements=[]): + super(OFPHello, self).__init__(datapath) + self.elements = elements + + @classmethod + def parser(cls, datapath, version, msg_type, msg_len, xid, buf): + msg = super(OFPHello, cls).parser(datapath, version, msg_type, + msg_len, xid, buf) + + offset = ofproto.OFP_HELLO_HEADER_SIZE + elems = [] + while offset < msg.msg_len: + type_, length = struct.unpack_from( + ofproto.OFP_HELLO_ELEM_HEADER_PACK_STR, msg.buf, offset) + + # better to register Hello Element classes but currently + # Only VerisonBitmap is supported so let's be simple. + + if type_ == ofproto.OFPHET_VERSIONBITMAP: + elem = OFPHelloElemVersionBitmap.parser(msg.buf, offset) + elems.append(elem) + + offset += length + msg.elements = elems + return msg + + +class OFPHelloElemVersionBitmap(StringifyMixin): + """ + Version bitmap Hello Element + + ========== ========================================================= + Attribute Description + ========== ========================================================= + versions list of versions of OpenFlow protocol a device supports + ========== ========================================================= + """ + def __init__(self, versions, type_=None, length=None): + super(OFPHelloElemVersionBitmap, self).__init__() + self.type = ofproto.OFPHET_VERSIONBITMAP + self.length = None + self._bitmaps = None + self.versions = versions + + @classmethod + def parser(cls, buf, offset): + type_, length = struct.unpack_from( + ofproto.OFP_HELLO_ELEM_VERSIONBITMAP_HEADER_PACK_STR, + buf, offset) + assert type_ == ofproto.OFPHET_VERSIONBITMAP + + bitmaps_len = (length - + ofproto.OFP_HELLO_ELEM_VERSIONBITMAP_HEADER_SIZE) + offset += ofproto.OFP_HELLO_ELEM_VERSIONBITMAP_HEADER_SIZE + bitmaps = [] + while bitmaps_len >= 4: + bitmap = struct.unpack_from('!I', buf, offset) + bitmaps.append(bitmap[0]) + offset += 4 + bitmaps_len -= 4 + + versions = [i * 32 + shift + for i, bitmap in enumerate(bitmaps) + for shift in range(31) if bitmap & (1 << shift)] + elem = cls(versions) + elem.length = length + elem._bitmaps = bitmaps + return elem + + +@_set_msg_type(ofproto.OFPT_FEATURES_REQUEST) +class OFPFeaturesRequest(MsgBase): + """ + Features request message + + The controller sends a feature request to the switch upon session + establishment. + + This message is handled by the Ryu framework, so the Ryu application + do not need to process this typically. + + Example:: + + def send_features_request(self, datapath): + ofp_parser = datapath.ofproto_parser + + req = ofp_parser.OFPFeaturesRequest(datapath) + datapath.send_msg(req) + """ + def __init__(self, datapath): + super(OFPFeaturesRequest, self).__init__(datapath) + + +@_register_parser +@_set_msg_type(ofproto.OFPT_FEATURES_REPLY) +class OFPSwitchFeatures(MsgBase): + """ + Features reply message + + The switch responds with a features reply message to a features + request. + + This message is handled by the Ryu framework, so the Ryu application + do not need to process this typically. + + Example:: + + @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) + def switch_features_handler(self, ev): + msg = ev.msg + + self.logger.debug('OFPSwitchFeatures received: ' + 'datapath_id=0x%016x n_buffers=%d ' + 'n_tables=%d auxiliary_id=%d ' + 'capabilities=0x%08x', + msg.datapath_id, msg.n_buffers, msg.n_tables, + msg.auxiliary_id, msg.capabilities) + """ + def __init__(self, datapath, datapath_id=None, n_buffers=None, + n_tables=None, auxiliary_id=None, capabilities=None): + super(OFPSwitchFeatures, self).__init__(datapath) + self.datapath_id = datapath_id + self.n_buffers = n_buffers + self.n_tables = n_tables + self.auxiliary_id = auxiliary_id + self.capabilities = capabilities + + @classmethod + def parser(cls, datapath, version, msg_type, msg_len, xid, buf): + msg = super(OFPSwitchFeatures, cls).parser(datapath, version, msg_type, + msg_len, xid, buf) + (msg.datapath_id, + msg.n_buffers, + msg.n_tables, + msg.auxiliary_id, + msg.capabilities, + msg._reserved) = struct.unpack_from( + ofproto.OFP_SWITCH_FEATURES_PACK_STR, msg.buf, + ofproto.OFP_HEADER_SIZE) + return msg + + +@_set_msg_type(ofproto.OFPT_GET_CONFIG_REQUEST) +class OFPGetConfigRequest(MsgBase): + """ + Get config request message + + The controller sends a get config request to query configuration + parameters in the switch. + + Example:: + + def send_get_config_request(self, datapath): + ofp_parser = datapath.ofproto_parser + + req = ofp_parser.OFPGetConfigRequest(datapath) + datapath.send_msg(req) + """ + def __init__(self, datapath): + super(OFPGetConfigRequest, self).__init__(datapath) + + +@_register_parser +@_set_msg_type(ofproto.OFPT_GET_CONFIG_REPLY) +class OFPGetConfigReply(MsgBase): + """ + Get config reply message + + The switch responds to a configuration request with a get config reply + message. + + ============= ========================================================= + Attribute Description + ============= ========================================================= + flags One of the following configuration flags. + OFPC_FRAG_NORMAL + OFPC_FRAG_DROP + OFPC_FRAG_REASM + OFPC_FRAG_MASK + miss_send_len Max bytes of new flow that datapath should send to the + controller + ============= ========================================================= + + Example:: + + @set_ev_cls(ofp_event.EventOFPGetConfigReply, MAIN_DISPATCHER) + def get_config_reply_handler(self, ev): + msg = ev.msg + dp = msg.datapath + ofp = dp.ofproto + + if msg.flags == ofp.OFPC_FRAG_NORMAL: + flags = 'NORMAL' + elif msg.flags == ofp.OFPC_FRAG_DROP: + flags = 'DROP' + elif msg.flags == ofp.OFPC_FRAG_REASM: + flags = 'REASM' + elif msg.flags == ofp.OFPC_FRAG_MASK: + flags = 'MASK' + else: + flags = 'unknown' + self.logger.debug('OFPGetConfigReply received: ' + 'flags=%s miss_send_len=%d', + flags, msg.miss_send_len) + """ + def __init__(self, datapath, flags=None, miss_send_len=None): + super(OFPGetConfigReply, self).__init__(datapath) + self.flags = flags + self.miss_send_len = miss_send_len + + @classmethod + def parser(cls, datapath, version, msg_type, msg_len, xid, buf): + msg = super(OFPGetConfigReply, cls).parser(datapath, version, msg_type, + msg_len, xid, buf) + msg.flags, msg.miss_send_len = struct.unpack_from( + ofproto.OFP_SWITCH_CONFIG_PACK_STR, msg.buf, + ofproto.OFP_HEADER_SIZE) + return msg + + +@_set_msg_type(ofproto.OFPT_SET_CONFIG) +class OFPSetConfig(MsgBase): + """ + Set config request message + + The controller sends a set config request message to set configuraion + parameters. + + ============= ========================================================= + Attribute Description + ============= ========================================================= + flags One of the following configuration flags. + OFPC_FRAG_NORMAL + OFPC_FRAG_DROP + OFPC_FRAG_REASM + OFPC_FRAG_MASK + miss_send_len Max bytes of new flow that datapath should send to the + controller + ============= ========================================================= + + Example:: + + def send_set_config(self, datapath): + ofp = datapath.ofproto + ofp_parser = datapath.ofproto_parser + + req = ofp_parser.OFPSetConfig(datapath, ofp.OFPC_FRAG_NORMAL, 256) + datapath.send_msg(req) + """ + def __init__(self, datapath, flags=0, miss_send_len=0): + super(OFPSetConfig, self).__init__(datapath) + self.flags = flags + self.miss_send_len = miss_send_len + + def _serialize_body(self): + assert self.flags is not None + assert self.miss_send_len is not None + msg_pack_into(ofproto.OFP_SWITCH_CONFIG_PACK_STR, + self.buf, ofproto.OFP_HEADER_SIZE, + self.flags, self.miss_send_len) + + +class OFPMatch(StringifyMixin): + """ + Flow Match Structure + + This class is implementation of the flow match structure having + compose/query API. + + You can define the flow match by the keyword arguments. + The following arguments are available. + + ================ =============== ================================== + Argument Value Description + ================ =============== ================================== + in_port Integer 32bit Switch input port + in_phy_port Integer 32bit Switch physical input port + metadata Integer 64bit Metadata passed between tables + eth_dst MAC address Ethernet destination address + eth_src MAC address Ethernet source address + eth_type Integer 16bit Ethernet frame type + vlan_vid Integer 16bit VLAN id + vlan_pcp Integer 8bit VLAN priority + ip_dscp Integer 8bit IP DSCP (6 bits in ToS field) + ip_ecn Integer 8bit IP ECN (2 bits in ToS field) + ip_proto Integer 8bit IP protocol + ipv4_src IPv4 address IPv4 source address + ipv4_dst IPv4 address IPv4 destination address + tcp_src Integer 16bit TCP source port + tcp_dst Integer 16bit TCP destination port + udp_src Integer 16bit UDP source port + udp_dst Integer 16bit UDP destination port + sctp_src Integer 16bit SCTP source port + sctp_dst Integer 16bit SCTP destination port + icmpv4_type Integer 8bit ICMP type + icmpv4_code Integer 8bit ICMP code + arp_op Integer 16bit ARP opcode + arp_spa IPv4 address ARP source IPv4 address + arp_tpa IPv4 address ARP target IPv4 address + arp_sha MAC address ARP source hardware address + arp_tha MAC address ARP target hardware address + ipv6_src IPv6 address IPv6 source address + ipv6_dst IPv6 address IPv6 destination address + ipv6_flabel Integer 32bit IPv6 Flow Label + icmpv6_type Integer 8bit ICMPv6 type + icmpv6_code Integer 8bit ICMPv6 code + ipv6_nd_target IPv6 address Target address for ND + ipv6_nd_sll MAC address Source link-layer for ND + ipv6_nd_tll MAC address Target link-layer for ND + mpls_label Integer 32bit MPLS label + mpls_tc Integer 8bit MPLS TC + mpls_bos Integer 8bit MPLS BoS bit + pbb_isid Integer 24bit PBB I-SID + tunnel_id Integer 64bit Logical Port Metadata + ipv6_exthdr Integer 16bit IPv6 Extension Header pseudo-field + pbb_uca Integer 8bit PBB UCA header field + ================ =============== ================================== + + Example:: + + >>> # compose + >>> match = parser.OFPMatch( + ... in_port=1, + ... eth_type=0x86dd, + ... ipv6_src=('2001:db8:bd05:1d2:288a:1fc0:1:10ee', + ... 'ffff:ffff:ffff:ffff::'), + ... ipv6_dst='2001:db8:bd05:1d2:288a:1fc0:1:10ee') + >>> # query + >>> if 'ipv6_src' in match: + ... print match['ipv6_src'] + ... + ('2001:db8:bd05:1d2:288a:1fc0:1:10ee', 'ffff:ffff:ffff:ffff::') + """ + + def __init__(self, type_=None, length=None, **kwargs): + super(OFPMatch, self).__init__() + self.type = ofproto.OFPMT_OXM + self.length = length + kwargs = dict(ofproto.oxm_normalize_user(k, v) for + (k, v) in kwargs.iteritems()) + fields = [ofproto.oxm_from_user(k, v) for (k, v) + in kwargs.iteritems()] + fields.sort() + self._fields2 = [ofproto.oxm_to_user(n, v, m) for (n, v, m) + in fields] + + @classmethod + def parser(cls, buf, offset): + """ + Returns an object which is generated from a buffer including the + expression of the wire protocol of the flow match. + """ + match = OFPMatch() + type_, length = struct.unpack_from('!HH', buf, offset) + + match.type = type_ + match.length = length + + # ofp_match adjustment + offset += 4 + length -= 4 + + fields = [] + while length > 0: + n, value, mask, field_len = ofproto.oxm_parse(buf, offset) + k, uv = ofproto.oxm_to_user(n, value, mask) + fields.append((k, uv)) + offset += field_len + length -= field_len + match._fields2 = fields + return match + + def serialize(self, buf, offset): + """ + Outputs the expression of the wire protocol of the flow match into + the buf. + Returns the output length. + """ + fields = [ofproto.oxm_from_user(k, uv) for (k, uv) + in self._fields2] + + hdr_pack_str = '!HH' + field_offset = offset + struct.calcsize(hdr_pack_str) + for (n, value, mask) in fields: + field_offset += ofproto.oxm_serialize(n, value, mask, buf, + field_offset) + + length = field_offset - offset + msg_pack_into(hdr_pack_str, buf, offset, ofproto.OFPMT_OXM, length) + self.length = length + + pad_len = utils.round_up(length, 8) - length + ofproto_parser.msg_pack_into("%dx" % pad_len, buf, field_offset) + + return length + pad_len + + def __getitem__(self, key): + return dict(self._fields2)[key] + + def __contains__(self, key): + return key in dict(self._fields2) + + def iteritems(self): + return dict(self._fields2).iteritems() + + def get(self, key, default=None): + return dict(self._fields2).get(key, default) + + def stringify_attrs(self): + yield "oxm_fields", dict(self._fields2) + + +class OFPPortDescPropUnknown(StringifyMixin): + def __init__(self, type_=None, length=None, buf=None): + self.buf = buf + + @classmethod + def parser(cls, buf): + return cls(buf=buf) + + +class OFPPortProp(StringifyMixin): + _PACK_STR = '!HH' + _TYPES = {} + + def __init__(self, type_, length=None): + self.type = type_ + self.length = length + + @classmethod + def register_type(cls, type_): + def _register_type(subcls): + cls._TYPES[type_] = subcls + return subcls + return _register_type + + @classmethod + def parse(cls, buf): + (type_, length) = struct.unpack_from(cls._PACK_STR, buf, 0) + # needs + rest = buf[utils.round_up(length, 8):] + try: + subcls = cls._TYPES[type_] + except KeyError: + subcls = OFPPortPropUnknown + prop = subcls.parser(buf) + prop.type = type_ + prop.length = length + return prop, rest + + +@OFPPortProp.register_type(ofproto.OFPPDPT_ETHERNET) +class OFPPortDescPropEthernet(StringifyMixin): + def __init__(self, type_=None, length=None, curr=None, advertised=None, + supported=None, peer=None, curr_speed=None, max_speed=None): + self.type = type_ + self.length = length + self.curr = curr + self.advertised = advertised + self.supported = supported + self.peer = peer + self.curr_speed = curr_speed + self.max_speed = max_speed + + @classmethod + def parser(cls, buf): + ether = cls() + (ether.type, ether.length, ether.curr, + ether.advertised, ether.supported, + ether.peer, ether.curr_speed, ether.max_speed) = struct.unpack_from( + ofproto.OFP_PORT_DESC_PROP_ETHERNET_PACK_STR, buf, 0) + return ether + + +@_register_parser +@_set_msg_type(ofproto.OFPT_PACKET_IN) +class OFPPacketIn(MsgBase): + """ + Packet-In message + + The switch sends the packet that received to the controller by this + message. + + ============= ========================================================= + Attribute Description + ============= ========================================================= + buffer_id ID assigned by datapath + total_len Full length of frame + reason Reason packet is being sent. + OFPR_TABLE_MISS + OFPR_APPLY_ACTION + OFPR_INVALID_TTL + OFPR_ACTION_SET + OFPR_GROUP + OFPR_PACKET_OUT + table_id ID of the table that was looked up + cookie Cookie of the flow entry that was looked up + match Instance of ``OFPMatch`` + data Ethernet frame + ============= ========================================================= + + Example:: + + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def packet_in_handler(self, ev): + msg = ev.msg + ofp = dp.ofproto + + if msg.reason == ofp.TABLE_MISS: + reason = 'TABLE MISS' + elif msg.reason == ofp.OFPR_APPLY_ACTION: + reason = 'APPLY ACTION' + elif msg.reason == ofp.OFPR_INVALID_TTL: + reason = 'INVALID TTL' + elif msg.reason == ofp.OFPR_ACTION_SET: + reason = 'ACTION SET' + elif msg.reason == ofp.OFPR_GROUP: + reason = 'GROUP' + elif msg.reason == ofp.OFPR_PACKET_OUT: + reason = 'PACKET OUT' + else: + reason = 'unknown' + + self.logger.debug('OFPPacketIn received: ' + 'buffer_id=%x total_len=%d reason=%s ' + 'table_id=%d cookie=%d match=%s data=%s', + msg.buffer_id, msg.total_len, reason, + msg.table_id, msg.cookie, msg.match, + utils.hex_array(msg.data)) + """ + def __init__(self, datapath, buffer_id=None, total_len=None, reason=None, + table_id=None, cookie=None, match=None, data=None): + super(OFPPacketIn, self).__init__(datapath) + self.buffer_id = buffer_id + self.total_len = total_len + self.reason = reason + self.table_id = table_id + self.cookie = cookie + self.match = match + self.data = data + + @classmethod + def parser(cls, datapath, version, msg_type, msg_len, xid, buf): + msg = super(OFPPacketIn, cls).parser(datapath, version, msg_type, + msg_len, xid, buf) + (msg.buffer_id, msg.total_len, msg.reason, + msg.table_id, msg.cookie) = struct.unpack_from( + ofproto.OFP_PACKET_IN_PACK_STR, + msg.buf, ofproto.OFP_HEADER_SIZE) + + msg.match = OFPMatch.parser(msg.buf, ofproto.OFP_PACKET_IN_SIZE - + ofproto.OFP_MATCH_SIZE) + + match_len = utils.round_up(msg.match.length, 8) + msg.data = msg.buf[(ofproto.OFP_PACKET_IN_SIZE - + ofproto.OFP_MATCH_SIZE + match_len + 2):] + + if msg.total_len < len(msg.data): + # discard padding for 8-byte alignment of OFP packet + msg.data = msg.data[:msg.total_len] + + return msg + + +class OFPPort(StringifyMixin): + _TYPE = { + 'ascii': [ + 'hw_addr', + ], + 'utf-8': [ + # OF spec is unclear about the encoding of name. + # we assumes UTF-8, which is used by OVS. + 'name', + ] + } + + def __init__(self, port_no=None, length=None, hw_addr=None, name=None, + config=None, state=None, properties=None): + super(OFPPort, self).__init__() + self.port_no = port_no + self.length = length + self.hw_addr = hw_addr + self.name = name + self.config = config + self.state = state + self.properties = properties + + @classmethod + def parser(cls, buf, offset): + (port_no, length, hw_addr, name, config, state) = struct.unpack_from( + ofproto.OFP_PORT_PACK_STR, buf, offset) + hw_addr = addrconv.mac.bin_to_text(hw_addr) + name = name.rstrip('\0') + props = [] + rest = buf[offset + ofproto.OFP_PORT_SIZE:offset + length] + while rest: + p, rest = OFPPortProp.parse(rest) + props.append(p) + ofpport = cls(port_no, length, hw_addr, name, config, state, props) + return ofpport + + +def _set_stats_type(stats_type, stats_body_cls): + def _set_cls_stats_type(cls): + cls.cls_stats_type = stats_type + cls.cls_stats_body_cls = stats_body_cls + return cls + return _set_cls_stats_type + + +@_set_msg_type(ofproto.OFPT_MULTIPART_REQUEST) +class OFPMultipartRequest(MsgBase): + def __init__(self, datapath, flags): + super(OFPMultipartRequest, self).__init__(datapath) + self.type = self.__class__.cls_stats_type + self.flags = flags + + def _serialize_stats_body(self): + pass + + def _serialize_body(self): + msg_pack_into(ofproto.OFP_MULTIPART_REQUEST_PACK_STR, + self.buf, ofproto.OFP_HEADER_SIZE, + self.type, self.flags) + self._serialize_stats_body() + + +@_register_parser +@_set_msg_type(ofproto.OFPT_MULTIPART_REPLY) +class OFPMultipartReply(MsgBase): + _STATS_MSG_TYPES = {} + + @staticmethod + def register_stats_type(body_single_struct=False): + def _register_stats_type(cls): + assert cls.cls_stats_type is not None + assert cls.cls_stats_type not in OFPMultipartReply._STATS_MSG_TYPES + assert cls.cls_stats_body_cls is not None + cls.cls_body_single_struct = body_single_struct + OFPMultipartReply._STATS_MSG_TYPES[cls.cls_stats_type] = cls + return cls + return _register_stats_type + + def __init__(self, datapath, body=None, flags=None): + super(OFPMultipartReply, self).__init__(datapath) + self.body = body + self.flags = flags + + @classmethod + def parser_stats_body(cls, buf, msg_len, offset): + body_cls = cls.cls_stats_body_cls + body = [] + while offset < msg_len: + entry = body_cls.parser(buf, offset) + body.append(entry) + offset += entry.length + + if cls.cls_body_single_struct: + return body[0] + return body + + @classmethod + def parser_stats(cls, datapath, version, msg_type, msg_len, xid, buf): + msg = MsgBase.parser.__func__( + cls, datapath, version, msg_type, msg_len, xid, buf) + msg.body = msg.parser_stats_body(msg.buf, msg.msg_len, + ofproto.OFP_MULTIPART_REPLY_SIZE) + return msg + + @classmethod + def parser(cls, datapath, version, msg_type, msg_len, xid, buf): + type_, flags = struct.unpack_from( + ofproto.OFP_MULTIPART_REPLY_PACK_STR, buffer(buf), + ofproto.OFP_HEADER_SIZE) + stats_type_cls = cls._STATS_MSG_TYPES.get(type_) + msg = super(OFPMultipartReply, stats_type_cls).parser( + datapath, version, msg_type, msg_len, xid, buf) + msg.type = type_ + msg.flags = flags + + offset = ofproto.OFP_MULTIPART_REPLY_SIZE + body = [] + while offset < msg_len: + b = stats_type_cls.cls_stats_body_cls.parser(msg.buf, offset) + body.append(b) + offset += b.length if hasattr(b, 'length') else b.len + + if stats_type_cls.cls_body_single_struct: + msg.body = body[0] + else: + msg.body = body + return msg + + +@_set_stats_type(ofproto.OFPMP_PORT_DESC, OFPPort) +@_set_msg_type(ofproto.OFPT_MULTIPART_REQUEST) +class OFPPortDescStatsRequest(OFPMultipartRequest): + """ + Port description request message + + The controller uses this message to query description of all the ports. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + flags Zero or ``OFPMPF_REQ_MORE`` + ================ ====================================================== + + Example:: + + def send_port_desc_stats_request(self, datapath): + ofp_parser = datapath.ofproto_parser + + req = ofp_parser.OFPPortDescStatsRequest(datapath, 0) + datapath.send_msg(req) + """ + def __init__(self, datapath, flags, type_=None): + super(OFPPortDescStatsRequest, self).__init__(datapath, flags) + + +@OFPMultipartReply.register_stats_type() +@_set_stats_type(ofproto.OFPMP_PORT_DESC, OFPPort) +@_set_msg_type(ofproto.OFPT_MULTIPART_REPLY) +class OFPPortDescStatsReply(OFPMultipartReply): + """ + Port description reply message + + The switch responds with this message to a port description request. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + body List of ``OFPPortDescStats`` instance + ================ ====================================================== + + Example:: + + @set_ev_cls(ofp_event.EventOFPPortDescStatsReply, MAIN_DISPATCHER) + def port_desc_stats_reply_handler(self, ev): + ports = [] + for p in ev.msg.body: + ports.append('port_no=%d hw_addr=%s name=%s config=0x%08x ' + 'state=0x%08x properties=%s' % + (p.port_no, p.hw_addr, + p.name, p.config, p.state, repr(p.properties))) + self.logger.debug('OFPPortDescStatsReply received: %s', ports) + """ + def __init__(self, datapath, type_=None, **kwargs): + super(OFPPortDescStatsReply, self).__init__(datapath, **kwargs) + + +class OFPFlowStats(StringifyMixin): + def __init__(self, table_id=None, duration_sec=None, duration_nsec=None, + priority=None, idle_timeout=None, hard_timeout=None, + flags=None, importance=None, cookie=None, packet_count=None, + byte_count=None, match=None, instructions=None, + length=None): + super(OFPFlowStats, self).__init__() + self.length = 0 + self.table_id = table_id + self.duration_sec = duration_sec + self.duration_nsec = duration_nsec + self.priority = priority + self.idle_timeout = idle_timeout + self.hard_timeout = hard_timeout + self.flags = flags + self.importance = importance + self.cookie = cookie + self.packet_count = packet_count + self.byte_count = byte_count + self.match = match + self.instructions = instructions + + @classmethod + def parser(cls, buf, offset): + flow_stats = cls() + + (flow_stats.length, flow_stats.table_id, + flow_stats.duration_sec, flow_stats.duration_nsec, + flow_stats.priority, flow_stats.idle_timeout, + flow_stats.hard_timeout, flow_stats.flags, + flow_stats.importance, flow_stats.cookie, + flow_stats.packet_count, + flow_stats.byte_count) = struct.unpack_from( + ofproto.OFP_FLOW_STATS_0_PACK_STR, buf, offset) + offset += ofproto.OFP_FLOW_STATS_0_SIZE + + flow_stats.match = OFPMatch.parser(buf, offset) + match_length = utils.round_up(flow_stats.match.length, 8) + inst_length = (flow_stats.length - (ofproto.OFP_FLOW_STATS_SIZE - + ofproto.OFP_MATCH_SIZE + + match_length)) + offset += match_length + instructions = [] + while inst_length > 0: + inst = OFPInstruction.parser(buf, offset) + instructions.append(inst) + offset += inst.len + inst_length -= inst.len + + flow_stats.instructions = instructions + return flow_stats + + +class OFPFlowStatsRequestBase(OFPMultipartRequest): + def __init__(self, datapath, flags, table_id, out_port, out_group, + cookie, cookie_mask, match): + super(OFPFlowStatsRequestBase, self).__init__(datapath, flags) + self.table_id = table_id + self.out_port = out_port + self.out_group = out_group + self.cookie = cookie + self.cookie_mask = cookie_mask + self.match = match + + def _serialize_stats_body(self): + offset = ofproto.OFP_MULTIPART_REQUEST_SIZE + msg_pack_into(ofproto.OFP_FLOW_STATS_REQUEST_0_PACK_STR, + self.buf, offset, self.table_id, self.out_port, + self.out_group, self.cookie, self.cookie_mask) + + offset += ofproto.OFP_FLOW_STATS_REQUEST_0_SIZE + self.match.serialize(self.buf, offset) + + +@_set_stats_type(ofproto.OFPMP_FLOW, OFPFlowStats) +@_set_msg_type(ofproto.OFPT_MULTIPART_REQUEST) +class OFPFlowStatsRequest(OFPFlowStatsRequestBase): + """ + Individual flow statistics request message + + The controller uses this message to query individual flow statistics. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + flags Zero or ``OFPMPF_REQ_MORE`` + table_id ID of table to read + out_port Require matching entries to include this as an output + port + out_group Require matching entries to include this as an output + group + cookie Require matching entries to contain this cookie value + cookie_mask Mask used to restrict the cookie bits that must match + match Instance of ``OFPMatch`` + ================ ====================================================== + + Example:: + + def send_flow_stats_request(self, datapath): + ofp = datapath.ofproto + ofp_parser = datapath.ofproto_parser + + cookie = cookie_mask = 0 + match = ofp_parser.OFPMatch(in_port=1) + req = ofp_parser.OFPFlowStatsRequest(datapath, 0, + ofp.OFPTT_ALL, + ofp.OFPP_ANY, ofp.OFPG_ANY, + cookie, cookie_mask, + match) + datapath.send_msg(req) + """ + def __init__(self, datapath, flags=0, table_id=ofproto.OFPTT_ALL, + out_port=ofproto.OFPP_ANY, + out_group=ofproto.OFPG_ANY, + cookie=0, cookie_mask=0, match=None, type_=None): + if match is None: + match = OFPMatch() + super(OFPFlowStatsRequest, self).__init__(datapath, flags, table_id, + out_port, out_group, + cookie, cookie_mask, match) + + +@OFPMultipartReply.register_stats_type() +@_set_stats_type(ofproto.OFPMP_FLOW, OFPFlowStats) +@_set_msg_type(ofproto.OFPT_MULTIPART_REPLY) +class OFPFlowStatsReply(OFPMultipartReply): + """ + Individual flow statistics reply message + + The switch responds with this message to an individual flow statistics + request. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + body List of ``OFPFlowStats`` instance + ================ ====================================================== + + Example:: + + @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) + def flow_stats_reply_handler(self, ev): + flows = [] + for stat in ev.msg.body: + flows.append('table_id=%s ' + 'duration_sec=%d duration_nsec=%d ' + 'priority=%d ' + 'idle_timeout=%d hard_timeout=%d flags=0x%04x ' + 'importance=%d cookie=%d packet_count=%d ' + 'byte_count=%d match=%s instructions=%s' % + (stat.table_id, + stat.duration_sec, stat.duration_nsec, + stat.priority, + stat.idle_timeout, stat.hard_timeout, + stat.flags, stat.importance, + stat.cookie, stat.packet_count, stat.byte_count, + stat.match, stat.instructions)) + self.logger.debug('FlowStats: %s', flows) + """ + def __init__(self, datapath, type_=None, **kwargs): + super(OFPFlowStatsReply, self).__init__(datapath, **kwargs) + + +@_set_msg_type(ofproto.OFPT_PACKET_OUT) +class OFPPacketOut(MsgBase): + """ + Packet-Out message + + The controller uses this message to send a packet out throught the + switch. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + buffer_id ID assigned by datapath (OFP_NO_BUFFER if none) + in_port Packet's input port or ``OFPP_CONTROLLER`` + actions list of OpenFlow action class + data Packet data + ================ ====================================================== + + Example:: + + def send_packet_out(self, datapath, buffer_id, in_port): + ofp = datapath.ofproto + ofp_parser = datapath.ofproto_parser + + actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD, 0)] + req = ofp_parser.OFPPacketOut(datapath, buffer_id, + in_port, actions) + datapath.send_msg(req) + """ + def __init__(self, datapath, buffer_id=None, in_port=None, actions=None, + data=None, actions_len=None): + assert in_port is not None + + super(OFPPacketOut, self).__init__(datapath) + self.buffer_id = buffer_id + self.in_port = in_port + self.actions_len = 0 + self.actions = actions + self.data = data + + def _serialize_body(self): + self.actions_len = 0 + offset = ofproto.OFP_PACKET_OUT_SIZE + for a in self.actions: + a.serialize(self.buf, offset) + offset += a.len + self.actions_len += a.len + + if self.data is not None: + assert self.buffer_id == 0xffffffff + self.buf += self.data + + msg_pack_into(ofproto.OFP_PACKET_OUT_PACK_STR, + self.buf, ofproto.OFP_HEADER_SIZE, + self.buffer_id, self.in_port, self.actions_len) + + +@_set_msg_type(ofproto.OFPT_FLOW_MOD) +class OFPFlowMod(MsgBase): + """ + Modify Flow entry message + + The controller sends this message to modify the flow table. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + cookie Opaque controller-issued identifier + cookie_mask Mask used to restrict the cookie bits that must match + when the command is ``OPFFC_MODIFY*`` or + ``OFPFC_DELETE*`` + table_id ID of the table to put the flow in + command One of the following values. + OFPFC_ADD + OFPFC_MODIFY + OFPFC_MODIFY_STRICT + OFPFC_DELETE + OFPFC_DELETE_STRICT + idle_timeout Idle time before discarding (seconds) + hard_timeout Max time before discarding (seconds) + priority Priority level of flow entry + buffer_id Buffered packet to apply to (or OFP_NO_BUFFER) + out_port For ``OFPFC_DELETE*`` commands, require matching + entries to include this as an output port + out_group For ``OFPFC_DELETE*`` commands, require matching + entries to include this as an output group + flags One of the following values. + OFPFF_SEND_FLOW_REM + OFPFF_CHECK_OVERLAP + OFPFF_RESET_COUNTS + OFPFF_NO_PKT_COUNTS + OFPFF_NO_BYT_COUNTS + importance Eviction precedence + match Instance of ``OFPMatch`` + instructions list of ``OFPInstruction*`` instance + ================ ====================================================== + + Example:: + + def send_flow_mod(self, datapath): + ofp = datapath.ofproto + ofp_parser = datapath.ofproto_parser + + cookie = cookie_mask = 0 + table_id = 0 + idle_timeout = hard_timeout = 0 + priority = 32768 + buffer_id = ofp.OFP_NO_BUFFER + importance = 0 + match = ofp_parser.OFPMatch(in_port=1, eth_dst='ff:ff:ff:ff:ff:ff') + actions = [ofp_parser.OFPActionOutput(ofp.OFPP_NORMAL, 0)] + inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, + actions)] + req = ofp_parser.OFPFlowMod(datapath, cookie, cookie_mask, + table_id, ofp.OFPFC_ADD, + idle_timeout, hard_timeout, + priority, buffer_id, + ofp.OFPP_ANY, ofp.OFPG_ANY, + ofp.OFPFF_SEND_FLOW_REM, + imporotance, + match, inst) + datapath.send_msg(req) + """ + def __init__(self, datapath, cookie=0, cookie_mask=0, table_id=0, + command=ofproto.OFPFC_ADD, + idle_timeout=0, hard_timeout=0, + priority=ofproto.OFP_DEFAULT_PRIORITY, + buffer_id=ofproto.OFP_NO_BUFFER, + out_port=0, out_group=0, flags=0, importance=0, + match=None, + instructions=[]): + super(OFPFlowMod, self).__init__(datapath) + self.cookie = cookie + self.cookie_mask = cookie_mask + self.table_id = table_id + 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.out_group = out_group + self.flags = flags + self.importance = importance + if match is None: + match = OFPMatch() + self.match = match + self.instructions = instructions + + def _serialize_body(self): + msg_pack_into(ofproto.OFP_FLOW_MOD_PACK_STR0, self.buf, + ofproto.OFP_HEADER_SIZE, + self.cookie, self.cookie_mask, self.table_id, + self.command, self.idle_timeout, self.hard_timeout, + self.priority, self.buffer_id, self.out_port, + self.out_group, self.flags, self.importance) + + offset = (ofproto.OFP_FLOW_MOD_SIZE - + ofproto.OFP_MATCH_SIZE) + + match_len = self.match.serialize(self.buf, offset) + offset += match_len + + for inst in self.instructions: + inst.serialize(self.buf, offset) + offset += inst.len + + +class OFPInstruction(object): + _INSTRUCTION_TYPES = {} + + @staticmethod + def register_instruction_type(types): + def _register_instruction_type(cls): + for type_ in types: + OFPInstruction._INSTRUCTION_TYPES[type_] = cls + return cls + return _register_instruction_type + + @classmethod + def parser(cls, buf, offset): + (type_, len_) = struct.unpack_from('!HH', buf, offset) + cls_ = cls._INSTRUCTION_TYPES.get(type_) + return cls_.parser(buf, offset) + + +@OFPInstruction.register_instruction_type([ofproto.OFPIT_GOTO_TABLE]) +class OFPInstructionGotoTable(StringifyMixin): + """ + Goto table instruction + + This instruction indicates the next table in the processing pipeline. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + table_id Next table + ================ ====================================================== + """ + def __init__(self, table_id, type_=None, len_=None): + super(OFPInstructionGotoTable, self).__init__() + self.type = ofproto.OFPIT_GOTO_TABLE + self.len = ofproto.OFP_INSTRUCTION_GOTO_TABLE_SIZE + self.table_id = table_id + + @classmethod + def parser(cls, buf, offset): + (type_, len_, table_id) = struct.unpack_from( + ofproto.OFP_INSTRUCTION_GOTO_TABLE_PACK_STR, + buf, offset) + return cls(table_id) + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_INSTRUCTION_GOTO_TABLE_PACK_STR, + buf, offset, self.type, self.len, self.table_id) + + +@OFPInstruction.register_instruction_type([ofproto.OFPIT_WRITE_METADATA]) +class OFPInstructionWriteMetadata(StringifyMixin): + """ + Write metadata instruction + + This instruction writes the masked metadata value into the metadata field. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + metadata Metadata value to write + metadata_mask Metadata write bitmask + ================ ====================================================== + """ + def __init__(self, metadata, metadata_mask, len_=None): + super(OFPInstructionWriteMetadata, self).__init__() + self.type = ofproto.OFPIT_WRITE_METADATA + self.len = ofproto.OFP_INSTRUCTION_WRITE_METADATA_SIZE + self.metadata = metadata + self.metadata_mask = metadata_mask + + @classmethod + def parser(cls, buf, offset): + (type_, len_, metadata, metadata_mask) = struct.unpack_from( + ofproto.OFP_INSTRUCTION_WRITE_METADATA_PACK_STR, + buf, offset) + return cls(metadata, metadata_mask) + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_INSTRUCTION_WRITE_METADATA_PACK_STR, + buf, offset, self.type, self.len, self.metadata, + self.metadata_mask) + + +@OFPInstruction.register_instruction_type([ofproto.OFPIT_WRITE_ACTIONS, + ofproto.OFPIT_APPLY_ACTIONS, + ofproto.OFPIT_CLEAR_ACTIONS]) +class OFPInstructionActions(StringifyMixin): + """ + Actions instruction + + This instruction writes/applies/clears the actions. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + type One of following values. + OFPIT_WRITE_ACTIONS + OFPIT_APPLY_ACTIONS + OFPIT_CLEAR_ACTIONS + actions list of OpenFlow action class + ================ ====================================================== + + ``type`` attribute corresponds to ``type_`` parameter of __init__. + """ + def __init__(self, type_, actions=None, len_=None): + super(OFPInstructionActions, self).__init__() + self.type = type_ + self.actions = actions + + @classmethod + def parser(cls, buf, offset): + (type_, len_) = struct.unpack_from( + ofproto.OFP_INSTRUCTION_ACTIONS_PACK_STR, + buf, offset) + + offset += ofproto.OFP_INSTRUCTION_ACTIONS_SIZE + actions = [] + actions_len = len_ - ofproto.OFP_INSTRUCTION_ACTIONS_SIZE + while actions_len > 0: + a = OFPAction.parser(buf, offset) + actions.append(a) + actions_len -= a.len + offset += a.len + + inst = cls(type_, actions) + inst.len = len_ + return inst + + def serialize(self, buf, offset): + action_offset = offset + ofproto.OFP_INSTRUCTION_ACTIONS_SIZE + if self.actions: + for a in self.actions: + a.serialize(buf, action_offset) + action_offset += a.len + + self.len = action_offset - offset + pad_len = utils.round_up(self.len, 8) - self.len + ofproto_parser.msg_pack_into("%dx" % pad_len, buf, action_offset) + self.len += pad_len + + msg_pack_into(ofproto.OFP_INSTRUCTION_ACTIONS_PACK_STR, + buf, offset, self.type, self.len) + + +@OFPInstruction.register_instruction_type([ofproto.OFPIT_METER]) +class OFPInstructionMeter(StringifyMixin): + """ + Meter instruction + + This instruction applies the meter. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + meter_id Meter instance + ================ ====================================================== + """ + _base_attributes = ['type', 'len'] + + def __init__(self, meter_id): + super(OFPInstructionMeter, self).__init__() + self.type = ofproto.OFPIT_METER + self.len = ofproto.OFP_INSTRUCTION_METER_SIZE + self.meter_id = meter_id + + @classmethod + def parser(cls, buf, offset): + (type_, len_, meter_id) = struct.unpack_from( + ofproto.OFP_INSTRUCTION_METER_PACK_STR, + buf, offset) + return cls(meter_id) + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_INSTRUCTION_METER_PACK_STR, + buf, offset, self.type, self.len, self.meter_id) + + +class OFPActionHeader(StringifyMixin): + def __init__(self, type_, len_): + self.type = type_ + self.len = len_ + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_ACTION_HEADER_PACK_STR, + buf, offset, self.type, self.len) + + +class OFPAction(OFPActionHeader): + _ACTION_TYPES = {} + + @staticmethod + def register_action_type(type_, len_): + def _register_action_type(cls): + cls.cls_action_type = type_ + cls.cls_action_len = len_ + OFPAction._ACTION_TYPES[cls.cls_action_type] = cls + return cls + return _register_action_type + + def __init__(self): + cls = self.__class__ + super(OFPAction, self).__init__(cls.cls_action_type, + cls.cls_action_len) + + @classmethod + def parser(cls, buf, offset): + type_, len_ = struct.unpack_from( + ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset) + cls_ = cls._ACTION_TYPES.get(type_) + assert cls_ is not None + return cls_.parser(buf, offset) + + +@OFPAction.register_action_type(ofproto.OFPAT_OUTPUT, + ofproto.OFP_ACTION_OUTPUT_SIZE) +class OFPActionOutput(OFPAction): + """ + Output action + + This action indicates output a packet to the switch port. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + port Output port + max_len Max length to send to controller + ================ ====================================================== + """ + def __init__(self, port, max_len=ofproto.OFPCML_MAX, + type_=None, len_=None): + super(OFPActionOutput, self).__init__() + self.port = port + self.max_len = max_len + + @classmethod + def parser(cls, buf, offset): + type_, len_, port, max_len = struct.unpack_from( + ofproto.OFP_ACTION_OUTPUT_PACK_STR, buf, offset) + return cls(port, max_len) + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_ACTION_OUTPUT_PACK_STR, buf, + offset, self.type, self.len, self.port, self.max_len) + + +@OFPAction.register_action_type(ofproto.OFPAT_GROUP, + ofproto.OFP_ACTION_GROUP_SIZE) +class OFPActionGroup(OFPAction): + """ + Group action + + This action indicates the group used to process the packet. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + group_id Group identifier + ================ ====================================================== + """ + def __init__(self, group_id, type_=None, len_=None): + super(OFPActionGroup, self).__init__() + self.group_id = group_id + + @classmethod + def parser(cls, buf, offset): + (type_, len_, group_id) = struct.unpack_from( + ofproto.OFP_ACTION_GROUP_PACK_STR, buf, offset) + return cls(group_id) + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_ACTION_GROUP_PACK_STR, buf, + offset, self.type, self.len, self.group_id) + + +@OFPAction.register_action_type(ofproto.OFPAT_SET_QUEUE, + ofproto.OFP_ACTION_SET_QUEUE_SIZE) +class OFPActionSetQueue(OFPAction): + """ + Set queue action + + This action sets the queue id that will be used to map a flow to an + already-configured queue on a port. + + ================ ====================================================== + Attribute Description + ================ ====================================================== + queue_id Queue ID for the packets + ================ ====================================================== + """ + def __init__(self, queue_id, type_=None, len_=None): + super(OFPActionSetQueue, self).__init__() + self.queue_id = queue_id + + @classmethod + def parser(cls, buf, offset): + (type_, len_, queue_id) = struct.unpack_from( + ofproto.OFP_ACTION_SET_QUEUE_PACK_STR, buf, offset) + return cls(queue_id) + + def serialize(self, buf, offset): + msg_pack_into(ofproto.OFP_ACTION_SET_QUEUE_PACK_STR, buf, + offset, self.type, self.len, self.queue_id) diff --git a/ryu/tests/unit/ofproto/test_ofproto.py b/ryu/tests/unit/ofproto/test_ofproto.py index f9546637..9ab5eef5 100644 --- a/ryu/tests/unit/ofproto/test_ofproto.py +++ b/ryu/tests/unit/ofproto/test_ofproto.py @@ -44,21 +44,26 @@ class TestOfprotCommon(unittest.TestCase): import ryu.ofproto.ofproto_v1_0 import ryu.ofproto.ofproto_v1_2 import ryu.ofproto.ofproto_v1_3 + import ryu.ofproto.ofproto_v1_4 eq_(set(ofp_modules.keys()), set([ryu.ofproto.ofproto_v1_0.OFP_VERSION, ryu.ofproto.ofproto_v1_2.OFP_VERSION, ryu.ofproto.ofproto_v1_3.OFP_VERSION, + ryu.ofproto.ofproto_v1_4.OFP_VERSION, ])) consts_mods = set([ofp_mod[0] for ofp_mod in ofp_modules.values()]) eq_(consts_mods, set([ryu.ofproto.ofproto_v1_0, ryu.ofproto.ofproto_v1_2, ryu.ofproto.ofproto_v1_3, + ryu.ofproto.ofproto_v1_4, ])) parser_mods = set([ofp_mod[1] for ofp_mod in ofp_modules.values()]) import ryu.ofproto.ofproto_v1_0_parser import ryu.ofproto.ofproto_v1_2_parser import ryu.ofproto.ofproto_v1_3_parser + import ryu.ofproto.ofproto_v1_4_parser eq_(parser_mods, set([ryu.ofproto.ofproto_v1_0_parser, ryu.ofproto.ofproto_v1_2_parser, ryu.ofproto.ofproto_v1_3_parser, + ryu.ofproto.ofproto_v1_4_parser, ])) |