blob: 496405e16f60d85b558140770510581d494e0c81 [file] [log] [blame]
Dan Talaycob66b1122010-02-10 22:38:49 -08001"""
Rich Lanef6883512013-03-11 17:00:09 -07002Utility parsing functions
Dan Talaycob66b1122010-02-10 22:38:49 -08003"""
4
5import sys
Rich Lane0f79bee2013-05-13 17:37:43 -07006import socket
Rich Lanea68176f2013-08-09 17:41:05 -07007import packet as scapy
Dan Talaycof75360a2010-02-05 22:22:54 -08008
Dan Talaycob66b1122010-02-10 22:38:49 -08009def parse_mac(mac_str):
Dan Talaycob9cb5482010-02-09 15:23:12 -080010 """
Dan Talaycob66b1122010-02-10 22:38:49 -080011 Parse a MAC address
12
13 Parse a MAC address ':' separated string of hex digits to an
14 array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0]
15 @param mac_str The string to convert
16 @return Array of 6 integer values
17 """
Rich Lanea92f2522012-10-04 18:11:04 -070018 return map(lambda val: int(val, 16), mac_str.split(":"))
Dan Talaycob66b1122010-02-10 22:38:49 -080019
20def parse_ip(ip_str):
21 """
22 Parse an IP address
23
24 Parse an IP address '.' separated string of decimal digits to an
25 host ordered integer. '172.24.74.77' =>
26 @param ip_str The string to convert
27 @return Integer value
28 """
Rich Lanea92f2522012-10-04 18:11:04 -070029 array = map(lambda val: int(val), ip_str.split("."))
Dan Talaycob66b1122010-02-10 22:38:49 -080030 val = 0
31 for a in array:
32 val <<= 8
33 val += a
34 return val
35
Rich Laneb6255512013-05-07 14:58:43 -070036def parse_ipv6(ip_str):
37 """
38 Parse an IPv6 address
39
40 Parse a textual IPv6 address and return a 16 byte binary string.
41 """
Rich Lane0f79bee2013-05-13 17:37:43 -070042 return socket.inet_pton(socket.AF_INET6, ip_str)
Rich Laneb6255512013-05-07 14:58:43 -070043
Dan Talaycob66b1122010-02-10 22:38:49 -080044def packet_type_classify(ether):
45 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080046 dot1q = ether[scapy.Dot1Q]
Dan Talaycob66b1122010-02-10 22:38:49 -080047 except:
48 dot1q = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080049
Dan Talaycob66b1122010-02-10 22:38:49 -080050 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080051 ip = ether[scapy.IP]
Dan Talaycob66b1122010-02-10 22:38:49 -080052 except:
53 ip = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080054
Dan Talaycob66b1122010-02-10 22:38:49 -080055 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080056 tcp = ether[scapy.TCP]
Dan Talaycob66b1122010-02-10 22:38:49 -080057 except:
58 tcp = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080059
Dan Talaycob66b1122010-02-10 22:38:49 -080060 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080061 udp = ether[scapy.UDP]
Dan Talaycob66b1122010-02-10 22:38:49 -080062 except:
63 udp = None
64
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -070065 try:
66 icmp = ether[scapy.ICMP]
67 except:
68 icmp = None
69
Christian Dickmann8b59b4b2012-09-23 16:48:30 -070070 try:
71 arp = ether[scapy.ARP]
72 except:
73 arp = None
Dan Talaycob66b1122010-02-10 22:38:49 -080074 return (dot1q, ip, tcp, udp, icmp, arp)
75
Rich Laneb6255512013-05-07 14:58:43 -070076def packet_to_flow_match(packet):
Dan Talaycob66b1122010-02-10 22:38:49 -080077 """
78 Create a flow match that matches packet with the given wildcards
Dan Talaycob9cb5482010-02-09 15:23:12 -080079
80 @param packet The packet to use as a flow template
Rich Laneb6255512013-05-07 14:58:43 -070081 @return An loxi.of10.match object
Dan Talaycob66b1122010-02-10 22:38:49 -080082
Dan Talaycoa3b20182010-02-10 22:49:34 -080083 @todo check min length of packet
Dan Talaycob9cb5482010-02-09 15:23:12 -080084 """
Rich Laneb6255512013-05-07 14:58:43 -070085 import ofp
86 if ofp.OFP_VERSION == 1:
87 return packet_to_flow_match_v1(packet)
88 elif ofp.OFP_VERSION == 3:
89 return packet_to_flow_match_v3(packet)
90 elif ofp.OFP_VERSION == 4:
91 return packet_to_flow_match_v4(packet)
Rich Lane2eba47a2014-12-09 14:29:08 -080092 elif ofp.OFP_VERSION == 5:
93 return packet_to_flow_match_v5(packet)
Rich Laneb6255512013-05-07 14:58:43 -070094 else:
95 raise NotImplementedError()
Dan Talaycob9cb5482010-02-09 15:23:12 -080096
Rich Laneb6255512013-05-07 14:58:43 -070097def packet_to_flow_match_v1(packet):
98 """
99 OpenFlow 1.0 implementation of packet_to_flow_match
100 """
101 import loxi.of10 as ofp
Dan Talaycob66b1122010-02-10 22:38:49 -0800102
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800103 if type(packet) == type(""):
Dan Talayco958f3b92010-03-12 21:58:57 -0800104 ether = scapy.Ether(packet)
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800105 else:
106 ether = packet
107
Dan Talaycob66b1122010-02-10 22:38:49 -0800108 # For now, assume ether IP packet and ignore wildcards
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800109 try:
110 (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
111 except:
Rich Laneb6255512013-05-07 14:58:43 -0700112 raise ValueError("could not classify packet")
Dan Talaycob66b1122010-02-10 22:38:49 -0800113
Rich Lane0237baf2013-03-11 22:34:59 -0700114 match = ofp.match()
Rich Lanef6883512013-03-11 17:00:09 -0700115 match.wildcards = ofp.OFPFW_ALL
Dan Talaycob66b1122010-02-10 22:38:49 -0800116 #@todo Check if packet is other than L2 format
Rich Laned0478ff2013-03-11 12:46:58 -0700117 match.eth_dst = parse_mac(ether.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700118 match.wildcards &= ~ofp.OFPFW_DL_DST
Rich Laned0478ff2013-03-11 12:46:58 -0700119 match.eth_src = parse_mac(ether.src)
Rich Lanef6883512013-03-11 17:00:09 -0700120 match.wildcards &= ~ofp.OFPFW_DL_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700121 match.eth_type = ether.type
Rich Lanef6883512013-03-11 17:00:09 -0700122 match.wildcards &= ~ofp.OFPFW_DL_TYPE
Dan Talaycob66b1122010-02-10 22:38:49 -0800123
124 if dot1q:
Rich Laned0478ff2013-03-11 12:46:58 -0700125 match.vlan_vid = dot1q.vlan
126 match.vlan_pcp = dot1q.prio
127 match.eth_type = dot1q.type
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700128 else:
Rich Lanef6883512013-03-11 17:00:09 -0700129 match.vlan_vid = ofp.OFP_VLAN_NONE
Rich Laned0478ff2013-03-11 12:46:58 -0700130 match.vlan_pcp = 0
Rich Lanef6883512013-03-11 17:00:09 -0700131 match.wildcards &= ~ofp.OFPFW_DL_VLAN
132 match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP
Dan Talaycob66b1122010-02-10 22:38:49 -0800133
134 if ip:
Rich Laned0478ff2013-03-11 12:46:58 -0700135 match.ipv4_src = parse_ip(ip.src)
Rich Lanef6883512013-03-11 17:00:09 -0700136 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700137 match.ipv4_dst = parse_ip(ip.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700138 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700139 match.ip_dscp = ip.tos
Rich Lanef6883512013-03-11 17:00:09 -0700140 match.wildcards &= ~ofp.OFPFW_NW_TOS
Dan Talaycob66b1122010-02-10 22:38:49 -0800141
142 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700143 match.ip_proto = 6
Rich Lanef6883512013-03-11 17:00:09 -0700144 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700145 elif not tcp and udp:
146 tcp = udp
Rich Laned0478ff2013-03-11 12:46:58 -0700147 match.ip_proto = 17
Rich Lanef6883512013-03-11 17:00:09 -0700148 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700149
150 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700151 match.tcp_src = tcp.sport
Rich Lanef6883512013-03-11 17:00:09 -0700152 match.wildcards &= ~ofp.OFPFW_TP_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700153 match.tcp_dst = tcp.dport
Rich Lanef6883512013-03-11 17:00:09 -0700154 match.wildcards &= ~ofp.OFPFW_TP_DST
Dan Talaycob66b1122010-02-10 22:38:49 -0800155
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700156 if icmp:
Rich Laned0478ff2013-03-11 12:46:58 -0700157 match.ip_proto = 1
158 match.tcp_src = icmp.type
159 match.tcp_dst = icmp.code
Rich Lanef6883512013-03-11 17:00:09 -0700160 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700161
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700162 if arp:
Rich Laned0478ff2013-03-11 12:46:58 -0700163 match.ip_proto = arp.op
Rich Lanef6883512013-03-11 17:00:09 -0700164 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Rich Laned0478ff2013-03-11 12:46:58 -0700165 match.ipv4_src = parse_ip(arp.psrc)
Rich Lanef6883512013-03-11 17:00:09 -0700166 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700167 match.ipv4_dst = parse_ip(arp.pdst)
Rich Lanef6883512013-03-11 17:00:09 -0700168 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Dan Talaycob66b1122010-02-10 22:38:49 -0800169
170 return match
Rich Laneb6255512013-05-07 14:58:43 -0700171
172def packet_to_flow_match_v3(packet):
173 """
174 OpenFlow 1.2 implementation of packet_to_flow_match
175 """
176 import loxi.of12 as ofp
177 return packet_to_flow_match_oxm(packet, ofp)
178
179def packet_to_flow_match_v4(packet):
180 """
181 OpenFlow 1.3 implementation of packet_to_flow_match
182 """
183 import loxi.of13 as ofp
184 return packet_to_flow_match_oxm(packet, ofp)
185
Rich Lane2eba47a2014-12-09 14:29:08 -0800186def packet_to_flow_match_v5(packet):
187 """
188 OpenFlow 1.3 implementation of packet_to_flow_match
189 """
190 import loxi.of14 as ofp
191 return packet_to_flow_match_oxm(packet, ofp)
192
Rich Laneb6255512013-05-07 14:58:43 -0700193def packet_to_flow_match_oxm(packet, ofp):
194 def parse_ether_layer(layer, match):
195 assert(type(layer) == scapy.Ether)
196 match.oxm_list.append(ofp.oxm.eth_dst(parse_mac(layer.dst)))
197 match.oxm_list.append(ofp.oxm.eth_src(parse_mac(layer.src)))
198
199 if type(layer.payload) == scapy.Dot1Q:
200 layer = layer.payload
201 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
Rich Lane3f17dbb2013-07-19 13:51:37 -0700202 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT|layer.vlan))
Rich Laneb6255512013-05-07 14:58:43 -0700203 match.oxm_list.append(ofp.oxm.vlan_pcp(layer.prio))
204 else:
205 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
206 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE))
207
208 if type(layer.payload) == scapy.IP:
209 parse_ipv4_layer(layer.payload, match)
210 elif type(layer.payload) == scapy.IPv6:
211 parse_ipv6_layer(layer.payload, match)
212 elif type(layer.payload) == scapy.ARP:
213 parse_arp_layer(layer.payload, match)
214 # TODO MPLS
215
216 def parse_ipv4_layer(layer, match):
217 assert(type(layer) == scapy.IP)
218 match.oxm_list.append(ofp.oxm.ip_proto(layer.proto))
219 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tos >> 2))
220 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tos & 3))
221 match.oxm_list.append(ofp.oxm.ipv4_src(parse_ip(layer.src)))
222 match.oxm_list.append(ofp.oxm.ipv4_dst(parse_ip(layer.dst)))
223
224 if type(layer.payload) == scapy.TCP:
225 parse_tcp_layer(layer.payload, match)
226 elif type(layer.payload) == scapy.UDP:
227 parse_udp_layer(layer.payload, match)
228 elif type(layer.payload) == scapy.ICMP:
229 parse_icmpv4_layer(layer.payload, match)
230 # TODO SCTP
231
232 def parse_tcp_layer(layer, match):
233 assert(type(layer) == scapy.TCP)
234 match.oxm_list.append(ofp.oxm.tcp_src(layer.sport))
235 match.oxm_list.append(ofp.oxm.tcp_dst(layer.dport))
236
237 def parse_udp_layer(layer, match):
238 assert(type(layer) == scapy.UDP)
239 match.oxm_list.append(ofp.oxm.udp_src(layer.sport))
240 match.oxm_list.append(ofp.oxm.udp_dst(layer.dport))
241
242 def parse_icmpv4_layer(layer, match):
243 assert(type(layer) == scapy.ICMP)
244 match.oxm_list.append(ofp.oxm.icmpv4_type(layer.type))
245 match.oxm_list.append(ofp.oxm.icmpv4_code(layer.code))
246
247 def parse_arp_layer(layer, match):
248 assert(type(layer) == scapy.ARP)
249 match.oxm_list.append(ofp.oxm.arp_op(layer.op))
250 match.oxm_list.append(ofp.oxm.arp_spa(parse_ip(layer.psrc)))
251 match.oxm_list.append(ofp.oxm.arp_tpa(parse_ip(layer.pdst)))
252 match.oxm_list.append(ofp.oxm.arp_sha(parse_mac(layer.hwsrc)))
253 match.oxm_list.append(ofp.oxm.arp_tha(parse_mac(layer.hwdst)))
254
255 def parse_ipv6_layer(layer, match):
256 assert(type(layer) == scapy.IPv6)
257 # TODO handle chained headers
258 match.oxm_list.append(ofp.oxm.ip_proto(layer.nh))
259 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tc >> 2))
260 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tc & 3))
261 match.oxm_list.append(ofp.oxm.ipv6_src(parse_ipv6(layer.src)))
262 match.oxm_list.append(ofp.oxm.ipv6_dst(parse_ipv6(layer.dst)))
263 match.oxm_list.append(ofp.oxm.ipv6_flabel(layer.fl))
264
265 if type(layer.payload) == scapy.TCP:
266 parse_tcp_layer(layer.payload, match)
267 elif type(layer.payload) == scapy.UDP:
268 parse_udp_layer(layer.payload, match)
269 elif layer.nh == 0x3a:
270 parse_icmpv6_layer(layer.payload, match)
271 # TODO ND
272 # TODO SCTP
273
274 def parse_icmpv6_layer(layer, match):
275 match.oxm_list.append(ofp.oxm.icmpv6_type(layer.type))
276 match.oxm_list.append(ofp.oxm.icmpv6_code(layer.code))
277
278 if type(packet) == type(""):
279 ether = scapy.Ether(packet)
280 else:
281 ether = packet
282
283 match = ofp.match()
284 parse_ether_layer(packet, match)
285 return match