# Copyright 2011,2012 James McCauley
#
# 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.

# This file was originally based on pyopenflow.py from NOX, which was
# autogenerated from openflow.h via a program by KK Yap.  It has been
# substantially altered since then.

from __future__ import print_function

import struct
import operator
from itertools import chain, repeat
import sys
from pox.lib.packet.packet_base import packet_base
from pox.lib.packet.ethernet import ethernet
from pox.lib.packet.vlan import vlan
from pox.lib.packet.llc import llc
from pox.lib.packet.ipv4 import ipv4
from pox.lib.packet.udp import udp
from pox.lib.packet.tcp import tcp
from pox.lib.packet.icmp import icmp
from pox.lib.packet.arp import arp

from pox.lib.addresses import *
from pox.lib.util import assert_type
from pox.lib.util import initHelper
from pox.lib.util import hexdump
from pox.lib.util import is_listlike


EMPTY_ETH = EthAddr(None)

# ----------------------------------------------------------------------
# Logging
# ----------------------------------------------------------------------

_logger = None
def _log (debug=None, info=None, warn=None, error=None):
    if not _logger: return
    if debug: _logger.debug(debug)
    if info: _logger.info(info)
    if warn: _logger.warn(warn)
    if error: _logger.error(error)

# ----------------------------------------------------------------------

# ----------------------------------------------------------------------
# XID Management
# ----------------------------------------------------------------------

MAX_XID = 0x7fFFffFF


def XIDGenerator (start = 1, stop = MAX_XID):
    i = start
    while True:
        yield i
        i += 1
        if i > stop:
            i = start

def xid_generator (start = 1, stop = MAX_XID):
    return XIDGenerator(start, stop).next

def user_xid_generator ():
    return xid_generator(0x80000000, 0xffFFffFF)

generate_xid = xid_generator()

# ----------------------------------------------------------------------


# ----------------------------------------------------------------------
# Packing / Unpacking
# ----------------------------------------------------------------------

_PAD = b'\x00'
_PAD2 = _PAD * 2
_PAD3 = _PAD * 3
_PAD4 = _PAD * 4
_PAD5 = _PAD * 5
_PAD6 = _PAD * 6
_PAD7 = _PAD * 7
_PAD8 = _PAD * 8

class UnderrunError (RuntimeError):
    """
    Raised when one tries to unpack more data than is available
    """
    pass

def _read (data, offset, length):
    if (len(data)-offset) < length:
        raise UnderrunError("wanted %s bytes but only have %s"
                        % (length, len(data)-offset))
    return (offset+length, data[offset:offset+length])

def _unpack (fmt, data, offset):
    size = struct.calcsize(fmt)
    if (len(data)-offset) < size:
        raise UnderrunError()
    return (offset+size, struct.unpack_from(fmt, data, offset))

def _skip (data, offset, num):
    offset += num
    if offset > len(data):
        raise UnderrunError()
    return offset

def _unpad (data, offset, num):
    (offset, o) = _read(data, offset, num)
    assert len(o.replace("\x00", "")) == 0
    return offset

def _readzs (data, offset, length):
    (offset, d) = _read(data, offset, length)
    d = d.split("\x00", 1)
    #if len(d[1].replace("\x00", "")) > 0:
    #  raise RuntimeError("Non-zero string padding")
    assert True if (len(d) == 1) else (len(d[1].replace("\x00", "")) == 0)
    return (offset, d[0])

def _readether (data, offset):
    (offset, d) = _read(data, offset, 6)
    return (offset, EthAddr(d))

def _readip (data, offset, networkOrder = True):
    (offset, d) = _read(data, offset, 4)
    return (offset, IPAddr(d, networkOrder = networkOrder))

# ----------------------------------------------------------------------


def _format_body (body, prefix):
    if hasattr(body, 'show'):
        #TODO: Check this (spacing may well be wrong)
        return body.show(prefix + '  ')
    else:
        return prefix + hexdump(body).replace("\n", "\n" + prefix)

TABLE_ALL = 0xff
TABLE_EMERGENCY = 0xfe


class _ofp_meta (type):
    """
    Metaclass for ofp messages/structures

    This takes care of making len() work as desired.
    """
    def __len__ (cls):
        try:
            return cls.__len__()
        except:
            return cls._MIN_LENGTH


class ofp_base (object):
    """
    Base class for OpenFlow messages/structures

    You should implement a __len__ method.  If your length is fixed, it
    should be a static method.  If your length is not fixed, you should
    implement a __len__ instance method and set a class level _MIN_LENGTH
    attribute to your minimum length.
    """
    __metaclass__ = _ofp_meta

    def _assert (self):
        r = self._validate()
        if r is not None:
            raise RuntimeError(r)
            return False # Never reached
        return True

    def _validate (self):
        return None

    def __ne__ (self, other):
        return not self.__eq__(other)

    @classmethod
    def unpack_new (cls, raw, offset=0):
        """
        Unpacks wire format into the appropriate message object.
    
        Returns newoffset,object
        """
        o = cls()
        r,length = o.unpack(raw, offset)   # cc: r -> new offset
        assert (r-offset) == length, o
        return (r, o)

# ----------------------------------------------------------------------
# Class decorators
# ----------------------------------------------------------------------

_message_type_to_class = {}
_message_class_to_types = {} # Do we need this?
ofp_type_rev_map = {}
ofp_type_map = {}

def openflow_message (ofp_type, type_val, reply_to=None,
                      request_for=None, switch=False, controller=False):
    ofp_type_rev_map[ofp_type] = type_val   # cc: ofp_type is a string(name) 
    ofp_type_map[type_val] = ofp_type
    def f (c):      # cc: c means class
        c.header_type = type_val
        c._from_switch = switch
        c._from_controller = controller
        _message_type_to_class[type_val] = c
        _message_class_to_types.setdefault(c, set()).add(type_val)
        return c
    return f

def openflow_sc_message (*args, **kw):
    return openflow_message(switch=True, controller=True, *args, **kw)

def openflow_c_message (*args, **kw):
    return openflow_message(controller=True, *args, **kw)

def openflow_s_message (*args, **kw):
    return openflow_message(switch=True, *args, **kw)


_action_type_to_class = {}
_action_class_to_types = {} # Do we need this?
ofp_action_type_rev_map = {}
ofp_action_type_map = {}

def openflow_action (action_type, type_val):
    ofp_action_type_rev_map[action_type] = type_val
    ofp_action_type_map[type_val] = action_type
    def f (c):
        c.type = type_val    # cc: class variable of ofp_action_base
        _action_type_to_class[type_val] = c
        _action_class_to_types.setdefault(c, set()).add(type_val)
        return c
    return f


class _StatsClassInfo (object):
    __slots__ = 'request reply reply_is_list'.split()

    def __init__ (self, **kw):
        self.request = None
        self.reply = None
        self.reply_is_list = False
        initHelper(self, kw)

    def __str__ (self):
        r = str(self.reply)
        if self.reply_is_list: r = "[%s]" % (r,)
        return "request:%s reply:%s" % (self.request, r)

_stats_type_to_class_info = {}
_stats_class_to_type = {}
ofp_stats_type_rev_map = {}
ofp_stats_type_map = {}

def openflow_stats_request  (stats_type, type_val=None, is_list=None,
                             is_reply = False):
    if type_val is not None:
        ofp_stats_type_rev_map[stats_type] = type_val
        ofp_stats_type_map[type_val] = stats_type
    else:
        type_val = ofp_stats_type_rev_map.get(stats_type)

    def f (c):
        if type_val is not None:
            ti = _stats_type_to_class_info.get(stats_type)
            if ti is not None:
                _stats_type_to_class_info[type_val] = ti
                del _stats_type_to_class_info[stats_type]
            else:
                ti = _stats_type_to_class_info.setdefault(type_val,
                    _StatsClassInfo())
            _stats_class_to_type[c] = type_val
        else:
            ti = _stats_type_to_class_info.setdefault(stats_type,
              _StatsClassInfo())
    
        if is_list is not None:
            ti.reply_is_list = is_list
        if is_reply:
            ti.reply = c
        else:
            ti.request = c
    
        if type_val is not None:
            yes = False
            if ti.reply is not None and issubclass(ti.reply,ofp_stats_body_base):
                ti.reply._type = type_val
                yes = True
            if ti.request is not None and issubclass(ti.request,ofp_stats_body_base):
                ti.request._type = type_val
                yes = True
            assert yes, "Type not set for " + str(stats_type)
    
        return c
    return f

def openflow_stats_reply (stats_type, type_val=None, is_list=None, is_reply = True):
    return openflow_stats_request(stats_type, type_val, is_list, is_reply)

# ----------------------------------------------------------------------


# ----------------------------------------------------------------------
# Constants, etc.
# ----------------------------------------------------------------------

# --------------- org.openflow.protocol.OFError ---------------- #
# modified by cc
ofp_error_type_rev_map = {     # modified by cc
  'OFPET_HELLO_FAILED'         : 0, 
  'OFPET_BAD_REQUEST'          : 1, 
  'OFPET_BAD_ACTION'           : 2,
  'OFPET_BAD_INSTRUCTION'      : 3,  
  'OFPET_BAD_MATCH'            : 4,
  'OFPET_FLOW_MOD_FAILED'      : 5, 
  'OFPET_GROUP_MOD_FAILED'     : 6,
  'OFPET_PORT_MOD_FAILED'      : 7, 
  'OFPET_TABLE_MOD_FAILED'     : 8,
  'OFPET_QUEUE_OP_FAILED'      : 9, 
  'OFPET_SWITCH_CONFIG_FAILED' : 10,
  'OFPET_ROLE_REQUEST_FAILED'  : 11,
  'OFPET_METER_MOD_FAILED'     : 12,
  'OFPET_TABLE_FEATURES_FAILED': 13,
  'OFPET_SOFTWARE_FAILED'      : 14,
  'OFPET_COUNTER_MOD_FAILED'   : 15,
  'OFPET_INSBLOCK_MOD_FAILED'  : 16,
  'OFPET_EXPERIMENTER_ERROR'   : 0xFFFF,
}

ofp_hello_failed_code_rev_map = {
  'OFPHFC_INCOMPATIBLE' : 0,
  'OFPHFC_EPERM'        : 1,
}

ofp_bad_request_code_rev_map = {  # modified by cc
  'OFPBRC_BAD_VERSION'            : 0,
  'OFPBRC_BAD_TYPE'               : 1,
  'OFPBRC_BAD_MULTIPART'          : 2,
  'OFPBRC_BAD_EXPERIMENTER'       : 3,
  'OFPBRC_BAD_EXPERIMENTER_TYPE'  : 4,
  'OFPBRC_EPERM'                  : 5,
  'OFPBRC_BAD_LEN'                : 6,
  'OFPBRC_BUFFER_EMPTY'           : 7,
  'OFPBRC_BUFFER_UNKNOWN'         : 8,
  'OFPBRC_BAD_TABLE_ID'           : 9,
  'OFPBRC_IS_SLAVE'               : 10,
  'OFPBRC_BAD_PORT'               : 11,
  'OFPBRC_BAD_PACKET'             : 12,
  'OFPBRC_MULTIPART_BUFFER_BUFFER_OVERFLOW' : 13,
}

ofp_bad_action_code_rev_map = {  # changed by cc
  'OFPBAC_BAD_TYPE'              : 0,
  'OFPBAC_BAD_LEN'               : 1,
  'OFPBAC_BAD_EXPERIMENTER'      : 2,
  'OFPBAC_BAD_EXPERIMENTER_TYPE' : 3,
  'OFPBAC_BAD_OUT_PORT'          : 4,
  'OFPBAC_BAD_ARGUMENT'          : 5,
  'OFPBAC_EPERM'                 : 6,
  'OFPBAC_TOO_MANY'              : 7,
  'OFPBAC_BAD_QUEUE'             : 8,
  'OFPBAC_BAD_OUT_GROUP'         : 9,
  'OFPBAC_MATCH_INCONSISTENT'    : 10,
  'OFPBAC_UNSUPPORTED_ORDER'     : 11,
  'OFPBAC_BAD_TAG'               : 12,
  'OFPBAC_BAD_SET_TYPE'          : 13,
  'OFPBAC_BAD_SET_LEN'           : 14,
  'OFPBAC_BAD_SET_ARGUMENT'      : 15,
}

ofp_bad_instruction_code_rev_map = {  # added by cc
  'OFPBIC_UNKNOW_INST'                : 0,
  'OFPBIC_UNSUP_INST'                 : 1,
  'OFPBIC_BAD_TABLE_ID'               : 2,
  'OFPBIC_UNSUP_METADATA'             : 3,
  'OFPBIC_UNSUP_METADATA_MASK'        : 4,
  'OFPBIC_BAD_EXPERIMENTER'           : 5,
  'OFPBIC_BAD_EXPERIMENTER_TYPE'      : 6,
  'OFPBIC_BAD_LEN'                    : 7,
  'OFPBIC_EPERM'                      : 8,
  'OFPBIC_TOO_MANY_ACTIONS'           : 9,
  'OFPBIC_TABLE_UNEXIST'              : 17,
  'OFPBIC_ENTRY_UNEXIST'              : 18,
  'OFPBIC_BAD_OFFSET'                 : 19,
  'OFPBIC_JUM_TO_INVALID_INST'        : 20,
}

ofp_bad_match_code_rev_map = {  # added by cc
  'OFPBMC_BAD_TYPE'             : 0,
  'OFPBMC_BAD_LEN'              : 1,
  'OFPBMC_BAD_TAG'              : 2,
  'OFPBMC_BAD_DL_ADDR_MASK'     : 3,
  'OFPBMC_BAD_NW_ADDR_MASK'     : 4,
  'OFPBMC_BAD_WILDCARD'         : 5,
  'OFPBMC_BAD_FIELD'            : 6,
  'OFPBMC_BAD_VALUE'            : 7,
  'OFPBMC_BAD_MASK'             : 8,
  'OFPBMC_BAD_PREERQ'           : 9,
  'OFPBMC_DUP_FIELD'            : 10,
  'OFPBMC_RPERM'                : 11,
}

ofp_flow_mod_failed_code_rev_map = {  #modified by cc
  'OFPFMFC_UNKNOWN'       : 0,
  'OFPFMFC_TABLE_FULL'    : 1,
  'OFPFMFC_BAD_TABLE_ID'  : 2,
  'OFPFMFC_OVERLAP'       : 3,
  'OFPFMFC_EPERM'         : 4,
  'OFPFMFC_BAD_TIMEOUT'   : 5,
  'OFPFMFC_BAD_COMMAND'   : 6,
  'OFPFMFC_BAD_FLAGS'     : 7,
  'OFPFMFC_ENTRY_EXIST'   : 8,
  'OFPFMFC_ENTRY_UNEXIST' : 9,
}

ofp_table_mod_failed_code_rev_map = {  #added by cc according to pofswitch
  'OFPTMFC_UNKNOWN'         : 0,
  'OFPTMFC_BAD_COMMAND'     : 1,
  'OFPTMFC_BAD_TABLE_TYPE'  : 2,
  'OFPTMFC_BAD_TABLE_ID'    : 3,
}

ofp_group_mod_failed_code_rev_map = {  # added by cc
  'OFPGMFC_GROUP_EXISTS'         : 0,
  'OFPGMFC_INVALID_GROUP'        : 1,
  'OFPGMFC_WEIGHT_UNSUPPORTED'   : 2,
  'OFPGMFC_OUT_OF_GROUPS'        : 3,
  'OFPGMFC_OUT_OF_BUCKETS'       : 4,
  'OFPGMFC_CHAINING_UNSUPPORTED' : 5,
  'OFPGMFC_WATCH_UNSUPPORTED'    : 6,
  'OFPGMFC_LOOP'                 : 7,
  'OFPGMFC_UNKNOWN_GROUP'        : 8,
  'OFPGMFC_CHAINED_GROUP'        : 9,
  'OFPGMFC_BAD_TYPE'             : 10,
  'OFPGMFC_BAD_COMMAND'          : 11,
  'OFPGMFC_BAD_BUCKET'           : 12,
  'OFPGMFC_BAD_WATCH'            : 13,
  'OFPGMFC_EPERM'                : 14,                  
}

ofp_meter_mod_failed_code_rev_map = {  # added by cc
  'OFPMMFC_UNKNOWN'         : 0,
  'OFPMMFC__METER_EXISTS'   : 1,
  'OFPMMFC_INVALID_METER'   : 2,
  'OFPMMFC_UNKNOWN_METER'   : 3,
  'OFPMMFC_BAD_COMMAND'     : 4,
  'OFPMMFC_BAD_FLAGS'       : 5,
  'OFPMMFC_BAD_RATE'        : 6,
  'OFPMMFC_BAD_BURST'       : 7,
  'OFPMMFC_BAD_BAND'        : 8,
  'OFPMMFC_BAD_BAND_VALUE'  : 9,
  'OFPMMFC_OUT_OF_METERS'   : 10,
  'OFPMMFC_OUT_OF_BANDS'    : 11,                 
}

ofp_counter_mod_failed_code_rev_map = {  # added by cc
  'OFPCMFC_UNKNOWN'           : 0,
  'OFPCMFC_BAD_COUNTER_ID'    : 1,
  'OFPCMFC_BAD_COMMAND'       : 2,
  'OFPCMFC_COUNTER_UNEXIST'   : 3,
  'OFPCMFC_COUNTER_EXIST'     : 4,         
}

ofp_port_mod_failed_code_rev_map = {
  'OFPPMFC_BAD_PORT'    : 0,
  'OFPPMFC_BAD_HW_ADDR' : 1,
}

ofp_queue_op_failed_code_rev_map = {
  'OFPQOFC_BAD_PORT'  : 0,
  'OFPQOFC_BAD_QUEUE' : 1,
  'OFPQOFC_EPERM'     : 2,
}

ofp_software_failed_code_rev_map ={  #added by cc
  'OFPSEC_OK'                              : 0,
  'OFPSEC_ALLOCATE_RESOURCE_FAILURE'       : 0x5001,
  'OFPSEC_ADD_EXIST_FLOW'                  : 0x5002,
  'OFPSEC_DELETE_UNEXIST_FLOW'             : 0x5003,
  'OFPSEC_COUNTER_REQUEST_FAILURE'         : 0x5004,
  'OFPSEC_DELETE_NOT_EMPTY_TABLE'          : 0x5005,
  'OFPSEC_INVALID_TABLE_TYPE'              : 0x6000,
  'OFPSEC_INVALID_KEY_LENGTH'              : 0x6001,
  'OFPSEC_INVALID_TABLE_SIZE'              : 0x6002,
  'OFPSEC_INVALID_MATCH_KEY'               : 0x6003,
  'OFPSEC_UNSUPPORT_INSTRUTION_LENGTH'     : 0x6004,
  'OFPSEC_UNSUPPORT_INSTRUTION_TYPE'       : 0x6005,
  'OFPSEC_UNSUPPORT_ACTION_LENGTH'         : 0x6006,
  'OFPSEC_UNSUPPORT_ACTION_TYPE'           : 0x6007,
  'OFPSEC_TABLE_NOT_CREATED'               : 0x6008,
  'OFPSEC_UNSUPPORT_COMMAND'               : 0x6009,
  'OFPSEC_UNSUPPORT_FLOW_TABLE_COMMAND'    : 0x600A,
  'OFPSEC_UPFORWARD_TOO_LARGE_PACKET'      : 0x600B,
  'OFPSEC_CREATE_SOCKET_FAILURE'           : 0x7001,
  'OFPSEC_CONNECT_SERVER_FAILURE'          : 0x7002,
  'OFPSEC_SEND_MSG_FAILURE'                : 0x7003,
  'OFPSEC_RECEIVE_MSG_FAILURE'             : 0x7004,
  'OFPSEC_WRONG_CHANNEL_STATE'             : 0x7005,
  'OFPSEC_WRITE_MSG_QUEUE_FAILURE'         : 0x7006,
  'OFPSEC_READ_MSG_QUEUE_FAILURE'          : 0x7007,
  'OFPSEC_MESSAGE_SIZE_TOO_BIG'            : 0x7008,
  'OFPSEC_IPC_SEND_FAILURE'                : 0x8001,
  'OFPSEC_CREATE_TASK_FAILURE'             : 0x8002,
  'OFPSEC_CREATE_MSGQUEUE_FAILURE'         : 0x8003,
  'OFPSEC_CREATE_TIMER_FAILURE'            : 0x8004,   
  'OFPSEC_ERROR'                           : 0xffff
}

