blob: ac76a160a358b9f62cad7ff4aba9bec64c15748a [file] [log] [blame]
Dan Talaycob66b1122010-02-10 22:38:49 -08001"""
2OpenFlow message parsing functions
3"""
4
5import sys
Dan Talayco235d7cb2010-03-12 10:03:18 -08006import logging
Rich Laneed1fa2d2013-01-08 13:23:37 -08007import message
8import error
9import action
10import cstruct
Dan Talaycof75360a2010-02-05 22:22:54 -080011from action_list import action_list
Dan Talaycob66b1122010-02-10 22:38:49 -080012try:
Dan Talayco958f3b92010-03-12 21:58:57 -080013 import scapy.all as scapy
Dan Talaycob66b1122010-02-10 22:38:49 -080014except:
Dan Talaycod2ca1032010-03-10 14:40:26 -080015 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080016 import scapy as scapy
Dan Talaycod2ca1032010-03-10 14:40:26 -080017 except:
18 sys.exit("Need to install scapy for packet parsing")
Dan Talaycof75360a2010-02-05 22:22:54 -080019
20"""
21of_message.py
22Contains wrapper functions and classes for the of_message namespace
23that are generated by hand. It includes the rest of the wrapper
24function information into the of_message namespace
25"""
26
Dan Talayco48370102010-03-03 15:17:33 -080027parse_logger = logging.getLogger("parse")
28#parse_logger.setLevel(logging.DEBUG)
Dan Talayco08d9dfe2010-02-12 23:02:11 -080029
Dan Talaycof75360a2010-02-05 22:22:54 -080030# These message types are subclassed
31msg_type_subclassed = [
Rich Laneed1fa2d2013-01-08 13:23:37 -080032 cstruct.OFPT_STATS_REQUEST,
33 cstruct.OFPT_STATS_REPLY,
34 cstruct.OFPT_ERROR
Dan Talaycof75360a2010-02-05 22:22:54 -080035]
36
37# Maps from sub-types to classes
38stats_reply_to_class_map = {
Rich Laneed1fa2d2013-01-08 13:23:37 -080039 cstruct.OFPST_DESC : message.desc_stats_reply,
40 cstruct.OFPST_AGGREGATE : message.aggregate_stats_reply,
41 cstruct.OFPST_FLOW : message.flow_stats_reply,
42 cstruct.OFPST_TABLE : message.table_stats_reply,
43 cstruct.OFPST_PORT : message.port_stats_reply,
44 cstruct.OFPST_QUEUE : message.queue_stats_reply
Dan Talaycof75360a2010-02-05 22:22:54 -080045}
46
47stats_request_to_class_map = {
Rich Laneed1fa2d2013-01-08 13:23:37 -080048 cstruct.OFPST_DESC : message.desc_stats_request,
49 cstruct.OFPST_AGGREGATE : message.aggregate_stats_request,
50 cstruct.OFPST_FLOW : message.flow_stats_request,
51 cstruct.OFPST_TABLE : message.table_stats_request,
52 cstruct.OFPST_PORT : message.port_stats_request,
53 cstruct.OFPST_QUEUE : message.queue_stats_request
Dan Talaycof75360a2010-02-05 22:22:54 -080054}
55
56error_to_class_map = {
Rich Laneed1fa2d2013-01-08 13:23:37 -080057 cstruct.OFPET_HELLO_FAILED : message.hello_failed_error_msg,
58 cstruct.OFPET_BAD_REQUEST : message.bad_request_error_msg,
59 cstruct.OFPET_BAD_ACTION : message.bad_action_error_msg,
60 cstruct.OFPET_FLOW_MOD_FAILED : message.flow_mod_failed_error_msg,
61 cstruct.OFPET_PORT_MOD_FAILED : message.port_mod_failed_error_msg,
62 cstruct.OFPET_QUEUE_OP_FAILED : message.queue_op_failed_error_msg
Dan Talaycof75360a2010-02-05 22:22:54 -080063}
64
65# Map from header type value to the underlieing message class
66msg_type_to_class_map = {
Rich Laneed1fa2d2013-01-08 13:23:37 -080067 cstruct.OFPT_HELLO : message.hello,
68 cstruct.OFPT_ERROR : message.error,
69 cstruct.OFPT_ECHO_REQUEST : message.echo_request,
70 cstruct.OFPT_ECHO_REPLY : message.echo_reply,
71 cstruct.OFPT_VENDOR : message.vendor,
72 cstruct.OFPT_FEATURES_REQUEST : message.features_request,
73 cstruct.OFPT_FEATURES_REPLY : message.features_reply,
74 cstruct.OFPT_GET_CONFIG_REQUEST : message.get_config_request,
75 cstruct.OFPT_GET_CONFIG_REPLY : message.get_config_reply,
76 cstruct.OFPT_SET_CONFIG : message.set_config,
77 cstruct.OFPT_PACKET_IN : message.packet_in,
78 cstruct.OFPT_FLOW_REMOVED : message.flow_removed,
79 cstruct.OFPT_PORT_STATUS : message.port_status,
80 cstruct.OFPT_PACKET_OUT : message.packet_out,
81 cstruct.OFPT_FLOW_MOD : message.flow_mod,
82 cstruct.OFPT_PORT_MOD : message.port_mod,
83 cstruct.OFPT_STATS_REQUEST : message.stats_request,
84 cstruct.OFPT_STATS_REPLY : message.stats_reply,
85 cstruct.OFPT_BARRIER_REQUEST : message.barrier_request,
86 cstruct.OFPT_BARRIER_REPLY : message.barrier_reply,
87 cstruct.OFPT_QUEUE_GET_CONFIG_REQUEST : message.queue_get_config_request,
88 cstruct.OFPT_QUEUE_GET_CONFIG_REPLY : message.queue_get_config_reply
Dan Talaycof75360a2010-02-05 22:22:54 -080089}
90
91def _of_message_to_object(binary_string):
92 """
93 Map a binary string to the corresponding class.
94
95 Appropriately resolves subclasses
96 """
Rich Laneed1fa2d2013-01-08 13:23:37 -080097 hdr = message.ofp_header()
Dan Talaycof75360a2010-02-05 22:22:54 -080098 hdr.unpack(binary_string)
Rich Laneb73808c2013-03-11 15:22:23 -070099 logging.info(hdr.show())
Dan Talaycof75360a2010-02-05 22:22:54 -0800100 # FIXME: Add error detection
101 if not hdr.type in msg_type_subclassed:
102 return msg_type_to_class_map[hdr.type]()
Rich Laneed1fa2d2013-01-08 13:23:37 -0800103 if hdr.type == cstruct.OFPT_STATS_REQUEST:
104 sub_hdr = message.ofp_stats_request()
Rich Laneb73808c2013-03-11 15:22:23 -0700105 sub_hdr.unpack(binary_string)
Dan Talaycob66b1122010-02-10 22:38:49 -0800106 try:
Rich Lane7c7342a2013-03-11 14:16:58 -0700107 obj = stats_request_to_class_map[sub_hdr.stats_type]()
Dan Talaycob66b1122010-02-10 22:38:49 -0800108 except KeyError:
109 obj = None
110 return obj
Rich Laneed1fa2d2013-01-08 13:23:37 -0800111 elif hdr.type == cstruct.OFPT_STATS_REPLY:
112 sub_hdr = message.ofp_stats_reply()
Rich Laneb73808c2013-03-11 15:22:23 -0700113 sub_hdr.unpack(binary_string)
Dan Talaycob66b1122010-02-10 22:38:49 -0800114 try:
Rich Lane7c7342a2013-03-11 14:16:58 -0700115 obj = stats_reply_to_class_map[sub_hdr.stats_type]()
Dan Talaycob66b1122010-02-10 22:38:49 -0800116 except KeyError:
117 obj = None
118 return obj
Rich Laneed1fa2d2013-01-08 13:23:37 -0800119 elif hdr.type == cstruct.OFPT_ERROR:
120 sub_hdr = message.ofp_error_msg()
Rich Laneb73808c2013-03-11 15:22:23 -0700121 sub_hdr.unpack(binary_string)
Rich Lane4e361bb2013-03-11 13:57:31 -0700122 return error_to_class_map[sub_hdr.err_type]()
Dan Talaycof75360a2010-02-05 22:22:54 -0800123 else:
Dan Talayco48370102010-03-03 15:17:33 -0800124 parse_logger.error("Cannot parse pkt to message")
Dan Talaycof75360a2010-02-05 22:22:54 -0800125 return None
126
127def of_message_parse(binary_string, raw=False):
128 """
129 Parse an OpenFlow packet
130
131 Parses a raw OpenFlow packet into a Python class, with class
132 members fully populated.
133
134 @param binary_string The packet (string) to be parsed
Dan Talaycof75360a2010-02-05 22:22:54 -0800135 @param raw If true, interpret the packet as an L2 packet. Not
136 yet supported.
Dan Talaycof75360a2010-02-05 22:22:54 -0800137 @return An object of some message class or None if fails
Dan Talayco08d9dfe2010-02-12 23:02:11 -0800138 Note that any data beyond that parsed is not returned
Dan Talaycof75360a2010-02-05 22:22:54 -0800139
140 """
141
142 if raw:
Dan Talayco48370102010-03-03 15:17:33 -0800143 parse_logger.error("raw packet message parsing not supported")
Dan Talaycof75360a2010-02-05 22:22:54 -0800144 return None
145
146 obj = _of_message_to_object(binary_string)
Dan Talaycob66b1122010-02-10 22:38:49 -0800147 if obj:
Dan Talaycof75360a2010-02-05 22:22:54 -0800148 obj.unpack(binary_string)
149 return obj
150
Dan Talaycob9cb5482010-02-09 15:23:12 -0800151
152def of_header_parse(binary_string, raw=False):
153 """
154 Parse only the header from an OpenFlow packet
155
156 Parses the header from a raw OpenFlow packet into a
157 an ofp_header Python class.
158
159 @param binary_string The packet (string) to be parsed
160 @param raw If true, interpret the packet as an L2 packet. Not
161 yet supported.
162 @return An ofp_header object
163
164 """
165
166 if raw:
Dan Talayco48370102010-03-03 15:17:33 -0800167 parse_logger.error("raw packet message parsing not supported")
Dan Talaycob9cb5482010-02-09 15:23:12 -0800168 return None
169
Rich Laneed1fa2d2013-01-08 13:23:37 -0800170 hdr = message.ofp_header()
Dan Talaycob9cb5482010-02-09 15:23:12 -0800171 hdr.unpack(binary_string)
172
173 return hdr
174
Dan Talaycob66b1122010-02-10 22:38:49 -0800175map_wc_field_to_match_member = {
Rich Laned0478ff2013-03-11 12:46:58 -0700176 'OFPFW_DL_VLAN' : 'vlan_vid',
177 'OFPFW_DL_SRC' : 'eth_src',
178 'OFPFW_DL_DST' : 'eth_dst',
179 'OFPFW_DL_TYPE' : 'eth_type',
180 'OFPFW_NW_PROTO' : 'ip_proto',
181 'OFPFW_TP_SRC' : 'tcp_src',
182 'OFPFW_TP_DST' : 'tcp_dst',
Dan Talaycob66b1122010-02-10 22:38:49 -0800183 'OFPFW_NW_SRC_SHIFT' : 'nw_src_shift',
184 'OFPFW_NW_SRC_BITS' : 'nw_src_bits',
185 'OFPFW_NW_SRC_MASK' : 'nw_src_mask',
186 'OFPFW_NW_SRC_ALL' : 'nw_src_all',
187 'OFPFW_NW_DST_SHIFT' : 'nw_dst_shift',
188 'OFPFW_NW_DST_BITS' : 'nw_dst_bits',
189 'OFPFW_NW_DST_MASK' : 'nw_dst_mask',
190 'OFPFW_NW_DST_ALL' : 'nw_dst_all',
Rich Laned0478ff2013-03-11 12:46:58 -0700191 'OFPFW_DL_VLAN_PCP' : 'vlan_pcp',
192 'OFPFW_NW_TOS' : 'ip_dscp'
Dan Talaycob66b1122010-02-10 22:38:49 -0800193}
194
195
196def parse_mac(mac_str):
Dan Talaycob9cb5482010-02-09 15:23:12 -0800197 """
Dan Talaycob66b1122010-02-10 22:38:49 -0800198 Parse a MAC address
199
200 Parse a MAC address ':' separated string of hex digits to an
201 array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0]
202 @param mac_str The string to convert
203 @return Array of 6 integer values
204 """
Rich Lanea92f2522012-10-04 18:11:04 -0700205 return map(lambda val: int(val, 16), mac_str.split(":"))
Dan Talaycob66b1122010-02-10 22:38:49 -0800206
207def parse_ip(ip_str):
208 """
209 Parse an IP address
210
211 Parse an IP address '.' separated string of decimal digits to an
212 host ordered integer. '172.24.74.77' =>
213 @param ip_str The string to convert
214 @return Integer value
215 """
Rich Lanea92f2522012-10-04 18:11:04 -0700216 array = map(lambda val: int(val), ip_str.split("."))
Dan Talaycob66b1122010-02-10 22:38:49 -0800217 val = 0
218 for a in array:
219 val <<= 8
220 val += a
221 return val
222
223def packet_type_classify(ether):
224 try:
Dan Talayco958f3b92010-03-12 21:58:57 -0800225 dot1q = ether[scapy.Dot1Q]
Dan Talaycob66b1122010-02-10 22:38:49 -0800226 except:
227 dot1q = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -0800228
Dan Talaycob66b1122010-02-10 22:38:49 -0800229 try:
Dan Talayco958f3b92010-03-12 21:58:57 -0800230 ip = ether[scapy.IP]
Dan Talaycob66b1122010-02-10 22:38:49 -0800231 except:
232 ip = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -0800233
Dan Talaycob66b1122010-02-10 22:38:49 -0800234 try:
Dan Talayco958f3b92010-03-12 21:58:57 -0800235 tcp = ether[scapy.TCP]
Dan Talaycob66b1122010-02-10 22:38:49 -0800236 except:
237 tcp = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -0800238
Dan Talaycob66b1122010-02-10 22:38:49 -0800239 try:
Dan Talayco958f3b92010-03-12 21:58:57 -0800240 udp = ether[scapy.UDP]
Dan Talaycob66b1122010-02-10 22:38:49 -0800241 except:
242 udp = None
243
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700244 try:
245 icmp = ether[scapy.ICMP]
246 except:
247 icmp = None
248
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700249 try:
250 arp = ether[scapy.ARP]
251 except:
252 arp = None
Dan Talaycob66b1122010-02-10 22:38:49 -0800253 return (dot1q, ip, tcp, udp, icmp, arp)
254
255def packet_to_flow_match(packet, pkt_format="L2"):
256 """
257 Create a flow match that matches packet with the given wildcards
Dan Talaycob9cb5482010-02-09 15:23:12 -0800258
259 @param packet The packet to use as a flow template
Dan Talaycoa3b20182010-02-10 22:49:34 -0800260 @param pkt_format Currently only L2 is supported. Will indicate the
261 overall packet type for parsing
Dan Talaycob66b1122010-02-10 22:38:49 -0800262 @return An ofp_match object if successful. None if format is not
263 recognized. The wildcards of the match will be cleared for the
264 values extracted from the packet.
265
Dan Talaycoa3b20182010-02-10 22:49:34 -0800266 @todo check min length of packet
267 @todo Check if packet is other than L2 format
268 @todo Implement ICMP and ARP fields
Dan Talaycob9cb5482010-02-09 15:23:12 -0800269 """
270
Dan Talaycob66b1122010-02-10 22:38:49 -0800271 #@todo check min length of packet
Dan Talaycob66b1122010-02-10 22:38:49 -0800272 if pkt_format.upper() != "L2":
Dan Talayco48370102010-03-03 15:17:33 -0800273 parse_logger.error("Only L2 supported for packet_to_flow")
Dan Talaycob66b1122010-02-10 22:38:49 -0800274 return None
275
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800276 if type(packet) == type(""):
Dan Talayco958f3b92010-03-12 21:58:57 -0800277 ether = scapy.Ether(packet)
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800278 else:
279 ether = packet
280
Dan Talaycob66b1122010-02-10 22:38:49 -0800281 # For now, assume ether IP packet and ignore wildcards
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800282 try:
283 (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
284 except:
285 parse_logger.error("packet_to_flow_match: Classify error")
286 return None
Dan Talaycob66b1122010-02-10 22:38:49 -0800287
Rich Laneed1fa2d2013-01-08 13:23:37 -0800288 match = cstruct.ofp_match()
289 match.wildcards = cstruct.OFPFW_ALL
Dan Talaycob66b1122010-02-10 22:38:49 -0800290 #@todo Check if packet is other than L2 format
Rich Laned0478ff2013-03-11 12:46:58 -0700291 match.eth_dst = parse_mac(ether.dst)
Rich Laneed1fa2d2013-01-08 13:23:37 -0800292 match.wildcards &= ~cstruct.OFPFW_DL_DST
Rich Laned0478ff2013-03-11 12:46:58 -0700293 match.eth_src = parse_mac(ether.src)
Rich Laneed1fa2d2013-01-08 13:23:37 -0800294 match.wildcards &= ~cstruct.OFPFW_DL_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700295 match.eth_type = ether.type
Rich Laneed1fa2d2013-01-08 13:23:37 -0800296 match.wildcards &= ~cstruct.OFPFW_DL_TYPE
Dan Talaycob66b1122010-02-10 22:38:49 -0800297
298 if dot1q:
Rich Laned0478ff2013-03-11 12:46:58 -0700299 match.vlan_vid = dot1q.vlan
300 match.vlan_pcp = dot1q.prio
301 match.eth_type = dot1q.type
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700302 else:
Rich Laned0478ff2013-03-11 12:46:58 -0700303 match.vlan_vid = cstruct.OFP_VLAN_NONE
304 match.vlan_pcp = 0
Rich Laneed1fa2d2013-01-08 13:23:37 -0800305 match.wildcards &= ~cstruct.OFPFW_DL_VLAN
306 match.wildcards &= ~cstruct.OFPFW_DL_VLAN_PCP
Dan Talaycob66b1122010-02-10 22:38:49 -0800307
308 if ip:
Rich Laned0478ff2013-03-11 12:46:58 -0700309 match.ipv4_src = parse_ip(ip.src)
Rich Laneed1fa2d2013-01-08 13:23:37 -0800310 match.wildcards &= ~cstruct.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700311 match.ipv4_dst = parse_ip(ip.dst)
Rich Laneed1fa2d2013-01-08 13:23:37 -0800312 match.wildcards &= ~cstruct.OFPFW_NW_DST_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700313 match.ip_dscp = ip.tos
Rich Laneed1fa2d2013-01-08 13:23:37 -0800314 match.wildcards &= ~cstruct.OFPFW_NW_TOS
Dan Talaycob66b1122010-02-10 22:38:49 -0800315
316 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700317 match.ip_proto = 6
Rich Laneed1fa2d2013-01-08 13:23:37 -0800318 match.wildcards &= ~cstruct.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700319 elif not tcp and udp:
320 tcp = udp
Rich Laned0478ff2013-03-11 12:46:58 -0700321 match.ip_proto = 17
Rich Laneed1fa2d2013-01-08 13:23:37 -0800322 match.wildcards &= ~cstruct.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700323
324 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700325 match.tcp_src = tcp.sport
Rich Laneed1fa2d2013-01-08 13:23:37 -0800326 match.wildcards &= ~cstruct.OFPFW_TP_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700327 match.tcp_dst = tcp.dport
Rich Laneed1fa2d2013-01-08 13:23:37 -0800328 match.wildcards &= ~cstruct.OFPFW_TP_DST
Dan Talaycob66b1122010-02-10 22:38:49 -0800329
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700330 if icmp:
Rich Laned0478ff2013-03-11 12:46:58 -0700331 match.ip_proto = 1
332 match.tcp_src = icmp.type
333 match.tcp_dst = icmp.code
Rich Laneed1fa2d2013-01-08 13:23:37 -0800334 match.wildcards &= ~cstruct.OFPFW_NW_PROTO
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700335
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700336 if arp:
Rich Laned0478ff2013-03-11 12:46:58 -0700337 match.ip_proto = arp.op
Rich Laneed1fa2d2013-01-08 13:23:37 -0800338 match.wildcards &= ~cstruct.OFPFW_NW_PROTO
Rich Laned0478ff2013-03-11 12:46:58 -0700339 match.ipv4_src = parse_ip(arp.psrc)
Rich Laneed1fa2d2013-01-08 13:23:37 -0800340 match.wildcards &= ~cstruct.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700341 match.ipv4_dst = parse_ip(arp.pdst)
Rich Laneed1fa2d2013-01-08 13:23:37 -0800342 match.wildcards &= ~cstruct.OFPFW_NW_DST_MASK
Dan Talaycob66b1122010-02-10 22:38:49 -0800343
344 return match