blob: df74b112ab3eed65efb96f06ec21198910839db3 [file] [log] [blame]
## This file is part of Scapy
## See http://www.secdev.org/projects/scapy for more information
## Copyright (C) Philippe Biondi <phil@secdev.org>
## This program is published under a GPLv2 license
## Copyright (C) 2014 Maxence Tury <maxence.tury@ssi.gouv.fr>
## OpenFlow is an open standard used in SDN deployments.
## Based on OpenFlow v1.0.1
## Specifications can be retrieved from https://www.opennetworking.org/
# scapy.contrib.description = Openflow v1.0
# scapy.contrib.status = loads
from __future__ import absolute_import
import struct
from scapy.fields import *
from scapy.layers.l2 import *
from scapy.layers.inet import *
from scapy.compat import orb
### If prereq_autocomplete is True then match prerequisites will be
### automatically handled. See OFPMatch class.
prereq_autocomplete = False
#####################################################
################# Predefined values #################
#####################################################
ofp_port_no = { 0xfff8: "IN_PORT",
0xfff9: "TABLE",
0xfffa: "NORMAL",
0xfffb: "FLOOD",
0xfffc: "ALL",
0xfffd: "CONTROLLER",
0xfffe: "LOCAL",
0xffff: "NONE" }
ofp_table = { 0xff: "ALL" }
ofp_queue = { 0xffffffff: "ALL" }
ofp_buffer = { 0xffffffff: "NO_BUFFER" }
ofp_max_len = { 0xffff: "NO_BUFFER" }
#####################################################
################# Common structures #################
#####################################################
### The following structures will be used in different types
### of OpenFlow messages: ports, matches, actions, queues.
##################### Ports #####################
ofp_port_config = [ "PORT_DOWN",
"NO_STP",
"NO_RECV",
"NO_RECV_STP",
"NO_FLOOD",
"NO_FWD",
"NO_PACKET_IN" ]
ofp_port_state = [ "LINK_DOWN" ]
ofp_port_state_stp = { 0: "OFPPS_STP_LISTEN",
1: "OFPPS_STP_LEARN",
2: "OFPPS_STP_FORWARD",
3: "OFPPS_STP_BLOCK" }
ofp_port_features = [ "10MB_HD",
"10MB_FD",
"100MB_HD",
"100MB_FD",
"1GB_HD",
"1GB_FD",
"10GB_FD",
"COPPER",
"FIBER",
"AUTONEG",
"PAUSE",
"PAUSE_ASYM" ]
class OFPPhyPort(Packet):
name = "OFP_PHY_PORT"
fields_desc = [ ShortEnumField("port_no", 0, ofp_port_no),
MACField("hw_addr", "0"),
StrFixedLenField("port_name", "", 16),
FlagsField("config", 0, 32, ofp_port_config),
BitEnumField("stp_state", 0, 24, ofp_port_state),
FlagsField("state", 0, 8, ofp_port_state),
FlagsField("curr", 0, 32, ofp_port_features),
FlagsField("advertised", 0, 32, ofp_port_features),
FlagsField("supported", 0, 32, ofp_port_features),
FlagsField("peer", 0, 32, ofp_port_features) ]
def extract_padding(self, s):
return "", s
class OFPMatch(Packet):
name = "OFP_MATCH"
fields_desc= [ FlagsField("wildcards1", None, 12, [ "DL_VLAN_PCP",
"NW_TOS" ]),
BitField("nw_dst_mask", None, 6),
BitField("nw_src_mask", None, 6),
FlagsField("wildcards2", None, 8, [ "IN_PORT",
"DL_VLAN",
"DL_SRC",
"DL_DST",
"DL_TYPE",
"NW_PROTO",
"TP_SRC",
"TP_DST" ]),
ShortEnumField("in_port", None, ofp_port_no),
MACField("dl_src", None),
MACField("dl_dst", None),
ShortField("dl_vlan", None),
ByteField("dl_vlan_pcp", None),
XByteField("pad1", None),
ShortField("dl_type", None),
ByteField("nw_tos", None),
ByteField("nw_proto", None),
XShortField("pad2", None),
IPField("nw_src", "0"),
IPField("nw_dst", "0"),
ShortField("tp_src", None),
ShortField("tp_dst", None) ]
def extract_padding(self, s):
return "", s
### with post_build we create the wildcards field bit by bit
def post_build(self, p, pay):
# first 10 bits of an ofp_match are always set to 0
l = "0"*10
# when one field has not been declared, it is assumed to be wildcarded
if self.wildcards1 is None:
if self.nw_tos is None: l+="1"
else: l+="0"
if self.dl_vlan_pcp is None: l+="1"
else: l+="0"
else:
w1 = binrepr(self.wildcards1)
l+="0"*(2-len(w1))
l+=w1
# ip masks use 6 bits each
if self.nw_dst_mask is None:
if self.nw_dst is "0": l+="111111"
# 0x100000 would be ok too (32-bit IP mask)
else: l+="0"*6
else:
m1 = binrepr(self.nw_dst_mask)
l+="0"*(6-len(m1))
l+=m1
if self.nw_src_mask is None:
if self.nw_src is "0": l+="111111"
else: l+="0"*6
else:
m2 = binrepr(self.nw_src_mask)
l+="0"*(6-len(m2))
l+=m2
# wildcards2 works the same way as wildcards1
if self.wildcards2 is None:
if self.tp_dst is None: l+="1"
else: l+="0"
if self.tp_src is None: l+="1"
else: l+="0"
if self.nw_proto is None: l+="1"
else: l+="0"
if self.dl_type is None: l+="1"
else: l+="0"
if self.dl_dst is None: l+="1"
else: l+="0"
if self.dl_src is None: l+="1"
else: l+="0"
if self.dl_vlan is None: l+="1"
else: l+="0"
if self.in_port is None: l+="1"
else: l+="0"
else:
w2 = binrepr(self.wildcards2)
l+="0"*(8-len(w2))
l+=w2
### In order to write OFPMatch compliant with the specifications,
### if prereq_autocomplete has been set to True
### we assume ethertype=IP or nwproto=TCP when appropriate subfields are provided.
if prereq_autocomplete:
if self.dl_type is None:
if self.nw_src is not "0" or self.nw_dst is not "0" or self.nw_proto is not None or self.nw_tos is not None:
p = p[:22] + struct.pack("!H", 0x0800) + p[24:]
l = l[:-5] + "0" + l[-4:]
if self.nw_proto is None:
if self.tp_src is not None or self.tp_dst is not None:
p = p[:22] + struct.pack("!H", 0x0800) + p[24:]
l = l[:-5] + "0" + l[-4:]
p = p[:25] + struct.pack("!B", 0x06) + p[26:]
l = l[:-6] + "0" + l[-5:]
ins = b"".join(chb(int("".join(x),2)) for x in zip(*[iter(l)]*8))
p = ins + p[4:]
return p + pay
###################### Actions ######################
class _ofp_action_header(Packet):
name = "Dummy OpenFlow Action Header"
def post_build(self, p, pay):
if self.len is None:
l = len(p)+len(pay)
p = p[:2] + struct.pack("!H", l) + p[4:]
return p + pay
ofp_action_types = { 0: "OFPAT_OUTPUT",
1: "OFPAT_SET_VLAN_VID",
2: "OFPAT_SET_VLAN_PCP",
3: "OFPAT_STRIP_VLAN",
4: "OFPAT_SET_DL_SRC",
5: "OFPAT_SET_DL_DST",
6: "OFPAT_SET_NW_SRC",
7: "OFPAT_SET_NW_DST",
8: "OFPAT_SET_NW_TOS",
9: "OFPAT_SET_TP_SRC",
10: "OFPAT_SET_TP_DST",
11: "OFPAT_ENQUEUE",
65535: "OFPAT_VENDOR" }
class OFPATOutput(_ofp_action_header):
name = "OFPAT_OUTPUT"
fields_desc = [ ShortEnumField("type", 0, ofp_action_types),
ShortField("len", 8),
ShortEnumField("port", 0, ofp_port_no),
ShortEnumField("max_len", "NO_BUFFER", ofp_max_len) ]
class OFPATSetVLANVID(_ofp_action_header):
name = "OFPAT_SET_VLAN_VID"
fields_desc = [ ShortEnumField("type", 1, ofp_action_types),
ShortField("len", 8),
ShortField("vlan_vid", 0),
XShortField("pad", 0) ]
class OFPATSetVLANPCP(_ofp_action_header):
name = "OFPAT_SET_VLAN_PCP"
fields_desc = [ ShortEnumField("type", 2, ofp_action_types),
ShortField("len", 8),
ByteField("vlan_pcp", 0),
X3BytesField("pad", 0) ]
class OFPATStripVLAN(_ofp_action_header):
name = "OFPAT_STRIP_VLAN"
fields_desc = [ ShortEnumField("type", 3, ofp_action_types),
ShortField("len", 8),
XIntField("pad", 0) ]
class OFPATSetDlSrc(_ofp_action_header):
name = "OFPAT_SET_DL_SRC"
fields_desc = [ ShortEnumField("type", 4, ofp_action_types),
ShortField("len", 16),
MACField("dl_addr", "0"),
XBitField("pad", 0, 48) ]
class OFPATSetDlDst(_ofp_action_header):
name = "OFPAT_SET_DL_DST"
fields_desc = [ ShortEnumField("type", 5, ofp_action_types),
ShortField("len", 16),
MACField("dl_addr", "0"),
XBitField("pad", 0, 48) ]
class OFPATSetNwSrc(_ofp_action_header):
name = "OFPAT_SET_NW_SRC"
fields_desc = [ ShortEnumField("type", 6, ofp_action_types),
ShortField("len", 8),
IPField("nw_addr", "0") ]
class OFPATSetNwDst(_ofp_action_header):
name = "OFPAT_SET_NW_DST"
fields_desc = [ ShortEnumField("type", 7, ofp_action_types),
ShortField("len", 8),
IPField("nw_addr", "0") ]
class OFPATSetNwToS(_ofp_action_header):
name = "OFPAT_SET_TP_TOS"
fields_desc = [ ShortEnumField("type", 8, ofp_action_types),
ShortField("len", 8),
ByteField("nw_tos", 0),
X3BytesField("pad", 0) ]
class OFPATSetTpSrc(_ofp_action_header):
name = "OFPAT_SET_TP_SRC"
fields_desc = [ ShortEnumField("type", 9, ofp_action_types),
ShortField("len", 8),
ShortField("tp_port", 0),
XShortField("pad", 0) ]
class OFPATSetTpDst(_ofp_action_header):
name = "OFPAT_SET_TP_DST"
fields_desc = [ ShortEnumField("type", 10, ofp_action_types),
ShortField("len", 8),
ShortField("tp_port", 0),
XShortField("pad", 0) ]
class OFPATEnqueue(_ofp_action_header):
name = "OFPAT_ENQUEUE"
fields_desc = [ ShortEnumField("type", 11, ofp_action_types),
ShortField("len", 16),
ShortEnumField("port", 0, ofp_port_no),
XBitField("pad", 0, 48),
IntField("queue_id", 0) ]
class OFPATVendor(_ofp_action_header):
name = "OFPAT_VENDOR"
fields_desc = [ ShortEnumField("type", 65535, ofp_action_types),
ShortField("len", 8),
IntField("vendor", 0) ]
ofp_action_cls = { 0: OFPATOutput,
1: OFPATSetVLANVID,
2: OFPATSetVLANPCP,
3: OFPATStripVLAN,
4: OFPATSetDlSrc,
5: OFPATSetDlDst,
6: OFPATSetNwSrc,
7: OFPATSetNwDst,
8: OFPATSetNwToS,
9: OFPATSetTpSrc,
10: OFPATSetTpDst,
11: OFPATEnqueue,
65535: OFPATVendor }
class ActionPacketListField(PacketListField):
def m2i(self, pkt, s):
t = struct.unpack("!H", s[:2])[0]
return ofp_action_cls.get(t, Raw)(s)
@staticmethod
def _get_action_length(s):
return struct.unpack("!H", s[2:4])[0]
def getfield(self, pkt, s):
lst = []
remain = s
while remain:
l = ActionPacketListField._get_action_length(remain)
current = remain[:l]
remain = remain[l:]
p = self.m2i(pkt, current)
lst.append(p)
return remain, lst
####################### Queues ######################
class _ofp_queue_property_header(Packet):
name = "Dummy OpenFlow Queue Property Header"
def post_build(self, p, pay):
if self.len is None:
l = len(p)+len(pay)
p = p[:2] + struct.pack("!H", l) + p[4:]
return p + pay
ofp_queue_property_types = { 0: "OFPQT_NONE",
1: "OFPQT_MIN_RATE" }
class OFPQTNone(_ofp_queue_property_header):
name = "OFPQT_NONE"
fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types),
ShortField("len", 8),
XIntField("pad", 0) ]
class OFPQTMinRate(_ofp_queue_property_header):
name = "OFPQT_MIN_RATE"
fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types),
ShortField("len", 16),
XIntField("pad", 0),
ShortField("rate", 0),
XBitField("pad2", 0, 48) ]
ofp_queue_property_cls = { 0: OFPQTNone,
1: OFPQTMinRate }
class QueuePropertyPacketListField(PacketListField):
def m2i(self, pkt, s):
t = struct.unpack("!H", s[:2])[0]
return ofp_queue_property_cls.get(t, Raw)(s)
@staticmethod
def _get_queue_property_length(s):
return struct.unpack("!H", s[2:4])[0]
def getfield(self, pkt, s):
lst = []
l = 0
ret = ""
remain = s
while remain:
l = QueuePropertyPacketListField._get_queue_property_length(remain)
current = remain[:l]
remain = remain[l:]
p = self.m2i(pkt, current)
lst.append(p)
return remain + ret, lst
class OFPPacketQueue(Packet):
def extract_padding(self, s):
return "", s
def post_build(self, p, pay):
if self.properties == []:
p += str(OFPQTNone())
if self.len is None:
l = len(p)+len(pay)
p = p[:4] + struct.pack("!H", l) + p[6:]
return p + pay
name = "OFP_PACKET_QUEUE"
fields_desc = [ IntField("queue_id", 0),
ShortField("len", None),
XShortField("pad", 0),
QueuePropertyPacketListField("properties", [], Packet,
length_from=lambda pkt:pkt.len-8) ]
class QueuePacketListField(PacketListField):
@staticmethod
def _get_queue_length(s):
return struct.unpack("!H", s[4:6])[0]
def getfield(self, pkt, s):
lst = []
l = 0
ret = ""
remain = s
while remain:
l = QueuePacketListField._get_queue_length(remain)
current = remain[:l]
remain = remain[l:]
p = OFPPacketQueue(current)
lst.append(p)
return remain + ret, lst
#####################################################
############## OpenFlow 1.0 Messages ################
#####################################################
class _ofp_header(Packet):
name = "Dummy OpenFlow Header"
def post_build(self, p, pay):
if self.len is None:
l = len(p)+len(pay)
p = p[:2] + struct.pack("!H", l) + p[4:]
return p + pay
ofp_version = { 0x01: "OpenFlow 1.0",
0x02: "OpenFlow 1.1",
0x03: "OpenFlow 1.2",
0x04: "OpenFlow 1.3",
0x05: "OpenFlow 1.4" }
ofp_type = { 0: "OFPT_HELLO",
1: "OFPT_ERROR",
2: "OFPT_ECHO_REQUEST",
3: "OFPT_ECHO_REPLY",
4: "OFPT_VENDOR",
5: "OFPT_FEATURES_REQUEST",
6: "OFPT_FEATURES_REPLY",
7: "OFPT_GET_CONFIG_REQUEST",
8: "OFPT_GET_CONFIG_REPLY",
9: "OFPT_SET_CONFIG",
10: "OFPT_PACKET_IN",
11: "OFPT_FLOW_REMOVED",
12: "OFPT_PORT_STATUS",
13: "OFPT_PACKET_OUT",
14: "OFPT_FLOW_MOD",
15: "OFPT_PORT_MOD",
16: "OFPT_STATS_REQUEST",
17: "OFPT_STATS_REPLY",
18: "OFPT_BARRIER_REQUEST",
19: "OFPT_BARRIER_REPLY",
20: "OFPT_QUEUE_GET_CONFIG_REQUEST",
21: "OFPT_QUEUE_GET_CONFIG_REPLY" }
class OFPTHello(_ofp_header):
name = "OFPT_HELLO"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 0, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"sport": 6653}}
#####################################################
#################### OFPT_ERROR #####################
#####################################################
### this class will be used to display some messages
### sent back by the switch after an error
class OFPacketField(PacketField):
def getfield(self, pkt, s):
try:
l = s[2:4]
l = struct.unpack("!H", l)[0]
ofload = s[:l]
remain = s[l:]
return remain, OpenFlow(None, ofload)(ofload)
except:
return "", Raw(s)
ofp_error_type = { 0: "OFPET_HELLO_FAILED",
1: "OFPET_BAD_REQUEST",
2: "OFPET_BAD_ACTION",
3: "OFPET_FLOW_MOD_FAILED",
4: "OFPET_PORT_MOD_FAILED",
5: "OFPET_QUEUE_OP_FAILED" }
class OFPETHelloFailed(_ofp_header):
name = "OFPET_HELLO_FAILED"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 1, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("errtype", 0, ofp_error_type),
ShortEnumField("errcode", 0, { 0: "OFPHFC_INCOMPATIBLE",
1: "OFPHFC_EPERM" }),
OFPacketField("data", "", Raw) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPETBadRequest(_ofp_header):
name = "OFPET_BAD_REQUEST"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 1, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("errtype", 1, ofp_error_type),
ShortEnumField("errcode", 0, { 0: "OFPBRC_BAD_VERSION",
1: "OFPBRC_BAD_TYPE",
2: "OFPBRC_BAD_STAT",
3: "OFPBRC_BAD_VENDOR",
4: "OFPBRC_BAD_SUBTYPE",
5: "OFPBRC_EPERM",
6: "OFPBRC_BAD_LEN",
7: "OFPBRC_BUFFER_EMPTY",
8: "OFPBRC_BUFFER_UNKNOWN" }),
OFPacketField("data", "", Raw) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPETBadAction(_ofp_header):
name = "OFPET_BAD_ACTION"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 1, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("errtype", 2, ofp_error_type),
ShortEnumField("errcode", 0, { 0: "OFPBAC_BAD_TYPE",
1: "OFPBAC_BAD_LEN",
2: "OFPBAC_BAD_VENDOR",
3: "OFPBAC_BAD_VENDOR_TYPE",
4: "OFPBAC_BAD_OUT_PORT",
5: "OFPBAC_BAD_ARGUMENT",
6: "OFPBAC_EPERM",
7: "OFPBAC_TOO_MANY",
8: "OFPBAC_BAD_QUEUE" }),
OFPacketField("data", "", Raw) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPETFlowModFailed(_ofp_header):
name = "OFPET_FLOW_MOD_FAILED"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 1, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("errtype", 3, ofp_error_type),
ShortEnumField("errcode", 0, { 0: "OFPFMFC_ALL_TABLES_FULL",
1: "OFPFMFC_OVERLAP",
2: "OFPFMFC_EPERM",
3: "OFPFMFC_BAD_EMERG_TIMEOUT",
4: "OFPFMFC_BAD_COMMAND",
5: "OFPFMFC_UNSUPPORTED" }),
OFPacketField("data", "", Raw) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPETPortModFailed(_ofp_header):
name = "OFPET_PORT_MOD_FAILED"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 1, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("errtype", 4, ofp_error_type),
ShortEnumField("errcode", 0, { 0: "OFPPMFC_BAD_PORT",
1: "OFPPMFC_BAD_HW_ADDR" }),
OFPacketField("data", "", Raw) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPETQueueOpFailed(_ofp_header):
name = "OFPET_QUEUE_OP_FAILED"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 1, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("errtype", 5, ofp_error_type),
ShortEnumField("errcode", 0, { 0: "OFPQOFC_BAD_PORT",
1: "OFPQOFC_BAD_QUEUE",
2: "OFPQOFC_EPERM" }),
OFPacketField("data", "", Raw) ]
overload_fields = {TCP: {"dport": 6653}}
# ofp_error_cls allows generic method OpenFlow() to choose the right class for dissection
ofp_error_cls = { 0: OFPETHelloFailed,
1: OFPETBadRequest,
2: OFPETBadAction,
3: OFPETFlowModFailed,
4: OFPETPortModFailed,
5: OFPETQueueOpFailed }
################ end of OFPT_ERRORS #################
class OFPTEchoRequest(_ofp_header):
name = "OFPT_ECHO_REQUEST"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 2, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTEchoReply(_ofp_header):
name = "OFPT_ECHO_REPLY"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 3, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTVendor(_ofp_header):
name = "OFPT_VENDOR"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 4, ofp_type),
ShortField("len", None),
IntField("xid", 0),
IntField("vendor", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTFeaturesRequest(_ofp_header):
name = "OFPT_FEATURES_REQUEST"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 5, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"sport": 6653}}
ofp_action_types_flags = list(ofp_action_types.values())[:-1] # no ofpat_vendor flag
class OFPTFeaturesReply(_ofp_header):
name = "OFPT_FEATURES_REPLY"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 6, ofp_type),
ShortField("len", None),
IntField("xid", 0),
LongField("datapath_id", 0),
IntField("n_buffers", 0),
ByteField("n_tables", 1),
X3BytesField("pad", 0),
FlagsField("capabilities", 0, 32, [ "FLOW_STATS",
"TABLE_STATS",
"PORT_STATS",
"STP",
"RESERVED",
"IP_REASM",
"QUEUE_STATS",
"ARP_MATCH_IP" ]),
FlagsField("actions", 0, 32, ofp_action_types_flags),
PacketListField("ports", None, OFPPhyPort,
length_from=lambda pkt:pkt.len-32) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTGetConfigRequest(_ofp_header):
name = "OFPT_GET_CONFIG_REQUEST"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 7, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTGetConfigReply(_ofp_header):
name = "OFPT_GET_CONFIG_REPLY"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 8, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("flags", 0, { 0: "FRAG_NORMAL",
1: "FRAG_DROP",
2: "FRAG_REASM",
3: "FRAG_MASK" }),
ShortField("miss_send_len", 0) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTSetConfig(_ofp_header):
name = "OFPT_SET_CONFIG"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 9, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("flags", 0, { 0: "FRAG_NORMAL",
1: "FRAG_DROP",
2: "FRAG_REASM",
3: "FRAG_MASK" }),
ShortField("miss_send_len", 128) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTPacketIn(_ofp_header):
name = "OFPT_PACKET_IN"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 10, ofp_type),
ShortField("len", None),
IntField("xid", 0),
IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
ShortField("total_len", 0),
ShortEnumField("in_port", 0, ofp_port_no),
ByteEnumField("reason", 0, { 0: "OFPR_NO_MATCH",
1: "OFPR_ACTION" }),
XByteField("pad", 0),
PacketField("data", None, Ether) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTFlowRemoved(_ofp_header):
name = "OFPT_FLOW_REMOVED"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 11, ofp_type),
ShortField("len", None),
IntField("xid", 0),
PacketField("match", OFPMatch(), OFPMatch),
LongField("cookie", 0),
ShortField("priority", 0),
ByteEnumField("reason", 0, { 0: "OFPRR_IDLE_TIMEOUT",
1: "OFPRR_HARD_TIMEOUT",
2: "OFPRR_DELETE" }),
XByteField("pad1", 0),
IntField("duration_sec", 0),
IntField("duration_nsec", 0),
ShortField("idle_timeout", 0),
XShortField("pad2", 0),
LongField("packet_count", 0),
LongField("byte_count", 0) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTPortStatus(_ofp_header):
name = "OFPT_PORT_STATUS"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 12, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ByteEnumField("reason", 0, { 0: "OFPPR_ADD",
1: "OFPPR_DELETE",
2: "OFPPR_MODIFY" }),
XBitField("pad", 0, 56),
PacketField("desc", OFPPhyPort(), OFPPhyPort) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTPacketOut(_ofp_header):
name = "OFPT_PACKET_OUT"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 13, ofp_type),
ShortField("len", None),
IntField("xid", 0),
IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
ShortEnumField("in_port", "NONE", ofp_port_no),
FieldLenField("actions_len", None, fmt="H", length_of="actions"),
ActionPacketListField("actions", [], Packet,
length_from=lambda pkt:pkt.actions_len),
PacketField("data", None, Ether) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTFlowMod(_ofp_header):
name = "OFPT_FLOW_MOD"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 14, ofp_type),
ShortField("len", None),
IntField("xid", 0),
PacketField("match", OFPMatch(), OFPMatch),
LongField("cookie", 0),
ShortEnumField("cmd", 0, { 0: "OFPFC_ADD",
1: "OFPFC_MODIFY",
2: "OFPFC_MODIFY_STRICT",
3: "OFPFC_DELETE",
4: "OFPFC_DELETE_STRICT" }),
ShortField("idle_timeout", 0),
ShortField("hard_timeout", 0),
ShortField("priority", 0),
IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
ShortEnumField("out_port", "NONE", ofp_port_no),
FlagsField("flags", 0, 16, [ "SEND_FLOW_REM",
"CHECK_OVERLAP",
"EMERG" ]),
ActionPacketListField("actions", [], Packet,
length_from=lambda pkt:pkt.len-72) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTPortMod(_ofp_header):
name = "OFPT_PORT_MOD"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 15, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("port_no", 0, ofp_port_no),
MACField("hw_addr", "0"),
FlagsField("config", 0, 32, ofp_port_config),
FlagsField("mask", 0, 32, ofp_port_config),
FlagsField("advertise", 0, 32, ofp_port_features),
IntField("pad", 0) ]
overload_fields = {TCP: {"sport": 6653}}
#####################################################
##################### OFPT_STATS ####################
#####################################################
ofp_stats_types = { 0: "OFPST_DESC",
1: "OFPST_FLOW",
2: "OFPST_AGGREGATE",
3: "OFPST_TABLE",
4: "OFPST_PORT",
5: "OFPST_QUEUE",
65535: "OFPST_VENDOR" }
class OFPTStatsRequestDesc(_ofp_header):
name = "OFPST_STATS_REQUEST_DESC"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 0, ofp_stats_types),
FlagsField("flags", 0, 16, []) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTStatsReplyDesc(_ofp_header):
name = "OFPST_STATS_REPLY_DESC"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 0, ofp_stats_types),
FlagsField("flags", 0, 16, []),
StrFixedLenField("mfr_desc", "", 256),
StrFixedLenField("hw_desc", "", 256),
StrFixedLenField("sw_desc", "", 256),
StrFixedLenField("serial_num", "", 32),
StrFixedLenField("dp_desc", "", 256) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTStatsRequestFlow(_ofp_header):
name = "OFPST_STATS_REQUEST_FLOW"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 1, ofp_stats_types),
FlagsField("flags", 0, 16, []),
PacketField("match", OFPMatch(), OFPMatch),
ByteEnumField("table_id", "ALL", ofp_table),
ByteField("pad", 0),
ShortEnumField("out_port", "NONE", ofp_port_no) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPFlowStats(Packet):
def post_build(self, p, pay):
if self.length is None:
l = len(p)+len(pay)
p = struct.pack("!H", l) + p[2:]
return p + pay
name = "OFP_FLOW_STATS"
fields_desc = [ ShortField("length", None),
ByteField("table_id", 0),
XByteField("pad1", 0),
PacketField("match", OFPMatch(), OFPMatch),
IntField("duration_sec", 0),
IntField("duration_nsec", 0),
ShortField("priority", 0),
ShortField("idle_timeout", 0),
ShortField("hard_timeout", 0),
XBitField("pad2", 0, 48),
LongField("cookie", 0),
LongField("packet_count", 0),
LongField("byte_count", 0),
ActionPacketListField("actions", [], Packet,
length_from=lambda pkt:pkt.length-88) ]
class FlowStatsPacketListField(PacketListField):
@staticmethod
def _get_flow_stats_length(s):
return struct.unpack("!H", s[:2])[0]
def getfield(self, pkt, s):
lst = []
remain = s
while remain:
l = FlowStatsPacketListField._get_flow_stats_length(remain)
current = remain[:l]
remain = remain[l:]
p = OFPFlowStats(current)
lst.append(p)
return remain, lst
class OFPTStatsReplyFlow(_ofp_header):
name = "OFPST_STATS_REPLY_FLOW"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 1, ofp_stats_types),
FlagsField("flags", 0, 16, []),
FlowStatsPacketListField("flow_stats", [], Packet,
length_from=lambda pkt:pkt.len-12) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTStatsRequestAggregate(_ofp_header):
name = "OFPST_STATS_REQUEST_AGGREGATE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 2, ofp_stats_types),
FlagsField("flags", 0, 16, []),
PacketField("match", OFPMatch(), OFPMatch),
ByteEnumField("table_id", "ALL", ofp_table),
ByteField("pad", 0),
ShortEnumField("out_port", "NONE", ofp_port_no) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTStatsReplyAggregate(_ofp_header):
name = "OFPST_STATS_REPLY_AGGREGATE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 2, ofp_stats_types),
FlagsField("flags", 0, 16, []),
LongField("packet_count", 0),
LongField("byte_count", 0),
IntField("flow_count", 0),
XIntField("pad", 0) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTStatsRequestTable(_ofp_header):
name = "OFPST_STATS_REQUEST_TABLE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 3, ofp_stats_types),
FlagsField("flags", 0, 16, []) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTableStats(Packet):
def extract_padding(self, s):
return "", s
name = "OFP_TABLE_STATS"
fields_desc = [ ByteField("table_id", 0),
X3BytesField("pad", 0),
StrFixedLenField("name", "", 32),
FlagsField("wildcards1", 0x003, 12, [ "DL_VLAN_PCP",
"NW_TOS" ]),
BitField("nw_dst_mask", 63, 6), # 32 would be enough
BitField("nw_src_mask", 63, 6),
FlagsField("wildcards2", 0xff, 8, [ "IN_PORT",
"DL_VLAN",
"DL_SRC",
"DL_DST",
"DL_TYPE",
"NW_PROTO",
"TP_SRC",
"TP_DST" ]),
IntField("max_entries", 0),
IntField("active_count", 0),
LongField("lookup_count", 0),
LongField("matched_count", 0) ]
class OFPTStatsReplyTable(_ofp_header):
name = "OFPST_STATS_REPLY_TABLE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 3, ofp_stats_types),
FlagsField("flags", 0, 16, []),
PacketListField("table_stats", None, OFPTableStats,
length_from=lambda pkt:pkt.len-12) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTStatsRequestPort(_ofp_header):
name = "OFPST_STATS_REQUEST_PORT"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 4, ofp_stats_types),
FlagsField("flags", 0, 16, []),
ShortEnumField("port_no", "NONE", ofp_port_no),
XBitField("pad", 0, 48) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPPortStats(Packet):
def extract_padding(self, s):
return "", s
name = "OFP_PORT_STATS"
fields_desc = [ ShortEnumField("port_no", 0, ofp_port_no),
XBitField("pad", 0, 48),
LongField("rx_packets", 0),
LongField("tx_packets", 0),
LongField("rx_bytes", 0),
LongField("tx_bytes", 0),
LongField("rx_dropped", 0),
LongField("tx_dropped", 0),
LongField("rx_errors", 0),
LongField("tx_errors", 0),
LongField("rx_frame_err", 0),
LongField("rx_over_err", 0),
LongField("rx_crc_err", 0),
LongField("collisions", 0) ]
class OFPTStatsReplyPort(_ofp_header):
name = "OFPST_STATS_REPLY_TABLE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 4, ofp_stats_types),
FlagsField("flags", 0, 16, []),
PacketListField("port_stats", None, OFPPortStats,
length_from=lambda pkt:pkt.len-12) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTStatsRequestQueue(_ofp_header):
name = "OFPST_STATS_REQUEST_QUEUE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 5, ofp_stats_types),
FlagsField("flags", 0, 16, []),
ShortEnumField("port_no", "NONE", ofp_port_no),
XShortField("pad", 0),
IntEnumField("queue_id", "ALL", ofp_queue) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTStatsReplyQueue(_ofp_header):
name = "OFPST_STATS_REPLY_QUEUE"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 5, ofp_stats_types),
FlagsField("flags", 0, 16, []),
ShortEnumField("port_no", "NONE", ofp_port_no),
XShortField("pad", 0),
IntEnumField("queue_id", "ALL", ofp_queue),
LongField("tx_bytes", 0),
LongField("tx_packets", 0),
LongField("tx_errors", 0) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTStatsRequestVendor(_ofp_header):
name = "OFPST_STATS_REQUEST_VENDOR"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 16, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 6, ofp_stats_types),
FlagsField("flags", 0, 16, []),
IntField("vendor", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTStatsReplyVendor(_ofp_header):
name = "OFPST_STATS_REPLY_VENDOR"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 17, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("stats_type", 6, ofp_stats_types),
FlagsField("flags", 0, 16, []),
IntField("vendor", 0) ]
overload_fields = {TCP: {"dport": 6653}}
# ofp_stats_request/reply_cls allows generic method OpenFlow() (end of script)
# to choose the right class for dissection
ofp_stats_request_cls = { 0: OFPTStatsRequestDesc,
1: OFPTStatsRequestFlow,
2: OFPTStatsRequestAggregate,
3: OFPTStatsRequestTable,
4: OFPTStatsRequestPort,
5: OFPTStatsRequestQueue,
65535: OFPTStatsRequestVendor }
ofp_stats_reply_cls = { 0: OFPTStatsReplyDesc,
1: OFPTStatsReplyFlow,
2: OFPTStatsReplyAggregate,
3: OFPTStatsReplyTable,
4: OFPTStatsReplyPort,
5: OFPTStatsReplyQueue,
65535: OFPTStatsReplyVendor }
################ end of OFPT_STATS ##################
class OFPTBarrierRequest(_ofp_header):
name = "OFPT_BARRIER_REQUEST"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 18, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTBarrierReply(_ofp_header):
name = "OFPT_BARRIER_REPLY"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 19, ofp_type),
ShortField("len", None),
IntField("xid", 0) ]
overload_fields = {TCP: {"dport": 6653}}
class OFPTQueueGetConfigRequest(_ofp_header):
name = "OFPT_QUEUE_GET_CONFIG_REQUEST"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 20, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("port", 0, ofp_port_no),
XShortField("pad", 0) ]
overload_fields = {TCP: {"sport": 6653}}
class OFPTQueueGetConfigReply(_ofp_header):
name = "OFPT_QUEUE_GET_CONFIG_REPLY"
fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
ByteEnumField("type", 21, ofp_type),
ShortField("len", None),
IntField("xid", 0),
ShortEnumField("port", 0, ofp_port_no),
XBitField("pad", 0, 48),
QueuePacketListField("queues", [], Packet,
length_from=lambda pkt:pkt.len-16) ]
overload_fields = {TCP: {"dport": 6653}}
# ofpt_cls allows generic method OpenFlow() to choose the right class for dissection
ofpt_cls = { 0: OFPTHello,
#1: OFPTError,
2: OFPTEchoRequest,
3: OFPTEchoReply,
4: OFPTVendor,
5: OFPTFeaturesRequest,
6: OFPTFeaturesReply,
7: OFPTGetConfigRequest,
8: OFPTGetConfigReply,
9: OFPTSetConfig,
10: OFPTPacketIn,
11: OFPTFlowRemoved,
12: OFPTPortStatus,
13: OFPTPacketOut,
14: OFPTFlowMod,
15: OFPTPortMod,
#16: OFPTStatsRequest,
#17: OFPTStatsReply,
18: OFPTBarrierRequest,
19: OFPTBarrierReply,
20: OFPTQueueGetConfigRequest,
21: OFPTQueueGetConfigReply }
TCP_guess_payload_class_copy = TCP.guess_payload_class
def OpenFlow(self, payload):
if self is None or self.dport == 6653 or self.dport == 6633 or self.sport == 6653 or self.sport == 6633:
# port 6653 has been allocated by IANA, port 6633 should no longer be used
# OpenFlow function may be called with None self in OFPPacketField
of_type = orb(payload[1])
if of_type == 1:
err_type = orb(payload[9])
# err_type is a short int, but last byte is enough
if err_type == 255: err_type = 65535
return ofp_error_cls[err_type]
elif of_type == 16:
mp_type = orb(payload[9])
if mp_type == 255: mp_type = 65535
return ofp_stats_request_cls[mp_type]
elif of_type == 17:
mp_type = orb(payload[9])
if mp_type == 255: mp_type = 65535
return ofp_stats_reply_cls[mp_type]
else:
return ofpt_cls[of_type]
else:
return TCP_guess_payload_class_copy(self, payload)
TCP.guess_payload_class = OpenFlow