# ------------ org.openflow.protocol.OFPhysicalPort -----------#
# modified by cc
# Delete items OFPPC_NO_STP, OFPPC_NO_RECV_STP, OFPPC_NO_FLOOD
ofp_port_config_rev_map = {
  'OFPPC_PORT_DOWN'    : 1,
  'OFPPC_NO_RECV'      : 4,
  'OFPPC_NO_FWD'       : 32,
  'OFPPC_NO_PACKET_IN' : 64,
}

# Delete items OFPPS_STP_LISTEN, OFPPS_STP_LEARN, OFPPS_STP_FORWARD, OFPPS_STP_BLOCK, OFPPS_STP_MASK
# Add items OFPPS_BLOCKED, OFPPS_LIVE
ofp_port_state_rev_map = {
  'OFPPS_LINK_DOWN'   : 1,
  'OFPPS_BLOCKED'     : 2,
  'OFPPS_LIVE'        : 4,
}

# Add items OFPPF_40GB_FD, OFPPF_100GB_FD, OFPPF_1TB_FD, OFPPF_OTHER
ofp_port_features_rev_map = {
  'OFPPF_10MB_HD'    : 1,
  'OFPPF_10MB_FD'    : 2,
  'OFPPF_100MB_HD'   : 4,
  'OFPPF_100MB_FD'   : 8,
  'OFPPF_1GB_HD'     : 16,
  'OFPPF_1GB_FD'     : 32,
  'OFPPF_10GB_FD'    : 64,
  'OFPPF_40GB_FD'    : 128,    # add
  'OFPPF_100GB_FD'   : 256,    # add
  'OFPPF_1TB_FD'     : 512,    # add
  'OFPPF_OTHER'      : 1024,   # add
  'OFPPF_COPPER'     : 2048,
  'OFPPF_FIBER'      : 4096,
  'OFPPF_AUTONEG'    : 8192,
  'OFPPF_PAUSE'      : 16384,
  'OFPPF_PAUSE_ASYM' : 32768,
}

# ------------ org.openflow.protocol.OFFeaturesReply ---------- #
# modified by cc
# Delete OFPC_STP, OFPC_RESERVED, OFPC_ARP_MATCH_IP
# Add OPFC_GROUP_STATS, OPFC_PROT_BLOCKED
ofp_capabilities_rev_map = {
  'OFPC_FLOW_STATS'   : 1,
  'OFPC_TABLE_STATS'  : 2,
  'OFPC_PORT_STATS'   : 4,
  'OFPC_GROUP_STATS'  : 8,
  'OFPC_IP_REASM'     : 32,
  'OFPC_QUEUE_STATS'  : 64,
  'OFPC_PORT_BLOCKED' : 256,
}

# ----------- org.openflow.protocol.OFSwitchConfig ----------- #
ofp_config_flags_rev_map = {
  'OFPC_FRAG_NORMAL' : 0,
  'OFPC_FRAG_DROP'   : 1,
  'OFPC_FRAG_REASM'  : 2,
  'OFPC_FRAG_MASK'   : 3,
}

# -------------- org.openflow.protocol.OFFlowMod ------------- #
ofp_flow_mod_command_rev_map = {
  'OFPFC_ADD'           : 0,
  'OFPFC_MODIFY'        : 1,
  'OFPFC_MODIFY_STRICT' : 2,
  'OFPFC_DELETE'        : 3,
  'OFPFC_DELETE_STRICT' : 4,
}

ofp_flow_mod_flags_rev_map = {
  'OFPFF_SEND_FLOW_REM' : 1,
  'OFPFF_CHECK_OVERLAP' : 2,
  'OFPFF_EMERG'         : 4,
}

# -------------- org.openflow.protocol.OFMultipartReply ------------- #
ofp_stats_reply_flags_rev_map = {
  'OFPSF_REPLY_MORE' : 1,    # or 'REPLY_MORE'?
}

# -------------- org.openflow.protocol.OFPacketIn ------------- #
# modified by cc
# Add INVALID_TTL
ofp_packet_in_reason_rev_map = {
  'OFPR_NO_MATCH'    : 0,
  'OFPR_ACTION'      : 1,
  'OFPR_INVALID_TTL' : 2,
}

# -------------- org.openflow.protocol.OFFlowRemoved ----------- #
ofp_flow_removed_reason_rev_map = {
  'OFPRR_IDLE_TIMEOUT' : 0,
  'OFPRR_HARD_TIMEOUT' : 1,
  'OFPRR_DELETE'       : 2,
}

# -------------- org.openflow.protocol.OFPortStatus ------------- #
ofp_port_reason_rev_map = {
  'OFPPR_ADD'    : 0,
  'OFPPR_DELETE' : 1,
  'OFPPR_MODIFY' : 2,
}

# -------------- org.openflow.protocol.OFPort ------------- #
# modified by cc
ofp_port_rev_map = {
  'OFPP_MAX'        : 65280,
  'OFPP_IN_PORT'    : 65528,
  'OFPP_TABLE'      : 65529,
  'OFPP_NORMAL'     : 65530,
  'OFPP_FLOOD'      : 65531,
  'OFPP_ALL'        : 65532,
  'OFPP_CONTROLLER' : 65533,
  'OFPP_LOCAL'      : 65534,
  'OFPP_ANY'        : 65535,   # changed from 'OFPP_NONE'
}

# -------------- org.openflow.protocol.OFMatch ------------- #
# modified by cc
ofp_flow_wildcards_rev_map = {
  'OFPFW_IN_PORT'      : 1,       # Switch input port
  'OFPFW_DL_VLAN'      : 2,       # VLAN id
  'OFPFW_DL_SRC'       : 4,       # Ethernet source address
  'OFPFW_DL_DST'       : 8,       # Ethernet destination address
  'OFPFW_DL_TYPE'      : 16,      # Ethernet frame type
  'OFPFW_NW_PROTO'     : 32,      # IP protocol
  'OFPFW_TP_SRC'       : 64,      # TCP/UDP source port
  'OFPFW_TP_DST'       : 128,     # TCP/UDP destination port
  'OFPFW_DL_VLAN_PCP'  : 1048576, # VLAN priority, 1<<20
  'OFPFW_NW_TOS'       : 1<<21,
}
OFPFW_NW_DST_BITS      = 6
OFPFW_NW_SRC_BITS      = 6
OFPFW_NW_SRC_SHIFT     = 8
OFPFW_NW_DST_SHIFT     = 14
OFPFW_NW_SRC_ALL       = 8192     # 32 << OFPFW_NW_SRC_SHIFT
OFPFW_NW_SRC_MASK      = 16128    # ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT
OFPFW_NW_DST_ALL       = 524288   # 32 << OFPFW_NW_DST_SHIFT
OFPFW_NW_DST_MASK      = 1032192  # ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT
# Note: Need to handle all flags that are set in this.
# glob-all masks in the packet handling methods.
# (Esp. ofp_match.from_packet)
# Otherwise, packets are not being matched as they should
OFPFW_ALL              = ((1 << 22) - 1)

# -------------- org.openflow.protocol.OFGroupMod ------------- #
# add by cc
ofp_group_type_rev_map = {
    'OFPGT_ALL'       : 0,
    'OFPGT_SELECT'    : 1,
    'OFPGT_INDIRECT'  : 2,
    'OFPGT_FF'        : 3,     
}
                            
ofp_group_mod_cmd_rev_map = {
    'OFPGC_ADD'      : 0,
    'OFPGC_MODIFY'   : 1,
    'OFPGC_DELETE'   : 2,
}

# -------------- org.openflow.protocol.OFMeterMod ------------- #
# add by cc
ofp_meter_mod_com_rev_map = {
    'OFPMC_ADD'     : 0,
    'OFPMC_MODIFY'  : 1,
    'OFPMC_DELETE'  : 2,
}

# -------------- org.openflow.protocol.OFCounter ------------- #
# add by cc
ofp_counter_mod_com_rev_map = {
    'OFPCC_ADD'        : 0,
    'OFPCC_DELETE'     : 1,
    'OFPCC_CLEAR'      : 2,
    'OFPCC_QUERY'      : 3,
    'OFPCC_QUERYREPLY' : 4,                    
}

# ---------- org.openflow.protocol.table.OFTableType ---------- #
# add by cc
ofp_table_type_rev_map={
    'OF_MM_TABLE'        : 0,  # MaskedMatch Table
    'OF_LPM_TABLE'       : 1,  # LongestPrefixMatch Table
    'OF_EM_TABLE'        : 2,  # ExactMatch Table
    'OF_LINEAR_TABLE'    : 3,  # Linear Table
    'OF_MAX_TABLE_TYPE'  : 4,
}

# ---------- org.openflow.protocol.table.OFTableMod ---------- #
# add by cc
ofp_table_mod_cmd_rev_map={
    'OFPTC_ADD'          : 0,
    'OFPTC_MODIFY'       : 1,
    'OFPTC_DELETE'       : 2,
}

# ------ org.openflow.protocol.instruction.OFInstructionCalculateField ------ #
# add by cc 
ofp_calc_type_rev_map = {
    'OFPCT_ADD'         : 0,  # +
    'OFPCT_SUBTRACT'    : 1,  # -
    'OFPCT_LEFT_SHIFT'  : 2,  # <<
    'OFPCT_RIGHT_SHIFT' : 3,  # >>
    'OFPCT_BITWISE_ADD' : 4,  # &
    'OFPCT_BITWISE_OR'  : 5,  # |
    'OFPCT_BITWISE_XOR' : 6,  # ^
    'OFPCT_BITWISE_NOR' : 7,
}

# -------------- org.openflow.protocol.action.OFActionDrop ------------- #
# add by cc
ofp_drop_reason_rev_map = {
    'OFPDT_TIMEOUT'  : 0,
    'OFPDT_HIT_MISS' : 1,
    'OFPDT_UNKNOW'   : 2
}

"""
ofp_instruction_type_rev_map = {   # add by cc
    'GOTO_TABLE'                : 1,
    'WRITE_METADATA'            : 2,
    'WRITE_ACTIONS'             : 3,
    'APPLY_ACTIONS'             : 4,
    'CLEAR_ACTIONS'             : 5,
    'METER'                     : 6,
    'WRITE_METADATA_FROM_PACKET': 7,
    'GOTO_DIRECT_TABLE'         : 8,
    'CONDITIONAL_JMP'           : 9,
    'CALCULATE_FIELD'           : 10,
    'MOVE_PACKET_OFFSET'        : 11,
    'EXPERIMENTER'              : 0xffff,                                
}
"""

NO_BUFFER = 4294967295


# -------------- org.openflow.protocol.OFGlobal -------------- #
OFP_INVALID_VALUE = 0xFFFFFFFF             # Define invalid value. {@value}.
OFP_NAME_MAX_LENGTH = 64                   # Define the length of device name.
OFP_ERROR_STRING_MAX_LENGTH = 256          # Define the max length of error string.
OFP_PACKET_IN_MAX_LENGTH = 2048            # Define the max length of packetin. {@value}.
OFP_MAX_FIELD_LENGTH_IN_BYTE = 16          # Define the max length in byte unit of match field. {@value}.
OFP_MAX_MATCH_FIELD_NUM = 8                # Define the max number of match field in one flow entry. {@value}.
OFP_MAX_INSTRUCTION_NUM = 6                # Define the max instruction number of one flow entry. {@value}.
OFP_MAX_PROTOCOL_FIELD_NUM  = 8            # Define the max field number of one protocol. {@value}.
OFP_MAX_ACTION_NUMBER_PER_INSTRUCTION = 6  # Define the max action number in one instruction. {@value}.
OFP_MAX_ACTION_NUMBER_PER_GROUP = 6        # Define the max action number in one group. {@value}.
OFP_MAX_ACTION_LENGTH = 44                 # Define the max action length in unit of byte. {@value}.
OFP_MAX_INSTRUCTION_LENGTH = (8 + 8 + OFP_MAX_ACTION_NUMBER_PER_INSTRUCTION * (OFP_MAX_ACTION_LENGTH + 4)) #Define the max instruction length in unit of byte. {@value}.


# ----------------------------------------------------------------------


# ----------------------------------------------------------------------
# Structure definitions
# ----------------------------------------------------------------------

#1. Openflow Header
class ofp_header (ofp_base):
    _MIN_LENGTH = 8
    def __init__ (self, **kw):
        self.version = OFP_VERSION
        #self.header_type = None # Set via class decorator
        self._xid = None
        if 'header_type' in kw: 
            self.header_type = kw.pop('header_type')
        initHelper(self, kw)

    @property
    def xid (self):
        if self._xid is None:
            self._xid = generate_xid()
        return self._xid

    @xid.setter
    def xid (self, val):
        self._xid = val

    def _validate (self):
        if self.header_type not in ofp_type_map:
            return "type is not a known message type"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!BBHL", self.version, self.header_type,
            len(self), self.xid)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        return offset,length

    def _unpack_header (self, raw, offset):
        offset,(self.version, self.header_type, length, self.xid) = \
            _unpack("!BBHL", raw, offset)
        return offset,length

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.version != other.version: return False
        if self.header_type != other.header_type: return False
        if len(self) != len(other): return False
        if self.xid != other.xid: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'version: ' + str(self.version) + '\n'
        outstr += prefix + 'type:    ' + str(self.header_type)# + '\n'
        outstr += " (" + ofp_type_map.get(self.header_type, "Unknown") + ")\n"
        try:
            outstr += prefix + 'length:  ' + str(len(self)) + '\n'
        except:
            pass
        outstr += prefix + 'xid:     ' + str(self.xid) + '\n'
        return outstr

    def __str__ (self):
        return self.__class__.__name__ + "\n  " + self.show('  ').strip()


class ofp_stats_body_base (ofp_base):
    """
    Base class for stats bodies
    """
    # Stats bodies don't actually have a type field in OpenFlow --
    # the type information is in the request or reply.  It's really
    # convenient, though, so we add it.  Note that you generally
    # don't need to set this yourself -- the openflow_stats_XXX
    # decorator will do it for you.
    _type = None

    """
    def unpack (self, data, offset=0, avail=None):
    """


class ofp_action_base (ofp_base):
    """
    Base class for actions

    This is sort of the equivalent of ofp_action_header in the spec.
    However, ofp_action_header as the spec defines it is not super
    useful for us, as it has the padding in it.
    """
    # modified by cc
    _MIN_LENGTH = 4
    _MAX_LENGTH = 4 + OFP_MAX_ACTION_LENGTH   # 48
    type = None       # ofp_action_type
    
    def __init__(self, **kw):
        self.length = ofp_action_base._MIN_LENGTH  # changed in subclass
        
        initHelper(self, kw)
        
    def pack(self):
        assert self._assert()
        packed = b""
        packed += struct.pack("!HH", self.type, self.length)
        return packed
    
    def unpack(self, raw, offset = 0):
        offset, length = self._unpack_header(raw, offset)
        return offset, length
    
    def _unpack_header(self, raw, offset):
        offset,(self.type, length) = _unpack("!HH", raw, offset)
        return offset, length
        
    def __eq__(self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        return True
    
    def __str__(self):
        return self.__class__.__name__ + "\n  " + self.show('  ').strip()
    
    def show(self, prefix=''):
        outstr = ''
        outstr += prefix + 'type:   ' + str(self.type)
        outstr += " (" + ofp_action_type_map.get(self.type, "Unknown") + ")\n"
        outstr += prefix + 'length: ' + str(self.length) + '\n'

    @classmethod
    def unpack_new (cls, raw, offset=0):
        """
        Unpacks wire format into the appropriate action object.
    
        Returns newoffset,object
        """
        o = cls()
        r = o.unpack(raw, offset)
        assert (r-offset) == len(o), o
        return (r, o)


#2. Common Structures
##2.1 Port Structures
class ofp_phy_port (ofp_base):
    """
    modified to suit for pof
    changed by cc
    """
    def __init__ (self, **kw):
        self.port_no = 0
        self.device_id = 0
        self.hw_addr = EMPTY_ETH
        self.name = ""
        self.config = 0
        self.state = 0
        self.curr = 0
        self.advertised = 0
        self.supported = 0
        self.peer = 0
        self.curr_speed = 0
        self.max_speed = 0
        self.of_enable = 0
        initHelper(self, kw)

    def enable_config (self, mask):
        """
        Turn on selected config bits
        """
        return self.set_config(0xffFFffFF, mask)

    def disable_config (self, mask):
        """
        Turn off selected config bits
        """
        return self.set_config(0, mask)

    def set_config (self, config, mask):
        """
        Updates the specified config bits
    
        Returns which bits were changed
        """
        old = self.config
        self.config &= ~mask
        self.config |= config
        return old ^ self.config
    
    """
    def __str__ (self):
        return "%s:%i" % (self.name, self.port_no)
    """

    def _validate (self):
        if isinstance(self.hw_addr, bytes) and len(self.hw_addr) == 6:
            pass
        elif not isinstance(self.hw_addr, EthAddr):
            return "hw_addr is not a valid format"
        if len(self.name) > OFP_MAX_PORT_NAME_LEN:
            return "name is too long"
        return None

    def pack (self):  #FIXME:
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!LL", self.port_no, self.device_id)
        packed += (self.hw_addr if isinstance(self.hw_addr, bytes) else
                   self.hw_addr.toRaw())
        packed += _PAD2
        #packed += struct.pack( "!64s" ,self.name)
        packed += self.name.ljust(OFP_MAX_PORT_NAME_LEN,'\0')
        packed += struct.pack("!LLLLLLLLB", self.config, self.state, self.curr, self.advertised,
            self.supported, self.peer, self.curr_speed, self.max_speed, self.of_enable)
        packed += _PAD * 7
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.port_no, self.device_id) = _unpack("!LL", raw, offset)
        offset,self.hw_addr = _readether(raw, offset)
        offset = _skip(raw, offset, 2)
        offset,self.name = _readzs(raw, offset, OFP_MAX_PORT_NAME_LEN)
        offset,(self.config, self.state, self.curr, self.advertised, self.supported,
                self.peer, self.curr_speed, self.max_speed, self.of_enable) = _unpack("!LLLLLLLLB", raw, offset)
        offset = _skip(raw, offset, 7)
        assert offset - _offset == len(self)
        return offset
  
    @staticmethod
    def __len__ ():
        return 120       # changed from 48 to 120

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.port_no != other.port_no: return False
        if self.hw_addr != other.hw_addr: return False
        if self.name != other.name: return False
        if self.config != other.config: return False
        if self.state != other.state: return False
        if self.curr != other.curr: return False
        if self.advertised != other.advertised: return False
        if self.supported != other.supported: return False
        if self.peer != other.peer: return False
        return True

    def __cmp__ (self, other):
        if type(other) != type(self): return id(self)-id(other)
        if self.port_no < other.port_no: return -1
        if self.port_no > other.port_no: return 1
        if self == other: return 0
        return id(self)-id(other)

    def __hash__(self, *args, **kwargs):
        return hash(self.port_no) ^ hash(self.hw_addr) ^ \
               hash(self.name) ^ hash(self.config) ^ \
               hash(self.state) ^ hash(self.curr) ^ \
               hash(self.advertised) ^ hash(self.supported) + \
               hash(self.peer)

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'port_id:    ' + str(self.port_no) + '\n'
        outstr += prefix + 'device_id:  ' + str(self.device_id) + '\n'
        outstr += prefix + 'hw_addr:    ' + str(EthAddr(self.hw_addr)) + '\n'
        outstr += prefix + 'name:       ' + str(self.name) + '\n'
        outstr += prefix + 'config:     ' + str(self.config) + '\n'
        outstr += prefix + 'state:      ' + str(self.state) + '\n'
        outstr += prefix + 'curr:       ' + str(self.curr) + '\n'
        outstr += prefix + 'advertised: ' + str(self.advertised) + '\n'
        outstr += prefix + 'supported:  ' + str(self.supported) + '\n'
        outstr += prefix + 'peer:       ' + str(self.peer) + '\n'
        outstr += prefix + 'curr_speed: ' + str(self.curr_speed) + '\n'
        outstr += prefix + 'max_speed:  ' + str(self.max_speed) + '\n'
        outstr += prefix + 'of_enable:  ' + str(self.of_enable) + '\n'
        return outstr

    def __repr__(self):
        return self.show()


