blob: 385e21d4f0bbdccceb1a1a4bab066c7c225c4782 [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
Dan Talaycob66b1122010-02-10 22:38:49 -08006try:
Dan Talayco958f3b92010-03-12 21:58:57 -08007 import scapy.all as scapy
Dan Talaycob66b1122010-02-10 22:38:49 -08008except:
Dan Talaycod2ca1032010-03-10 14:40:26 -08009 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080010 import scapy as scapy
Dan Talaycod2ca1032010-03-10 14:40:26 -080011 except:
12 sys.exit("Need to install scapy for packet parsing")
Dan Talaycof75360a2010-02-05 22:22:54 -080013
Dan Talaycob66b1122010-02-10 22:38:49 -080014def parse_mac(mac_str):
Dan Talaycob9cb5482010-02-09 15:23:12 -080015 """
Dan Talaycob66b1122010-02-10 22:38:49 -080016 Parse a MAC address
17
18 Parse a MAC address ':' separated string of hex digits to an
19 array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0]
20 @param mac_str The string to convert
21 @return Array of 6 integer values
22 """
Rich Lanea92f2522012-10-04 18:11:04 -070023 return map(lambda val: int(val, 16), mac_str.split(":"))
Dan Talaycob66b1122010-02-10 22:38:49 -080024
25def parse_ip(ip_str):
26 """
27 Parse an IP address
28
29 Parse an IP address '.' separated string of decimal digits to an
30 host ordered integer. '172.24.74.77' =>
31 @param ip_str The string to convert
32 @return Integer value
33 """
Rich Lanea92f2522012-10-04 18:11:04 -070034 array = map(lambda val: int(val), ip_str.split("."))
Dan Talaycob66b1122010-02-10 22:38:49 -080035 val = 0
36 for a in array:
37 val <<= 8
38 val += a
39 return val
40
Rich Laneb6255512013-05-07 14:58:43 -070041def parse_ipv6(ip_str):
42 """
43 Parse an IPv6 address
44
45 Parse a textual IPv6 address and return a 16 byte binary string.
46 """
47 import ipaddr # TODO remove dependency on ipaddr?
48 return str(ipaddr.IPv6Address(ip_str).packed)
49
Dan Talaycob66b1122010-02-10 22:38:49 -080050def packet_type_classify(ether):
51 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080052 dot1q = ether[scapy.Dot1Q]
Dan Talaycob66b1122010-02-10 22:38:49 -080053 except:
54 dot1q = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080055
Dan Talaycob66b1122010-02-10 22:38:49 -080056 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080057 ip = ether[scapy.IP]
Dan Talaycob66b1122010-02-10 22:38:49 -080058 except:
59 ip = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080060
Dan Talaycob66b1122010-02-10 22:38:49 -080061 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080062 tcp = ether[scapy.TCP]
Dan Talaycob66b1122010-02-10 22:38:49 -080063 except:
64 tcp = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080065
Dan Talaycob66b1122010-02-10 22:38:49 -080066 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080067 udp = ether[scapy.UDP]
Dan Talaycob66b1122010-02-10 22:38:49 -080068 except:
69 udp = None
70
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -070071 try:
72 icmp = ether[scapy.ICMP]
73 except:
74 icmp = None
75
Christian Dickmann8b59b4b2012-09-23 16:48:30 -070076 try:
77 arp = ether[scapy.ARP]
78 except:
79 arp = None
Dan Talaycob66b1122010-02-10 22:38:49 -080080 return (dot1q, ip, tcp, udp, icmp, arp)
81
Rich Laneb6255512013-05-07 14:58:43 -070082def packet_to_flow_match(packet):
Dan Talaycob66b1122010-02-10 22:38:49 -080083 """
84 Create a flow match that matches packet with the given wildcards
Dan Talaycob9cb5482010-02-09 15:23:12 -080085
86 @param packet The packet to use as a flow template
Rich Laneb6255512013-05-07 14:58:43 -070087 @return An loxi.of10.match object
Dan Talaycob66b1122010-02-10 22:38:49 -080088
Dan Talaycoa3b20182010-02-10 22:49:34 -080089 @todo check min length of packet
Dan Talaycob9cb5482010-02-09 15:23:12 -080090 """
Rich Laneb6255512013-05-07 14:58:43 -070091 import ofp
92 if ofp.OFP_VERSION == 1:
93 return packet_to_flow_match_v1(packet)
94 elif ofp.OFP_VERSION == 3:
95 return packet_to_flow_match_v3(packet)
96 elif ofp.OFP_VERSION == 4:
97 return packet_to_flow_match_v4(packet)
98 else:
99 raise NotImplementedError()
Dan Talaycob9cb5482010-02-09 15:23:12 -0800100
Rich Laneb6255512013-05-07 14:58:43 -0700101def packet_to_flow_match_v1(packet):
102 """
103 OpenFlow 1.0 implementation of packet_to_flow_match
104 """
105 import loxi.of10 as ofp
Dan Talaycob66b1122010-02-10 22:38:49 -0800106
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800107 if type(packet) == type(""):
Dan Talayco958f3b92010-03-12 21:58:57 -0800108 ether = scapy.Ether(packet)
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800109 else:
110 ether = packet
111
Dan Talaycob66b1122010-02-10 22:38:49 -0800112 # For now, assume ether IP packet and ignore wildcards
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800113 try:
114 (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
115 except:
Rich Laneb6255512013-05-07 14:58:43 -0700116 raise ValueError("could not classify packet")
Dan Talaycob66b1122010-02-10 22:38:49 -0800117
Rich Lane0237baf2013-03-11 22:34:59 -0700118 match = ofp.match()
Rich Lanef6883512013-03-11 17:00:09 -0700119 match.wildcards = ofp.OFPFW_ALL
Dan Talaycob66b1122010-02-10 22:38:49 -0800120 #@todo Check if packet is other than L2 format
Rich Laned0478ff2013-03-11 12:46:58 -0700121 match.eth_dst = parse_mac(ether.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700122 match.wildcards &= ~ofp.OFPFW_DL_DST
Rich Laned0478ff2013-03-11 12:46:58 -0700123 match.eth_src = parse_mac(ether.src)
Rich Lanef6883512013-03-11 17:00:09 -0700124 match.wildcards &= ~ofp.OFPFW_DL_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700125 match.eth_type = ether.type
Rich Lanef6883512013-03-11 17:00:09 -0700126 match.wildcards &= ~ofp.OFPFW_DL_TYPE
Dan Talaycob66b1122010-02-10 22:38:49 -0800127
128 if dot1q:
Rich Laned0478ff2013-03-11 12:46:58 -0700129 match.vlan_vid = dot1q.vlan
130 match.vlan_pcp = dot1q.prio
131 match.eth_type = dot1q.type
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700132 else:
Rich Lanef6883512013-03-11 17:00:09 -0700133 match.vlan_vid = ofp.OFP_VLAN_NONE
Rich Laned0478ff2013-03-11 12:46:58 -0700134 match.vlan_pcp = 0
Rich Lanef6883512013-03-11 17:00:09 -0700135 match.wildcards &= ~ofp.OFPFW_DL_VLAN
136 match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP
Dan Talaycob66b1122010-02-10 22:38:49 -0800137
138 if ip:
Rich Laned0478ff2013-03-11 12:46:58 -0700139 match.ipv4_src = parse_ip(ip.src)
Rich Lanef6883512013-03-11 17:00:09 -0700140 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700141 match.ipv4_dst = parse_ip(ip.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700142 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700143 match.ip_dscp = ip.tos
Rich Lanef6883512013-03-11 17:00:09 -0700144 match.wildcards &= ~ofp.OFPFW_NW_TOS
Dan Talaycob66b1122010-02-10 22:38:49 -0800145
146 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700147 match.ip_proto = 6
Rich Lanef6883512013-03-11 17:00:09 -0700148 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700149 elif not tcp and udp:
150 tcp = udp
Rich Laned0478ff2013-03-11 12:46:58 -0700151 match.ip_proto = 17
Rich Lanef6883512013-03-11 17:00:09 -0700152 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700153
154 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700155 match.tcp_src = tcp.sport
Rich Lanef6883512013-03-11 17:00:09 -0700156 match.wildcards &= ~ofp.OFPFW_TP_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700157 match.tcp_dst = tcp.dport
Rich Lanef6883512013-03-11 17:00:09 -0700158 match.wildcards &= ~ofp.OFPFW_TP_DST
Dan Talaycob66b1122010-02-10 22:38:49 -0800159
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700160 if icmp:
Rich Laned0478ff2013-03-11 12:46:58 -0700161 match.ip_proto = 1
162 match.tcp_src = icmp.type
163 match.tcp_dst = icmp.code
Rich Lanef6883512013-03-11 17:00:09 -0700164 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700165
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700166 if arp:
Rich Laned0478ff2013-03-11 12:46:58 -0700167 match.ip_proto = arp.op
Rich Lanef6883512013-03-11 17:00:09 -0700168 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Rich Laned0478ff2013-03-11 12:46:58 -0700169 match.ipv4_src = parse_ip(arp.psrc)
Rich Lanef6883512013-03-11 17:00:09 -0700170 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700171 match.ipv4_dst = parse_ip(arp.pdst)
Rich Lanef6883512013-03-11 17:00:09 -0700172 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Dan Talaycob66b1122010-02-10 22:38:49 -0800173
174 return match
Rich Laneb6255512013-05-07 14:58:43 -0700175
176def packet_to_flow_match_v3(packet):
177 """
178 OpenFlow 1.2 implementation of packet_to_flow_match
179 """
180 import loxi.of12 as ofp
181 return packet_to_flow_match_oxm(packet, ofp)
182
183def packet_to_flow_match_v4(packet):
184 """
185 OpenFlow 1.3 implementation of packet_to_flow_match
186 """
187 import loxi.of13 as ofp
188 return packet_to_flow_match_oxm(packet, ofp)
189
190def packet_to_flow_match_oxm(packet, ofp):
191 def parse_ether_layer(layer, match):
192 assert(type(layer) == scapy.Ether)
193 match.oxm_list.append(ofp.oxm.eth_dst(parse_mac(layer.dst)))
194 match.oxm_list.append(ofp.oxm.eth_src(parse_mac(layer.src)))
195
196 if type(layer.payload) == scapy.Dot1Q:
197 layer = layer.payload
198 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
199 match.oxm_list.append(ofp.oxm.vlan_vid(layer.vlan))
200 match.oxm_list.append(ofp.oxm.vlan_pcp(layer.prio))
201 else:
202 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
203 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE))
204
205 if type(layer.payload) == scapy.IP:
206 parse_ipv4_layer(layer.payload, match)
207 elif type(layer.payload) == scapy.IPv6:
208 parse_ipv6_layer(layer.payload, match)
209 elif type(layer.payload) == scapy.ARP:
210 parse_arp_layer(layer.payload, match)
211 # TODO MPLS
212
213 def parse_ipv4_layer(layer, match):
214 assert(type(layer) == scapy.IP)
215 match.oxm_list.append(ofp.oxm.ip_proto(layer.proto))
216 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tos >> 2))
217 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tos & 3))
218 match.oxm_list.append(ofp.oxm.ipv4_src(parse_ip(layer.src)))
219 match.oxm_list.append(ofp.oxm.ipv4_dst(parse_ip(layer.dst)))
220
221 if type(layer.payload) == scapy.TCP:
222 parse_tcp_layer(layer.payload, match)
223 elif type(layer.payload) == scapy.UDP:
224 parse_udp_layer(layer.payload, match)
225 elif type(layer.payload) == scapy.ICMP:
226 parse_icmpv4_layer(layer.payload, match)
227 # TODO SCTP
228
229 def parse_tcp_layer(layer, match):
230 assert(type(layer) == scapy.TCP)
231 match.oxm_list.append(ofp.oxm.tcp_src(layer.sport))
232 match.oxm_list.append(ofp.oxm.tcp_dst(layer.dport))
233
234 def parse_udp_layer(layer, match):
235 assert(type(layer) == scapy.UDP)
236 match.oxm_list.append(ofp.oxm.udp_src(layer.sport))
237 match.oxm_list.append(ofp.oxm.udp_dst(layer.dport))
238
239 def parse_icmpv4_layer(layer, match):
240 assert(type(layer) == scapy.ICMP)
241 match.oxm_list.append(ofp.oxm.icmpv4_type(layer.type))
242 match.oxm_list.append(ofp.oxm.icmpv4_code(layer.code))
243
244 def parse_arp_layer(layer, match):
245 assert(type(layer) == scapy.ARP)
246 match.oxm_list.append(ofp.oxm.arp_op(layer.op))
247 match.oxm_list.append(ofp.oxm.arp_spa(parse_ip(layer.psrc)))
248 match.oxm_list.append(ofp.oxm.arp_tpa(parse_ip(layer.pdst)))
249 match.oxm_list.append(ofp.oxm.arp_sha(parse_mac(layer.hwsrc)))
250 match.oxm_list.append(ofp.oxm.arp_tha(parse_mac(layer.hwdst)))
251
252 def parse_ipv6_layer(layer, match):
253 assert(type(layer) == scapy.IPv6)
254 # TODO handle chained headers
255 match.oxm_list.append(ofp.oxm.ip_proto(layer.nh))
256 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tc >> 2))
257 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tc & 3))
258 match.oxm_list.append(ofp.oxm.ipv6_src(parse_ipv6(layer.src)))
259 match.oxm_list.append(ofp.oxm.ipv6_dst(parse_ipv6(layer.dst)))
260 match.oxm_list.append(ofp.oxm.ipv6_flabel(layer.fl))
261
262 if type(layer.payload) == scapy.TCP:
263 parse_tcp_layer(layer.payload, match)
264 elif type(layer.payload) == scapy.UDP:
265 parse_udp_layer(layer.payload, match)
266 elif layer.nh == 0x3a:
267 parse_icmpv6_layer(layer.payload, match)
268 # TODO ND
269 # TODO SCTP
270
271 def parse_icmpv6_layer(layer, match):
272 match.oxm_list.append(ofp.oxm.icmpv6_type(layer.type))
273 match.oxm_list.append(ofp.oxm.icmpv6_code(layer.code))
274
275 if type(packet) == type(""):
276 ether = scapy.Ether(packet)
277 else:
278 ether = packet
279
280 match = ofp.match()
281 parse_ether_layer(packet, match)
282 return match