blob: dfe24c663b69785b86217aee3dbfafa74ac00a31 [file] [log] [blame]
Rich Lane5ec97b82013-01-06 18:04:25 -08001"""
2OpenFlow message parsing functions
3"""
4
5import logging
6import message
7import cstruct as ofp
8
9"""
10of_message.py
11Contains wrapper functions and classes for the of_message namespace
12that are generated by hand. It includes the rest of the wrapper
13function information into the of_message namespace
14"""
15
16parse_logger = logging.getLogger("parse")
17#parse_logger.setLevel(logging.DEBUG)
18
19# These message types are subclassed
20msg_type_subclassed = [
21 ofp.OFPT_STATS_REQUEST,
22 ofp.OFPT_STATS_REPLY,
23 ofp.OFPT_ERROR
24]
25
26# Maps from sub-types to classes
27stats_reply_to_class_map = {
28 ofp.OFPST_DESC : message.desc_stats_reply,
29 ofp.OFPST_AGGREGATE : message.aggregate_stats_reply,
30 ofp.OFPST_FLOW : message.flow_stats_reply,
31 ofp.OFPST_TABLE : message.table_stats_reply,
32 ofp.OFPST_PORT : message.port_stats_reply,
33 ofp.OFPST_QUEUE : message.queue_stats_reply
34}
35
36stats_request_to_class_map = {
37 ofp.OFPST_DESC : message.desc_stats_request,
38 ofp.OFPST_AGGREGATE : message.aggregate_stats_request,
39 ofp.OFPST_FLOW : message.flow_stats_request,
40 ofp.OFPST_TABLE : message.table_stats_request,
41 ofp.OFPST_PORT : message.port_stats_request,
42 ofp.OFPST_QUEUE : message.queue_stats_request
43}
44
45error_to_class_map = {
46 ofp.OFPET_HELLO_FAILED : message.hello_failed_error_msg,
47 ofp.OFPET_BAD_REQUEST : message.bad_request_error_msg,
48 ofp.OFPET_BAD_ACTION : message.bad_action_error_msg,
49 ofp.OFPET_FLOW_MOD_FAILED : message.flow_mod_failed_error_msg,
50 ofp.OFPET_PORT_MOD_FAILED : message.port_mod_failed_error_msg,
51 ofp.OFPET_QUEUE_OP_FAILED : message.queue_op_failed_error_msg,
52 ofp.OFPET_TABLE_MOD_FAILED : message.table_mod_failed_error_msg,
53 ofp.OFPET_QUEUE_OP_FAILED : message.queue_op_failed_error_msg,
54 ofp.OFPET_SWITCH_CONFIG_FAILED : message.switch_config_failed_error_msg
55}
56
57# Map from header type value to the underlieing message class
58msg_type_to_class_map = {
59 ofp.OFPT_HELLO : message.hello,
60 ofp.OFPT_ERROR : message.error,
61 ofp.OFPT_ECHO_REQUEST : message.echo_request,
62 ofp.OFPT_ECHO_REPLY : message.echo_reply,
63 ofp.OFPT_EXPERIMENTER : message.experimenter,
64 ofp.OFPT_FEATURES_REQUEST : message.features_request,
65 ofp.OFPT_FEATURES_REPLY : message.features_reply,
66 ofp.OFPT_GET_CONFIG_REQUEST : message.get_config_request,
67 ofp.OFPT_GET_CONFIG_REPLY : message.get_config_reply,
68 ofp.OFPT_SET_CONFIG : message.set_config,
69 ofp.OFPT_PACKET_IN : message.packet_in,
70 ofp.OFPT_FLOW_REMOVED : message.flow_removed,
71 ofp.OFPT_PORT_STATUS : message.port_status,
72 ofp.OFPT_PACKET_OUT : message.packet_out,
73 ofp.OFPT_FLOW_MOD : message.flow_mod,
74 ofp.OFPT_PORT_MOD : message.port_mod,
75 ofp.OFPT_TABLE_MOD : message.table_mod,
76 ofp.OFPT_STATS_REQUEST : message.stats_request,
77 ofp.OFPT_STATS_REPLY : message.stats_reply,
78 ofp.OFPT_BARRIER_REQUEST : message.barrier_request,
79 ofp.OFPT_BARRIER_REPLY : message.barrier_reply,
80 ofp.OFPT_QUEUE_GET_CONFIG_REQUEST : message.queue_get_config_request,
81 ofp.OFPT_QUEUE_GET_CONFIG_REPLY : message.queue_get_config_reply,
82}
83
84def _of_message_to_object(binary_string):
85 """
86 Map a binary string to the corresponding class.
87
88 Appropriately resolves subclasses
89 """
90 hdr = ofp.ofp_header()
91 hdr.unpack(binary_string)
92 # FIXME: Add error detection
93 if not hdr.type in msg_type_subclassed:
94 return msg_type_to_class_map[hdr.type]()
95 if hdr.type == ofp.OFPT_STATS_REQUEST:
96 sub_hdr = ofp.ofp_stats_request()
97 sub_hdr.unpack(binary_string[ofp.OFP_HEADER_BYTES:])
98 try:
99 obj = stats_request_to_class_map[sub_hdr.type]()
100 except LookupError:
101 obj = None
102 return obj
103 elif hdr.type == ofp.OFPT_STATS_REPLY:
104 sub_hdr = ofp.ofp_stats_reply()
105 sub_hdr.unpack(binary_string[ofp.OFP_HEADER_BYTES:])
106 try:
107 obj = stats_reply_to_class_map[sub_hdr.type]()
108 except LookupError:
109 obj = None
110 return obj
111 elif hdr.type == ofp.OFPT_ERROR:
112 sub_hdr = ofp.ofp_error_msg()
113 sub_hdr.unpack(binary_string[ofp.OFP_HEADER_BYTES:])
114 return error_to_class_map[sub_hdr.type]()
115 else:
116 parse_logger.error("Cannot parse pkt to message")
117 return None
118
119def of_message_parse(binary_string, raw=False):
120 """
121 Parse an OpenFlow packet
122
123 Parses a raw OpenFlow packet into a Python class, with class
124 members fully populated.
125
126 @param binary_string The packet (string) to be parsed
127 @param raw If true, interpret the packet as an L2 packet. Not
128 yet supported.
129 @return An object of some message class or None if fails
130 Note that any data beyond that parsed is not returned
131
132 """
133
134 if raw:
135 parse_logger.error("raw packet message parsing not supported")
136 return None
137
138 obj = _of_message_to_object(binary_string)
139 if obj:
140 obj.unpack(binary_string)
141 return obj
142
143
144def of_header_parse(binary_string, raw=False):
145 """
146 Parse only the header from an OpenFlow packet
147
148 Parses the header from a raw OpenFlow packet into a
149 an ofp_header Python class.
150
151 @param binary_string The packet (string) to be parsed
152 @param raw If true, interpret the packet as an L2 packet. Not
153 yet supported.
154 @return An ofp_header object
155
156 """
157
158 if raw:
159 parse_logger.error("raw packet message parsing not supported")
160 return None
161
162 hdr = ofp.ofp_header()
163 hdr.unpack(binary_string)
164
165 return hdr
166
167map_wc_field_to_match_member = {
168 'OFPFW_DL_VLAN' : 'dl_vlan',
169 'OFPFW_DL_SRC' : 'dl_src',
170 'OFPFW_DL_DST' : 'dl_dst',
171 'OFPFW_DL_TYPE' : 'dl_type',
172 'OFPFW_NW_PROTO' : 'nw_proto',
173 'OFPFW_TP_SRC' : 'tp_src',
174 'OFPFW_TP_DST' : 'tp_dst',
175 'OFPFW_NW_SRC_SHIFT' : 'nw_src_shift',
176 'OFPFW_NW_SRC_BITS' : 'nw_src_bits',
177 'OFPFW_NW_SRC_MASK' : 'nw_src_mask',
178 'OFPFW_NW_SRC_ALL' : 'nw_src_all',
179 'OFPFW_NW_DST_SHIFT' : 'nw_dst_shift',
180 'OFPFW_NW_DST_BITS' : 'nw_dst_bits',
181 'OFPFW_NW_DST_MASK' : 'nw_dst_mask',
182 'OFPFW_NW_DST_ALL' : 'nw_dst_all',
183 'OFPFW_DL_VLAN_PCP' : 'dl_vlan_pcp',
184 'OFPFW_NW_TOS' : 'nw_tos'
185}
186
187
188def parse_mac(mac_str):
189 """
190 Parse a MAC address
191
192 Parse a MAC address ':' separated string of hex digits to an
193 array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0]
194 @param mac_str The string to convert
195 @return Array of 6 integer values
196 """
197 return map(lambda val:eval("0x" + val), mac_str.split(":"))
198
199def parse_ip(ip_str):
200 """
201 Parse an IP address
202
203 Parse an IP address '.' separated string of decimal digits to an
204 host ordered integer. '172.24.74.77' =>
205 @param ip_str The string to convert
206 @return Integer value
207 """
208 array = map(lambda val:eval(val),ip_str.split("."))
209 val = 0
210 for a in array:
211 val <<= 8
212 val += a
213 return val
214
215def packet_to_flow_match(packet):
216 """
217 Create a flow match that matches packet with the given wildcards
218
219 @param packet The packet to use as a flow template
220 @param pkt_format Currently only L2 is supported. Will indicate the
221 overall packet type for parsing
222 @return An ofp_match object if successful. None if format is not
223 recognized. The wildcards of the match will be cleared for the
224 values extracted from the packet.
225
226 @todo check min length of packet
227 @todo Check if packet is other than L2 format
228 @todo Implement ICMP and ARP fields
229 """
230
231 return packet.parse()