##2.3 Flow Match Structures
class ofp_match (ofp_base):
    adjust_wildcards = True # Set to true to "fix" outgoing wildcards

    @classmethod
    def from_packet (cls, packet, in_port = None, spec_frags = False):
        """
        Constructs an exact match for the given packet
    
        @param in_port The switch port the packet arrived on if you want
                       the resulting match to have its in_port set.
                       If "packet" is a packet_in, this is ignored.
        @param packet  A pox.packet.ethernet instance or a packet_in
        @param spec_frags Handle IP fragments as specified in the spec.
        """
        if isinstance(packet, ofp_packet_in):
            in_port = packet.in_port
            packet = ethernet(packet.data)
        assert assert_type("packet", packet, ethernet, none_ok=False)
    
        match = cls()
    
        if in_port is not None:
            match.in_port = in_port
    
        match.dl_src = packet.src
        match.dl_dst = packet.dst
        match.dl_type = packet.type
        p = packet.next
    
        # Is this in the spec?
        if packet.type < 1536:
            match.dl_type = OFP_DL_TYPE_NOT_ETH_TYPE
        # LLC then VLAN?  VLAN then LLC?
        if isinstance(p, llc):
            if p.has_snap and p.oui == '\0\0\0':
                match.dl_type = p.eth_type
                p = p.next
        if isinstance(p, vlan):
            match.dl_type = p.eth_type
            match.dl_vlan = p.id
            match.dl_vlan_pcp = p.pcp
            p = p.next
        else:
            match.dl_vlan = OFP_VLAN_NONE
            match.dl_vlan_pcp = 0
    
        if isinstance(p, ipv4):
            match.nw_src = p.srcip
            match.nw_dst = p.dstip
            match.nw_proto = p.protocol
            match.nw_tos = p.tos
            if spec_frags and ((p.flags & p.MF_FLAG) or p.frag != 0):
                # This seems a bit strange, but see page 9 of the spec.
                match.tp_src = 0
                match.tp_dst = 0
                return match
            p = p.next
    
            if isinstance(p, udp) or isinstance(p, tcp):
                match.tp_src = p.srcport
                match.tp_dst = p.dstport
            elif isinstance(p, icmp):
                match.tp_src = p.type
                match.tp_dst = p.code
        elif isinstance(p, arp):
            if p.opcode <= 255:
                match.nw_proto = p.opcode
                match.nw_src = p.protosrc
                match.nw_dst = p.protodst
    
        return match

    def clone (self):
        n = ofp_match()
        for k,v in ofp_match_data.iteritems():
            setattr(n, '_' + k, getattr(self, '_' + k))
        n.wildcards = self.wildcards
        return n

    def flip (self, in_port = True):
        """
        Return version of this match with src and dst fields swapped
    
        in_port can be:
          True  : Include same in_port in new match
          Other : Set Other as in_port in new match
        """
        reversed = self.clone()
        for field in ('dl','nw','tp'):
            setattr(reversed, field + '_src', getattr(self, field + '_dst'))
            setattr(reversed, field + '_dst', getattr(self, field + '_src'))
        if in_port is not True:
            reversed.in_port = in_port
    
        return reversed

    def __init__ (self, **kw):
        self._locked = False
    
        for k,v in ofp_match_data.iteritems():
            setattr(self, '_' + k, v[0])
    
        self.wildcards = self._normalize_wildcards(OFPFW_ALL)
    
        # This is basically initHelper(), but tweaked slightly since this
        # class does some magic of its own.
        for k,v in kw.iteritems():
            if not hasattr(self, '_'+k):
                raise TypeError(self.__class__.__name__ + " constructor got "
                  + "unexpected keyword argument '" + k + "'")
            setattr(self, k, v)

    def get_nw_dst (self):
        if (self.wildcards & OFPFW_NW_DST_ALL) == OFPFW_NW_DST_ALL:
            return (None, 0)
    
        w = (self.wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT
        return (self._nw_dst,32-w if w <= 32 else 0)
    
    def get_nw_src (self):
        if (self.wildcards & OFPFW_NW_SRC_ALL) == OFPFW_NW_SRC_ALL:
            return (None, 0)
    
        w = (self.wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT
        return (self._nw_src,32-w if w <= 32 else 0)

    def set_nw_dst (self, *args, **kw):
        a = self._make_addr(*args, **kw)
        if a is None:
            self._nw_dst = ofp_match_data['nw_dst'][0]
            self.wildcards &= ~OFPFW_NW_DST_MASK
            self.wildcards |= ofp_match_data['nw_dst'][1]
            return
        self._nw_dst = a[0]
        self.wildcards &= ~OFPFW_NW_DST_MASK
        self.wildcards |= ((32-a[1]) << OFPFW_NW_DST_SHIFT)

    def set_nw_src (self, *args, **kw):
        a = self._make_addr(*args, **kw)
        if a is None:
            self._nw_src = ofp_match_data['nw_src'][0]
            self.wildcards &= ~OFPFW_NW_SRC_MASK
            self.wildcards |= ofp_match_data['nw_src'][1]
            return
        self._nw_src = a[0]
        self.wildcards &= ~OFPFW_NW_SRC_MASK
        self.wildcards |= ((32-a[1]) << OFPFW_NW_SRC_SHIFT)

    def _make_addr (self, ipOrIPAndBits, bits=None):
        if ipOrIPAndBits is None: return None
        b = None
        if type(ipOrIPAndBits) is tuple:
            ip = ipOrIPAndBits[0]
            b = int(ipOrIPAndBits[1])
    
        if (type(ipOrIPAndBits) is str) and (len(ipOrIPAndBits) != 4):
            if ipOrIPAndBits.find('/') != -1:
                #s = ipOrIPAndBits.split('/')
                s = parse_cidr(ipOrIPAndBits, infer=False)
                ip = s[0]
                b = int(s[1]) if b is None else b
            else:
                ip = ipOrIPAndBits
                b = 32 if b is None else b
        else:
            ip = ipOrIPAndBits
            b = 32 if b is None else b
    
        if type(ip) is str:
            ip = IPAddr(ip)
    
        if bits != None: b = bits
        if b > 32: b = 32
        elif b < 0: b = 0
    
        return (ip, b)

    def __setattr__ (self, name, value):
        if name == '_locked':
            super(ofp_match,self).__setattr__(name, value)
            return
    
        if self._locked:
            raise AttributeError('match object is locked')
    
        if name not in ofp_match_data:
            self.__dict__[name] = value
            return
    
        if name == 'nw_dst' or name == 'nw_src':
            # Special handling
            getattr(self, 'set_' + name)(value)
            return value
    
        if value is None:
            setattr(self, '_' + name, ofp_match_data[name][0])
            self.wildcards |= ofp_match_data[name][1]
        else:
            setattr(self, '_' + name, value)
            self.wildcards = self.wildcards & ~ofp_match_data[name][1]
    
        return value

    def __getattr__ (self, name):
        if name in ofp_match_data:
            if ( (self.wildcards & ofp_match_data[name][1])
               == ofp_match_data[name][1] ):
                # It's wildcarded -- always return None
                return None
            if name == 'nw_dst' or name == 'nw_src':
                # Special handling
                return getattr(self, 'get_' + name)()[0]
            return self.__dict__['_' + name]
        raise AttributeError("attribute not found: "+name)

    def _validate (self):
        # TODO
        return None

    def _prereq_warning (self):
        # Only checked when assertions are on
        if not _logger: return True
        om = self.clone()
        om.fix()
    
        if om == self: return True
    
        msg = "Fields ignored due to unspecified prerequisites: "
        wcs = []
    
        for name in ofp_match_data.keys():
            if getattr(self,name) is None:
                continue
            if getattr(om,name) is not None:
                continue
            wcs.append(name)
    
        msg = msg + " ".join(wcs)
    
        _log(warn = msg)
        _log(debug = "Problematic match: " + str(self))
    
        return True # Always; we don't actually want an assertion error

    def pack (self, flow_mod=False):
        assert self._assert()
    
        packed = b""
        if self.adjust_wildcards and flow_mod:
            wc = self._wire_wildcards(self.wildcards)
            assert self._prereq_warning()
        else:
            wc = self.wildcards
        packed += struct.pack("!LH", wc, self.in_port or 0)
        if self.dl_src is None:
            packed += EMPTY_ETH.toRaw()
        elif type(self.dl_src) is bytes:
            packed += self.dl_src
        else:
            packed += self.dl_src.toRaw()
        if self.dl_dst is None:
            packed += EMPTY_ETH.toRaw()
        elif type(self.dl_dst) is bytes:
            packed += self.dl_dst
        else:
            packed += self.dl_dst.toRaw()
    
        def check_ip(val):
            return (val or 0) if self.dl_type == 0x0800 else 0
        def check_ip_or_arp(val):
            return (val or 0) if self.dl_type == 0x0800 \
                               or self.dl_type == 0x0806 else 0
        def check_tp(val):
            return (val or 0) if self.dl_type == 0x0800 \
                               and self.nw_proto in (1,6,17) else 0
    
        packed += struct.pack("!HB", self.dl_vlan or 0, self.dl_vlan_pcp or 0)
        packed += _PAD # Hardcode padding
        packed += struct.pack("!HBB", self.dl_type or 0,
            check_ip(self.nw_tos), check_ip_or_arp(self.nw_proto))
        packed += _PAD2 # Hardcode padding
        def fix (addr):
            if addr is None: return 0
            if type(addr) is int: return addr & 0xffFFffFF
            if type(addr) is long: return addr & 0xffFFffFF
            return addr.toUnsigned()
    
        packed += struct.pack("!LLHH", check_ip_or_arp(fix(self.nw_src)),
            check_ip_or_arp(fix(self.nw_dst)),
            check_tp(self.tp_src), check_tp(self.tp_dst))
    
        return packed

    def _normalize_wildcards (self, wildcards):
        """
        nw_src and nw_dst values greater than 32 mean the same thing as 32.
        We normalize them here just to be clean and so that comparisons act
        as you'd want them to.
        """
        if ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT) > 32:
            wildcards &= ~OFPFW_NW_SRC_MASK
            wildcards |= (32 << OFPFW_NW_SRC_SHIFT)
        if ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT) > 32:
            wildcards &= ~OFPFW_NW_DST_MASK
            wildcards |= (32 << OFPFW_NW_DST_SHIFT)
        return wildcards

    def _wire_wildcards (self, wildcards):
        """
        Normalize the wildcard bits
    
        Note the following from the OpenFlow 1.1 spec:
    
          Protocol-specific fields within ofp_match will be ignored within
          a single table when the corresponding protocol is not specified in the
          match.  The IP header and transport header fields
          will be ignored unless the Ethertype is specified as either IPv4 or
          ARP. The tp_src and tp_dst fields will be ignored unless the network
          protocol specified is as TCP, UDP or SCTP. Fields that are ignored
          don't need to be wildcarded and should be set to 0.
    
        OpenFlow 1.0.1 Section 3.4 actually has an improved version of the above,
        but we won't quote it here because it seems to have a restrictive license.
        """
        #TODO: Set the masked fields to 0.
        if self.dl_type == 0x0800:
            # IP
            if  self.nw_proto not in (1,6,17):
                # not TCP/UDP/ICMP -> Clear TP wildcards for the wire
                return wildcards & ~(OFPFW_TP_SRC | OFPFW_TP_DST)
            else:
                return wildcards
        elif self.dl_type == 0x0806:
            # ARP: clear NW_TOS / TP wildcards for the wire
            return wildcards & ~( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST)
        else:
            # not even IP. Clear NW/TP wildcards for the wire
            return wildcards & ~( OFPFW_NW_TOS | OFPFW_NW_PROTO
                | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK
                | OFPFW_TP_SRC | OFPFW_TP_DST)

    def fix (self):
        """
        Removes unmatchable fields
    
        The logic in this should exactly match that in _wire_wildcards()
        """
        if self.dl_type == 0x0800:
            # IP
            if  self.nw_proto not in (1,6,17):
                # not TCP/UDP/ICMP -> Clear TP wildcards for the wire
                self.tp_src = None
                self.tp_dst = None
                return
        elif self.dl_type == 0x0806:
            # ARP: clear NW_TOS / TP wildcards for the wire
            self.tp_src = None
            self.tp_dst = None
            self.nw_tos = None
            return
        else:
            # not even IP. Clear NW/TP wildcards for the wire
            self.nw_tos = None
            self.nw_proto = None
            self.nw_src = None
            self.nw_dst = None
            self.tp_src = None
            self.tp_dst = None
            return

    def _unwire_wildcards (self, wildcards):
        """
        Normalize the wildcard bits from the openflow wire representation.
    
        Note this atrocity from the OF1.1 spec:
        Protocol-specific fields within ofp_match will be ignored within
        a single table when the corresponding protocol is not specified in the
        match.  The IP header and transport header fields
        will be ignored unless the Ethertype is specified as either IPv4 or
        ARP. The tp_src and tp_dst fields will be ignored unless the network
        protocol specified is as TCP, UDP or SCTP. Fields that are ignored
        don't need to be wildcarded and should be set to 0.
        """
        if self._dl_type == 0x0800:
            # IP
            if  self._nw_proto not in (1,6,17):
                # not TCP/UDP/ICMP -> Set TP wildcards for the object
                return wildcards | (OFPFW_TP_SRC | OFPFW_TP_DST)
            else:
                return wildcards
        elif self._dl_type == 0x0806:
            # ARP: Set NW_TOS / TP wildcards for the object
            return wildcards | ( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST)
        else:
            # not even IP. Set NW/TP wildcards for the object
            return wildcards | ( OFPFW_NW_TOS | OFPFW_NW_PROTO
                                 | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK
                                 | OFPFW_TP_SRC | OFPFW_TP_DST)


    @property
    def is_wildcarded (self):
        return self.wildcards & OFPFW_ALL != 0

    @property
    def is_exact (self):
        return not self.is_wildcarded

    def unpack (self, raw, offset=0, flow_mod=False):
        _offset = offset
        offset,(wildcards, self._in_port) = _unpack("!LH",raw, offset)
        offset,self._dl_src = _readether(raw, offset)
        offset,self._dl_dst = _readether(raw, offset)
        offset,(self._dl_vlan, self._dl_vlan_pcp) = \
            _unpack("!HB", raw, offset)
        offset = _skip(raw, offset, 1)
        offset,(self._dl_type, self._nw_tos, self._nw_proto) = \
            _unpack("!HBB", raw, offset)
        offset = _skip(raw, offset, 2)
        offset,self._nw_src = _readip(raw, offset)
        offset,self._nw_dst = _readip(raw, offset)
        offset,(self._tp_src, self._tp_dst) = _unpack("!HH", raw, offset)
    
        # Only unwire wildcards for flow_mod
        self.wildcards = self._normalize_wildcards(
            self._unwire_wildcards(wildcards) if flow_mod else wildcards)
    
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 40

    def hash_code (self):
        """
        generate a hash value for this match
    
        This generates a hash code which might be useful, but without locking
        the match object.
        """
    
        h = self.wildcards
        for f in ofp_match_data:
            v = getattr(self, f)
            if type(v) is int:
                h ^= v
            elif type(v) is long:
                h ^= v
            else:
                h ^= hash(v)
    
        return int(h & 0x7fFFffFF)

    def __hash__ (self):
        self._locked = True
        return self.hash_code()

    def matches_with_wildcards (self, other, consider_other_wildcards=True):
        """
        Test whether /this/ match completely encompasses the other match.
    
        if consider_other_wildcards, then the *other* match must also have
        no more wildcards than we do (it must be no wider than we are)
    
        Important for non-strict modify flow_mods etc.
        """
        assert assert_type("other", other, ofp_match, none_ok=False)
    
        # shortcut for equal matches
        if self == other: return True
    
        if consider_other_wildcards:
            # Check that other doesn't have more wildcards than we do -- it
            # must be narrower (or equal) to us.
            self_bits  = self.wildcards&~(OFPFW_NW_SRC_MASK|OFPFW_NW_DST_MASK)
            other_bits = other.wildcards&~(OFPFW_NW_SRC_MASK|OFPFW_NW_DST_MASK)
            if (self_bits | other_bits) != self_bits: return False
    
        def match_fail (mine, others):
            if mine is None: return False # Wildcarded
            return mine != others
    
        if match_fail(self.in_port, other.in_port): return False
        if match_fail(self.dl_vlan, other.dl_vlan): return False
        if match_fail(self.dl_src, other.dl_src): return False
        if match_fail(self.dl_dst, other.dl_dst): return False
        if match_fail(self.dl_type, other.dl_type): return False
        if match_fail(self.nw_proto, other.nw_proto): return False
        if match_fail(self.tp_src, other.tp_src): return False
        if match_fail(self.tp_dst, other.tp_dst): return False
        if match_fail(self.dl_vlan_pcp, other.dl_vlan_pcp): return False
        if match_fail(self.nw_tos, other.nw_tos): return False
    
        #FIXME: The two ??? checks below look like they compare other
        #       wildcards always -- even when consider_other_wildcards=False.
        #       Is this intentional?  (I think it might be subtly wrong and
        #       we actually may need to mask off some bits and do the
        #       inNetwork check or something...)
    
        self_nw_src = self.get_nw_src()
        if self_nw_src[0] is not None:
            other_nw_src = other.get_nw_src()
            if self_nw_src[1] > other_nw_src[1]: return False #???
            if not IPAddr(other_nw_src[0]).inNetwork(
                (self_nw_src[0], self_nw_src[1])): return False
    
        self_nw_dst = self.get_nw_dst()
        if self_nw_dst[0] is not None:
            other_nw_dst = other.get_nw_dst()
            if self_nw_dst[1] > other_nw_dst[1]: return False #???
            if not IPAddr(other_nw_dst[0]).inNetwork(
                (self_nw_dst[0], self_nw_dst[1])): return False
    
        return True

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.wildcards != other.wildcards: return False
        if self.in_port != other.in_port: return False
        if self.dl_src != other.dl_src: return False
        if self.dl_dst != other.dl_dst: return False
        if self.dl_vlan != other.dl_vlan: return False
        if self.dl_vlan_pcp != other.dl_vlan_pcp: return False
        if self.dl_type != other.dl_type: return False
        if self.nw_tos != other.nw_tos: return False
        if self.nw_proto != other.nw_proto: return False
        if self.nw_src != other.nw_src: return False
        if self.nw_dst != other.nw_dst: return False
        if self.tp_src != other.tp_src: return False
        if self.tp_dst != other.tp_dst: return False
        return True

    def __str__ (self):
        return self.__class__.__name__ + "\n  " + self.show('  ').strip()

    def show (self, prefix=''):
        def binstr (n):
            s = ''
            while True:
                s = ('1' if n & 1 else '0') + s
                n >>= 1
                if n == 0: break
            return s
        def safehex(n):
            if n is None:
                return "(None)"
            else:
                return hex(n)
    
        def show_wildcards(w):
            parts = [ k.lower()[len("OFPFW_"):]
                    for (k,v) in ofp_flow_wildcards_rev_map.iteritems()
                    if v & w == v ]
            nw_src_bits = (w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT
            if nw_src_bits > 0:
                parts.append("nw_src(/%d)" % (32 - nw_src_bits))
    
            nw_dst_bits = (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT
            if nw_dst_bits > 0:
                parts.append("nw_dst(/%d)" % (32 - nw_dst_bits))
    
            return "|".join(parts)
    
        outstr = ''
        outstr += prefix + 'wildcards: '
        outstr += show_wildcards(self.wildcards)
        outstr += ' (%s = %x)\n' % (binstr(self.wildcards), self.wildcards)
        def append (f, formatter=str):
            v = self.__getattr__(f)
            if v is None: return ''
            return prefix + f + ": " + formatter(v) + "\n"
        outstr += append('in_port')
        outstr += append('dl_src')
        outstr += append('dl_dst')
        outstr += append('dl_vlan')
        outstr += append('dl_vlan_pcp')
        outstr += append('dl_type', safehex)
        outstr += append('nw_tos')
        outstr += append('nw_proto')
        outstr += append('nw_src')
        outstr += append('nw_dst')
        outstr += append('tp_src')
        outstr += append('tp_dst')
        return outstr

class ofp_match20 (ofp_base):   # add by cc
    _MIN_LENGTH = 8
    
    def __init__(self,**kw):
        self.field_name = None
        self.field_id = 0   # 2 bytes
        self.offset = 0     # 2 bytes
        self.length = 0     # 2 bytes
        
        initHelper(self, kw)
    
    def __eq__(self,other):
        if type(self) != type(other): return False
        if self.field_id != other.field_id: return False
        if self.offset != other.offset: return False
        if self.length != other.length: return False
        return True
  
    def __len__(self):
        return 8
    
    def pack(self):
        assert self._assert()
        packed=b""
        packed += struct.pack("!H" ,self.field_id)
        packed += struct.pack("!H" ,self.offset)
        packed += struct.pack("!H" ,self.length)
        packed += _PAD2
        return packed

    def unpack(self, raw, offset=0):
        _offset = offset
        offset, (self.field_id, self.offset, self.length) = _unpack('!HHH', raw, offset)
        offset = _skip(raw, offset, 2)
        assert offset - _offset == len(self)
        return offset

    def show(self,prefix=''):
        outstr =''
        outstr += prefix + 'field_id: '+  str(self.field_id) + '\n' 
        outstr += prefix + 'offset: '+  str(self.offset) + '\n' 
        outstr += prefix + 'length: '+  str(self.length) + '\n' 
        return outstr  

class ofp_matchx (ofp_base):    # add by cc
    _MIN_LENGTH = 40
    
    def __init__(self, **kw):
        if 'match20' in kw:
            match = kw['match20']
            self.field_name = match.field_name
            self.field_id = match.field_id  # 2 bytes
            self.offset = match.offset      # 2 bytes
            self.length = match.length      # 2 bytes
        else:
            self.field_name = None
            self.field_id = 0
            self.offset = 0
            self.length = 0
            
        if 'value' in kw:
            self.value = kw['value']        # 16 bytes
        else:
            self.value = []
            
        if 'mask' in kw:
            self.mask = kw['mask']          # 16 bytes
        else:
            self.mask = []
        
        initHelper(self, kw)

    def __eq__(self,other):
        if type(self) != type(other): return False
        if self.field_id != other.field_id: return False
        if self.offset != other.offset: return False
        if self.length != other.length: return False       
        if self.value != other.value: return False
        if self.mask != other.mask: return False
        return True
  
    def __len__(self):
        return 40
    
    def pack(self):
        assert self._assert()
        packed=b""
        packed += struct.pack("!H" ,self.field_id)
        packed += struct.pack("!H" ,self.offset)
        packed += struct.pack("!H" ,self.length)
        packed += _PAD2
        
        for i in self.value:
            packed += struct.pack("!B" ,i)
        packed += _PAD * (OFP_MAX_FIELD_LENGTH_IN_BYTE - len(self.value))
        
        for i in self.mask:
            packed += struct.pack("!B" ,i)
        packed += _PAD * (OFP_MAX_FIELD_LENGTH_IN_BYTE - len(self.mask))
        
        return packed

    def unpack(self, raw, offset=0):
        _offset = offset
        offset, (self.field_id, self.offset, self.length) = _unpack('!HHH', raw, offset)
        offset = _skip(raw, offset, 2)
        
        for _ in xrange(0, OFP_MAX_FIELD_LENGTH_IN_BYTE):
            offset, temp_value = _unpack('!B', raw, offset)
            self.value.append(temp_value)
            
        for _ in xrange(0, OFP_MAX_FIELD_LENGTH_IN_BYTE):
            offset, temp_mask = _unpack('!B', raw, offset)
            self.mask.append(temp_mask)
            
        assert offset - _offset == len(self)
        return offset

    def show(self,prefix=''):
        outstr =''
        outstr += prefix + 'field_id: '+  str(self.field_id) + '\n' 
        outstr += prefix + 'offset: '+  str(self.offset) + '\n' 
        outstr += prefix + 'length: '+  str(self.length) + '\n' 
        outstr += prefix + 'value: '+  str(self.value) + '\n' 
        outstr += prefix + 'mask: '+  str(self.mask) + '\n'
        return outstr

class ofp_instruction (ofp_base):
    _MIN_LENGTH = 8
    _MAX_LENGTH = OFP_MAX_INSTRUCTION_LENGTH   #304

    def __init__(self, **kw):
        self.type = 1    # 2 bytes, ofp_instruction_type_rev_map
        self.length = 0  # 2 bytes
        
        initHelper(self, kw)

    def __eq__(self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if self.length != other.length: return False
        return True

    def show(self,prefix=''):
        outstr =''
        outstr += prefix + 'type:   '+  str(self.type) + '\n' 
        outstr += prefix + 'length: '+  str(self.len) + '\n' 
        return outstr

    def __len__(self):
        return 8     # FIXME:
    
    def pack(self):
        assert self._assert()
        packed=b""
        packed += struct.pack("!HH" ,self.type, self.length)
        packed += _PAD4
        return packed

    def unpack(self,raw,offset=0):
        _offset = offset
        offset, (self.type, self.length) = _unpack('!HH', raw, offset)
        offset = _skip(raw, offset, 4)
        assert offset - _offset == len(self)
        return offset
    
class ofp_action_generic (ofp_action_base):   # ?????
    _MIN_LENGTH = 8
    def __init__ (self, **kw):
        self.type = None # Purposely bad
        self.data = _PAD4
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HH", self.type, len(self))
        packed += self.data
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length) = _unpack("!HH", raw, offset)
        offset,self.data = _read(raw, offset, length-4)
        assert offset - _offset == len(self)
        return offset

    def __len__ (self):
        return 4 + len(self.data)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        return outstr

# ------------------------ openflow action -------------------------- #

@openflow_action('OFPAT_OUTPUT', 0)
class ofp_action_output (ofp_action_base):
    """
    modified by cc
    according to org.openflow.protocol.action.OFActionOutput
    """
    #_MIN_LENGTH = ofp_action_base._MIN_LENGTH + 8 + ofp_match20._MIN_LENGTH
    _MIN_LENGTH = ofp_action_base._MIN_LENGTH + 16
    
    def __init__ (self, **kw):
        #ofp_action_base.__init__(self)
        #self.port = None # Purposely bad -- require specification
        #self.max_len = 0xffFF  # do we need this?
        self.port_id_value_type = 0  # 1 bytes
        self.metadata_offset = 0   # 2 bytes
        self.metadata_length = 0   # 2 bytes
        self.packet_offset = 0     # 2 bytes
        self.port_id = 0          # 4 bytes
        self.port_id_field = None   # ofp_match20
        self.length = ofp_action_output._MIN_LENGTH  # 2 bytes, override
        
        initHelper(self, kw)

    def pack (self):
        """
        if self.port != OFPP_CONTROLLER:  #65533
            self.max_len = 0
        """
        assert self._assert()
        packed = b""
        packed += ofp_action_base.pack(self)   # super method
        packed += struct.pack("!B", self.port_id_value_type)
        packed += _PAD
        packed += struct.pack("!HHH", self.metadata_offset, self.metadata_length, self.packet_offset)
        if self.port_id_value_type == 0:
            packed += struct.pack("!L", self.port_id)
            packed += _PAD4
        elif self.port_id_value_type == 1 and self.port_id_field != None:
            packed += self.port_id_field.pack()       #ofp_match20.pack
        else:
            packed += _PAD * ofp_match20._MIN_LENGTH
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        #offset = ofp_action_base.unpack(raw, offset)  # super method
        offset, length = self._unpack_header(raw, offset)
        offset, self.port_id_value_type = _unpack('!B', raw, offset)
        offset = _skip(raw, offset, 1)
        offset, (self.metadata_offset, self.metadata_length, self.packet_offset) = _unpack("!HHH", raw, offset)
        if self.port_id_value_type == 0:
            offset, self.port_id = _unpack('!L', raw, offset)
            offset = _skip(raw, offset , 4)
            self.port_id_field = None
        elif self.port_id_value_type == 1:
            self.port_id = 0
            self.port_id_field = ofp_match20()
            offset = self.port_id_field.unpack(raw, offset)   #ofp_match20.uppack
        else:
            self.port_id = 0
            self.port_id_field = None
            offset = _skip(raw, offset , ofp_match20._MIN_LENGTH)
        assert offset - _offset == len(self)
        return offset, length

    @staticmethod
    def __len__ ():
        return ofp_action_output._MIN_LENGTH

    def __eq__ (self, other):
        #if type(self) != type(other): return False
        #if self.type != other.type: return False
        #if len(self) != len(other): return False
        if not ofp_action_base.__eq__(self, other): return False
        if self.port_id_value_type != other.port_id_value_type: return False
        if self.metadata_offset != other.metadata_offset: return False
        if self.metadata_length != other.metadata_length: return False
        if self.packet_offset != other.packet_offset: return False
        if self.port_id != other.port_id: return False
        if self.port_id_field != other.port_id_field: return False
        #if self.port != other.port: return False
        #if self.max_len != other.max_len: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        #outstr += prefix + 'type: ' + str(self.type) + '\n'
        #outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += ofp_action_base.show(self, prefix + '  ')
        #outstr += prefix + 'port: ' + str(self.port) + '\n'
        #outstr += prefix + 'max_len: ' + str(self.max_len) + '\n'
        outstr += prefix + 'port_id_value_type: ' + str(self.port_id_value_type) + '\n'
        outstr += prefix + 'metadata_offset: ' + str(self.metadata_offset) + '\n'
        outstr += prefix + 'metadata_length: ' + str(self.metadata_length) + '\n'
        outstr += prefix + 'packet_offset: ' + str(self.packet_offset) + '\n'
        outstr += prefix + 'port_id: ' + str(self.port_id) + '\n'
        outstr += prefix + 'port_id_field: ' + str(self.port_id_field) + '\n'
        return outstr

@openflow_action('OFPAT_SET_FIELD', 1)
class ofp_action_set_field (ofp_action_base):
    """
    modified by cc
    accoding to org.openflow.protocol.action.OFActionSetField
    """
    #_MIN_LENGTH = ofp_action_base._MIN_LENGTH + ofp_matchx._MIN_LENGTH
    _MIN_LENGTH = ofp_action_base._MIN_LENGTH + 40
    def __init__ (self, **kw):
        #ofp_action_base.__init__(self)
        self.length = ofp_action_set_field._MIN_LENGTH
        self.field_setting = None    #ofp_matchx
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
        packed = b""
        packed += ofp_action_base.pack(self)   #super method
        packed += self.field_setting.pack()
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        #offset = ofp_action_base.unpack(self, raw, offset)   #super method
        offset, length = self._unpack_header(raw, offset)
        offset = self.field_setting.unpack(raw, offset)  #ofp_matchx.upack  #FIXME:
        #TODO: check length for this and other actions
        assert offset - _offset == len(self)
        return offset, length

    @staticmethod
    def __len__ ():
        return ofp_action_set_field._MIN_LENGTH   #44

    def __eq__ (self, other):
        #if type(self) != type(other): return False
        #if self.type != other.type: return False
        #if len(self) != len(other): return False
        if not ofp_action_base.__eq__(self, other): return False
        #if self.vlan_vid != other.vlan_vid: return False
        if self.field_setting != other.field_setting: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        #outstr += prefix + 'type: ' + str(self.type) + '\n'
        #outstr += prefix + 'len: ' + str(len(self)) + '\n'
        #outstr += prefix + 'vlan_vid: ' + str(self.vlan_vid) + '\n'
        outstr += ofp_action_base.show(self, prefix + '  ')   # FIXME:
        return outstr

@openflow_action('OFPAT_SET_FIELD_FROM_METADATA', 2)
class ofp_action_set_field_from_metadata (ofp_action_base):
    """
    modified by cc
    accoding to org.openflow.protocol.action.OFActionSetFieldFromMetadata
    """
    #_MIN_LENGTH = ofp_action_base._MIN_LENGTH + ofp_match20._MIN_LENGTH + 8
    _MIN_LENGTH = ofp_action_base._MIN_LENGTH + 16
    def __init__ (self, **kw):
        self.field_setting = None   # ofp_match20
        self.metadata = 0   # 2 bytes
        self.length = ofp_action_set_field_from_metadata._MIN_LENGTH
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
        packed = b""
        packed += ofp_action_base.pack(self)   #super method
        packed += self.field_setting.pack()
        packed += struct.pack("!H", self.metadata)
        packed += _PAD6
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset, length = self._unpack_header(raw, offset)
        offset = self.field_setting.unpack(raw, offset)  #FIXME:
        offset, self.metadata = _unpack("!H", raw, offset)
        offset = _skip(raw, offset, 6)
        assert offset - _offset == len(self)
        return offset, length

    @staticmethod
    def __len__ ():
        return ofp_action_set_field_from_metadata._MIN_LENGTH

    def __eq__ (self, other):
        #if type(self) != type(other): return False
        #if self.type != other.type: return False
        #if len(self) != len(other): return False
        if not ofp_action_base.__eq__(self, other): return False
        if self.field_setting != other.field_setting: return False
        if self.metadata != other.metadata: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += ofp_action_base.show(self, prefix + '  ')   # FIXME:
        outstr += prefix + 'field_setting: ' + str(self.field_setting) + '\n'
        outstr += prefix + 'metadata: ' + str(self.metadata) + '\n'
        return outstr
    
@openflow_action('OFPAT_MODIFY_FIELD', 3)
class ofp_action_modify_field (ofp_action_base):
    """
    modified by cc
    accoding to org.openflow.protocol.action.OFActionModifyField
    """
    #_MIN_LENGTH = ofp_action_base._MIN_LENGTH + ofp_match20._MIN_LENGTH + 8
    _MIN_LENGTH = ofp_action_base._MIN_LENGTH + 16
    
    def __init__ (self, **kw):
        self.length = ofp_action_modify_field._MIN_LENGTH
        self.match_field = None  #ofp_match20
        self.increment = 0   # 4 bytes
        initHelper(self, kw)

    def pack (self):
        #packed = struct.pack("!HHi", self.type, len(self), 0)
        assert self._assert()
        packed = b""
        packed += ofp_action_base.pack(self)   #super method
        packed += self.match_field.pack()
        packed += _PAD4
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        #offset,(self.type, length) = _unpack("!HH", raw, offset)
        offset = self._unpack_header(raw, offset)
        self.match_field = ofp_match20()
        offset = self.match_field.unpack(raw, offset)
        offset, self.increment = _unpack("!L", raw, offset)
        offset = _skip(raw, offset, 4)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return ofp_action_modify_field._MIN_LENGTH

    def __eq__ (self, other):
        if not ofp_action_base.__eq__(self, other): return False
        if self.match_field != other.match_field: return 
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += ofp_action_base.show(self, prefix + '  ')   # FIXME:
        outstr += prefix + 'match_field: ' + str(self.match_field) + '\n'
        outstr += prefix + 'increment: ' + str(self.increment) + '\n'
        return outstr
    
@openflow_action('OFPAT_ADD_FIELD', 4)
class ofp_action_add_field(ofp_action_base):
    """
    add by cc
    accoding to org.openflow.protocol.action.OFActionAddField
    """
    _MIN_LENGTH = ofp_action_base._MIN_LENGTH + 8 + OFP_MAX_FIELD_LENGTH_IN_BYTE  #28
    
    def __init__ (self, **kw):
        self.length = ofp_action_add_field._MIN_LENGTH
        self.field_id = 0               # 2 bytes
        self.field_position = 0         # 2 bytes
        self.field_length= 0            # 4 bytes
        self.field_value = b""          # bytes[16]
        initHelper(self, kw)
    
    @staticmethod
    def __len__():
        return ofp_action_add_field._MIN_LENGTH
        
    def pack(self):
        assert self._assert()
        packed = b""
        packed += ofp_action_base.pack(self)   #super method
        packed += struct.pack("!HHL", self.field_id, self.field_position, self.field_length)
        if len(self.field_value == 0):
            packed += _PAD * OFP_MAX_FIELD_LENGTH_IN_BYTE
        else:
            if len(self.field_value) > OFP_MAX_FIELD_LENGTH_IN_BYTE:
                packed += self.field_value[:OFP_MAX_FIELD_LENGTH_IN_BYTE]
            else:
                packed += self.field_value
                packed += _PAD * (OFP_MAX_FIELD_LENGTH_IN_BYTE - len(self.field_value))
        return packed
            
    def unpack(self, raw, offset = 0):
        _offset = offset
        offset, self.length = self._unpack_header(raw, offset)
        offset, (self.field_id, self.field_position, self.field_length) = _unpack("!HHL", raw, offset)
        offset, self.field_value = _readzs(raw, offset, OFP_MAX_FIELD_LENGTH_IN_BYTE)
        assert offset - _offset == len(self)
        return offset
    
@openflow_action('OFPAT_DELETE_FIELD', 4)
class ofp_action_delete_field(ofp_action_base):
    """
    add by cc
    accoding to org.openflow.protocol.action.OFActionAddField
    """
    _MIN_LENGTH = ofp_action_base._MIN_LENGTH + 16   #20
    
    def __init__ (self, **kw):
        self.length = ofp_action_delete_field._MIN_LENGTH
        self.tag_position = 0               # 2 bytes
        self.tag_length_value_type = 0      # 1 bytes
        self.tag_length_value = 0           # 4 bytes
        self.tag_length_field = None        # ofp_match20, 8 bytes
        initHelper(self, kw)
    
    @staticmethod
    def __len__():
        return ofp_action_delete_field._MIN_LENGTH
        
    def pack(self):
        assert self._assert()
        packed = b""
        packed += ofp_action_base.pack(self)   #super method
        packed += struct.pack("!HB", self.tag_position, self.tag_length_value_type)
        packed += _PAD5
        
        if self.tag_length_value_type == 0:
            packed += struct.pack("!L", self.tag_length_value)
            packed += _PAD4
        elif self.tag_length_value_type == 1 and self.tag_length_field != None:
            packed += self.tag_length_field.pack()  #??
        else:
            packed += _PAD8
        return packed
            
    def unpack(self, raw, offset = 0):
        _offset = offset
        offset, self.length = self._unpack_header(raw, offset)
        offset, (self.tag_position, self.tag_length_value_type) = _unpack("!HB", raw, offset)
        offset = _skip(raw, offset, 5)
        if self.tag_length_value_type == 0:
            offset, self.tag_length_value = _unpack("!L", raw, offset)
            offset = _skip(raw, offset, 4)
            self.tag_length_field = None
        elif self.tag_length_value_type == 1:
            self.tag_length_value = 0
            self.tag_length_field = ofp_match20()
            offset = self.tag_length_field.unpack()
        else:
            self.tag_length_value = 0
            self.tag_length_field = None
            offset = _skip(raw, offset, 8)
        assert offset - _offset == len(self)
        return offset

@openflow_action('OFPAT_ENQUEUE', 11)
class ofp_action_enqueue (ofp_action_base):
    def __init__ (self, **kw):
        self.port = None # Require user to set
        self.queue_id = 0
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HHH", self.type, len(self), self.port)
        packed += _PAD6 # Pad
        packed += struct.pack("!L", self.queue_id)
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length, self.port) = _unpack("!HHH", raw, offset)
        offset = _skip(raw, offset, 6)
        offset,(self.queue_id,) = _unpack("!L", raw, offset)
        
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 16

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.port != other.port: return False
        if self.queue_id != other.queue_id: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'port: ' + str(self.port) + '\n'
        outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
        return outstr








@openflow_action('OFPAT_SET_DL_DST', 5)
@openflow_action('OFPAT_SET_DL_SRC', 4)
class ofp_action_dl_addr (ofp_action_base):
    @classmethod
    def set_dst (cls, dl_addr = None):
        return cls(OFPAT_SET_DL_DST, dl_addr)
  
    @classmethod
    def set_src (cls, dl_addr = None):
        return cls(OFPAT_SET_DL_SRC, dl_addr)

    def __init__ (self, type = None, dl_addr = None):
        """
        'type' should be OFPAT_SET_DL_SRC or OFPAT_SET_DL_DST.
        """
        self.type = type
        self.dl_addr = EMPTY_ETH
    
        if dl_addr is not None:
            self.dl_addr = EthAddr(dl_addr)

    def _validate (self):
        if (not isinstance(self.dl_addr, EthAddr)
                and not isinstance(self.dl_addr, bytes)):
            return "dl_addr is not string or EthAddr"
        if isinstance(self.dl_addr, bytes) and len(self.dl_addr) != 6:
            return "dl_addr is not of size 6"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HH", self.type, len(self))
        if isinstance(self.dl_addr, EthAddr):
            packed += self.dl_addr.toRaw()
        else:
            packed += self.dl_addr
        packed += _PAD6
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length) = _unpack("!HH", raw, offset)
        offset,self.dl_addr = _readether(raw, offset)
        offset = _skip(raw, offset, 6)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 16

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.dl_addr != other.dl_addr: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'dl_addr: ' + str(self.dl_addr) + '\n'
        return outstr


