abb436deb3
This patch also removes some unneeded imported libraries. Story: #2010182 Task: #45863 Change-Id: Ife9d349eb9c92f2a39719e76e82fe20c010fe230
3304 lines
112 KiB
Python
3304 lines
112 KiB
Python
# Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
|
|
# Copyright (C) 2011, 2012 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.
|
|
|
|
"""
|
|
Decoder/Encoder implementations of OpenFlow 1.0.
|
|
"""
|
|
|
|
import struct
|
|
import base64
|
|
|
|
import netaddr
|
|
|
|
from os_ken.ofproto.ofproto_parser import StringifyMixin, MsgBase
|
|
from os_ken.lib import addrconv
|
|
from os_ken.lib import ip
|
|
from os_ken.lib import mac
|
|
from os_ken.lib.packet import packet
|
|
from os_ken.lib.pack_utils import msg_pack_into
|
|
from os_ken.ofproto import nx_match
|
|
from os_ken.ofproto import ofproto_common
|
|
from os_ken.ofproto import ofproto_parser
|
|
from os_ken.ofproto import ofproto_v1_0 as ofproto
|
|
from os_ken.ofproto import nx_actions
|
|
from os_ken import utils
|
|
|
|
import logging
|
|
LOG = logging.getLogger('os_ken.ofproto.ofproto_v1_0_parser')
|
|
|
|
_MSG_PARSERS = {}
|
|
|
|
|
|
def _set_msg_type(msg_type):
|
|
'''Annotate corresponding OFP message 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)
|
|
|
|
|
|
# OFP_MSG_REPLY = {
|
|
# OFPFeaturesRequest: OFPSwitchFeatures,
|
|
# OFPBarrierRequest: OFPBarrierReply,
|
|
# OFPQueueGetConfigRequest: OFPQueueGetConfigReply,
|
|
#
|
|
# # ofp_stats_request -> ofp_stats_reply
|
|
# OFPDescStatsRequest: OFPDescStatsReply,
|
|
# OFPFlowStatsRequest: OFPFlowStatsReply,
|
|
# OFPAggregateStatsRequest: OFPAggregateStatsReply,
|
|
# OFPTableStatsRequest: OFPTableStatsReply,
|
|
# OFPPortStatsRequest: OFPPortStatsReply,
|
|
# OFPQueueStatsRequest: OFPQueueStatsReply,
|
|
# OFPVendorStatsRequest: OFPVendorStatsReply,
|
|
# }
|
|
def _set_msg_reply(msg_reply):
|
|
'''Annotate OFP reply message class'''
|
|
def _set_cls_msg_reply(cls):
|
|
cls.cls_msg_reply = msg_reply
|
|
return cls
|
|
return _set_cls_msg_reply
|
|
|
|
|
|
#
|
|
# common structures
|
|
#
|
|
|
|
class OFPPhyPort(ofproto_parser.namedtuple('OFPPhyPort', (
|
|
'port_no', 'hw_addr', 'name', 'config', 'state', 'curr', 'advertised',
|
|
'supported', 'peer'))):
|
|
"""
|
|
Description of a port
|
|
|
|
========== =========================================================
|
|
Attribute Description
|
|
========== =========================================================
|
|
port_no Port number and it uniquely identifies a port within
|
|
a switch.
|
|
hw_addr MAC address for the port.
|
|
name Null-terminated string containing a human-readable name
|
|
for the interface.
|
|
config Bitmap of port configuration flags.
|
|
|
|
| OFPPC_PORT_DOWN
|
|
| OFPPC_NO_STP
|
|
| OFPPC_NO_RECV
|
|
| OFPPC_NO_RECV_STP
|
|
| OFPPC_NO_FLOOD
|
|
| OFPPC_NO_FWD
|
|
| OFPPC_NO_PACKET_IN
|
|
state Bitmap of port state flags.
|
|
|
|
| OFPPS_LINK_DOWN
|
|
| OFPPS_STP_LISTEN
|
|
| OFPPS_STP_LEARN
|
|
| OFPPS_STP_FORWARD
|
|
| OFPPS_STP_BLOCK
|
|
| OFPPS_STP_MASK
|
|
curr Current features.
|
|
advertised Features being advertised by the port.
|
|
supported Features supported by the port.
|
|
peer Features advertised by peer.
|
|
========== =========================================================
|
|
"""
|
|
_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',
|
|
]
|
|
}
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
port = struct.unpack_from(ofproto.OFP_PHY_PORT_PACK_STR,
|
|
buf, offset)
|
|
port = list(port)
|
|
i = cls._fields.index('hw_addr')
|
|
port[i] = addrconv.mac.bin_to_text(port[i])
|
|
i = cls._fields.index('name')
|
|
port[i] = port[i].rstrip(b'\0')
|
|
return cls(*port)
|
|
|
|
|
|
class OFPMatch(StringifyMixin):
|
|
"""
|
|
Flow Match Structure
|
|
|
|
This class is implementation of the flow match structure having
|
|
compose/query API.
|
|
|
|
================ ==================================================
|
|
Attribute Description
|
|
================ ==================================================
|
|
wildcards Wildcard fields.
|
|
(match fields) For the available match fields,
|
|
please refer to the following.
|
|
================ ==================================================
|
|
|
|
================ =============== ==================================
|
|
Argument Value Description
|
|
================ =============== ==================================
|
|
in_port Integer 16bit Switch input port.
|
|
dl_src MAC address Ethernet source address.
|
|
dl_dst MAC address Ethernet destination address.
|
|
dl_vlan Integer 16bit Input VLAN id.
|
|
dl_vlan_pcp Integer 8bit Input VLAN priority.
|
|
dl_type Integer 16bit Ethernet frame type.
|
|
nw_tos Integer 8bit IP ToS (actually DSCP field, 6 bits).
|
|
nw_proto Integer 8bit IP protocol or lower 8 bits of
|
|
ARP opcode.
|
|
nw_src IPv4 address IP source address.
|
|
nw_dst IPv4 address IP destination address.
|
|
tp_src Integer 16bit TCP/UDP source port.
|
|
tp_dst Integer 16bit TCP/UDP destination port.
|
|
nw_src_mask Integer 8bit IP source address mask
|
|
specified as IPv4 address prefix.
|
|
nw_dst_mask Integer 8bit IP destination address mask
|
|
specified as IPv4 address prefix.
|
|
================ =============== ==================================
|
|
|
|
Example::
|
|
|
|
>>> # compose
|
|
>>> match = parser.OFPMatch(
|
|
... in_port=1,
|
|
... dl_type=0x0800,
|
|
... dl_src='aa:bb:cc:dd:ee:ff',
|
|
... nw_src='192.168.0.1')
|
|
>>> # query
|
|
>>> if 'nw_src' in match:
|
|
... print match['nw_src']
|
|
...
|
|
'192.168.0.1'
|
|
"""
|
|
|
|
def __init__(self, wildcards=None, in_port=None, dl_src=None, dl_dst=None,
|
|
dl_vlan=None, dl_vlan_pcp=None, dl_type=None, nw_tos=None,
|
|
nw_proto=None, nw_src=None, nw_dst=None,
|
|
tp_src=None, tp_dst=None, nw_src_mask=32, nw_dst_mask=32):
|
|
super(OFPMatch, self).__init__()
|
|
wc = ofproto.OFPFW_ALL
|
|
if in_port is None:
|
|
self.in_port = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_IN_PORT
|
|
self.in_port = in_port
|
|
|
|
if dl_src is None:
|
|
self.dl_src = mac.DONTCARE
|
|
else:
|
|
wc &= ~ofproto.OFPFW_DL_SRC
|
|
if (isinstance(dl_src, (str, str)) and
|
|
netaddr.valid_mac(dl_src)):
|
|
dl_src = addrconv.mac.text_to_bin(dl_src)
|
|
if dl_src == 0:
|
|
self.dl_src = mac.DONTCARE
|
|
else:
|
|
self.dl_src = dl_src
|
|
|
|
if dl_dst is None:
|
|
self.dl_dst = mac.DONTCARE
|
|
else:
|
|
wc &= ~ofproto.OFPFW_DL_DST
|
|
if (isinstance(dl_dst, (str, str)) and
|
|
netaddr.valid_mac(dl_dst)):
|
|
dl_dst = addrconv.mac.text_to_bin(dl_dst)
|
|
if dl_dst == 0:
|
|
self.dl_dst = mac.DONTCARE
|
|
else:
|
|
self.dl_dst = dl_dst
|
|
|
|
if dl_vlan is None:
|
|
self.dl_vlan = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_DL_VLAN
|
|
self.dl_vlan = dl_vlan
|
|
|
|
if dl_vlan_pcp is None:
|
|
self.dl_vlan_pcp = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_DL_VLAN_PCP
|
|
self.dl_vlan_pcp = dl_vlan_pcp
|
|
|
|
if dl_type is None:
|
|
self.dl_type = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_DL_TYPE
|
|
self.dl_type = dl_type
|
|
|
|
if nw_tos is None:
|
|
self.nw_tos = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_NW_TOS
|
|
self.nw_tos = nw_tos
|
|
|
|
if nw_proto is None:
|
|
self.nw_proto = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_NW_PROTO
|
|
self.nw_proto = nw_proto
|
|
|
|
if nw_src is None:
|
|
self.nw_src = 0
|
|
else:
|
|
wc &= (32 - nw_src_mask) << ofproto.OFPFW_NW_SRC_SHIFT \
|
|
| ~ofproto.OFPFW_NW_SRC_MASK
|
|
if not isinstance(nw_src, int):
|
|
nw_src = ip.ipv4_to_int(nw_src)
|
|
self.nw_src = nw_src
|
|
|
|
if nw_dst is None:
|
|
self.nw_dst = 0
|
|
else:
|
|
wc &= (32 - nw_dst_mask) << ofproto.OFPFW_NW_DST_SHIFT \
|
|
| ~ofproto.OFPFW_NW_DST_MASK
|
|
if not isinstance(nw_dst, int):
|
|
nw_dst = ip.ipv4_to_int(nw_dst)
|
|
self.nw_dst = nw_dst
|
|
|
|
if tp_src is None:
|
|
self.tp_src = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_TP_SRC
|
|
self.tp_src = tp_src
|
|
|
|
if tp_dst is None:
|
|
self.tp_dst = 0
|
|
else:
|
|
wc &= ~ofproto.OFPFW_TP_DST
|
|
self.tp_dst = tp_dst
|
|
|
|
if wildcards is None:
|
|
self.wildcards = wc
|
|
else:
|
|
self.wildcards = wildcards
|
|
|
|
def __getitem__(self, name):
|
|
if not isinstance(name, str):
|
|
raise KeyError(name)
|
|
elif name == 'nw_src_mask':
|
|
_m = 32 - ((self.wildcards & ofproto.OFPFW_NW_SRC_MASK) >>
|
|
ofproto.OFPFW_NW_SRC_SHIFT)
|
|
return 0 if _m < 0 else _m
|
|
elif name == 'nw_dst_mask':
|
|
_m = 32 - ((self.wildcards & ofproto.OFPFW_NW_DST_MASK) >>
|
|
ofproto.OFPFW_NW_DST_SHIFT)
|
|
return 0 if _m < 0 else _m
|
|
elif name == 'wildcards':
|
|
return self.wildcards
|
|
|
|
wc = getattr(ofproto, 'OFPFW_' + name.upper(), 0)
|
|
if ~self.wildcards & wc:
|
|
value = getattr(self, name)
|
|
if name in ['dl_src', 'dl_dst']:
|
|
value = addrconv.mac.bin_to_text(value)
|
|
elif name in ['nw_src', 'nw_dst']:
|
|
value = ip.ipv4_to_str(value)
|
|
return value
|
|
else:
|
|
raise KeyError(name)
|
|
|
|
def __contains__(self, name):
|
|
wc = getattr(ofproto, 'OFPFW_' + name.upper(), 0)
|
|
return ~self.wildcards & wc
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_MATCH_PACK_STR, buf, offset,
|
|
self.wildcards, self.in_port, self.dl_src,
|
|
self.dl_dst, self.dl_vlan, self.dl_vlan_pcp,
|
|
self.dl_type, self.nw_tos, self.nw_proto,
|
|
self.nw_src, self.nw_dst, self.tp_src, self.tp_dst)
|
|
|
|
@classmethod
|
|
def parse(cls, buf, offset):
|
|
match = struct.unpack_from(ofproto.OFP_MATCH_PACK_STR,
|
|
buf, offset)
|
|
return cls(*match)
|
|
|
|
def to_jsondict(self):
|
|
fields = {}
|
|
# copy values to avoid original values conversion
|
|
for k, v in self.__dict__.items():
|
|
if k in ['dl_src', 'dl_dst']:
|
|
fields[k] = addrconv.mac.bin_to_text(v)
|
|
elif k in ['nw_src', 'nw_dst']:
|
|
fields[k] = ip.ipv4_to_str(v)
|
|
else:
|
|
fields[k] = v
|
|
return {self.__class__.__name__: fields}
|
|
|
|
@classmethod
|
|
def from_jsondict(cls, dict_):
|
|
return cls(**dict_)
|
|
|
|
|
|
class OFPActionHeader(StringifyMixin):
|
|
_base_attributes = ['type', 'len']
|
|
|
|
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.
|
|
================ ======================================================
|
|
|
|
Note::
|
|
The reason of this magic number (0xffe5)
|
|
is because there is no good constant in of1.0.
|
|
The same value as OFPCML_MAX of of1.2 and of1.3 is used.
|
|
"""
|
|
|
|
def __init__(self, port, max_len=0xffe5):
|
|
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)
|
|
assert type_ == ofproto.OFPAT_OUTPUT
|
|
assert len_ == ofproto.OFP_ACTION_OUTPUT_SIZE
|
|
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_SET_VLAN_VID,
|
|
ofproto.OFP_ACTION_VLAN_VID_SIZE)
|
|
class OFPActionVlanVid(OFPAction):
|
|
"""
|
|
Set the 802.1q VLAN id action
|
|
|
|
This action indicates the 802.1q VLAN id to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
vlan_vid VLAN id.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, vlan_vid):
|
|
super(OFPActionVlanVid, self).__init__()
|
|
self.vlan_vid = vlan_vid
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, vlan_vid = struct.unpack_from(
|
|
ofproto.OFP_ACTION_VLAN_VID_PACK_STR, buf, offset)
|
|
assert type_ == ofproto.OFPAT_SET_VLAN_VID
|
|
assert len_ == ofproto.OFP_ACTION_VLAN_VID_SIZE
|
|
return cls(vlan_vid)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_VLAN_VID_PACK_STR,
|
|
buf, offset, self.type, self.len, self.vlan_vid)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_VLAN_PCP,
|
|
ofproto.OFP_ACTION_VLAN_PCP_SIZE)
|
|
class OFPActionVlanPcp(OFPAction):
|
|
"""
|
|
Set the 802.1q priority action
|
|
|
|
This action indicates the 802.1q priority to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
vlan_pcp VLAN priority.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, vlan_pcp):
|
|
super(OFPActionVlanPcp, self).__init__()
|
|
self.vlan_pcp = vlan_pcp
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, vlan_pcp = struct.unpack_from(
|
|
ofproto.OFP_ACTION_VLAN_PCP_PACK_STR, buf, offset)
|
|
assert type_ == ofproto.OFPAT_SET_VLAN_PCP
|
|
assert len_ == ofproto.OFP_ACTION_VLAN_PCP_SIZE
|
|
return cls(vlan_pcp)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_VLAN_PCP_PACK_STR,
|
|
buf, offset, self.type, self.len, self.vlan_pcp)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_STRIP_VLAN,
|
|
ofproto.OFP_ACTION_HEADER_SIZE)
|
|
class OFPActionStripVlan(OFPAction):
|
|
"""
|
|
Strip the 802.1q header action
|
|
|
|
This action indicates the 802.1q priority to be striped.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(OFPActionStripVlan, self).__init__()
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_ = struct.unpack_from(
|
|
ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset)
|
|
assert type_ == ofproto.OFPAT_STRIP_VLAN
|
|
assert len_ == ofproto.OFP_ACTION_HEADER_SIZE
|
|
return cls()
|
|
|
|
|
|
class OFPActionDlAddr(OFPAction):
|
|
def __init__(self, dl_addr):
|
|
super(OFPActionDlAddr, self).__init__()
|
|
if (isinstance(dl_addr, (str, str)) and
|
|
netaddr.valid_mac(dl_addr)):
|
|
dl_addr = addrconv.mac.text_to_bin(dl_addr)
|
|
self.dl_addr = dl_addr
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, dl_addr = struct.unpack_from(
|
|
ofproto.OFP_ACTION_DL_ADDR_PACK_STR, buf, offset)
|
|
assert type_ in (ofproto.OFPAT_SET_DL_SRC,
|
|
ofproto.OFPAT_SET_DL_DST)
|
|
assert len_ == ofproto.OFP_ACTION_DL_ADDR_SIZE
|
|
return cls(dl_addr)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_DL_ADDR_PACK_STR,
|
|
buf, offset, self.type, self.len, self.dl_addr)
|
|
|
|
def to_jsondict(self):
|
|
body = {"dl_addr": addrconv.mac.bin_to_text(self.dl_addr)}
|
|
return {self.__class__.__name__: body}
|
|
|
|
@classmethod
|
|
def from_jsondict(cls, dict_):
|
|
return cls(**dict_)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_DL_SRC,
|
|
ofproto.OFP_ACTION_DL_ADDR_SIZE)
|
|
class OFPActionSetDlSrc(OFPActionDlAddr):
|
|
"""
|
|
Set the ethernet source address action
|
|
|
|
This action indicates the ethernet source address to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
dl_addr Ethernet address.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, dl_addr):
|
|
super(OFPActionSetDlSrc, self).__init__(dl_addr)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_DL_DST,
|
|
ofproto.OFP_ACTION_DL_ADDR_SIZE)
|
|
class OFPActionSetDlDst(OFPActionDlAddr):
|
|
"""
|
|
Set the ethernet destination address action
|
|
|
|
This action indicates the ethernet destination address to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
dl_addr Ethernet address.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, dl_addr):
|
|
super(OFPActionSetDlDst, self).__init__(dl_addr)
|
|
|
|
|
|
class OFPActionNwAddr(OFPAction):
|
|
def __init__(self, nw_addr):
|
|
super(OFPActionNwAddr, self).__init__()
|
|
if not isinstance(nw_addr, int):
|
|
nw_addr = ip.ipv4_to_int(nw_addr)
|
|
self.nw_addr = nw_addr
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, nw_addr = struct.unpack_from(
|
|
ofproto.OFP_ACTION_NW_ADDR_PACK_STR, buf, offset)
|
|
assert type_ in (ofproto.OFPAT_SET_NW_SRC,
|
|
ofproto.OFPAT_SET_NW_DST)
|
|
assert len_ == ofproto.OFP_ACTION_NW_ADDR_SIZE
|
|
return cls(nw_addr)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_NW_ADDR_PACK_STR,
|
|
buf, offset, self.type, self.len, self.nw_addr)
|
|
|
|
def to_jsondict(self):
|
|
body = {"nw_addr": ip.ipv4_to_str(self.nw_addr)}
|
|
return {self.__class__.__name__: body}
|
|
|
|
@classmethod
|
|
def from_jsondict(cls, dict_):
|
|
return cls(**dict_)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_NW_SRC,
|
|
ofproto.OFP_ACTION_NW_ADDR_SIZE)
|
|
class OFPActionSetNwSrc(OFPActionNwAddr):
|
|
"""
|
|
Set the IP source address action
|
|
|
|
This action indicates the IP source address to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
nw_addr IP address.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, nw_addr):
|
|
super(OFPActionSetNwSrc, self).__init__(nw_addr)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_NW_DST,
|
|
ofproto.OFP_ACTION_NW_ADDR_SIZE)
|
|
class OFPActionSetNwDst(OFPActionNwAddr):
|
|
"""
|
|
Set the IP destination address action
|
|
|
|
This action indicates the IP destination address to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
nw_addr IP address.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, nw_addr):
|
|
super(OFPActionSetNwDst, self).__init__(nw_addr)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_NW_TOS,
|
|
ofproto.OFP_ACTION_NW_TOS_SIZE)
|
|
class OFPActionSetNwTos(OFPAction):
|
|
"""
|
|
Set the IP ToS action
|
|
|
|
This action indicates the IP ToS (DSCP field, 6 bits) to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
tos IP ToS (DSCP field, 6 bits).
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, tos):
|
|
super(OFPActionSetNwTos, self).__init__()
|
|
self.tos = tos
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, tos = struct.unpack_from(
|
|
ofproto.OFP_ACTION_NW_TOS_PACK_STR, buf, offset)
|
|
assert type_ == ofproto.OFPAT_SET_NW_TOS
|
|
assert len_ == ofproto.OFP_ACTION_NW_TOS_SIZE
|
|
return cls(tos)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_NW_TOS_PACK_STR,
|
|
buf, offset, self.type, self.len, self.tos)
|
|
|
|
|
|
class OFPActionTpPort(OFPAction):
|
|
def __init__(self, tp):
|
|
super(OFPActionTpPort, self).__init__()
|
|
self.tp = tp
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, tp = struct.unpack_from(
|
|
ofproto.OFP_ACTION_TP_PORT_PACK_STR, buf, offset)
|
|
assert type_ in (ofproto.OFPAT_SET_TP_SRC,
|
|
ofproto.OFPAT_SET_TP_DST)
|
|
assert len_ == ofproto.OFP_ACTION_TP_PORT_SIZE
|
|
return cls(tp)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_TP_PORT_PACK_STR,
|
|
buf, offset, self.type, self.len, self.tp)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_TP_SRC,
|
|
ofproto.OFP_ACTION_TP_PORT_SIZE)
|
|
class OFPActionSetTpSrc(OFPActionTpPort):
|
|
"""
|
|
Set the TCP/UDP source port action
|
|
|
|
This action indicates the TCP/UDP source port to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
tp TCP/UDP port.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, tp):
|
|
super(OFPActionSetTpSrc, self).__init__(tp)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_SET_TP_DST,
|
|
ofproto.OFP_ACTION_TP_PORT_SIZE)
|
|
class OFPActionSetTpDst(OFPActionTpPort):
|
|
"""
|
|
Set the TCP/UDP destination port action
|
|
|
|
This action indicates the TCP/UDP destination port to be set.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
tp TCP/UDP port.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, tp):
|
|
super(OFPActionSetTpDst, self).__init__(tp)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_ENQUEUE,
|
|
ofproto.OFP_ACTION_ENQUEUE_SIZE)
|
|
class OFPActionEnqueue(OFPAction):
|
|
"""
|
|
Output to queue action
|
|
|
|
This action indicates send packets to given queue on port.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
port Port that queue belongs.
|
|
queue_id Where to enqueue the packets.
|
|
================ ======================================================
|
|
"""
|
|
|
|
def __init__(self, port, queue_id):
|
|
super(OFPActionEnqueue, self).__init__()
|
|
self.port = port
|
|
self.queue_id = queue_id
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, port, queue_id = struct.unpack_from(
|
|
ofproto.OFP_ACTION_ENQUEUE_PACK_STR, buf, offset)
|
|
assert type_ == ofproto.OFPAT_ENQUEUE
|
|
assert len_ == ofproto.OFP_ACTION_ENQUEUE_SIZE
|
|
return cls(port, queue_id)
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_ENQUEUE_PACK_STR, buf, offset,
|
|
self.type, self.len, self.port, self.queue_id)
|
|
|
|
|
|
@OFPAction.register_action_type(ofproto.OFPAT_VENDOR, 0)
|
|
class OFPActionVendor(OFPAction):
|
|
"""
|
|
Vendor action
|
|
|
|
This action is an extensible action for the vendor.
|
|
"""
|
|
_ACTION_VENDORS = {}
|
|
|
|
@staticmethod
|
|
def register_action_vendor(vendor):
|
|
def _register_action_vendor(cls):
|
|
cls.cls_vendor = vendor
|
|
OFPActionVendor._ACTION_VENDORS[cls.cls_vendor] = cls
|
|
return cls
|
|
return _register_action_vendor
|
|
|
|
def __init__(self, vendor=None):
|
|
super(OFPActionVendor, self).__init__()
|
|
self.type = ofproto.OFPAT_VENDOR
|
|
self.len = None
|
|
|
|
if vendor is None:
|
|
self.vendor = self.cls_vendor
|
|
else:
|
|
self.vendor = vendor
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, vendor = struct.unpack_from(
|
|
ofproto.OFP_ACTION_VENDOR_HEADER_PACK_STR, buf, offset)
|
|
|
|
data = buf[(offset + ofproto.OFP_ACTION_VENDOR_HEADER_SIZE
|
|
): offset + len_]
|
|
|
|
if vendor == ofproto_common.NX_EXPERIMENTER_ID:
|
|
obj = NXAction.parse(data) # noqa
|
|
else:
|
|
cls_ = cls._ACTION_VENDORS.get(vendor, None)
|
|
|
|
if cls_ is None:
|
|
obj = OFPActionVendorUnknown(vendor, data)
|
|
else:
|
|
obj = cls_.parser(buf, offset)
|
|
|
|
obj.len = len_
|
|
return obj
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_VENDOR_HEADER_PACK_STR,
|
|
buf, offset, self.type, self.len, self.vendor)
|
|
|
|
|
|
# OpenFlow1.2 or later compatible
|
|
OFPActionExperimenter = OFPActionVendor
|
|
|
|
|
|
class OFPActionVendorUnknown(OFPActionVendor):
|
|
def __init__(self, vendor, data=None, type_=None, len_=None):
|
|
super(OFPActionVendorUnknown,
|
|
self).__init__(vendor=vendor)
|
|
self.data = data
|
|
|
|
def serialize(self, buf, offset):
|
|
# fixup
|
|
data = self.data
|
|
if data is None:
|
|
data = bytearray()
|
|
self.len = (utils.round_up(len(data), 8) +
|
|
ofproto.OFP_ACTION_VENDOR_HEADER_SIZE)
|
|
super(OFPActionVendorUnknown, self).serialize(buf, offset)
|
|
msg_pack_into('!%ds' % len(self.data),
|
|
buf,
|
|
offset + ofproto.OFP_ACTION_VENDOR_HEADER_SIZE,
|
|
self.data)
|
|
|
|
|
|
@OFPActionVendor.register_action_vendor(ofproto_common.NX_EXPERIMENTER_ID)
|
|
class NXActionHeader(OFPActionVendor):
|
|
_NX_ACTION_SUBTYPES = {}
|
|
|
|
@staticmethod
|
|
def register_nx_action_subtype(subtype, len_):
|
|
def _register_nx_action_subtype(cls):
|
|
cls.cls_action_len = len_
|
|
cls.cls_subtype = subtype
|
|
NXActionHeader._NX_ACTION_SUBTYPES[cls.cls_subtype] = cls
|
|
return cls
|
|
return _register_nx_action_subtype
|
|
|
|
def __init__(self):
|
|
super(NXActionHeader, self).__init__()
|
|
self.subtype = self.cls_subtype
|
|
|
|
def serialize(self, buf, offset):
|
|
msg_pack_into(ofproto.OFP_ACTION_HEADER_PACK_STR,
|
|
buf, offset, self.type, self.len)
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
type_, len_, vendor, subtype = struct.unpack_from(
|
|
ofproto.NX_ACTION_HEADER_PACK_STR, buf, offset)
|
|
cls_ = cls._NX_ACTION_SUBTYPES.get(subtype)
|
|
return cls_.parser(buf, offset)
|
|
|
|
|
|
class OFPDescStats(ofproto_parser.namedtuple('OFPDescStats', (
|
|
'mfr_desc', 'hw_desc', 'sw_desc', 'serial_num', 'dp_desc'))):
|
|
|
|
_TYPE = {
|
|
'ascii': [
|
|
'mfr_desc',
|
|
'hw_desc',
|
|
'sw_desc',
|
|
'serial_num',
|
|
'dp_desc',
|
|
]
|
|
}
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
desc = struct.unpack_from(ofproto.OFP_DESC_STATS_PACK_STR,
|
|
buf, offset)
|
|
desc = list(desc)
|
|
desc = [x.rstrip(b'\0') for x in desc]
|
|
stats = cls(*desc)
|
|
stats.length = ofproto.OFP_DESC_STATS_SIZE
|
|
return stats
|
|
|
|
|
|
class OFPFlowStats(StringifyMixin):
|
|
def __init__(self):
|
|
super(OFPFlowStats, self).__init__()
|
|
self.length = None
|
|
self.table_id = None
|
|
self.match = None
|
|
self.duration_sec = None
|
|
self.duration_nsec = None
|
|
self.priority = None
|
|
self.idle_timeout = None
|
|
self.hard_timeout = None
|
|
self.cookie = None
|
|
self.packet_count = None
|
|
self.byte_count = None
|
|
self.actions = None
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
flow_stats = cls()
|
|
|
|
flow_stats.length, flow_stats.table_id = struct.unpack_from(
|
|
ofproto.OFP_FLOW_STATS_0_PACK_STR, buf, offset)
|
|
offset += ofproto.OFP_FLOW_STATS_0_SIZE
|
|
|
|
flow_stats.match = OFPMatch.parse(buf, offset)
|
|
offset += ofproto.OFP_MATCH_SIZE
|
|
|
|
(flow_stats.duration_sec,
|
|
flow_stats.duration_nsec,
|
|
flow_stats.priority,
|
|
flow_stats.idle_timeout,
|
|
flow_stats.hard_timeout,
|
|
flow_stats.cookie,
|
|
flow_stats.packet_count,
|
|
flow_stats.byte_count) = struct.unpack_from(
|
|
ofproto.OFP_FLOW_STATS_1_PACK_STR, buf, offset)
|
|
offset += ofproto.OFP_FLOW_STATS_1_SIZE
|
|
|
|
flow_stats.actions = []
|
|
length = ofproto.OFP_FLOW_STATS_SIZE
|
|
while length < flow_stats.length:
|
|
action = OFPAction.parser(buf, offset)
|
|
flow_stats.actions.append(action)
|
|
|
|
offset += action.len
|
|
length += action.len
|
|
|
|
return flow_stats
|
|
|
|
|
|
class OFPAggregateStats(ofproto_parser.namedtuple('OFPAggregateStats', (
|
|
'packet_count', 'byte_count', 'flow_count'))):
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
agg = struct.unpack_from(
|
|
ofproto.OFP_AGGREGATE_STATS_REPLY_PACK_STR, buf, offset)
|
|
stats = cls(*agg)
|
|
stats.length = ofproto.OFP_AGGREGATE_STATS_REPLY_SIZE
|
|
return stats
|
|
|
|
|
|
class OFPTableStats(ofproto_parser.namedtuple('OFPTableStats', (
|
|
'table_id', 'name', 'wildcards', 'max_entries', 'active_count',
|
|
'lookup_count', 'matched_count'))):
|
|
|
|
_TYPE = {
|
|
'utf-8': [
|
|
# OF spec is unclear about the encoding of name.
|
|
# we assumes UTF-8.
|
|
'name',
|
|
]
|
|
}
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
tbl = struct.unpack_from(ofproto.OFP_TABLE_STATS_PACK_STR,
|
|
buf, offset)
|
|
tbl = list(tbl)
|
|
i = cls._fields.index('name')
|
|
tbl[i] = tbl[i].rstrip(b'\0')
|
|
stats = cls(*tbl)
|
|
stats.length = ofproto.OFP_TABLE_STATS_SIZE
|
|
return stats
|
|
|
|
|
|
class OFPPortStats(ofproto_parser.namedtuple('OFPPortStats', (
|
|
'port_no', 'rx_packets', 'tx_packets', 'rx_bytes', 'tx_bytes',
|
|
'rx_dropped', 'tx_dropped', 'rx_errors', 'tx_errors',
|
|
'rx_frame_err', 'rx_over_err', 'rx_crc_err', 'collisions'))):
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
port = struct.unpack_from(ofproto.OFP_PORT_STATS_PACK_STR,
|
|
buf, offset)
|
|
stats = cls(*port)
|
|
stats.length = ofproto.OFP_PORT_STATS_SIZE
|
|
return stats
|
|
|
|
|
|
class OFPQueueStats(ofproto_parser.namedtuple('OFPQueueStats', (
|
|
'port_no', 'queue_id', 'tx_bytes', 'tx_packets', 'tx_errors'))):
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
queue = struct.unpack_from(ofproto.OFP_QUEUE_STATS_PACK_STR,
|
|
buf, offset)
|
|
stats = cls(*queue)
|
|
stats.length = ofproto.OFP_QUEUE_STATS_SIZE
|
|
return stats
|
|
|
|
|
|
class OFPVendorStats(ofproto_parser.namedtuple('OFPVendorStats',
|
|
('specific_data'))):
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
stats = cls(buf[offset:])
|
|
stats.length = len(stats.specific_data)
|
|
return stats
|
|
|
|
|
|
class NXFlowStats(StringifyMixin):
|
|
def __init__(self):
|
|
super(NXFlowStats, self).__init__()
|
|
self.length = None
|
|
self.table_id = None
|
|
self.duration_sec = None
|
|
self.duration_nsec = None
|
|
self.priority = None
|
|
self.idle_timeout = None
|
|
self.hard_timeout = None
|
|
self.match_len = None
|
|
self.idle_age = None
|
|
self.hard_age = None
|
|
self.cookie = None
|
|
self.packet_count = None
|
|
self.byte_count = None
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
original_offset = offset
|
|
nxflow_stats = cls()
|
|
(nxflow_stats.length, nxflow_stats.table_id,
|
|
nxflow_stats.duration_sec, nxflow_stats.duration_nsec,
|
|
nxflow_stats.priority, nxflow_stats.idle_timeout,
|
|
nxflow_stats.hard_timeout, nxflow_stats.match_len,
|
|
nxflow_stats.idle_age, nxflow_stats.hard_age,
|
|
nxflow_stats.cookie, nxflow_stats.packet_count,
|
|
nxflow_stats.byte_count) = struct.unpack_from(
|
|
ofproto.NX_FLOW_STATS_PACK_STR, buf, offset)
|
|
offset += ofproto.NX_FLOW_STATS_SIZE
|
|
|
|
fields = []
|
|
match_len = nxflow_stats.match_len
|
|
match_len -= 4
|
|
while match_len > 0:
|
|
field = nx_match.MFField.parser(buf, offset)
|
|
offset += field.length
|
|
match_len -= field.length
|
|
fields.append(field)
|
|
nxflow_stats.fields = fields
|
|
|
|
actions = []
|
|
total_len = original_offset + nxflow_stats.length
|
|
match_len = nxflow_stats.match_len
|
|
offset += utils.round_up(match_len, 8) - match_len
|
|
while offset < total_len:
|
|
action = OFPAction.parser(buf, offset)
|
|
actions.append(action)
|
|
offset += action.len
|
|
nxflow_stats.actions = actions
|
|
|
|
return nxflow_stats
|
|
|
|
|
|
class NXAggregateStats(ofproto_parser.namedtuple('NXAggregateStats', (
|
|
'packet_count', 'byte_count', 'flow_count'))):
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
agg = struct.unpack_from(
|
|
ofproto.NX_AGGREGATE_STATS_REPLY_PACK_STR, buf, offset)
|
|
stats = cls(*agg)
|
|
stats.length = ofproto.NX_AGGREGATE_STATS_REPLY_SIZE
|
|
|
|
return stats
|
|
|
|
|
|
class OFPQueuePropHeader(StringifyMixin):
|
|
_QUEUE_PROPERTIES = {}
|
|
|
|
@staticmethod
|
|
def register_queue_property(prop_type, prop_len):
|
|
def _register_queue_propery(cls):
|
|
cls.cls_prop_type = prop_type
|
|
cls.cls_prop_len = prop_len
|
|
OFPQueuePropHeader._QUEUE_PROPERTIES[prop_type] = cls
|
|
return cls
|
|
return _register_queue_propery
|
|
|
|
def __init__(self):
|
|
self.property = self.cls_prop_type
|
|
self.len = self.cls_prop_len
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
property_, len_ = struct.unpack_from(
|
|
ofproto.OFP_QUEUE_PROP_HEADER_PACK_STR, buf, offset)
|
|
prop_cls = cls._QUEUE_PROPERTIES[property_]
|
|
assert property_ == prop_cls.cls_prop_type
|
|
assert len_ == prop_cls.cls_prop_len
|
|
|
|
offset += ofproto.OFP_QUEUE_PROP_HEADER_SIZE
|
|
return prop_cls.parser(buf, offset)
|
|
|
|
|
|
@OFPQueuePropHeader.register_queue_property(
|
|
ofproto.OFPQT_NONE, ofproto.OFP_QUEUE_PROP_HEADER_SIZE)
|
|
class OFPQueuePropNone(OFPQueuePropHeader):
|
|
def __init__(self):
|
|
super(OFPQueuePropNone, self).__init__()
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
return cls()
|
|
|
|
|
|
@OFPQueuePropHeader.register_queue_property(
|
|
ofproto.OFPQT_MIN_RATE, ofproto.OFP_QUEUE_PROP_MIN_RATE_SIZE)
|
|
class OFPQueuePropMinRate(OFPQueuePropHeader):
|
|
def __init__(self, rate):
|
|
super(OFPQueuePropMinRate, self).__init__()
|
|
self.rate = rate
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
(rate,) = struct.unpack_from(
|
|
ofproto.OFP_QUEUE_PROP_MIN_RATE_PACK_STR,
|
|
buf, offset)
|
|
return cls(rate)
|
|
|
|
|
|
class OFPPacketQueue(StringifyMixin):
|
|
"""
|
|
Description of a queue
|
|
|
|
========== =========================================================
|
|
Attribute Description
|
|
========== =========================================================
|
|
queue_id ID for the specific queue.
|
|
len Length in bytes of this queue desc.
|
|
properties List of ``OFPQueueProp*`` instance.
|
|
========== =========================================================
|
|
"""
|
|
|
|
def __init__(self, queue_id, len_):
|
|
self.queue_id = queue_id
|
|
self.len = len_
|
|
self.properties = None
|
|
|
|
@classmethod
|
|
def parser(cls, buf, offset):
|
|
queue_id, len_ = struct.unpack_from(
|
|
ofproto.OFP_PACKET_QUEUE_PQCK_STR, buf, offset)
|
|
packet_queue = cls(queue_id, len_)
|
|
|
|
packet_queue.properties = []
|
|
cur_len = ofproto.OFP_PACKET_QUEUE_SIZE
|
|
offset += ofproto.OFP_PACKET_QUEUE_SIZE
|
|
while (cur_len + ofproto.OFP_QUEUE_PROP_HEADER_SIZE <=
|
|
packet_queue.len):
|
|
prop = OFPQueuePropHeader.parser(buf, offset)
|
|
packet_queue.properties.append(prop)
|
|
|
|
cur_len += prop.len
|
|
offset += prop.len
|
|
|
|
return packet_queue
|
|
|
|
#
|
|
# Symmetric messages
|
|
# parser + serializer
|
|
#
|
|
|
|
|
|
@_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 OSKen framework, so the OSKen application
|
|
do not need to process this typically.
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPHello, self).__init__(datapath)
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_ERROR)
|
|
class OFPErrorMsg(MsgBase):
|
|
"""
|
|
Error message
|
|
|
|
The switch notifies controller of problems by this message.
|
|
|
|
========== =========================================================
|
|
Attribute Description
|
|
========== =========================================================
|
|
type High level type of error
|
|
code Details depending on the type
|
|
data Variable length data depending on the type and code
|
|
========== =========================================================
|
|
|
|
``type`` attribute corresponds to ``type_`` parameter of __init__.
|
|
|
|
Types and codes are defined in ``os_ken.ofproto.ofproto``.
|
|
|
|
=========================== ===========
|
|
Type Code
|
|
=========================== ===========
|
|
OFPET_HELLO_FAILED OFPHFC_*
|
|
OFPET_BAD_REQUEST OFPBRC_*
|
|
OFPET_BAD_ACTION OFPBAC_*
|
|
OFPET_FLOW_MOD_FAILED OFPFMFC_*
|
|
OFPET_PORT_MOD_FAILED OFPPMFC_*
|
|
OFPET_QUEUE_OP_FAILED OFPQOFC_*
|
|
=========================== ===========
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPErrorMsg,
|
|
[HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
|
|
def error_msg_handler(self, ev):
|
|
msg = ev.msg
|
|
|
|
self.logger.debug('OFPErrorMsg received: type=0x%02x code=0x%02x '
|
|
'message=%s',
|
|
msg.type, msg.code, utils.hex_array(msg.data))
|
|
"""
|
|
|
|
def __init__(self, datapath, type_=None, code=None, data=None):
|
|
super(OFPErrorMsg, self).__init__(datapath)
|
|
self.type = type_
|
|
self.code = code
|
|
if isinstance(data, str):
|
|
data = data.encode('ascii')
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPErrorMsg, cls).parser(datapath, version, msg_type,
|
|
msg_len, xid, buf)
|
|
msg.type, msg.code = struct.unpack_from(
|
|
ofproto.OFP_ERROR_MSG_PACK_STR, msg.buf,
|
|
ofproto.OFP_HEADER_SIZE)
|
|
msg.data = msg.buf[ofproto.OFP_ERROR_MSG_SIZE:]
|
|
return msg
|
|
|
|
def _serialize_body(self):
|
|
assert self.data is not None
|
|
msg_pack_into(ofproto.OFP_ERROR_MSG_PACK_STR, self.buf,
|
|
ofproto.OFP_HEADER_SIZE, self.type, self.code)
|
|
self.buf += self.data
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_ECHO_REQUEST)
|
|
class OFPEchoRequest(MsgBase):
|
|
"""
|
|
Echo request message
|
|
|
|
This message is handled by the OSKen framework, so the OSKen application
|
|
do not need to process this typically.
|
|
|
|
========== =========================================================
|
|
Attribute Description
|
|
========== =========================================================
|
|
data An arbitrary length data.
|
|
========== =========================================================
|
|
|
|
Example::
|
|
|
|
def send_echo_request(self, datapath, data):
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPEchoRequest(datapath, data)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, data=None):
|
|
super(OFPEchoRequest, self).__init__(datapath)
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPEchoRequest, cls).parser(datapath, version, msg_type,
|
|
msg_len, xid, buf)
|
|
msg.data = msg.buf[ofproto.OFP_HEADER_SIZE:]
|
|
return msg
|
|
|
|
def _serialize_body(self):
|
|
if self.data is not None:
|
|
self.buf += self.data
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_ECHO_REPLY)
|
|
class OFPEchoReply(MsgBase):
|
|
"""
|
|
Echo reply message
|
|
|
|
This message is handled by the OSKen framework, so the OSKen application
|
|
do not need to process this typically.
|
|
|
|
========== =========================================================
|
|
Attribute Description
|
|
========== =========================================================
|
|
data An arbitrary length data.
|
|
========== =========================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPEchoReply,
|
|
[HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
|
|
def echo_reply_handler(self, ev):
|
|
self.logger.debug('OFPEchoReply received: data=%s',
|
|
utils.hex_array(ev.msg.data))
|
|
"""
|
|
|
|
def __init__(self, datapath, data=None):
|
|
super(OFPEchoReply, self).__init__(datapath)
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPEchoReply, cls).parser(datapath, version, msg_type,
|
|
msg_len, xid, buf)
|
|
msg.data = msg.buf[ofproto.OFP_HEADER_SIZE:]
|
|
return msg
|
|
|
|
def _serialize_body(self):
|
|
assert self.data is not None
|
|
self.buf += self.data
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_VENDOR)
|
|
class OFPVendor(MsgBase):
|
|
"""
|
|
Vendor message
|
|
|
|
The controller send this message to send the vendor-specific
|
|
information to a switch.
|
|
"""
|
|
_VENDORS = {}
|
|
|
|
@staticmethod
|
|
def register_vendor(id_):
|
|
def _register_vendor(cls):
|
|
OFPVendor._VENDORS[id_] = cls
|
|
return cls
|
|
return _register_vendor
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPVendor, self).__init__(datapath)
|
|
self.data = None
|
|
self.vendor = None
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPVendor, cls).parser(datapath, version, msg_type,
|
|
msg_len, xid, buf)
|
|
(msg.vendor,) = struct.unpack_from(
|
|
ofproto.OFP_VENDOR_HEADER_PACK_STR, msg.buf,
|
|
ofproto.OFP_HEADER_SIZE)
|
|
|
|
cls_ = cls._VENDORS.get(msg.vendor)
|
|
if cls_:
|
|
msg.data = cls_.parser(datapath, msg.buf, 0)
|
|
else:
|
|
msg.data = msg.buf[ofproto.OFP_VENDOR_HEADER_SIZE:]
|
|
|
|
return msg
|
|
|
|
def serialize_header(self):
|
|
msg_pack_into(ofproto.OFP_VENDOR_HEADER_PACK_STR,
|
|
self.buf, ofproto.OFP_HEADER_SIZE, self.vendor)
|
|
|
|
def _serialize_body(self):
|
|
assert self.data is not None
|
|
self.serialize_header()
|
|
self.buf += self.data
|
|
|
|
|
|
@OFPVendor.register_vendor(ofproto_common.NX_EXPERIMENTER_ID)
|
|
class NiciraHeader(OFPVendor):
|
|
_NX_SUBTYPES = {}
|
|
|
|
@staticmethod
|
|
def register_nx_subtype(subtype):
|
|
def _register_nx_subtype(cls):
|
|
cls.cls_subtype = subtype
|
|
NiciraHeader._NX_SUBTYPES[cls.cls_subtype] = cls
|
|
return cls
|
|
return _register_nx_subtype
|
|
|
|
def __init__(self, datapath, subtype):
|
|
super(NiciraHeader, self).__init__(datapath)
|
|
self.vendor = ofproto_common.NX_EXPERIMENTER_ID
|
|
self.subtype = subtype
|
|
|
|
def serialize_header(self):
|
|
super(NiciraHeader, self).serialize_header()
|
|
msg_pack_into(ofproto.NICIRA_HEADER_PACK_STR,
|
|
self.buf, ofproto.OFP_HEADER_SIZE,
|
|
self.vendor, self.subtype)
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, buf, offset):
|
|
vendor, subtype = struct.unpack_from(
|
|
ofproto.NICIRA_HEADER_PACK_STR, buf,
|
|
offset + ofproto.OFP_HEADER_SIZE)
|
|
cls_ = cls._NX_SUBTYPES.get(subtype)
|
|
return cls_.parser(datapath, buf,
|
|
offset + ofproto.NICIRA_HEADER_SIZE)
|
|
|
|
|
|
class NXTSetFlowFormat(NiciraHeader):
|
|
def __init__(self, datapath, flow_format):
|
|
super(NXTSetFlowFormat, self).__init__(
|
|
datapath, ofproto.NXT_SET_FLOW_FORMAT)
|
|
self.format = flow_format
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
msg_pack_into(ofproto.NX_SET_FLOW_FORMAT_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE, self.format)
|
|
|
|
|
|
class NXTFlowMod(NiciraHeader):
|
|
def __init__(self, datapath, cookie, command,
|
|
idle_timeout=0, hard_timeout=0,
|
|
priority=ofproto.OFP_DEFAULT_PRIORITY,
|
|
buffer_id=0xffffffff, out_port=ofproto.OFPP_NONE,
|
|
flags=0, rule=None, actions=None):
|
|
|
|
# the argument, rule, is positioned at the one before the last due
|
|
# to the layout struct nxt_flow_mod.
|
|
# Although rule must be given, default argument to rule, None,
|
|
# is given to allow other default value of argument before rule.
|
|
assert rule is not None
|
|
|
|
if actions is None:
|
|
actions = []
|
|
super(NXTFlowMod, self).__init__(datapath, ofproto.NXT_FLOW_MOD)
|
|
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.rule = rule
|
|
self.actions = actions
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
|
|
offset = ofproto.NX_FLOW_MOD_SIZE
|
|
match_len = nx_match.serialize_nxm_match(self.rule, self.buf, offset)
|
|
offset += nx_match.round_up(match_len)
|
|
|
|
msg_pack_into(ofproto.NX_FLOW_MOD_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE,
|
|
self.cookie, self.command, self.idle_timeout,
|
|
self.hard_timeout, self.priority, self.buffer_id,
|
|
self.out_port, self.flags, match_len)
|
|
|
|
if self.actions is not None:
|
|
for a in self.actions:
|
|
a.serialize(self.buf, offset)
|
|
offset += a.len
|
|
|
|
|
|
class NXTRoleRequest(NiciraHeader):
|
|
def __init__(self, datapath, role):
|
|
super(NXTRoleRequest, self).__init__(
|
|
datapath, ofproto.NXT_ROLE_REQUEST)
|
|
self.role = role
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
msg_pack_into(ofproto.NX_ROLE_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE, self.role)
|
|
|
|
|
|
@NiciraHeader.register_nx_subtype(ofproto.NXT_ROLE_REPLY)
|
|
class NXTRoleReply(NiciraHeader):
|
|
def __init__(self, datapath, role):
|
|
super(NXTRoleReply, self).__init__(
|
|
datapath, ofproto.NXT_ROLE_REPLY)
|
|
self.role = role
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, buf, offset):
|
|
(role,) = struct.unpack_from(
|
|
ofproto.NX_ROLE_PACK_STR, buf, offset)
|
|
return cls(datapath, role)
|
|
|
|
|
|
class NXTFlowModTableId(NiciraHeader):
|
|
def __init__(self, datapath, set_):
|
|
super(NXTFlowModTableId, self).__init__(
|
|
datapath, ofproto.NXT_FLOW_MOD_TABLE_ID)
|
|
self.set = set_
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
msg_pack_into(ofproto.NX_FLOW_MOD_TABLE_ID_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE,
|
|
self.set)
|
|
|
|
|
|
@NiciraHeader.register_nx_subtype(ofproto.NXT_FLOW_REMOVED)
|
|
class NXTFlowRemoved(NiciraHeader):
|
|
def __init__(self, datapath, cookie, priority, reason,
|
|
duration_sec, duration_nsec, idle_timeout, match_len,
|
|
packet_count, byte_count, match):
|
|
super(NXTFlowRemoved, self).__init__(
|
|
datapath, ofproto.NXT_FLOW_REMOVED)
|
|
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.match_len = match_len
|
|
self.packet_count = packet_count
|
|
self.byte_count = byte_count
|
|
self.match = match
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, buf, offset):
|
|
(cookie, priority, reason, duration_sec, duration_nsec,
|
|
idle_timeout, match_len,
|
|
packet_count, byte_count) = struct.unpack_from(
|
|
ofproto.NX_FLOW_REMOVED_PACK_STR, buf, offset)
|
|
offset += (ofproto.NX_FLOW_REMOVED_SIZE -
|
|
ofproto.NICIRA_HEADER_SIZE)
|
|
match = nx_match.NXMatch.parser(buf, offset, match_len)
|
|
return cls(datapath, cookie, priority, reason, duration_sec,
|
|
duration_nsec, idle_timeout, match_len, packet_count,
|
|
byte_count, match)
|
|
|
|
|
|
class NXTSetPacketInFormat(NiciraHeader):
|
|
def __init__(self, datapath, packet_in_format):
|
|
super(NXTSetPacketInFormat, self).__init__(
|
|
datapath, ofproto.NXT_SET_PACKET_IN_FORMAT)
|
|
self.format = packet_in_format
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
msg_pack_into(ofproto.NX_SET_PACKET_IN_FORMAT_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE,
|
|
self.format)
|
|
|
|
|
|
@NiciraHeader.register_nx_subtype(ofproto.NXT_PACKET_IN)
|
|
class NXTPacketIn(NiciraHeader):
|
|
def __init__(self, datapath, buffer_id, total_len, reason, table_id,
|
|
cookie, match_len, match, frame):
|
|
super(NXTPacketIn, self).__init__(
|
|
datapath, ofproto.NXT_PACKET_IN)
|
|
self.buffer_id = buffer_id
|
|
self.total_len = total_len
|
|
self.reason = reason
|
|
self.table_id = table_id
|
|
self.cookie = cookie
|
|
self.match_len = match_len
|
|
self.match = match
|
|
self.frame = frame
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, buf, offset):
|
|
(buffer_id, total_len, reason, table_id,
|
|
cookie, match_len) = struct.unpack_from(
|
|
ofproto.NX_PACKET_IN_PACK_STR, buf, offset)
|
|
|
|
offset += (ofproto.NX_PACKET_IN_SIZE -
|
|
ofproto.NICIRA_HEADER_SIZE)
|
|
|
|
match = nx_match.NXMatch.parser(buf, offset, match_len)
|
|
offset += (match_len + 7) // 8 * 8
|
|
frame = buf[offset:]
|
|
if total_len < len(frame):
|
|
frame = frame[:total_len]
|
|
return cls(datapath, buffer_id, total_len, reason, table_id,
|
|
cookie, match_len, match, frame)
|
|
|
|
|
|
class NXTFlowAge(NiciraHeader):
|
|
def __init__(self, datapath):
|
|
super(NXTFlowAge, self).__init__(
|
|
datapath, ofproto.NXT_FLOW_AGE)
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
|
|
|
|
class NXTSetAsyncConfig(NiciraHeader):
|
|
def __init__(self, datapath, packet_in_mask, port_status_mask,
|
|
flow_removed_mask):
|
|
super(NXTSetAsyncConfig, self).__init__(
|
|
datapath, ofproto.NXT_SET_ASYNC_CONFIG)
|
|
self.packet_in_mask = packet_in_mask
|
|
self.port_status_mask = port_status_mask
|
|
self.flow_removed_mask = flow_removed_mask
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
msg_pack_into(ofproto.NX_ASYNC_CONFIG_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE,
|
|
self.packet_in_mask[0], self.packet_in_mask[1],
|
|
self.port_status_mask[0], self.port_status_mask[1],
|
|
self.flow_removed_mask[0], self.flow_removed_mask[1])
|
|
|
|
|
|
class NXTSetControllerId(NiciraHeader):
|
|
def __init__(self, datapath, controller_id):
|
|
super(NXTSetControllerId, self).__init__(
|
|
datapath, ofproto.NXT_SET_CONTROLLER_ID)
|
|
self.controller_id = controller_id
|
|
|
|
def _serialize_body(self):
|
|
self.serialize_header()
|
|
msg_pack_into(ofproto.NX_CONTROLLER_ID_PACK_STR,
|
|
self.buf, ofproto.NICIRA_HEADER_SIZE,
|
|
self.controller_id)
|
|
|
|
|
|
#
|
|
# asymmetric message (datapath -> controller)
|
|
# parser only
|
|
#
|
|
|
|
|
|
@_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 OSKen framework, so the OSKen application
|
|
do not need to process this typically.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
datapath_id Datapath unique ID.
|
|
n_buffers Max packets buffered at once.
|
|
n_tables Number of tables supported by datapath.
|
|
capabilities Bitmap of capabilities flag.
|
|
|
|
| OFPC_FLOW_STATS
|
|
| OFPC_TABLE_STATS
|
|
| OFPC_PORT_STATS
|
|
| OFPC_STP
|
|
| OFPC_RESERVED
|
|
| OFPC_IP_REASM
|
|
| OFPC_QUEUE_STATS
|
|
| OFPC_ARP_MATCH_IP
|
|
actions Bitmap of supported OFPAT_*.
|
|
ports List of ``OFPPhyPort`` instances.
|
|
================ ======================================================
|
|
|
|
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 capabilities=0x%08x ports=%s',
|
|
msg.datapath_id, msg.n_buffers, msg.n_tables,
|
|
msg.capabilities, msg.ports)
|
|
"""
|
|
|
|
def __init__(self, datapath, datapath_id=None, n_buffers=None,
|
|
n_tables=None, capabilities=None, actions=None, ports=None):
|
|
super(OFPSwitchFeatures, self).__init__(datapath)
|
|
self.datapath_id = datapath_id
|
|
self.n_buffers = n_buffers
|
|
self.n_tables = n_tables
|
|
self.capabilities = capabilities
|
|
self.actions = actions
|
|
self.ports = ports
|
|
|
|
@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.capabilities,
|
|
msg.actions) = struct.unpack_from(
|
|
ofproto.OFP_SWITCH_FEATURES_PACK_STR, msg.buf,
|
|
ofproto.OFP_HEADER_SIZE)
|
|
|
|
msg.ports = {}
|
|
n_ports = ((msg_len - ofproto.OFP_SWITCH_FEATURES_SIZE) //
|
|
ofproto.OFP_PHY_PORT_SIZE)
|
|
offset = ofproto.OFP_SWITCH_FEATURES_SIZE
|
|
for _i in range(n_ports):
|
|
port = OFPPhyPort.parser(msg.buf, offset)
|
|
# print 'port = %s' % str(port)
|
|
msg.ports[port.port_no] = port
|
|
offset += ofproto.OFP_PHY_PORT_SIZE
|
|
|
|
return msg
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_PORT_STATUS)
|
|
class OFPPortStatus(MsgBase):
|
|
"""
|
|
Port status message
|
|
|
|
The switch notifies controller of change of ports.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
reason One of the following values.
|
|
|
|
| OFPPR_ADD
|
|
| OFPPR_DELETE
|
|
| OFPPR_MODIFY
|
|
desc instance of ``OFPPhyPort``
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
|
|
def port_status_handler(self, ev):
|
|
msg = ev.msg
|
|
dp = msg.datapath
|
|
ofp = dp.ofproto
|
|
|
|
if msg.reason == ofp.OFPPR_ADD:
|
|
reason = 'ADD'
|
|
elif msg.reason == ofp.OFPPR_DELETE:
|
|
reason = 'DELETE'
|
|
elif msg.reason == ofp.OFPPR_MODIFY:
|
|
reason = 'MODIFY'
|
|
else:
|
|
reason = 'unknown'
|
|
|
|
self.logger.debug('OFPPortStatus received: reason=%s desc=%s',
|
|
reason, msg.desc)
|
|
"""
|
|
|
|
def __init__(self, datapath, reason=None, desc=None):
|
|
super(OFPPortStatus, self).__init__(datapath)
|
|
self.reason = reason
|
|
self.desc = desc
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPPortStatus, cls).parser(datapath, version, msg_type,
|
|
msg_len, xid, buf)
|
|
msg.reason = struct.unpack_from(
|
|
ofproto.OFP_PORT_STATUS_PACK_STR,
|
|
msg.buf, ofproto.OFP_HEADER_SIZE)[0]
|
|
msg.desc = OFPPhyPort.parser(msg.buf,
|
|
ofproto.OFP_PORT_STATUS_DESC_OFFSET)
|
|
return msg
|
|
|
|
|
|
@_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.
|
|
in_port Port on which frame was received.
|
|
reason Reason packet is being sent.
|
|
|
|
| OFPR_NO_MATCH
|
|
| OFPR_ACTION
|
|
| OFPR_INVALID_TTL
|
|
data Ethernet frame.
|
|
============= =========================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
|
|
def packet_in_handler(self, ev):
|
|
msg = ev.msg
|
|
dp = msg.datapath
|
|
ofp = dp.ofproto
|
|
|
|
if msg.reason == ofp.OFPR_NO_MATCH:
|
|
reason = 'NO MATCH'
|
|
elif msg.reason == ofp.OFPR_ACTION:
|
|
reason = 'ACTION'
|
|
elif msg.reason == ofp.OFPR_INVALID_TTL:
|
|
reason = 'INVALID TTL'
|
|
else:
|
|
reason = 'unknown'
|
|
|
|
self.logger.debug('OFPPacketIn received: '
|
|
'buffer_id=%x total_len=%d in_port=%d, '
|
|
'reason=%s data=%s',
|
|
msg.buffer_id, msg.total_len, msg.in_port,
|
|
reason, utils.hex_array(msg.data))
|
|
"""
|
|
|
|
def __init__(self, datapath, buffer_id=None, total_len=None, in_port=None,
|
|
reason=None, data=None):
|
|
super(OFPPacketIn, self).__init__(datapath)
|
|
self.buffer_id = buffer_id
|
|
self.total_len = total_len
|
|
self.in_port = in_port
|
|
self.reason = reason
|
|
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.in_port,
|
|
msg.reason) = struct.unpack_from(
|
|
ofproto.OFP_PACKET_IN_PACK_STR,
|
|
msg.buf, ofproto.OFP_HEADER_SIZE)
|
|
msg.data = msg.buf[ofproto.OFP_PACKET_IN_SIZE:]
|
|
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
|
|
|
|
|
|
@_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):
|
|
super(OFPGetConfigReply, self).__init__(datapath)
|
|
|
|
@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
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_BARRIER_REPLY)
|
|
class OFPBarrierReply(MsgBase):
|
|
"""
|
|
Barrier reply message
|
|
|
|
The switch responds with this message to a barrier request.
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER)
|
|
def barrier_reply_handler(self, ev):
|
|
self.logger.debug('OFPBarrierReply received')
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPBarrierReply, self).__init__(datapath)
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_FLOW_REMOVED)
|
|
class OFPFlowRemoved(MsgBase):
|
|
"""
|
|
Flow removed message
|
|
|
|
When flow entries time out or are deleted, the switch notifies controller
|
|
with this message.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
match Instance of ``OFPMatch``.
|
|
cookie Opaque controller-issued identifier.
|
|
priority Priority level of flow entry.
|
|
reason One of the following values.
|
|
|
|
| OFPRR_IDLE_TIMEOUT
|
|
| OFPRR_HARD_TIMEOUT
|
|
| OFPRR_DELETE
|
|
duration_sec Time flow was alive in seconds.
|
|
duration_nsec Time flow was alive in nanoseconds
|
|
beyond duration_sec.
|
|
idle_timeout Idle timeout from original flow mod.
|
|
packet_count Number of packets that was associated with the flow.
|
|
byte_count Number of bytes that was associated with the flow.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPFlowRemoved, MAIN_DISPATCHER)
|
|
def flow_removed_handler(self, ev):
|
|
msg = ev.msg
|
|
dp = msg.datapath
|
|
ofp = dp.ofproto
|
|
|
|
if msg.reason == ofp.OFPRR_IDLE_TIMEOUT:
|
|
reason = 'IDLE TIMEOUT'
|
|
elif msg.reason == ofp.OFPRR_HARD_TIMEOUT:
|
|
reason = 'HARD TIMEOUT'
|
|
elif msg.reason == ofp.OFPRR_DELETE:
|
|
reason = 'DELETE'
|
|
elif msg.reason == ofp.OFPRR_GROUP_DELETE:
|
|
reason = 'GROUP DELETE'
|
|
else:
|
|
reason = 'unknown'
|
|
|
|
self.logger.debug('OFPFlowRemoved received: '
|
|
'match=%s cookie=%d priority=%d reason=%s '
|
|
'duration_sec=%d duration_nsec=%d '
|
|
'idle_timeout=%d packet_count=%d byte_count=%d',
|
|
msg.match, msg.cookie, msg.priority, reason,
|
|
msg.duration_sec, msg.duration_nsec,
|
|
msg.idle_timeout, msg.packet_count,
|
|
msg.byte_count)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPFlowRemoved, self).__init__(datapath)
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPFlowRemoved, cls).parser(datapath, version, msg_type,
|
|
msg_len, xid, buf)
|
|
|
|
msg.match = OFPMatch.parse(msg.buf, ofproto.OFP_HEADER_SIZE)
|
|
|
|
(msg.cookie,
|
|
msg.priority,
|
|
msg.reason,
|
|
msg.duration_sec,
|
|
msg.duration_nsec,
|
|
msg.idle_timeout,
|
|
msg.packet_count,
|
|
msg.byte_count) = struct.unpack_from(
|
|
ofproto.OFP_FLOW_REMOVED_PACK_STR0, msg.buf,
|
|
ofproto.OFP_HEADER_SIZE + ofproto.OFP_MATCH_SIZE)
|
|
|
|
return msg
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_QUEUE_GET_CONFIG_REPLY)
|
|
class OFPQueueGetConfigReply(MsgBase):
|
|
"""
|
|
Queue configuration reply message
|
|
|
|
The switch responds with this message to a queue configuration request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
port Port to be queried.
|
|
queues List of ``OFPPacketQueue`` instance.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPQueueGetConfigReply, MAIN_DISPATCHER)
|
|
def queue_get_config_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
|
|
self.logger.debug('OFPQueueGetConfigReply received: '
|
|
'port=%s queues=%s',
|
|
msg.port, msg.queues)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPQueueGetConfigReply, self).__init__(datapath)
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPQueueGetConfigReply, cls).parser(
|
|
datapath, version, msg_type, msg_len, xid, buf)
|
|
|
|
offset = ofproto.OFP_HEADER_SIZE
|
|
(msg.port,) = struct.unpack_from(
|
|
ofproto.OFP_QUEUE_GET_CONFIG_REPLY_PACK_STR, msg.buf, offset)
|
|
|
|
msg.queues = []
|
|
offset = ofproto.OFP_QUEUE_GET_CONFIG_REPLY_SIZE
|
|
while offset + ofproto.OFP_PACKET_QUEUE_SIZE <= msg_len:
|
|
queue = OFPPacketQueue.parser(msg.buf, offset)
|
|
msg.queues.append(queue)
|
|
|
|
offset += queue.len
|
|
|
|
return msg
|
|
|
|
|
|
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
|
|
|
|
|
|
@_register_parser
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPStatsReply(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 OFPStatsReply._STATS_MSG_TYPES
|
|
assert cls.cls_stats_body_cls is not None
|
|
cls.cls_body_single_struct = body_single_struct
|
|
OFPStatsReply._STATS_MSG_TYPES[cls.cls_stats_type] = cls
|
|
return cls
|
|
return _register_stats_type
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPStatsReply, self).__init__(datapath)
|
|
self.type = None
|
|
self.flags = None
|
|
self.body = None
|
|
|
|
@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):
|
|
# call MsgBase::parser, not OFPStatsReply::parser
|
|
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_STATS_MSG_SIZE)
|
|
return msg
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
type_, flags = struct.unpack_from(ofproto.OFP_STATS_MSG_PACK_STR,
|
|
bytes(buf),
|
|
ofproto.OFP_HEADER_SIZE)
|
|
stats_type_cls = cls._STATS_MSG_TYPES.get(type_)
|
|
msg = stats_type_cls.parser_stats(
|
|
datapath, version, msg_type, msg_len, xid, buf)
|
|
msg.type = type_
|
|
msg.flags = flags
|
|
return msg
|
|
|
|
|
|
@OFPStatsReply.register_stats_type(body_single_struct=True)
|
|
@_set_stats_type(ofproto.OFPST_DESC, OFPDescStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPDescStatsReply(OFPStatsReply):
|
|
"""
|
|
Description statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
a description statistics request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
mfr_desc Manufacturer description.
|
|
hw_desc Hardware description.
|
|
sw_desc Software description.
|
|
serial_num Serial number.
|
|
dp_desc Human readable description of datapath.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPDescStatsReply, MAIN_DISPATCHER)
|
|
def desc_stats_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
ofp = msg.datapath.ofproto
|
|
body = ev.msg.body
|
|
|
|
self.logger.debug('DescStats: mfr_desc=%s hw_desc=%s sw_desc=%s '
|
|
'serial_num=%s dp_desc=%s',
|
|
body.mfr_desc, body.hw_desc, body.sw_desc,
|
|
body.serial_num, body.dp_desc)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPDescStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@OFPStatsReply.register_stats_type()
|
|
@_set_stats_type(ofproto.OFPST_FLOW, OFPFlowStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPFlowStatsReply(OFPStatsReply):
|
|
"""
|
|
Individual flow statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
an individual flow statistics request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
table_id ID of table flow came from.
|
|
match Instance of ``OFPMatch``.
|
|
duration_sec Time flow has been alive in seconds.
|
|
duration_nsec Time flow has been alive in nanoseconds beyond
|
|
duration_sec.
|
|
priority Priority of the entry. Only meaningful
|
|
when this is not an exact-match entry.
|
|
idle_timeout Number of seconds idle before expiration.
|
|
hard_timeout Number of seconds before expiration.
|
|
cookie Opaque controller-issued identifier.
|
|
packet_count Number of packets in flow.
|
|
byte_count Number of bytes in flow.
|
|
actions List of ``OFPAction*`` instance
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
|
|
def flow_stats_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
ofp = msg.datapath.ofproto
|
|
body = ev.msg.body
|
|
|
|
flows = []
|
|
for stat in body:
|
|
flows.append('table_id=%s match=%s '
|
|
'duration_sec=%d duration_nsec=%d '
|
|
'priority=%d '
|
|
'idle_timeout=%d hard_timeout=%d '
|
|
'cookie=%d packet_count=%d byte_count=%d '
|
|
'actions=%s' %
|
|
(stat.table_id, stat.match,
|
|
stat.duration_sec, stat.duration_nsec,
|
|
stat.priority,
|
|
stat.idle_timeout, stat.hard_timeout,
|
|
stat.cookie, stat.packet_count, stat.byte_count,
|
|
stat.actions))
|
|
self.logger.debug('FlowStats: %s', flows)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPFlowStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@OFPStatsReply.register_stats_type()
|
|
@_set_stats_type(ofproto.OFPST_AGGREGATE, OFPAggregateStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPAggregateStatsReply(OFPStatsReply):
|
|
"""
|
|
Aggregate flow statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
an aggregate flow statistics request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
packet_count Number of packets in flows.
|
|
byte_count Number of bytes in flows.
|
|
flow_count Number of flows.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPAggregateStatsReply, MAIN_DISPATCHER)
|
|
def aggregate_stats_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
ofp = msg.datapath.ofproto
|
|
body = ev.msg.body
|
|
|
|
self.logger.debug('AggregateStats: packet_count=%d byte_count=%d '
|
|
'flow_count=%d',
|
|
body.packet_count, body.byte_count,
|
|
body.flow_count)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPAggregateStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@OFPStatsReply.register_stats_type()
|
|
@_set_stats_type(ofproto.OFPST_TABLE, OFPTableStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPTableStatsReply(OFPStatsReply):
|
|
"""
|
|
Table statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
a table statistics request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
table_id ID of table.
|
|
name table name.
|
|
wildcards Bitmap of OFPFW_* wildcards that are
|
|
supported by the table.
|
|
max_entries Max number of entries supported
|
|
active_count Number of active entries
|
|
lookup_count Number of packets looked up in table
|
|
matched_count Number of packets that hit table
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPTableStatsReply, MAIN_DISPATCHER)
|
|
def stats_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
ofp = msg.datapath.ofproto
|
|
body = ev.msg.body
|
|
|
|
tables = []
|
|
for stat in body:
|
|
tables.append('table_id=%d name=%s wildcards=0x%02x '
|
|
'max_entries=%d active_count=%d '
|
|
'lookup_count=%d matched_count=%d' %
|
|
(stat.table_id, stat.name, stat.wildcards,
|
|
stat.max_entries, stat.active_count,
|
|
stat.lookup_count, stat.matched_count))
|
|
self.logger.debug('TableStats: %s', tables)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPTableStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@OFPStatsReply.register_stats_type()
|
|
@_set_stats_type(ofproto.OFPST_PORT, OFPPortStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPPortStatsReply(OFPStatsReply):
|
|
"""
|
|
Port statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
a port statistics request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
port_no Port number.
|
|
rx_packets Number of received packets.
|
|
tx_packets Number of transmitted packets.
|
|
rx_bytes Number of received bytes.
|
|
tx_bytes Number of transmitted bytes.
|
|
rx_dropped Number of packets dropped by RX.
|
|
tx_dropped Number of packets dropped by TX.
|
|
rx_errors Number of receive errors.
|
|
tx_errors Number of transmit errors.
|
|
rx_frame_err Number of frame alignment errors.
|
|
rx_over_err Number of packet with RX overrun.
|
|
rx_crc_err Number of CRC errors.
|
|
collisions Number of collisions.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
|
|
def port_stats_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
ofp = msg.datapath.ofproto
|
|
body = ev.msg.body
|
|
|
|
ports = []
|
|
for stat in body:
|
|
ports.append('port_no=%d '
|
|
'rx_packets=%d tx_packets=%d '
|
|
'rx_bytes=%d tx_bytes=%d '
|
|
'rx_dropped=%d tx_dropped=%d '
|
|
'rx_errors=%d tx_errors=%d '
|
|
'rx_frame_err=%d rx_over_err=%d rx_crc_err=%d '
|
|
'collisions=%d' %
|
|
(stat.port_no,
|
|
stat.rx_packets, stat.tx_packets,
|
|
stat.rx_bytes, stat.tx_bytes,
|
|
stat.rx_dropped, stat.tx_dropped,
|
|
stat.rx_errors, stat.tx_errors,
|
|
stat.rx_frame_err, stat.rx_over_err,
|
|
stat.rx_crc_err, stat.collisions))
|
|
self.logger.debug('PortStats: %s', ports)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPPortStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@OFPStatsReply.register_stats_type()
|
|
@_set_stats_type(ofproto.OFPST_QUEUE, OFPQueueStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPQueueStatsReply(OFPStatsReply):
|
|
"""
|
|
Queue statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
an aggregate flow statistics request.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
port_no Port number.
|
|
queue_id ID of queue.
|
|
tx_bytes Number of transmitted bytes.
|
|
tx_packets Number of transmitted packets.
|
|
tx_errors Number of packets dropped due to overrun.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
@set_ev_cls(ofp_event.EventOFPQueueStatsReply, MAIN_DISPATCHER)
|
|
def stats_reply_handler(self, ev):
|
|
msg = ev.msg
|
|
ofp = msg.datapath.ofproto
|
|
body = ev.msg.body
|
|
|
|
queues = []
|
|
for stat in body:
|
|
queues.append('port_no=%d queue_id=%d '
|
|
'tx_bytes=%d tx_packets=%d tx_errors=%d ' %
|
|
(stat.port_no, stat.queue_id,
|
|
stat.tx_bytes, stat.tx_packets, stat.tx_errors))
|
|
self.logger.debug('QueueStats: %s', queues)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPQueueStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@OFPStatsReply.register_stats_type()
|
|
@_set_stats_type(ofproto.OFPST_VENDOR, OFPVendorStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REPLY)
|
|
class OFPVendorStatsReply(OFPStatsReply):
|
|
"""
|
|
Vendor statistics reply message
|
|
|
|
The switch responds with a stats reply that include this message to
|
|
an vendor statistics request.
|
|
"""
|
|
_STATS_VENDORS = {}
|
|
|
|
@staticmethod
|
|
def register_stats_vendor(vendor):
|
|
def _register_stats_vendor(cls):
|
|
cls.cls_vendor = vendor
|
|
OFPVendorStatsReply._STATS_VENDORS[cls.cls_vendor] = cls
|
|
return cls
|
|
return _register_stats_vendor
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPVendorStatsReply, self).__init__(datapath)
|
|
|
|
@classmethod
|
|
def parser_stats(cls, datapath, version, msg_type, msg_len, xid,
|
|
buf):
|
|
(type_,) = struct.unpack_from(
|
|
ofproto.OFP_VENDOR_STATS_MSG_PACK_STR, bytes(buf),
|
|
ofproto.OFP_STATS_MSG_SIZE)
|
|
|
|
cls_ = cls._STATS_VENDORS.get(type_)
|
|
|
|
if cls_ is None:
|
|
msg = MsgBase.parser.__func__(
|
|
cls, datapath, version, msg_type, msg_len, xid, buf)
|
|
body_cls = cls.cls_stats_body_cls
|
|
body = body_cls.parser(buf,
|
|
ofproto.OFP_STATS_MSG_SIZE)
|
|
msg.body = body
|
|
return msg
|
|
|
|
return cls_.parser(
|
|
datapath, version, msg_type, msg_len, xid, buf,
|
|
ofproto.OFP_VENDOR_STATS_MSG_SIZE)
|
|
|
|
|
|
@OFPVendorStatsReply.register_stats_vendor(ofproto_common.NX_EXPERIMENTER_ID)
|
|
class NXStatsReply(OFPStatsReply):
|
|
_NX_STATS_TYPES = {}
|
|
|
|
@staticmethod
|
|
def register_nx_stats_type(body_single_struct=False):
|
|
def _register_nx_stats_type(cls):
|
|
assert cls.cls_stats_type is not None
|
|
assert cls.cls_stats_type not in \
|
|
NXStatsReply._NX_STATS_TYPES
|
|
assert cls.cls_stats_body_cls is not None
|
|
cls.cls_body_single_struct = body_single_struct
|
|
NXStatsReply._NX_STATS_TYPES[cls.cls_stats_type] = cls
|
|
return cls
|
|
return _register_nx_stats_type
|
|
|
|
@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, offset):
|
|
msg = MsgBase.parser.__func__(
|
|
cls, datapath, version, msg_type, msg_len, xid, buf)
|
|
msg.body = msg.parser_stats_body(msg.buf, msg.msg_len, offset)
|
|
|
|
return msg
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf,
|
|
offset):
|
|
(type_,) = struct.unpack_from(
|
|
ofproto.NX_STATS_MSG_PACK_STR, bytes(buf), offset)
|
|
offset += ofproto.NX_STATS_MSG0_SIZE
|
|
|
|
cls_ = cls._NX_STATS_TYPES.get(type_)
|
|
|
|
msg = cls_.parser_stats(
|
|
datapath, version, msg_type, msg_len, xid, buf, offset)
|
|
|
|
return msg
|
|
|
|
|
|
@NXStatsReply.register_nx_stats_type()
|
|
@_set_stats_type(ofproto.NXST_FLOW, NXFlowStats)
|
|
class NXFlowStatsReply(NXStatsReply):
|
|
def __init__(self, datapath):
|
|
super(NXFlowStatsReply, self).__init__(datapath)
|
|
|
|
|
|
@NXStatsReply.register_nx_stats_type()
|
|
@_set_stats_type(ofproto.NXST_AGGREGATE, NXAggregateStats)
|
|
class NXAggregateStatsReply(NXStatsReply):
|
|
def __init__(self, datapath):
|
|
super(NXAggregateStatsReply, self).__init__(datapath)
|
|
|
|
|
|
#
|
|
# controller-to-switch message
|
|
# serializer only
|
|
#
|
|
|
|
|
|
@_set_msg_reply(OFPSwitchFeatures)
|
|
@_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 OSKen framework, so the OSKen 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)
|
|
|
|
|
|
@_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)
|
|
|
|
|
|
@_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=None, miss_send_len=None):
|
|
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)
|
|
|
|
|
|
@_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 (0xffffffff if none).
|
|
in_port Packet's input port (OFPP_NONE if none).
|
|
actions ist of ``OFPAction*`` instance.
|
|
data Packet data of a binary type value or
|
|
an instances of packet.Packet.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_packet_out(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
buffer_id = 0xffffffff
|
|
in_port = ofp.OFPP_NONE
|
|
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):
|
|
super(OFPPacketOut, self).__init__(datapath)
|
|
self.buffer_id = buffer_id
|
|
self.in_port = in_port
|
|
self._actions_len = None
|
|
self.actions = actions
|
|
self.data = data
|
|
|
|
def _serialize_body(self):
|
|
assert self.buffer_id is not None
|
|
assert self.in_port is not None
|
|
assert self.actions is not None
|
|
|
|
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
|
|
if isinstance(self.data, packet.Packet):
|
|
self.data.serialize()
|
|
self.buf += self.data.data
|
|
else:
|
|
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)
|
|
|
|
@classmethod
|
|
def from_jsondict(cls, dict_, decode_string=base64.b64decode,
|
|
**additional_args):
|
|
if isinstance(dict_['data'], dict):
|
|
data = dict_.pop('data')
|
|
ins = super(OFPPacketOut, cls).from_jsondict(dict_,
|
|
decode_string,
|
|
**additional_args)
|
|
ins.data = packet.Packet.from_jsondict(data['Packet'])
|
|
dict_['data'] = data
|
|
else:
|
|
ins = super(OFPPacketOut, cls).from_jsondict(dict_,
|
|
decode_string,
|
|
**additional_args)
|
|
|
|
return ins
|
|
|
|
|
|
@_register_parser
|
|
@_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
|
|
================ ======================================================
|
|
match Instance of ``OFPMatch``.
|
|
cookie Opaque controller-issued identifier.
|
|
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 0xffffffff).
|
|
Not meaningful for OFPFC_DELETE*.
|
|
out_port For OFPFC_DELETE* commands, require
|
|
matching entries to include this as an
|
|
output port. A value of OFPP_NONE
|
|
indicates no restriction.
|
|
flags One of the following values.
|
|
|
|
| OFPFF_SEND_FLOW_REM
|
|
| OFPFF_CHECK_OVERLAP
|
|
| OFPFF_EMERG
|
|
actions List of ``OFPAction*`` instance.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_flow_mod(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
match = ofp_parser.OFPMatch(in_port=1)
|
|
cookie = 0
|
|
command = ofp.OFPFC_ADD
|
|
idle_timeout = hard_timeout = 0
|
|
priority = 32768
|
|
buffer_id = 0xffffffff
|
|
out_port = ofproto.OFPP_NONE
|
|
flags = 0
|
|
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_NORMAL, 0)]
|
|
req = ofp_parser.OFPFlowMod(
|
|
datapath, match, cookie, command, idle_timeout, hard_timeout,
|
|
priority, buffer_id, out_port, flags, actions)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, match=None, cookie=0,
|
|
command=ofproto.OFPFC_ADD,
|
|
idle_timeout=0, hard_timeout=0,
|
|
priority=ofproto.OFP_DEFAULT_PRIORITY,
|
|
buffer_id=0xffffffff, out_port=ofproto.OFPP_NONE,
|
|
flags=0, actions=None):
|
|
super(OFPFlowMod, self).__init__(datapath)
|
|
self.match = OFPMatch() if match is None else 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
|
|
|
|
def _serialize_body(self):
|
|
offset = ofproto.OFP_HEADER_SIZE
|
|
self.match.serialize(self.buf, offset)
|
|
|
|
offset += ofproto.OFP_MATCH_SIZE
|
|
msg_pack_into(ofproto.OFP_FLOW_MOD_PACK_STR0, self.buf, offset,
|
|
self.cookie, self.command,
|
|
self.idle_timeout, self.hard_timeout,
|
|
self.priority, self.buffer_id, self.out_port,
|
|
self.flags)
|
|
|
|
offset = ofproto.OFP_FLOW_MOD_SIZE
|
|
if self.actions is not None:
|
|
for a in self.actions:
|
|
a.serialize(self.buf, offset)
|
|
offset += a.len
|
|
|
|
@classmethod
|
|
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
|
|
msg = super(OFPFlowMod, cls).parser(
|
|
datapath, version, msg_type, msg_len, xid, buf)
|
|
offset = ofproto.OFP_HEADER_SIZE
|
|
|
|
msg.match = OFPMatch.parse(msg.buf, offset)
|
|
offset += ofproto.OFP_MATCH_SIZE
|
|
|
|
(msg.cookie, msg.command, msg.idle_timeout, msg.hard_timeout,
|
|
msg.priority, msg.buffer_id, msg.out_port,
|
|
msg.flags) = struct.unpack_from(
|
|
ofproto.OFP_FLOW_MOD_PACK_STR0, msg.buf, offset)
|
|
offset = ofproto.OFP_FLOW_MOD_SIZE
|
|
|
|
actions = []
|
|
while offset < msg_len:
|
|
a = OFPAction.parser(buf, offset)
|
|
actions.append(a)
|
|
offset += a.len
|
|
msg.actions = actions
|
|
|
|
return msg
|
|
|
|
|
|
@_set_msg_type(ofproto.OFPT_PORT_MOD)
|
|
class OFPPortMod(MsgBase):
|
|
"""
|
|
Port modification message
|
|
|
|
The controller send this message to modify the behavior of the port.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
port_no Port number to modify.
|
|
hw_addr The hardware address that must be the same as hw_addr
|
|
of ``OFPPhyPort`` of ``OFPSwitchFeatures``.
|
|
config Bitmap of configuration flags.
|
|
|
|
| OFPPC_PORT_DOWN
|
|
| OFPPC_NO_STP
|
|
| OFPPC_NO_RECV
|
|
| OFPPC_NO_RECV_STP
|
|
| OFPPC_NO_FLOOD
|
|
| OFPPC_NO_FWD
|
|
| OFPPC_NO_PACKET_IN
|
|
mask Bitmap of configuration flags above to be changed
|
|
advertise Bitmap of the following flags.
|
|
|
|
| OFPPF_10MB_HD
|
|
| OFPPF_10MB_FD
|
|
| OFPPF_100MB_HD
|
|
| OFPPF_100MB_FD
|
|
| OFPPF_1GB_HD
|
|
| OFPPF_1GB_FD
|
|
| OFPPF_10GB_FD
|
|
| OFPPF_COPPER
|
|
| OFPPF_FIBER
|
|
| OFPPF_AUTONEG
|
|
| OFPPF_PAUSE
|
|
| OFPPF_PAUSE_ASYM
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_port_mod(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
port_no = 3
|
|
hw_addr = 'fa:c8:e8:76:1d:7e'
|
|
config = 0
|
|
mask = (ofp.OFPPC_PORT_DOWN | ofp.OFPPC_NO_RECV |
|
|
ofp.OFPPC_NO_FWD | ofp.OFPPC_NO_PACKET_IN)
|
|
advertise = (ofp.OFPPF_10MB_HD | ofp.OFPPF_100MB_FD |
|
|
ofp.OFPPF_1GB_FD | ofp.OFPPF_COPPER |
|
|
ofp.OFPPF_AUTONEG | ofp.OFPPF_PAUSE |
|
|
ofp.OFPPF_PAUSE_ASYM)
|
|
req = ofp_parser.OFPPortMod(datapath, port_no, hw_addr, config,
|
|
mask, advertise)
|
|
datapath.send_msg(req)
|
|
"""
|
|
_TYPE = {
|
|
'ascii': [
|
|
'hw_addr',
|
|
]
|
|
}
|
|
|
|
def __init__(self, datapath, port_no=0, hw_addr='00:00:00:00:00:00',
|
|
config=0, mask=0, advertise=0):
|
|
super(OFPPortMod, self).__init__(datapath)
|
|
self.port_no = port_no
|
|
self.hw_addr = hw_addr
|
|
self.config = config
|
|
self.mask = mask
|
|
self.advertise = advertise
|
|
|
|
def _serialize_body(self):
|
|
msg_pack_into(ofproto.OFP_PORT_MOD_PACK_STR,
|
|
self.buf, ofproto.OFP_HEADER_SIZE,
|
|
self.port_no, addrconv.mac.text_to_bin(self.hw_addr),
|
|
self.config, self.mask, self.advertise)
|
|
|
|
|
|
@_set_msg_reply(OFPBarrierReply)
|
|
@_set_msg_type(ofproto.OFPT_BARRIER_REQUEST)
|
|
class OFPBarrierRequest(MsgBase):
|
|
"""
|
|
Barrier request message
|
|
|
|
The controller sends this message to ensure message dependencies have
|
|
been met or receive notifications for completed operations.
|
|
|
|
Example::
|
|
|
|
def send_barrier_request(self, datapath):
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPBarrierRequest(datapath)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath):
|
|
super(OFPBarrierRequest, self).__init__(datapath)
|
|
|
|
|
|
@_set_msg_reply(OFPQueueGetConfigReply)
|
|
@_set_msg_type(ofproto.OFPT_QUEUE_GET_CONFIG_REQUEST)
|
|
class OFPQueueGetConfigRequest(MsgBase):
|
|
"""
|
|
Queue configuration request message
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
port Port to be queried. Should refer
|
|
to a valid physical port (i.e. < OFPP_MAX).
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_queue_get_config_request(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPQueueGetConfigRequest(datapath,
|
|
ofp.OFPP_NONE)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, port):
|
|
super(OFPQueueGetConfigRequest, self).__init__(datapath)
|
|
self.port = port
|
|
|
|
def _serialize_body(self):
|
|
msg_pack_into(ofproto.OFP_QUEUE_GET_CONFIG_REQUEST_PACK_STR,
|
|
self.buf, ofproto.OFP_HEADER_SIZE, self.port)
|
|
|
|
|
|
class OFPStatsRequest(MsgBase):
|
|
def __init__(self, datapath, flags):
|
|
assert flags == 0 # none yet defined
|
|
|
|
super(OFPStatsRequest, 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_STATS_MSG_PACK_STR,
|
|
self.buf, ofproto.OFP_HEADER_SIZE,
|
|
self.type, self.flags)
|
|
self._serialize_stats_body()
|
|
|
|
|
|
@_set_msg_reply(OFPDescStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_DESC, OFPDescStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPDescStatsRequest(OFPStatsRequest):
|
|
"""
|
|
Description statistics request message
|
|
|
|
The controller uses this message to query description of the switch.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
flags Zero (none yet defined in the spec).
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_desc_stats_request(self, datapath):
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPDescStatsRequest(datapath)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, flags):
|
|
super(OFPDescStatsRequest, self).__init__(datapath, flags)
|
|
|
|
|
|
class OFPFlowStatsRequestBase(OFPStatsRequest):
|
|
def __init__(self, datapath, flags, match, table_id, out_port):
|
|
super(OFPFlowStatsRequestBase, self).__init__(datapath, flags)
|
|
self.match = match
|
|
self.table_id = table_id
|
|
self.out_port = out_port
|
|
|
|
def _serialize_stats_body(self):
|
|
offset = ofproto.OFP_STATS_MSG_SIZE
|
|
self.match.serialize(self.buf, offset)
|
|
|
|
offset += ofproto.OFP_MATCH_SIZE
|
|
msg_pack_into(ofproto.OFP_FLOW_STATS_REQUEST_ID_PORT_STR,
|
|
self.buf, offset, self.table_id, self.out_port)
|
|
|
|
|
|
@_set_msg_reply(OFPFlowStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_FLOW, OFPFlowStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPFlowStatsRequest(OFPFlowStatsRequestBase):
|
|
"""
|
|
Individual flow statistics request message
|
|
|
|
The controller uses this message to query individual flow statistics.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
flags Zero (none yet defined in the spec).
|
|
match Instance of ``OFPMatch``.
|
|
table_id ID of table to read (from ofp_table_stats),
|
|
0xff for all tables or 0xfe for emergency.
|
|
out_port Require matching entries to include this
|
|
as an output port. A value of OFPP_NONE
|
|
indicates no restriction.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_flow_stats_request(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
match = ofp_parser.OFPMatch(in_port=1)
|
|
table_id = 0xff
|
|
out_port = ofp.OFPP_NONE
|
|
req = ofp_parser.OFPFlowStatsRequest(
|
|
datapath, 0, match, table_id, out_port)
|
|
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, flags, match, table_id, out_port):
|
|
super(OFPFlowStatsRequest, self).__init__(
|
|
datapath, flags, match, table_id, out_port)
|
|
|
|
|
|
@_set_msg_reply(OFPAggregateStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_AGGREGATE, OFPAggregateStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPAggregateStatsRequest(OFPFlowStatsRequestBase):
|
|
"""
|
|
Aggregate flow statistics request message
|
|
|
|
The controller uses this message to query aggregate flow statictics.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
flags Zero (none yet defined in the spec).
|
|
match Fields to match.
|
|
table_id ID of table to read (from ofp_table_stats)
|
|
0xff for all tables or 0xfe for emergency.
|
|
out_port Require matching entries to include this
|
|
as an output port. A value of OFPP_NONE
|
|
indicates no restriction.
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_aggregate_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.OFPAggregateStatsRequest(
|
|
datapath, 0, match, 0xff, ofp.OFPP_NONE)
|
|
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, flags, match, table_id, out_port):
|
|
super(OFPAggregateStatsRequest, self).__init__(
|
|
datapath, flags, match, table_id, out_port)
|
|
|
|
|
|
@_set_msg_reply(OFPTableStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_TABLE, OFPTableStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPTableStatsRequest(OFPStatsRequest):
|
|
"""
|
|
Table statistics request message
|
|
|
|
The controller uses this message to query flow table statictics.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
flags Zero (none yet defined in the spec).
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_table_stats_request(self, datapath):
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPTableStatsRequest(datapath)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, flags):
|
|
super(OFPTableStatsRequest, self).__init__(datapath, flags)
|
|
|
|
|
|
@_set_msg_reply(OFPPortStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_PORT, OFPPortStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPPortStatsRequest(OFPStatsRequest):
|
|
"""
|
|
Port statistics request message
|
|
|
|
The controller uses this message to query information about ports
|
|
statistics.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
flags Zero (none yet defined in the spec).
|
|
port_no Port number to read (OFPP_NONE to all ports).
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_port_stats_request(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPPortStatsRequest(datapath, 0, ofp.OFPP_ANY)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, flags, port_no):
|
|
super(OFPPortStatsRequest, self).__init__(datapath, flags)
|
|
self.port_no = port_no
|
|
|
|
def _serialize_stats_body(self):
|
|
msg_pack_into(ofproto.OFP_PORT_STATS_REQUEST_PACK_STR,
|
|
self.buf, ofproto.OFP_STATS_MSG_SIZE, self.port_no)
|
|
|
|
|
|
@_set_msg_reply(OFPQueueStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_QUEUE, OFPQueueStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPQueueStatsRequest(OFPStatsRequest):
|
|
"""
|
|
Queue statistics request message
|
|
|
|
The controller uses this message to query queue statictics.
|
|
|
|
================ ======================================================
|
|
Attribute Description
|
|
================ ======================================================
|
|
flags Zero (none yet defined in the spec)
|
|
port_no Port number to read (All ports if OFPT_ALL).
|
|
queue_id ID of queue to read (All queues if OFPQ_ALL).
|
|
================ ======================================================
|
|
|
|
Example::
|
|
|
|
def send_queue_stats_request(self, datapath):
|
|
ofp = datapath.ofproto
|
|
ofp_parser = datapath.ofproto_parser
|
|
|
|
req = ofp_parser.OFPQueueStatsRequest(datapath, 0, ofp.OFPT_ALL,
|
|
ofp.OFPQ_ALL)
|
|
datapath.send_msg(req)
|
|
"""
|
|
|
|
def __init__(self, datapath, flags, port_no, queue_id):
|
|
super(OFPQueueStatsRequest, self).__init__(datapath, flags)
|
|
self.port_no = port_no
|
|
self.queue_id = queue_id
|
|
|
|
def _serialize_stats_body(self):
|
|
msg_pack_into(ofproto.OFP_QUEUE_STATS_REQUEST_PACK_STR,
|
|
self.buf, ofproto.OFP_STATS_MSG_SIZE,
|
|
self.port_no, self.queue_id)
|
|
|
|
|
|
@_set_msg_reply(OFPVendorStatsReply)
|
|
@_set_stats_type(ofproto.OFPST_VENDOR, OFPVendorStats)
|
|
@_set_msg_type(ofproto.OFPT_STATS_REQUEST)
|
|
class OFPVendorStatsRequest(OFPStatsRequest):
|
|
"""
|
|
Vendor statistics request message
|
|
|
|
The controller uses this message to query vendor-specific information
|
|
of a switch.
|
|
"""
|
|
|
|
def __init__(self, datapath, flags, vendor, specific_data=None):
|
|
super(OFPVendorStatsRequest, self).__init__(datapath, flags)
|
|
self.vendor = vendor
|
|
self.specific_data = specific_data
|
|
|
|
def _serialize_vendor_stats(self):
|
|
self.buf += self.specific_data
|
|
|
|
def _serialize_stats_body(self):
|
|
msg_pack_into(ofproto.OFP_VENDOR_STATS_MSG_PACK_STR,
|
|
self.buf, ofproto.OFP_STATS_MSG_SIZE,
|
|
self.vendor)
|
|
self._serialize_vendor_stats()
|
|
|
|
|
|
class NXStatsRequest(OFPVendorStatsRequest):
|
|
def __init__(self, datapath, flags, subtype):
|
|
super(NXStatsRequest, self).__init__(datapath, flags,
|
|
ofproto_common.NX_EXPERIMENTER_ID)
|
|
self.subtype = subtype
|
|
|
|
def _serialize_vendor_stats_body(self):
|
|
pass
|
|
|
|
def _serialize_vendor_stats(self):
|
|
msg_pack_into(ofproto.NX_STATS_MSG_PACK_STR, self.buf,
|
|
ofproto.OFP_VENDOR_STATS_MSG_SIZE,
|
|
self.subtype)
|
|
self._serialize_vendor_stats_body()
|
|
|
|
|
|
class NXFlowStatsRequest(NXStatsRequest):
|
|
def __init__(self, datapath, flags, out_port, table_id, rule=None):
|
|
super(NXFlowStatsRequest, self).__init__(datapath, flags,
|
|
ofproto.NXST_FLOW)
|
|
self.out_port = out_port
|
|
self.table_id = table_id
|
|
self.rule = rule
|
|
self.match_len = 0
|
|
|
|
def _serialize_vendor_stats_body(self):
|
|
if self.rule is not None:
|
|
offset = ofproto.NX_STATS_MSG_SIZE + \
|
|
ofproto.NX_FLOW_STATS_REQUEST_SIZE
|
|
self.match_len = nx_match.serialize_nxm_match(
|
|
self.rule, self.buf, offset)
|
|
|
|
msg_pack_into(
|
|
ofproto.NX_FLOW_STATS_REQUEST_PACK_STR,
|
|
self.buf, ofproto.NX_STATS_MSG_SIZE, self.out_port,
|
|
self.match_len, self.table_id)
|
|
|
|
|
|
class NXAggregateStatsRequest(NXStatsRequest):
|
|
def __init__(self, datapath, flags, out_port, table_id, rule=None):
|
|
super(NXAggregateStatsRequest, self).__init__(
|
|
datapath, flags, ofproto.NXST_AGGREGATE)
|
|
self.out_port = out_port
|
|
self.table_id = table_id
|
|
self.rule = rule
|
|
self.match_len = 0
|
|
|
|
def _serialize_vendor_stats_body(self):
|
|
if self.rule is not None:
|
|
offset = ofproto.NX_STATS_MSG_SIZE + \
|
|
ofproto.NX_AGGREGATE_STATS_REQUEST_SIZE
|
|
self.match_len = nx_match.serialize_nxm_match(
|
|
self.rule, self.buf, offset)
|
|
|
|
msg_pack_into(
|
|
ofproto.NX_AGGREGATE_STATS_REQUEST_PACK_STR,
|
|
self.buf, ofproto.NX_STATS_MSG_SIZE, self.out_port,
|
|
self.match_len, self.table_id)
|
|
|
|
|
|
nx_actions.generate(
|
|
'os_ken.ofproto.ofproto_v1_0',
|
|
'os_ken.ofproto.ofproto_v1_0_parser'
|
|
)
|