@openflow_action('OFPAT_SET_NW_DST', 7)
@openflow_action('OFPAT_SET_NW_SRC', 6)
class ofp_action_nw_addr (ofp_action_base):
    @classmethod
    def set_dst (cls, nw_addr = None):
        return cls(OFPAT_SET_NW_DST, nw_addr)
    @classmethod
    def set_src (cls, nw_addr = None):
        return cls(OFPAT_SET_NW_SRC, nw_addr)

    def __init__ (self, type = None, nw_addr = None):
        """
        'type' should be OFPAT_SET_NW_SRC or OFPAT_SET_NW_DST
        """
        self.type = type
    
        if nw_addr is not None:
            self.nw_addr = IPAddr(nw_addr)
        else:
            self.nw_addr = IPAddr(0)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HHl", self.type, len(self),
                              self.nw_addr.toSigned())
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length) = _unpack("!HH", raw, offset)
        offset,self.nw_addr = _readip(raw, offset)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.nw_addr != other.nw_addr: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'nw_addr: ' + str(self.nw_addr) + '\n'
        return outstr


@openflow_action('OFPAT_SET_NW_TOS', 8)
class ofp_action_nw_tos (ofp_action_base):
    def __init__ (self, nw_tos = 0):
        self.nw_tos = nw_tos

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HHB", self.type, len(self), self.nw_tos)
        packed += _PAD3
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length, self.nw_tos) = _unpack("!HHB", raw, offset)
        offset = _skip(raw, offset, 3)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.nw_tos != other.nw_tos: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'nw_tos: ' + str(self.nw_tos) + '\n'
        return outstr


@openflow_action('OFPAT_SET_TP_DST', 10)
@openflow_action('OFPAT_SET_TP_SRC', 9)
class ofp_action_tp_port (ofp_action_base):
    @classmethod
    def set_dst (cls, tp_port = None):
        return cls(OFPAT_SET_TP_DST, tp_port)
    @classmethod
    def set_src (cls, tp_port = None):
        return cls(OFPAT_SET_TP_SRC, tp_port)

    def __init__ (self, type=None, tp_port = 0):
        """
        'type' is OFPAT_SET_TP_SRC/DST
        """
        self.type = type
        self.tp_port = tp_port

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HHH", self.type, len(self), self.tp_port)
        packed += _PAD2
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length, self.tp_port) = \
            _unpack("!HHH", raw, offset)
        offset = _skip(raw, offset, 2)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.tp_port != other.tp_port: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'tp_port: ' + str(self.tp_port) + '\n'
        return outstr


class ofp_action_vendor_base (ofp_action_base):
    """
    Base class for vendor actions
    """
    type = 65535 # OFPAT_VENDOR

    def _eq (self, other):
        """
        Return True if equal
    
        Overide this.
        """
        return True

    def _init (self, kw):
        """
        Initialize fields
    
        Overide this.
        """
        pass

    def _pack_body (self):
        """
        Pack body.
        """
        return b""

    def _unpack_body (self, raw, offset, avail):
        """
        Unpack body in raw starting at offset.
    
        Return new offset
        """
        return offset

    def _body_length (self):
        """
        Return length of body.
    
        This should include everything after the length field.
        Optionally override this.
        """
        return len(self._pack_body())

    def _show (self, prefix):
        """
        Format additional fields as text
        """
        return ""

    def __init__ (self, **kw):
        self._init(kw)
        assert hasattr(self, 'vendor')
        #self.vendor = 0
        initHelper(self, kw)

    def _pack_body (self):
        if hasattr(self.body, 'pack'):
            return self.body.pack()
        else:
            return bytes(self.body)

    def pack (self):
        assert self._assert()
    
        body = self._pack_body()
    
        packed = b""
        packed += struct.pack("!HHL", self.type, 8 + len(body), self.vendor)
        packed += body
        assert (len(packed) % 8) == 0, "Vendor action length not multiple of 8"
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length, self.vendor) = _unpack("!HHL", raw, offset)
        offset = self._unpack_body(raw, offset, length - 8)
        assert offset - _offset == len(self)
        return offset

    def __len__ (self):
        return 8 + self._body_length()

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.vendor != other.vendor: return False
        return self._eq(other)

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'vendor: ' + str(self.vendor) + '\n'
        outstr += self._show(prefix)
        return outstr


@openflow_action('OFPAT_VENDOR', 65535)
class ofp_action_vendor_generic (ofp_action_base):
    def __init__ (self, **kw):
        self.vendor = 0
        self.body = b""
    
        initHelper(self, kw)

    def _pack_body (self):
        if hasattr(self.body, 'pack'):
            return self.body.pack()
        else:
            return bytes(self.body)

    def pack (self):
        assert self._assert()
    
        body = self._pack_body()
    
        packed = b""
        packed += struct.pack("!HHL", self.type, 8 + len(body), self.vendor)
        packed += body
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,(self.type, length, self.vendor) = _unpack("!HHL", raw, offset)
        offset,self.body = _read(raw, offset, length - 8)
        assert offset - _offset == len(self)
        return offset

    def __len__ (self):
        return 8 + len(self._pack_body())

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.type != other.type: return False
        if len(self) != len(other): return False
        if self.vendor != other.vendor: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'len: ' + str(len(self)) + '\n'
        outstr += prefix + 'vendor: ' + str(self.vendor) + '\n'
        return outstr


#3. Controller-to-Switch Messages

@openflow_sc_message("OFPT_HELLO", 0)
class ofp_hello (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
        
        packed = b""
        packed += ofp_header.pack(self)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        return outstr




@openflow_s_message("OFPT_ERROR", 1)
class ofp_error (ofp_header):
    # changed by cc
    _MIN_LENGTH = 16
    _MAX_LENGTH = 272
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.type = 0        # 2 bytes
        self.code = 0        # 2 bytes
        self.device_id = 0   # 4 bytes
        self.data = b''      # 256 bytes
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!HHL", self.type, self.code, self.device_id)
        packed += self.data
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.type, self.code, self.device_id) = _unpack("!HHL", raw, offset)
        offset,self.data = _read(raw, offset, length - 16)
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 16 + len(self.data)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.type != other.type: return False
        if self.code != other.code: return False
        if self.device_id != other.device_id: return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        t = self.type
        c = self.code
        if t < len(ofp_error_type):
            n = ofp_error_type_map[t]
            t = "%s (%i)" % (n, t)
            n = 'ofp' + n.lower()[5:] + '_code_map'
            if n in sys.modules[__name__].__dict__:
                if c in sys.modules[__name__].__dict__[n]:
                    c = "%s (%i)" % (sys.modules[__name__].__dict__[n][c], c)
        outstr += prefix + 'type:      ' + str(t) + '\n'
        outstr += prefix + 'code:      ' + str(c) + '\n'
        outstr += prefix + 'device_id: ' + str(self.device_id) + '\n'
        if len(self.data):
            outstr += prefix + 'datalen: %s\n' % (len(self.data),)
            outstr += prefix + hexdump(self.data).replace("\n", "\n" + prefix)
        return outstr.strip()


@openflow_sc_message("OFPT_ECHO_REQUEST", 2,
    request_for="ofp_echo_reply")
class ofp_echo_request (ofp_header):
    _MIN_LENGTH = 8
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.body = b''
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.body
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,self.body = _read(raw, offset, length - 8)
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 8 + len(self.body)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.body != other.body: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'body:\n'
        outstr += _format_body(self.body, prefix + '  ') + '\n'
        return outstr


@openflow_sc_message("OFPT_ECHO_REPLY", 3,
    reply_to="ofp_echo_request")
class ofp_echo_reply (ofp_header):
    _MIN_LENGTH = 8
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.body = b''
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.body
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,self.body = _read(raw, offset, length - 8)
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 8 + len(self.body)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.body != other.body: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'body:\n'
        outstr += _format_body(self.body, prefix + '  ') + '\n'
        return outstr

class ofp_vendor_base (ofp_header):
    header_type = 4 # OFPT_VENDOR
    """
    Base class for vendor messages
    """
    pass


@openflow_sc_message("OFPT_VENDOR", 4)     # FIXME: should by changed to EXPERIMENTER
class ofp_vendor_generic (ofp_vendor_base):
    _MIN_LENGTH = 12
    _collect_raw = False

    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.vendor = 0
        self.data = b''
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!L", self.vendor)
        if hasattr(self.data, "pack"):
            packed += self.data.pack()
        else:
            packed += self.data
        return packed

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,length = self._unpack_header(raw, offset)
        offset,(self.vendor,) = _unpack("!L", raw, offset)
        offset,self.data = _read(raw, offset, length-12)
        if self._collect_raw:
            self.raw = raw[_offset, _offset+length]
        return offset,length

    def __len__ (self):
        return 12 + len(self.data)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.vendor != other.vendor: return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'vendor: ' + str(self.vendor) + '\n'
        outstr += prefix + 'datalen: ' + str(len(self.data)) + '\n'
        #outstr += prefix + hexdump(self.data).replace("\n", "\n" + prefix)
        return outstr


@openflow_c_message("OFPT_FEATURES_REQUEST", 5,
    request_for="ofp_features_reply")
class ofp_features_request (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        return outstr


@openflow_s_message("OFPT_FEATURES_REPLY", 6,
    reply_to="ofp_features_request")
class ofp_features_reply (ofp_header):
    """
    changed by cc
    """
    _MIN_LENGTH = 32
    
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.dev_id = 0         # 4 bytes
        self.port_num = 0       # 2 bytes
        self.table_num = 0      # 2 bytes
        self.capabilities = 0   # 4 bytes
        self.vendor_id = b""     # 64 bytes
        self.dev_fw_id = b""     # 64 bytes
        self.dev_lkup_id = b""   # 64 bytes
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!LHHLB", self.dev_id, self.port_num, self.table_num, self.capabilities)
        packed += _PAD4
        packed += self.vendor_id.ljust(OFP_MAX_PORT_NAME_LEN,'\0')
        packed += self.dev_fw_id.ljust(OFP_MAX_PORT_NAME_LEN,'\0')
        packed += self.dev_lkup_id.ljust(OFP_MAX_PORT_NAME_LEN,'\0')
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.dev_id, self.port_num, self.table_num, self.capabilities) = _unpack("!LHHL", raw, offset)
        offset = _skip(raw, offset, 4)
        offset,self.vendor_id = _readzs(raw, offset, OFP_MAX_PORT_NAME_LEN)
        offset,self.dev_fw_id = _readzs(raw, offset, OFP_MAX_PORT_NAME_LEN)
        offset,self.dev_lkup_id = _readzs(raw, offset, OFP_MAX_PORT_NAME_LEN)
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 216

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.dev_id != other.dev_id: return False
        if self.port_num != other.port_num: return False
        if self.table_num != other.table_num: return False
        if self.capabilities != other.capabilities: return False
        if self.vendor_id != other.vendor_id: return False
        if self.dev_fw_id != other.dev_fw_id: return False
        if self.dev_lkup_id != other.dev_lkup_id: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'dev_id:       ' + str(self.dev_id) + '\n'
        outstr += prefix + 'port_num:     ' + str(self.port_num) + '\n'
        outstr += prefix + 'table_num:    ' + str(self.table_num) + '\n'
        outstr += prefix + 'capabilities: ' + str(self.capabilities) + '\n'
        outstr += prefix + 'vendor_id:    ' + self.vendor_id + '\n'
        outstr += prefix + 'dev_fw_id:    ' + self.dev_fw_id + '\n'
        outstr += prefix + 'dev_lkup_id:  ' + self.dev_lkup_id + '\n'
        return outstr
    
ofp_switch_features = ofp_features_reply


@openflow_c_message("OFPT_GET_CONFIG_REQUEST", 7,   # added by cc
    request_for="ofp_get_config_reply")
class ofp_get_config_request (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        return outstr

@openflow_s_message("OFPT_GET_CONFIG_REPLY", 8,    #added by cc
    reply_to="ofp_get_config_request")
class ofp_get_config_reply (ofp_header): # uses ofp_switch_config
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.flags = 0
        self.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!HH", self.flags, self.miss_send_len)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.flags, self.miss_send_len) = \
            _unpack("!HH", raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 12

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.flags != other.flags: return False
        if self.miss_send_len != other.miss_send_len: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'flags:         ' + str(self.flags) + '\n'
        outstr += prefix + 'miss_send_len: ' + str(self.miss_send_len) + '\n'
        return outstr


@openflow_c_message("OFPT_SET_CONFIG", 9)    #changed by cc
class ofp_set_config (ofp_header): # uses ofp_switch_config
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.flags = 0
        self.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN   # 128
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!HH", self.flags, self.miss_send_len)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.flags, self.miss_send_len) = _unpack("!HH", raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 12

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.flags != other.flags: return False
        if self.miss_send_len != other.miss_send_len: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'flags:         ' + str(self.flags) + '\n'
        outstr += prefix + 'miss_send_len: ' + str(self.miss_send_len) + '\n'
        return outstr
    
ofp_packet_in_reason_rev_map = {    # add by cc
    'OFPR_NO_MATCH'     : 0, 
    'OFPR_ACTION'       : 1, 
    'OFPR_INVALID_TTL'  : 2,
}

@openflow_s_message("OFPT_PACKET_IN", 10)  #chaged by cc
class ofp_packet_in (ofp_header):
    _MIN_LENGTH = 32
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self._buffer_id = NO_BUFFER   # 4 bytes
        self._total_len = 0           # 2 bytes
        self.reason = 0               # 1 bytes, ofp_packet_in_reason_rev_map
        self.table_id = 0             # 1 bytes
        self.cookie = 0               # 8 bytes
        self.device_id = 0            # 4 bytes
        #self.slotID = 0
        #self.port_id = 0
        self.data = None
        
        if 'total_len' in kw:
            self._total_len = kw.pop('total_len')
        
        initHelper(self, kw)

    def _validate (self):
        if self.data and (self.total_len < len(self.data)):
            return "total len less than data len"

    @property
    def total_len (self):
        if self._total_len is None:
            return len(self.data) if self.data else 0
        return self._total_len
    @total_len.setter
    def total_len (self, value):
        self._total_len = value

    @property
    def buffer_id (self):
        if self._buffer_id == NO_BUFFER: return None
        return self._buffer_id
    @buffer_id.setter
    def buffer_id (self, val):
        if val is None: val = NO_BUFFER
        self._buffer_id = val

    @property
    def data (self):
        return self._data
    @data.setter
    def data (self, data):
        assert assert_type("data", data, (packet_base, str))
        if data is None:
            self._data = ''
        elif isinstance(data, packet_base):
            self._data = data.pack()
        else:
            self._data = data

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!LHBBQL", self._buffer_id, self.total_len,
            self.in_port, self.reason, self.cookie, self.device_id)
        packed += _PAD4
        packed += self.data
        return packed

    @property
    def is_complete (self):
        if self.buffer_id is not None: return True
        return len(self.data) == self.total_len

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self._buffer_id, self._total_len, self.reason, self.table_id, self.cookie,
                self.device_id) = _unpack("!LHBBQL", raw, offset)
        offset = _skip(raw, offset , 4)
        offset,self.data = _read(raw, offset, length-32)
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 32 + len(self.data)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.buffer_id != other.buffer_id: return False
        if self.total_len != other.total_len: return False
        if self.reason != other.reason: return False
        if self.table_id != other.table_id: return False
        if self.cookie != other.cookie: return False
        if self.device_id != other.device_id: return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'buffer_id: ' + str(self.buffer_id) + '\n'
        outstr += prefix + 'total_len: ' + str(self._total_len) + '\n'  # FIXME: total_len
        outstr += prefix + 'reason:    ' + str(self.reason) + '\n'
        outstr += prefix + 'table_id:  ' + str(self.table_id) + '\n'
        outstr += prefix + 'cookie:    ' + str(self.cookie) + '\n'
        outstr += prefix + 'device_id: ' + str(self.device_id) + '\n'
        #outstr += prefix + 'data: ' + str(self.data) + '\n'
        return outstr


@openflow_c_message("OFPT_FLOW_REMOVED", 11)  #changed by cc
class ofp_flow_removed (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)  # FIXME:
        self.match = ofp_match()
        self.cookie = 0
        self.priority = 0
        self.reason = 0
        self.duration_sec = 0
        self.duration_nsec = 0
        self.idle_timeout = 0
        self.packet_count = 0
        self.byte_count = 0
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.match, ofp_match):
            return "match is not class ofp_match"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.match.pack()
        packed += struct.pack("!QHB", self.cookie, self.priority, self.reason)
        packed += _PAD
        packed += struct.pack("!LLH", self.duration_sec, self.duration_nsec,
                              self.idle_timeout)
        packed += _PAD2
        packed += struct.pack("!QQ", self.packet_count, self.byte_count)
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset = self.match.unpack(raw, offset)
        offset,(self.cookie, self.priority, self.reason) = \
            _unpack("!QHB", raw, offset)
        offset = _skip(raw, offset, 1)
        offset,(self.duration_sec, self.duration_nsec, self.idle_timeout) = \
            _unpack("!LLH", raw, offset)
        offset = _skip(raw, offset, 2)
        offset,(self.packet_count, self.byte_count) = \
            _unpack("!QQ", raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 48 + len(ofp_match)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.match != other.match: return False
        if self.cookie != other.cookie: return False
        if self.priority != other.priority: return False
        if self.reason != other.reason: return False
        if self.duration_sec != other.duration_sec: return False
        if self.duration_nsec != other.duration_nsec: return False
        if self.idle_timeout != other.idle_timeout: return False
        if self.packet_count != other.packet_count: return False
        if self.byte_count != other.byte_count: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'match: \n'
        outstr += self.match.show(prefix + '  ')
        outstr += prefix + 'cookie: ' + str(self.cookie) + '\n'
        outstr += prefix + 'priority: ' + str(self.priority) + '\n'
        outstr += prefix + 'reason: ' + str(self.reason) + '\n'
        outstr += prefix + 'duration_sec: ' + str(self.duration_sec) + '\n'
        outstr += prefix + 'duration_nsec: ' + str(self.duration_nsec) + '\n'
        outstr += prefix + 'idle_timeout: ' + str(self.idle_timeout) + '\n'
        outstr += prefix + 'packet_count: ' + str(self.packet_count) + '\n'
        outstr += prefix + 'byte_count: ' + str(self.byte_count) + '\n'
        return outstr


@openflow_s_message("OFPT_PORT_STATUS", 12)
class ofp_port_status (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.reason = 0
        self.desc = ofp_phy_port()
    
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.desc, ofp_phy_port):
            return "desc is not class ofp_phy_port"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!B", self.reason)
        packed += _PAD * 7 # Pad
        packed += self.desc.pack()
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.reason,) = _unpack("!B", raw, offset)
        offset = _skip(raw, offset, 7)
        offset = self.desc.unpack(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 136

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.reason != other.reason: return False
        if self.desc != other.desc: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'reason: ' + str(self.reason) + '\n'
        outstr += prefix + 'desc: \n'
        outstr += self.desc.show(prefix + '  ')
        return outstr

class OFTableResource():
    """
    used in ROURCE_REPORT message
    created by cc
    """
    
    def __init__(self):
        self.device_id = 0       # uint32
        self.tableType = 0       # uint8, ofp_table_type
        self.tableNum = 0        # uint8
        self.keyLength = 0       # uint16
        self.totalSize = 0       # uint32
        self.reserve = 0         # uint32
    
    def unpack(self, raw, offset = 0):
        offset, (self.device_id, self.tableType, self.tableNum, self.keyLength,
                 self.totalSize, self.reserve) = _unpack("!LBBHLL", raw, offset)
        #print(self.device_id, self.tableType, self.tableNum, self.keyLength, self.totalSize, self.reserve)
        return offset
    
    def show(self, prefix=''):
        outstr = ''
        outstr += prefix + 'device_id: ' + str(self.device_id) + '\n'
        outstr += prefix + 'tableType: ' + str(self.tableType)
        outstr += " (" + ofp_table_type[self.tableType] + ")\n"
        outstr += prefix + 'tableNum:  ' + str(self.tableNum) + '\n'
        outstr += prefix + 'keyLength: ' + str(self.keyLength) + '\n'
        outstr += prefix + 'totalSize: ' + str(self.totalSize) + '\n'
        outstr += prefix + 'reserve:   ' + str(self.reserve) + '\n'
        return outstr
        
@openflow_s_message("OFPT_RESOURCE_REPORT", 13)
class ofp_resource_report (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.resouceType = 0     #uint32
        self.counter_num = 0     #uint32
        self.meter_num = 0       #uint32
        self.group_num = 0       #uint32
        self.tableResourcesMap = {}
        
        initHelper(self, kw)
        
    def pack(self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        offset, (self.resouceType,) = _unpack("!B", raw, offset)
        offset = _skip(raw, offset, 3)
        offset, (self.counter_num, self.meter_num, self.group_num) = _unpack("!LLL", raw, offset)
        #print(self.resouceType, self.counter_num, self.meter_num, self.group_num)
        #tableResource = OFTableResource()
        for i in range(OF_MAX_TABLE_TYPE):
            tableResource = OFTableResource()
            offset = tableResource.unpack(raw, offset)
            self.tableResourcesMap[i] = tableResource
        #print('offset',offset)
        #print('length',length)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 88
    
    def __eq__ (self, other):
        pass     # FIXME:
    
    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'resouceType: ' + str(self.resouceType) + '\n'
        outstr += prefix + 'counter_num: ' + str(self.counter_num) + '\n'
        outstr += prefix + 'meter_num:   ' + str(self.meter_num) + '\n'
        outstr += prefix + 'group_num:   ' + str(self.group_num) + '\n'
        for i in xrange(OF_MAX_TABLE_TYPE):
            outstr += prefix + 'pof_table_resouce_desc' + str(i) + '\n'
            outstr += self.tableResourcesMap[i].show(prefix + '   ')
        return outstr
    
@openflow_c_message("OFPT_PACKET_OUT", 14)
class ofp_packet_out (ofp_header):
    _MIN_LENGTH = 16
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self._buffer_id = NO_BUFFER
        self.in_port = OFPP_NONE
        self.actions = []
        self._data = b''
    
        # ofp_flow_mod & ofp_packet_out do some special handling of 'actions'
    
        # Allow "action" as a synonym for "actions"
        if 'action' in kw and 'actions' not in kw:
            kw['actions'] = kw['action']
            del kw['action']
        initHelper(self, kw)
    
        # Allow use of actions=<a single action> for kw args.
        if not hasattr(self.actions, '__getitem__'):
            self.actions = [self.actions]

    @property
    def buffer_id (self):
        if self._buffer_id == NO_BUFFER: return None
        return self._buffer_id
    @buffer_id.setter
    def buffer_id (self, val):
        if val is None: val = NO_BUFFER
        self._buffer_id = val

    @property
    def data (self):
        return self._data
    @data.setter
    def data (self, data):
        if data is None:
            self._data = b''
        elif isinstance(data, packet_base):
            self._data = data.pack()
        elif isinstance(data, ofp_packet_in):
            # Enable you to easily resend a packet
            self._data = b''
            self.buffer_id = data.buffer_id
            if self.buffer_id is None:
                #TODO: It'd be nice to log and then ignore if data is incomplete
                #      Unfortunately, we currently have no logging in here, so we
                #      assert instead which is a either too drastic or too quiet.
                assert data.is_complete
                self._data = data._data
            self.in_port = data.in_port
        elif isinstance(data, bytes):
            self._data = data
        assert assert_type("data", self._data, (bytes,))

    def _validate (self):
        if self.buffer_id is not None and self.data != b'':
            return "can not have both buffer_id and data set"
        return None

    def pack (self):
        assert self._assert()
    
        actions = b''.join((i.pack() for i in self.actions))
        actions_len = len(actions)
    
        if self.data is not None:
            return b''.join((ofp_header.pack(self),
                struct.pack("!LHH", self._buffer_id, self.in_port, actions_len),
                actions, self.data))
        else:
            return b''.join((ofp_header.pack(self),
                struct.pack("!LHH", self._buffer_id, self.in_port, actions_len),
                actions))

    def unpack (self, raw, offset=0):
        _offset = offset
        offset,length = self._unpack_header(raw, offset)
        offset,(self._buffer_id, self.in_port, actions_len) = \
            _unpack("!LHH", raw, offset)
        offset,self.actions = _unpack_actions(raw, actions_len, offset)
    
        remaining = length - (offset - _offset)
        if remaining <= 0:
            self.data = None
        else:
            offset,self.data = _read(raw, offset, remaining)
    
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 16 + reduce(operator.add, (len(a) for a in self.actions),
            0) + (len(self.data) if self.data else 0)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.buffer_id != other.buffer_id: return False
        if self.in_port != other.in_port: return False
        if self.actions != other.actions: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'buffer_id: ' + str(self.buffer_id) + '\n'
        outstr += prefix + 'in_port: ' + str(self.in_port) + '\n'
        outstr += prefix + 'actions_len: ' + str(len(self.actions)) + '\n'
        outstr += prefix + 'actions: \n'
        for obj in self.actions:
            if obj is None:
                raise RuntimeError("An element of self.actions was None! "
                               + "Bad formatting...")
            outstr += obj.show(prefix + '  ')
        return outstr



@openflow_c_message("OFPT_FLOW_MOD", 15)
class ofp_flow_mod (ofp_header):
    _MIN_LENGTH = 48
    _MAX_LENGTH = 48 + ofp_matchx._MIN_LENGTH * OFP_MAX_MATCH_FIELD_NUM + \
                    ofp_instruction._MAX_LENGTH * OFP_MAX_INSTRUCTION_NUM     #2192
    
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        """
        if 'match' in kw:
            self.match = None
        else:
            self.match = ofp_match()
        """
        self.command = OFPFC_ADD
        self.matchFieldNum = 0
        self.instructionNum = 0
        self.counter_id = 0
        self.cookie = 0
        self.cookie_mask = 0
        self.table_id = 0
        self.table_type = OF_LPM_TABLE  # 1
        self.idle_timeout = 0
        self.hard_timeout = 0
        self.priority = OFP_DEFAULT_PRIORITY
        self.index = 0
        self.matchList =[]
        self.instruction =[]
        #self._buffer_id = NO_BUFFER
        #self.out_port = OFPP_NONE
        #self.flags = 0
        #self.actions = []
        #self.data = None # Not in the spec!  Special magic!  Can be packet_in.
    
        # ofp_flow_mod/ofp_packet_out do some special handling of 'actions'...
    
        # Allow "action" as a synonym for "actions"
        """
        if 'action' in kw and 'actions' not in kw:
            kw['actions'] = kw['action']
            del kw['action']
        """
        initHelper(self, kw)
        """
        # Allow use of actions=<a single action> for kw args.
        if not hasattr(self.actions, '__getitem__'):
            self.actions = [self.actions]
        """

    @property
    def buffer_id (self):
        if self._buffer_id == NO_BUFFER: return None
        return self._buffer_id
    @buffer_id.setter
    def buffer_id (self, val):
        if val is None: val = NO_BUFFER
        self._buffer_id = val

    def _validate (self):
        """
        if not isinstance(self.match, ofp_match):
            return "match is not class ofp_match"
        """
        # FIXME:
        return None

    def pack (self):
        assert self._assert()
        
        packed = b""
        packed += ofp_header.pack(self)
        #packed += self.match.pack(flow_mod=True)
        packed += struct.pack ("!BBB" ,self.command, self.matchFieldNum, self.instructionNum)
        packed += _PAD
        packed += struct.pack ("!LQQBBHHHL", self.counter_id, self.cookie, self.cookie_mask, 
                               self.table_id, self.table_type, self.idle_timeout,
                               self.hard_timeout, self.priority, self.index)
        packed += _PAD4
        
        for i in self.matchList:
            packed += i.pack()
        if (len(self.matchList) < OFP_MAX_MATCH_FIELD_NUM):
            packed += _PAD * ((OFP_MAX_MATCH_FIELD_NUM - len(self.matchList)) * ofp_matchx._MIN_LENGTH)
            
        for i in self.instruction:
            packed += i.pack()
            if (len(i) < ofp_instruction._MAX_LENGTH):
                packed += _PAD*(ofp_instruction._MAX_LENGTH - len(i) )
        if (len(self.instruction) < OFP_MAX_INSTRUCTION_NUM):
            packed += _PAD * ((OFP_MAX_INSTRUCTION_NUM - len(self.instruction)) * ofp_instruction._MAX_LENGTH)
        """
        if po:
            packed += ofp_barrier_request().pack()
            packed += po.pack()
        """
        return packed

    def unpack (self, raw, offset=0):    # FIXME:
        offset,length = self._unpack_header(raw, offset)
        offset = self.match.unpack(raw, offset, flow_mod=True)
        offset,(self.cookie, self.command, self.idle_timeout,
                self.hard_timeout, self.priority, self._buffer_id,
                self.out_port, self.flags) = \
                _unpack("!QHHHHLHH", raw, offset)
        offset,self.actions = _unpack_actions(raw,
            length-(32 + len(self.match)), offset)
        assert length == len(self)
        return offset,length

    def __len__ (self):
        """
        l = 32 + len(self.match)
        for i in self.actions:
            l += len(i)
        return l
        """
        return ofp_flow_mod._MAX_LENGTH

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.match != other.match: return False
        if self.cookie != other.cookie: return False
        if self.command != other.command: return False
        if self.idle_timeout != other.idle_timeout: return False
        if self.hard_timeout != other.hard_timeout: return False
        if self.priority != other.priority: return False
        if self.buffer_id != other.buffer_id: return False
        if self.out_port != other.out_port: return False
        if self.flags != other.flags: return False
        if self.actions != other.actions: return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'match: \n'
        outstr += self.match.show(prefix + '  ')
        outstr += prefix + 'cookie: ' + str(self.cookie) + '\n'
        outstr += prefix + 'command: ' + str(self.command) + '\n'
        outstr += prefix + 'idle_timeout: ' + str(self.idle_timeout) + '\n'
        outstr += prefix + 'hard_timeout: ' + str(self.hard_timeout) + '\n'
        outstr += prefix + 'priority: ' + str(self.priority) + '\n'
        outstr += prefix + 'buffer_id: ' + str(self.buffer_id) + '\n'
        outstr += prefix + 'out_port: ' + str(self.out_port) + '\n'
        outstr += prefix + 'flags: ' + str(self.flags) + '\n'
        outstr += prefix + 'actions: \n'
        for obj in self.actions:
            outstr += obj.show(prefix + '  ')
        return outstr
    

@openflow_c_message("OFPT_GROUP_MOD", 16)
class ofp_group_mod (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack (self, raw, offset=0):
        pass
    
    @staticmethod
    def __len__ ():
        return 32   # FIXME:
    
    def __eq__ (self, other):
        pass
    
    def show (self, prefix=''):
        pass

@openflow_c_message("OFPT_PORT_MOD", 17)
class ofp_port_mod (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.reason = 2
        self.desc = ofp_phy_port()
    
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.desc, ofp_phy_port):
            return "desc is not class ofp_phy_port"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!B", self.reason)
        packed += _PAD * 7
        packed += self.desc.pack()
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.reason,) = _unpack("!B", raw, offset)
        offset = _skip(raw, offset, 7)
        offset = self.desc.unpack(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 136

    def __eq__ (self, other):   # FIXME: check
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.reason != other.reason: return False
        if self.desc != other.desc: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'reason: ' + str(self.reason) + '\n'
        outstr += prefix + 'desc: \n'
        outstr += self.desc.show(prefix + '  ')
        return outstr
    
ofp_flow_table_mod_rev_map = {
    'OFPTC_ADD'    : 0,
    'OFPTC_MODIFY' : 1,
    'OFPTC_DELETE' : 2,
}
    
class ofp_flow_table(ofp_base):  # add by cc
    _MIN_LENGTH = OFP_NAME_MAX_LENGTH + 16     # 80
    _MAX_LENGTH = OFP_NAME_MAX_LENGTH + 16 + OFP_MAX_MATCH_FIELD_NUM *  ofp_match20._MIN_LENGTH  # 144
    
    def __init__ (self, **kw):
        self.command = 0           # 1 bytes, ofp_flow_table_mod_rev_map
        self.table_id = 0          # 1 bytes
        self.table_type = 0        # 1 bytes, ofp_table_type_rev_map
        self.match_field_num = 0   # 1 bytes
        self.table_size = 0        # 4 bytes
        self.key_length = 0        # 2 bytes
        self.table_name = None     # 64 bytes, String
        self.match_field_list = []   # match20, 8 * 8 = 64 bytes
        
        initHelper(self, kw)
        
    def pack (self):
        assert self._assert()
        
        packed=b""
        packed += struct.pack("!BBBBLH" ,self.command, self.table_id, self.table_type, 
                              self.match_field_num, self.table_size, self.key_length)
        packed += _PAD6
        packed += self.table_name.ljust(OFP_NAME_MAX_LENGTH,'\0')
        
        for m in self.match_field_list:   # match20
            packed += m.pack()
        if len(self.match_field_list) < OFP_MAX_MATCH_FIELD_NUM:
            packed += _PAD * (OFP_MAX_MATCH_FIELD_NUM - len(self.match_field_list)) * ofp_match20._MIN_LENGTH
        return packed
    
    def unpack (self, raw, offset=0):
        _offset = offset
        offset, (self.command, self.table_id, self.table_type, self.match_field_num,
                 self.table_size, self.key_length) = _unpack('!BBBBLH', raw, offset)        
        offset = _skip(raw, offset, 6)
        offset,self.name = _readzs(raw, offset, OFP_NAME_MAX_LENGTH)
        for _ in xrange(0, OFP_MAX_MATCH_FIELD_NUM):
            m = ofp_match20()
            offset = m.unpack(raw, offset)
            self.match_field_list.append(m)
        assert offset - _offset == len(self)
        return offset
    
    @staticmethod
    def __len__ ():
        return 144   # FIXME:
    
    def __eq__(self,other):
        if type(self) != type(other): return False
        if self.command != other.command: return False
        if self.table_id != other.table_id: return False
        if self.table_type != other.table_type: return False
        if self.match_field_num != other.match_field_num: return False
        if self.table_size != other.table_size: return False
        if self.key_length != other.key_length: return False
        if self.table_name != other.table_name: return False
        if self.match_field_list != other.match_field_num: return False
        return True
    
    def show (self, prefix=''):
        outstr =''
        outstr += prefix + 'command:          '+  str(self.command) + '\n' 
        outstr += prefix + 'table_id:         '+  str(self.table_id) + '\n' 
        outstr += prefix + 'table_type:       '+  str(self.table_type) + '\n' 
        outstr += prefix + 'match_field_num:  '+  str(self.match_field_num) + '\n' 
        outstr += prefix + 'table_size:       '+  str(self.table_size) + '\n' 
        outstr += prefix + 'key_length:       '+  str(self.key_length) + '\n' 
        outstr += prefix + 'table_name:       '+  str(self.table_name) + '\n' 
        outstr += prefix + 'match_field_list: '+  str(self.match_field_list) + '\n' 
        return outstr
    
@openflow_c_message("OFPT_TABLE_MOD", 18)
class ofp_table_mod (ofp_header):
    _MIN_LENGTH = 8 + ofp_flow_table._MAX_LENGTH   # 152
    
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.flow_table = ofp_flow_table()
        
        initHelper(self, kw)
        
    def pack (self):
        assert self._assert()
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.flow_table.pack()
        return packed
    
    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset = self.flow_table.unpack(raw, offset)
        assert length == len(self)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 152   # FIXME:
    
    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other) : return False
        if self.flow_table != other.flow_table: return False
        return True
    
    def show (self, prefix=''):
        outstr =''
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'flow_table: \n'
        outstr += self.flow_table.show(prefix + '  ')
        return outstr
    
@openflow_c_message("OFPT_STATS_REQUEST", 19)     # FIXME: should be changed to MULTIPART_REQUEST
class ofp_stats_request (ofp_header):
    _MIN_LENGTH = 12
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.type = None # Try to guess
        self.flags = 0
        self._body = b''
        self._body_packed = None # Cache
    
        initHelper(self, kw)

    def pack (self):
        if self.type is None:
            if isinstance(self.body, ofp_stats_body_base):
                self.type = self.body._type
            else:
                raise RuntimeError("Can't determine body type; specify it "
                               + "explicitly")
    
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!HH", self.type, self.flags)
        packed += self._pack_body()
        return packed

    def _pack_body (self):
        if self._body_packed is None:
            if hasattr(self.body, 'pack'):
                self._body_packed = self._body.pack()
            else:
                self._body_packed = self._body
        return self._body_packed

    @property
    def body (self):
        return self._body
    @body.setter
    def body (self, data):
        self._body = data
        self._body_packed_cache = None

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.type, self.flags) = _unpack("!HH", raw, offset)
        offset,body = _read(raw, offset, length - 12)
        si = _stats_type_to_class_info.get(self.type)
        if si is None:
            self.body = ofp_generic_stats_body()
            self.body.unpack(body, 0, len(body))
        else:
            if si.request is None:
                raise RuntimeError("No request for " + str(si))
            self.body = si.request()
            self.body.unpack(body, 0, len(body))
            #TODO: assert entire body is unpacked
    
        assert length == len(self)
        return offset,length

    def __len__ (self):
        return 12 + len(self._pack_body())

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.type != other.type: return False
        if self.flags != other.flags: return False
        if self._pack_body() != other._pack_body(): return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'flags: ' + str(self.flags) + '\n'
        outstr += prefix + 'body:\n'
        outstr += _format_body(self.body, prefix + '  ') + '\n'
        return outstr


@openflow_s_message("OFPT_STATS_REPLY", 20,    # FIXME: should be changed to MULTIPART_REPLY
    reply_to="ofp_stats_request")
class ofp_stats_reply (ofp_header):
    _MIN_LENGTH = 12
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.type = None # Guess
        self.flags = 0
        self.body = b''
        self._body_data = (None, None)
    
        initHelper(self, kw)

    @property
    def is_last_reply (self):
        return (self.flags & 1) == 0
    @is_last_reply.setter
    def is_last_reply (self, value):
        self.flags = self.flags & 0xfffe
        if not value:
            self.flags |= 1

    @property
    def body_data (self):
        if self._body_data[0] is not self.body:
            def _pack(b):
                return b.pack() if hasattr(b, 'pack') else b
    
            data = b''
            if is_listlike(self.body):
                for b in self.body:
                    data += _pack(b)
            else:
                data = _pack(self.body)
            self._body_data = (self.body, data)
        return self._body_data[1]

    def pack (self):
        if self.type is None:
            if is_listlike(self.body):
                if len(self.body):
                    b = self.body[0]
                else:
                    b = None # Will fail below
            else:
                b = self.body
            if isinstance(b, ofp_stats_body_base):
                self.type = b._type
            else:
                raise RuntimeError("Can't determine body type; specify it "
                               + "explicitly")
    
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!HH", self.type, self.flags)
        packed += self.body_data
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.type, self.flags) = _unpack("!HH", raw, offset)
        offset,packed = _read(raw, offset, length - 12)
        t = _stats_type_to_class_info.get(self.type)
        if t is None:
            #FIXME: Put in a generic container?
            self.body = packed
        else:
            if t.reply is None:
                #FIXME: Put in a generic container?
                self.body = packed
            else:
                if not t.reply_is_list:
                    self.body = t.reply()
                    self.body.unpack(packed, 0, len(packed))
                else:
                    prev_len = len(packed)
                    self.body = []
                    while len(packed):
                        part = t.reply()
                        off = part.unpack(packed, 0, len(packed))
                        packed = packed[off:]
                        assert len(packed) != prev_len
                        prev_len = len(packed)
                        self.body.append(part)
    
        assert length == len(self)
        return offset,length

    def __len__ (self):
        if isinstance(self.body, list):
            return 12 + sum(len(part) for part in self.body)
        return 12 + len(self.body)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.type != other.type: return False
        if self.flags != other.flags: return False
        if self.body != other.body: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'type: ' + str(self.type) + '\n'
        outstr += prefix + 'flags: ' + str(self.flags) + '\n'
        outstr += prefix + 'body:\n'
        body = self.body
        if not is_listlike(body):
            body = [body]
        for b in body:
            outstr += _format_body(b, prefix + '  ') + '\n'
        return outstr
    

@openflow_c_message("OFPT_BARRIER_REQUEST", 21, 
                    request_for="ofp_barrier_reply")
class ofp_barrier_request (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        return packed

    #def unpack (self, raw, offset=0):
        #offset,length = self._unpack_header(raw, offset)
        #assert length == len(self)
        #return offset,length

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        return outstr
    

@openflow_s_message("OFPT_BARRIER_REPLY", 22,
    reply_to="ofp_barrier_request")
class ofp_barrier_reply (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        return packed

    #def unpack (self, raw, offset=0):
    #  offset,length = self._unpack_header(raw, offset)
    #  assert length == len(self)
    #  return offset,length

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        return outstr


@openflow_c_message("OFPT_QUEUE_GET_CONFIG_REQUEST", 23,
    request_for = "ofp_queue_get_config_reply")
class ofp_queue_get_config_request (ofp_header):
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.port = 0
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!H", self.port)
        packed += _PAD2
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.port,) = _unpack("!H", raw, offset)
        offset = _skip(raw, offset, 2)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 12

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.port != other.port: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'port: ' + str(self.port) + '\n'
        return outstr


@openflow_s_message("OFPT_QUEUE_GET_CONFIG_REPLY", 24,
    reply_to = "ofp_ofp_queue_get_config_request")
class ofp_queue_get_config_reply (ofp_header):
    _MIN_LENGTH = 16
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.port = 0
        self.queues = []
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += ofp_header.pack(self)
        packed += struct.pack("!H", self.port)
        packed += _PAD6
        for i in self.queues:
            packed += i.pack()
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset,(self.port,) = _unpack("!H", raw, offset)
        offset = _skip(raw, offset, 6)
        remaining = length - 6 - 2 - len(ofp_header)
    
        del self.queues[:]
    
        # Not tested; probably buggy
        while remaining > 0:
            q = ofp_packet_queue()
            _offset = q.unpack(raw, offset)
            l = _offset - offset
            offset = _offset
            if l < 1: raise RuntimeError("Can't parse")
            remaining -= l
            self.queues.append(q)
    
        assert length == len(self)
        return offset,length

    def __len__ (self):
        l = 16
        for i in self.queues:
            l += len(i)
        return l

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if not ofp_header.__eq__(self, other): return False
        if self.port != other.port: return False
        if self.queues != other.queues: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'port: ' + str(self.port) + '\n'
        outstr += prefix + 'queues: \n'
        for obj in self.queues:
            outstr += obj.show(prefix + '  ')
        return outstr
    


@openflow_c_message("OFPT_ROLL_REQUEST", 25,
    request_for = "ofp_roll_reply")
class ofp_roll_request(ofp_header):
    _MIN_LENGTH = 8    # FIXME:
    def __init__(self,  **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 8
    
    def show(self):
        pass

@openflow_s_message("OFPT_ROLL_REPLY", 26,
    reply_to = "ofp_roll_request")
class ofp_roll_reply(ofp_header):
    _MIN_LENGTH = 8    # FIXME:
    def __init__(self,  **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 8
    
    def show(self):
        pass
    
@openflow_c_message("OFPT_GET_ASYNC_REQUEST", 27,
    request_for = "ofp_get_async_reply")
class ofp_get_async_request(ofp_header):
    _MIN_LENGTH = 8    # FIXME:
    def __init__(self,  **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 8
    
    def show(self):
        pass
    
@openflow_s_message("OFPT_GET_ASYNC_REPLY", 28,
    reply_to = "ofp_get_async_request")
class ofp_get_async_reply(ofp_header):
    _MIN_LENGTH = 8    # FIXME:
    def __init__(self,  **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 8
    
    def show(self):
        pass
    
@openflow_c_message("OFPT_SET_ASYNC", 29)
class ofp_set_async(ofp_header):
    _MIN_LENGTH = 8    # FIXME:
    def __init__(self,  **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 8
    
    def show(self):
        pass

@openflow_c_message("OFPT_METER_MOD", 30)
class ofp_meter_mod(ofp_header):
    _MIN_LENGTH = 8    # FIXME:
    def __init__(self,  **kw):
        ofp_header.__init__(self)
        
    def pack (self):
        pass
    
    def unpack(self, raw, offset=0):
        offset, length = self._unpack_header(raw, offset)
        return offset,length
    
    @staticmethod
    def __len__ ():
        return 8
    
    def show(self):
        pass
    

ofp_counter_mod_cmd_rev_map = {   # add by cc
    'OFPCC_ADD'        : 0,
    'OFPCC_DELETE'     : 1,
    'OFPCC_CLEAR'      : 2,
    'OFPCC_QUERY'      : 3,
    'OFPCC_QUERYREPLY' : 4,
}

class ofp_counter(ofp_base):
    _MIN_LENGTH = 24
    
    def __init__(self, **kw):
        self.command = 0         # 1 bytes
        self.counter_id = 0      # 4 bytes
        self.counter_value = 0   # 8 bytes
        self.byte_value = 0      # 8 bytes
        
        initHelper(self, kw)
        
    def pack(self):
        assert self._assert()
        packed = b""
        packed += struct.pack("!B", self.command)
        packed += _PAD * 3
        packed += struct.pack("!LQQ", self.counter_id, self.counter_value, self.byte_value)
        return packed
    
    def unpack (self, raw, offset=0):
        offset,(self.command,) = _unpack("!B", raw, offset)
        offset = _skip(raw, offset, 3)
        offset,(self.counter_id, self.counter_value, self.byte_value) = _unpack("!LQQ", raw, offset)
        return offset

    @staticmethod
    def __len__ ():
        return 24

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'command:       ' + str(self.command) + '\n'
        outstr += prefix + 'counter_id:    ' + str(self.counter_id) + '\n'
        outstr += prefix + 'counter_value: ' + str(self.counter_value) + '\n'
        outstr += prefix + 'byte_value:    ' + str(self.byte_value) + '\n'
        return outstr 
        
    
@openflow_c_message("OFPT_COUNTER_MOD", 31)
class ofp_counter_mod(ofp_header):
    _MIN_LENGTH = 8 + ofp_counter._MIN_LENGTH    # 32
    
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.counter = ofp_counter()
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.counter.pack()
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset = self.counter.unpack(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 8 + ofp_counter._MIN_LENGTH    # 32

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'counter: \n'
        outstr += self.counter.show(prefix + '  ')
        return outstr 


@openflow_s_message("OFPT_COUNTER_REQUEST", 32,
    request_for="ofp_counter_reply")
class ofp_counter_request(ofp_header):
    _MIN_LENGTH = 8 + ofp_counter._MIN_LENGTH    # 32
    
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.counter = ofp_counter()
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.counter.pack()
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset = self.counter.unpack(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 8 + ofp_counter._MIN_LENGTH    # 32

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'counter: \n'
        outstr += self.counter.show(prefix + '  ')
        return outstr 

@openflow_s_message("OFPT_COUNTER_REPLY", 33,
    reply_to="ofp_counter_request")
class ofp_counter_reply(ofp_header):
    _MIN_LENGTH = 8 + ofp_counter._MIN_LENGTH    # 32
    
    def __init__ (self, **kw):
        ofp_header.__init__(self)
        self.counter = ofp_counter()
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
        packed = b""
        packed += ofp_header.pack(self)
        packed += self.counter.pack()
        return packed

    def unpack (self, raw, offset=0):
        offset,length = self._unpack_header(raw, offset)
        offset = self.counter.unpack(raw, offset)
        assert length == len(self)
        return offset,length

    @staticmethod
    def __len__ ():
        return 8 + ofp_counter._MIN_LENGTH    # 32

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'header: \n'
        outstr += ofp_header.show(self, prefix + '  ')
        outstr += prefix + 'counter: \n'
        outstr += self.counter.show(prefix + '  ')
        return outstr 


@openflow_stats_reply("OFPST_DESC", 0)
class ofp_desc_stats (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.mfr_desc   = ""
        self.hw_desc    = ""
        self.sw_desc    = ""
        self.serial_num = ""
        self.dp_desc    = ""
    
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.mfr_desc, str):
            return "mfr_desc is not string"
        if len(self.mfr_desc) > DESC_STR_LEN:
            return "mfr_desc is not of size 256"
        if not isinstance(self.hw_desc, str):
            return "hw_desc is not string"
        if len(self.hw_desc) > DESC_STR_LEN:
            return "hw_desc is not of size 256"
        if not isinstance(self.sw_desc, str):
            return "sw_desc is not string"
        if len(self.sw_desc) > DESC_STR_LEN:
            return "sw_desc is not of size 256"
        if not isinstance(self.serial_num, str):
            return "serial_num is not string"
        if len(self.serial_num) > SERIAL_NUM_LEN:
            return "serial_num is not of size 32"
        if not isinstance(self.dp_desc, str):
            return "dp_desc is not string"
        if len(self.dp_desc) > DESC_STR_LEN:
            return "dp_desc is not of size 256"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += self.mfr_desc.ljust(DESC_STR_LEN,'\0')
        packed += self.hw_desc.ljust(DESC_STR_LEN,'\0')
        packed += self.sw_desc.ljust(DESC_STR_LEN,'\0')
        packed += self.serial_num.ljust(SERIAL_NUM_LEN,'\0')
        packed += self.dp_desc.ljust(DESC_STR_LEN,'\0')
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,self.mfr_desc   = _readzs(raw, offset, DESC_STR_LEN)
        offset,self.hw_desc    = _readzs(raw, offset, DESC_STR_LEN)
        offset,self.sw_desc    = _readzs(raw, offset, DESC_STR_LEN)
        offset,self.serial_num = _readzs(raw, offset, SERIAL_NUM_LEN)
        offset,self.dp_desc    = _readzs(raw, offset, DESC_STR_LEN)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 1056

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.mfr_desc != other.mfr_desc: return False
        if self.hw_desc != other.hw_desc: return False
        if self.sw_desc != other.sw_desc: return False
        if self.serial_num != other.serial_num: return False
        if self.dp_desc != other.dp_desc: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'mfr_desc: ' + str(self.mfr_desc) + '\n'
        outstr += prefix + 'hw_desc: ' + str(self.hw_desc) + '\n'
        outstr += prefix + 'sw_desc: ' + str(self.sw_desc) + '\n'
        outstr += prefix + 'serial_num: ' + str(self.serial_num) + '\n'
        outstr += prefix + 'dp_desc: ' + str(self.dp_desc) + '\n'
        return outstr

ofp_desc_stats_reply = ofp_desc_stats


class _empty_stats_request_body (ofp_stats_body_base):
    """
    Superclass for table stats requests with empty bodies

    OFPST_DESC and OFPST_TABLE have empty request bodies.  In order
    to make type guessing and unpacking consistent, we define
    classes for them anyway.
    """
    def __init__ (self, **kw):
        pass

    def pack (self):
        return b""

    def unpack (self, raw, offset, avail):
        if avail != 0:
            raise RuntimeError("Expected empty body")
        return offset

    @staticmethod
    def __len__ ():
        return 0

    def __eq__ (self, other):
        if type(self) != type(other): return False
        return True

    def show (self, prefix=''):
        return "<empty>"

@openflow_stats_request('OFPST_DESC', 0)
class ofp_desc_stats_request (_empty_stats_request_body):
    """
    See _empty_stats_request_body superclass documentation
    """
    pass

@openflow_stats_request('OFPST_TABLE', 3)
class ofp_table_stats_request (_empty_stats_request_body):
    """
    See _empty_stats_request_body superclass documentation
    """
    pass


@openflow_stats_request('OFPST_FLOW', 1)
class ofp_flow_stats_request (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.match = ofp_match()
        self.table_id = TABLE_ALL
        self.out_port = OFPP_NONE
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.match, ofp_match):
            return "match is not class ofp_match"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += self.match.pack()
        packed += struct.pack("!BBH", self.table_id, 0, self.out_port)
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset = self.match.unpack(raw, offset)
        offset,(self.table_id, pad, self.out_port) = \
            _unpack("!BBH", raw, offset)
        assert pad == 0
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 4 + len(ofp_match)

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.match != other.match: return False
        if self.table_id != other.table_id: return False
        if self.out_port != other.out_port: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'match: \n'
        outstr += self.match.show(prefix + '  ')
        outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
        outstr += prefix + 'out_port: ' + str(self.out_port) + '\n'
        return outstr


@openflow_stats_reply('OFPST_FLOW', is_list = True)
class ofp_flow_stats (ofp_stats_body_base):
    _MIN_LENGTH = 88
    def __init__ (self, **kw):
        self.table_id = 0
        self.match = ofp_match()
        self.duration_sec = 0
        self.duration_nsec = 0
        self.priority = OFP_DEFAULT_PRIORITY
        self.idle_timeout = 0
        self.hard_timeout = 0
        self.cookie = 0
        self.packet_count = 0
        self.byte_count = 0
        self.actions = []
    
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.match, ofp_match):
            return "match is not class ofp_match"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!HBB", len(self), self.table_id, 0)
        packed += self.match.pack()
        packed += struct.pack("!LLHHH", self.duration_sec,
                              self.duration_nsec, self.priority,
                              self.idle_timeout, self.hard_timeout)
        packed += _PAD6 # Pad
        packed += struct.pack("!QQQ", self.cookie, self.packet_count,
                              self.byte_count)
        for i in self.actions:
            packed += i.pack()
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(length, self.table_id, pad) = _unpack("!HBB", raw, offset)
        assert pad == 0
        offset = self.match.unpack(raw, offset)
        offset,(self.duration_sec, self.duration_nsec, self.priority,
                self.idle_timeout, self.hard_timeout) = \
                _unpack("!LLHHH", raw, offset)
        offset = _skip(raw, offset, 6)
        offset,(self.cookie, self.packet_count, self.byte_count) = \
            _unpack("!QQQ", raw, offset)
        assert (offset - _offset) == 48 + len(self.match)
        offset,self.actions = _unpack_actions(raw,
            length - (48 + len(self.match)), offset)
        assert offset - _offset == len(self)
        return offset

    def __len__ (self):
        l = 48 + len(self.match)
        for i in self.actions:
            l += len(i)
        return l

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if len(self) != len(other): return False
        if self.table_id != other.table_id: return False
        if self.match != other.match: return False
        if self.duration_sec != other.duration_sec: return False
        if self.duration_nsec != other.duration_nsec: return False
        if self.priority != other.priority: return False
        if self.idle_timeout != other.idle_timeout: return False
        if self.hard_timeout != other.hard_timeout: return False
        if self.cookie != other.cookie: return False
        if self.packet_count != other.packet_count: return False
        if self.byte_count != other.byte_count: return False
        if self.actions != other.actions: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'length: ' + str(len(self)) + '\n'
        outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
        outstr += prefix + 'match: \n'
        outstr += self.match.show(prefix + '  ')
        outstr += prefix + 'duration_sec: ' + str(self.duration_sec) + '\n'
        outstr += prefix + 'duration_nsec: ' + str(self.duration_nsec) + '\n'
        outstr += prefix + 'priority: ' + str(self.priority) + '\n'
        outstr += prefix + 'idle_timeout: ' + str(self.idle_timeout) + '\n'
        outstr += prefix + 'hard_timeout: ' + str(self.hard_timeout) + '\n'
        outstr += prefix + 'cookie: ' + str(self.cookie) + '\n'
        outstr += prefix + 'packet_count: ' + str(self.packet_count) + '\n'
        outstr += prefix + 'byte_count: ' + str(self.byte_count) + '\n'
        outstr += prefix + 'actions: \n'
        for obj in self.actions:
            outstr += obj.show(prefix + '  ')
        return outstr
    
ofp_flow_stats_reply = ofp_flow_stats


@openflow_stats_request('OFPST_AGGREGATE', 2)
class ofp_aggregate_stats_request (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.match = ofp_match()
        self.table_id = TABLE_ALL
        self.out_port = OFPP_NONE
    
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.match, ofp_match):
            return "match is not class ofp_match"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += self.match.pack()
        packed += struct.pack("!BBH", self.table_id, 0, self.out_port)
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset = self.match.unpack(raw, offset)
        offset,(self.table_id, pad, self.out_port) = \
            _unpack("!BBH", raw, offset)
        assert pad == 0
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 44

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.match != other.match: return False
        if self.table_id != other.table_id: return False
        if self.out_port != other.out_port: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'match: \n'
        outstr += self.match.show(prefix + '  ')
        outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
        outstr += prefix + 'out_port: ' + str(self.out_port) + '\n'
        return outstr


@openflow_stats_reply('OFPST_AGGREGATE')
class ofp_aggregate_stats (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.packet_count = 0
        self.byte_count = 0
        self.flow_count = 0
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!QQL", self.packet_count, self.byte_count,
                              self.flow_count)
        packed += _PAD4 # Pad
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(self.packet_count, self.byte_count, self.flow_count) = \
            _unpack("!QQL", raw, offset)
        offset = _skip(raw, offset, 4)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 24

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.packet_count != other.packet_count: return False
        if self.byte_count != other.byte_count: return False
        if self.flow_count != other.flow_count: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'packet_count: ' + str(self.packet_count) + '\n'
        outstr += prefix + 'byte_count: ' + str(self.byte_count) + '\n'
        outstr += prefix + 'flow_count: ' + str(self.flow_count) + '\n'
        return outstr
    
ofp_aggregate_stats_reply = ofp_aggregate_stats


@openflow_stats_reply('OFPST_TABLE', 3, is_list = True)
class ofp_table_stats (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.table_id = 0
        self.name = ""
        self.wildcards = 0
        self.max_entries = 0
        self.active_count = 0
        self.lookup_count = 0
        self.matched_count = 0
    
        initHelper(self, kw)

    def _validate (self):
        if not isinstance(self.name, str):
            return "name is not string"
        if len(self.name) > OFP_MAX_TABLE_NAME_LEN:
            return "name is too long"
        return None

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!B", self.table_id)
        packed += _PAD3
        packed += self.name.ljust(OFP_MAX_TABLE_NAME_LEN,'\0')
        packed += struct.pack("!LLLQQ", self.wildcards, self.max_entries,
                              self.active_count, self.lookup_count,
                              self.matched_count)
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(self.table_id,) = _unpack("!B", raw, offset)
        offset = _skip(raw, offset, 3)
        offset,self.name = _readzs(raw, offset, OFP_MAX_TABLE_NAME_LEN)
        offset,(self.wildcards, self.max_entries, self.active_count,
                self.lookup_count, self.matched_count) = \
                _unpack("!LLLQQ", raw, offset)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 64

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.table_id != other.table_id: return False
        if self.name != other.name: return False
        if self.wildcards != other.wildcards: return False
        if self.max_entries != other.max_entries: return False
        if self.active_count != other.active_count: return False
        if self.lookup_count != other.lookup_count: return False
        if self.matched_count != other.matched_count: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
        outstr += prefix + 'name: ' + str(self.name) + '\n'
        outstr += prefix + 'wildcards: ' + str(self.wildcards) + '\n'
        outstr += prefix + 'max_entries: ' + str(self.max_entries) + '\n'
        outstr += prefix + 'active_count: ' + str(self.active_count) + '\n'
        outstr += prefix + 'lookup_count: ' + str(self.lookup_count) + '\n'
        outstr += prefix + 'matched_count: ' + str(self.matched_count) + '\n'
        return outstr
    
ofp_table_stats_reply = ofp_table_stats


@openflow_stats_request("OFPST_PORT", 4)
class ofp_port_stats_request (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.port_no = OFPP_NONE
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!H", self.port_no)
        packed += _PAD6
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(self.port_no,) = _unpack("!H", raw, offset)
        offset = _skip(raw, offset, 6)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.port_no != other.port_no: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
        return outstr


@openflow_stats_reply("OFPST_PORT", is_list = True)
class ofp_port_stats (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.port_no = OFPP_NONE
        self.rx_packets = 0
        self.tx_packets = 0
        self.rx_bytes = 0
        self.tx_bytes = 0
        self.rx_dropped = 0
        self.tx_dropped = 0
        self.rx_errors = 0
        self.tx_errors = 0
        self.rx_frame_err = 0
        self.rx_over_err = 0
        self.rx_crc_err = 0
        self.collisions = 0
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!H", self.port_no)
        packed += _PAD6
        packed += struct.pack("!QQQQQQQQQQQQ", self.rx_packets,
                              self.tx_packets, self.rx_bytes, self.tx_bytes,
                              self.rx_dropped, self.tx_dropped,
                              self.rx_errors, self.tx_errors,
                              self.rx_frame_err, self.rx_over_err,
                              self.rx_crc_err, self.collisions)
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(self.port_no,) = _unpack("!H", raw, offset)
        offset = _skip(raw, offset, 6)
        offset,(self.rx_packets, self.tx_packets, self.rx_bytes,
                self.tx_bytes, self.rx_dropped, self.tx_dropped,
                self.rx_errors, self.tx_errors, self.rx_frame_err,
                self.rx_over_err, self.rx_crc_err, self.collisions) = \
                _unpack("!QQQQQQQQQQQQ", raw, offset)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 104

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.port_no != other.port_no: return False
        if self.rx_packets != other.rx_packets: return False
        if self.tx_packets != other.tx_packets: return False
        if self.rx_bytes != other.rx_bytes: return False
        if self.tx_bytes != other.tx_bytes: return False
        if self.rx_dropped != other.rx_dropped: return False
        if self.tx_dropped != other.tx_dropped: return False
        if self.rx_errors != other.rx_errors: return False
        if self.tx_errors != other.tx_errors: return False
        if self.rx_frame_err != other.rx_frame_err: return False
        if self.rx_over_err != other.rx_over_err: return False
        if self.rx_crc_err != other.rx_crc_err: return False
        if self.collisions != other.collisions: return False
        return True

    def __add__(self, other):
        if type(self) != type(other): raise NotImplemented()
        port_no = OFPP_NONE
        if self.port_no == other.port_no:
            port_no = self.port_no
        return ofp_port_stats(
            port_no=port_no,
            rx_packets = self.rx_packets + other.rx_packets,
            tx_packets = self.tx_packets + other.tx_packets,
            rx_bytes = self.rx_bytes + other.rx_bytes,
            tx_bytes = self.tx_bytes + other.tx_bytes,
            rx_dropped = self.rx_dropped + other.rx_dropped,
            tx_dropped = self.tx_dropped + other.tx_dropped,
            rx_errors = self.rx_errors + other.rx_errors,
            tx_errors = self.tx_errors + other.tx_errors,
            rx_frame_err = self.rx_frame_err + other.rx_frame_err,
            rx_over_err = self.rx_over_err + other.rx_over_err,
            rx_crc_err = self.rx_crc_err + other.rx_crc_err,
            collisions = self.collisions + other.collisions)

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
        outstr += prefix + 'rx_packets: ' + str(self.rx_packets) + '\n'
        outstr += prefix + 'tx_packets: ' + str(self.tx_packets) + '\n'
        outstr += prefix + 'rx_bytes: ' + str(self.rx_bytes) + '\n'
        outstr += prefix + 'tx_bytes: ' + str(self.tx_bytes) + '\n'
        outstr += prefix + 'rx_dropped: ' + str(self.rx_dropped) + '\n'
        outstr += prefix + 'tx_dropped: ' + str(self.tx_dropped) + '\n'
        outstr += prefix + 'rx_errors: ' + str(self.rx_errors) + '\n'
        outstr += prefix + 'tx_errors: ' + str(self.tx_errors) + '\n'
        outstr += prefix + 'rx_frame_err: ' + str(self.rx_frame_err) + '\n'
        outstr += prefix + 'rx_over_err: ' + str(self.rx_over_err) + '\n'
        outstr += prefix + 'rx_crc_err: ' + str(self.rx_crc_err) + '\n'
        outstr += prefix + 'collisions: ' + str(self.collisions) + '\n'
        return outstr
    
ofp_port_stats_reply = ofp_port_stats


@openflow_stats_request("OFPST_QUEUE", 5)
class ofp_queue_stats_request (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.port_no = OFPP_ALL
        self.queue_id = OFPQ_ALL
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!H", self.port_no)
        packed += _PAD2
        packed += struct.pack("!L", self.queue_id)
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(self.port_no,pad,self.queue_id) = _unpack("!HHL", raw, offset)
        assert pad == 0
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 8

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.port_no != other.port_no: return False
        if self.queue_id != other.queue_id: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
        outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
        return outstr


@openflow_stats_reply("OFPST_QUEUE", is_list = True)
class ofp_queue_stats (ofp_stats_body_base):
    def __init__ (self, **kw):
        self.port_no = 0
        self.queue_id = 0
        self.tx_bytes = 0
        self.tx_packets = 0
        self.tx_errors = 0
    
        initHelper(self, kw)

    def pack (self):
        assert self._assert()
    
        packed = b""
        packed += struct.pack("!H", self.port_no)
        packed += _PAD2
        packed += struct.pack("!LQQQ", self.queue_id, self.tx_bytes,
                              self.tx_packets, self.tx_errors)
        return packed

    def unpack (self, raw, offset, avail):
        _offset = offset
        offset,(self.port_no, pad, self.queue_id, self.tx_bytes,
                self.tx_packets, self.tx_errors) = \
                _unpack("!HHLQQQ", raw, offset)
        assert offset - _offset == len(self)
        return offset

    @staticmethod
    def __len__ ():
        return 32

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.port_no != other.port_no: return False
        if self.queue_id != other.queue_id: return False
        if self.tx_bytes != other.tx_bytes: return False
        if self.tx_packets != other.tx_packets: return False
        if self.tx_errors != other.tx_errors: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
        outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
        outstr += prefix + 'tx_bytes: ' + str(self.tx_bytes) + '\n'
        outstr += prefix + 'tx_packets: ' + str(self.tx_packets) + '\n'
        outstr += prefix + 'tx_errors: ' + str(self.tx_errors) + '\n'
        return outstr
    
ofp_queue_stats_reply = ofp_queue_stats


@openflow_stats_request("OFPST_VENDOR", 65535, is_list = False)
@openflow_stats_reply("OFPST_VENDOR", 65535, is_list = False)
class ofp_vendor_stats_generic (ofp_stats_body_base):
    _MIN_LENGTH = 4
    def __init__ (self, **kw):
        self.vendor = None
        self.data = b""
    
        initHelper(self, kw)

    def _pack_body (self):
        if hasattr(self.data, "pack"):
            return self.data.pack()
        else:
            return self.data

    def pack (self):
        assert self._assert()
    
        packed = struct.pack("!L", self.vendor)
        packed += self._pack_body()
        return packed

    def unpack (self, raw, offset, avail):
        if avail is None: RuntimeError("Requires length")
        _offset = offset
        offset,(self.vendor,) = _unpack("!L", raw, offset)
        offset,self.data = _read(raw, offset, avail-4)
        return offset

    @staticmethod
    def __len__ ():
        return 4+len(self._pack_body())

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.vendor != other.vendor: return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'vendor id: ' + str(self.vendor) + '\n'
        outstr += prefix + 'data len: ' + str(len(self.data)) + '\n'
        return outstr


class ofp_generic_stats_body (ofp_stats_body_base):
    _MIN_LENGTH = 0
    def __init__ (self, **kw):
        self.data = b""
    
        initHelper(self, kw)

    def _pack_body (self):
        if hasattr(self.data, "pack"):
            return self.data.pack()
        else:
            return self.data

    def pack (self):
        assert self._assert()
    
        packed += self._pack_body()
        return packed

    def unpack (self, raw, offset, avail):
        if avail is None: RuntimeError("Requires length")
        _offset = offset
        offset,self.data = _read(raw, offset, avail)
        return offset

    @staticmethod
    def __len__ ():
        return len(self._pack_body())

    def __eq__ (self, other):
        if type(self) != type(other): return False
        if self.data != other.data: return False
        return True

    def show (self, prefix=''):
        outstr = ''
        outstr += prefix + 'data len: ' + str(len(self.data)) + '\n'
        return outstr
    
def _unpack_actions (b, length, offset=0):
    """
    Parses actions from a buffer
    b is a buffer (bytes)
    offset, if specified, is where in b to start decoding
    returns (next_offset, [Actions])
    """
    if (len(b) - offset) < length: raise UnderrunError
    actions = []
    end = length + offset
    while offset < end:
        (t,l) = struct.unpack_from("!HH", b, offset)
        if (len(b) - offset) < l: raise UnderrunError
        a = _action_type_to_class.get(t)
        if a is None:
            # Use generic action header for unknown type
            a = ofp_action_generic()
        else:
            a = a()
        a.unpack(b[offset:offset+l])
        assert len(a) == l
        actions.append(a)
        offset += l
    return (offset, actions)

def _init ():   # FIXME: CC
    def formatMap (name, m):
        o = name + " = {\n"
        vk = sorted([(v,k) for k,v in m.iteritems()])
        maxlen = 2 + len(reduce(lambda a,b: a if len(a)>len(b) else b,
                                (v for k,v in vk)))
        fstr = "  %-" + str(maxlen) + "s : %s,\n"
        for v,k in vk:
            o += fstr % ("'" + k + "'",v)
        o += "}"
        return o
    """
    maps = []
    for k,v in globals().iteritems():
        if k.startswith("ofp_") and k.endswith("_map") and type(v) == dict:
          maps.append((k,v))
    for name,m in maps:
        rev = {}
        name = name[:-4]
        names = globals()[name]
        for n in names:
          rev[n] = globals()[n]
    
        globals()[name + '_rev_map'] = rev
        print(formatMap(name + "_rev_map", rev))
    return
    """
    maps = []
    for k,v in globals().iteritems():
        if (k.startswith("ofp_") and k.endswith("_rev_map") and type(v) == dict):
            maps.append((k[:-8],v))     # cc:delete '_rev_map'
    for name,m in maps:
        # Try to generate forward maps
        forward = dict(((v,k) for k,v in m.iteritems()))   # reverse the map (or dict)
        if len(forward) == len(m):
            if name + "_map" not in globals():  # cc:add '_map'
                globals()[name + "_map"] = forward
        else:
            print(name + "_rev_map is not a map")
    
        # Try to generate lists
        v = m.values()
        v.sort()
        if v[-1] != len(v)-1:
            # Allow ones where the last value is a special value (e.g., VENDOR)
            del v[-1]
        if len(v) > 0 and v[0] == 0 and v[-1] == len(v)-1:
            globals()[name] = v    # list:values of *_rev_map
    
        # Generate gobals
        for k,v in m.iteritems():
            globals()[k] = v

_init()


# Values from macro definitions
OFP_FLOW_PERMANENT = 0
OFP_DL_TYPE_ETH2_CUTOFF = 0x0600
DESC_STR_LEN = 256
OFPFW_ICMP_CODE = OFPFW_TP_DST
OFPQ_MIN_RATE_UNCFG = 0xffff
OFP_VERSION = 0x04                 # OpenFlow version -> POF
OFP_MAX_TABLE_NAME_LEN = 32
OFP_DL_TYPE_NOT_ETH_TYPE = 0x05ff
OFP_DEFAULT_MISS_SEND_LEN = 128
OFP_MAX_PORT_NAME_LEN = 64         # changed from 16 to 64
POF_NAME_MAX_LENGTH = 64           # add new
OFP_SSL_PORT = 6633
OFPFW_ICMP_TYPE = OFPFW_TP_SRC
OFP_TCP_PORT = 6633
SERIAL_NUM_LEN = 32
OFP_DEFAULT_PRIORITY = 0x8000
OFP_VLAN_NONE = 0xffff
OFPQ_ALL = 0xffffffff

ofp_match_data = {
  'in_port' : (0, OFPFW_IN_PORT),
  'dl_src' : (EMPTY_ETH, OFPFW_DL_SRC),
  'dl_dst' : (EMPTY_ETH, OFPFW_DL_DST),
  'dl_vlan' : (0, OFPFW_DL_VLAN),
  'dl_vlan_pcp' : (0, OFPFW_DL_VLAN_PCP),
  'dl_type' : (0, OFPFW_DL_TYPE),
  'nw_tos' : (0, OFPFW_NW_TOS),
  'nw_proto' : (0, OFPFW_NW_PROTO),
  'nw_src' : (0, OFPFW_NW_SRC_ALL),
  'nw_dst' : (0, OFPFW_NW_DST_ALL),
  'tp_src' : (0, OFPFW_TP_SRC),
  'tp_dst' : (0, OFPFW_TP_DST),
}
