blob: 0d57443a163dc964de52a175e419900d5ca8550a [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)
92 else:
93 raise NotImplementedError()
Dan Talaycob9cb5482010-02-09 15:23:12 -080094
Rich Laneb6255512013-05-07 14:58:43 -070095def packet_to_flow_match_v1(packet):
96 """
97 OpenFlow 1.0 implementation of packet_to_flow_match
98 """
99 import loxi.of10 as ofp
Dan Talaycob66b1122010-02-10 22:38:49 -0800100
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800101 if type(packet) == type(""):
Dan Talayco958f3b92010-03-12 21:58:57 -0800102 ether = scapy.Ether(packet)
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800103 else:
104 ether = packet
105
Dan Talaycob66b1122010-02-10 22:38:49 -0800106 # For now, assume ether IP packet and ignore wildcards
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800107 try:
108 (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
109 except:
Rich Laneb6255512013-05-07 14:58:43 -0700110 raise ValueError("could not classify packet")
Dan Talaycob66b1122010-02-10 22:38:49 -0800111
Rich Lane0237baf2013-03-11 22:34:59 -0700112 match = ofp.match()
Rich Lanef6883512013-03-11 17:00:09 -0700113 match.wildcards = ofp.OFPFW_ALL
Dan Talaycob66b1122010-02-10 22:38:49 -0800114 #@todo Check if packet is other than L2 format
Rich Laned0478ff2013-03-11 12:46:58 -0700115 match.eth_dst = parse_mac(ether.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700116 match.wildcards &= ~ofp.OFPFW_DL_DST
Rich Laned0478ff2013-03-11 12:46:58 -0700117 match.eth_src = parse_mac(ether.src)
Rich Lanef6883512013-03-11 17:00:09 -0700118 match.wildcards &= ~ofp.OFPFW_DL_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700119 match.eth_type = ether.type
Rich Lanef6883512013-03-11 17:00:09 -0700120 match.wildcards &= ~ofp.OFPFW_DL_TYPE
Dan Talaycob66b1122010-02-10 22:38:49 -0800121
122 if dot1q:
Rich Laned0478ff2013-03-11 12:46:58 -0700123 match.vlan_vid = dot1q.vlan
124 match.vlan_pcp = dot1q.prio
125 match.eth_type = dot1q.type
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700126 else:
Rich Lanef6883512013-03-11 17:00:09 -0700127 match.vlan_vid = ofp.OFP_VLAN_NONE
Rich Laned0478ff2013-03-11 12:46:58 -0700128 match.vlan_pcp = 0
Rich Lanef6883512013-03-11 17:00:09 -0700129 match.wildcards &= ~ofp.OFPFW_DL_VLAN
130 match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP
Dan Talaycob66b1122010-02-10 22:38:49 -0800131
132 if ip:
Rich Laned0478ff2013-03-11 12:46:58 -0700133 match.ipv4_src = parse_ip(ip.src)
Rich Lanef6883512013-03-11 17:00:09 -0700134 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700135 match.ipv4_dst = parse_ip(ip.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700136 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700137 match.ip_dscp = ip.tos
Rich Lanef6883512013-03-11 17:00:09 -0700138 match.wildcards &= ~ofp.OFPFW_NW_TOS
Dan Talaycob66b1122010-02-10 22:38:49 -0800139
140 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700141 match.ip_proto = 6
Rich Lanef6883512013-03-11 17:00:09 -0700142 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700143 elif not tcp and udp:
144 tcp = udp
Rich Laned0478ff2013-03-11 12:46:58 -0700145 match.ip_proto = 17
Rich Lanef6883512013-03-11 17:00:09 -0700146 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700147
148 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700149 match.tcp_src = tcp.sport
Rich Lanef6883512013-03-11 17:00:09 -0700150 match.wildcards &= ~ofp.OFPFW_TP_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700151 match.tcp_dst = tcp.dport
Rich Lanef6883512013-03-11 17:00:09 -0700152 match.wildcards &= ~ofp.OFPFW_TP_DST
Dan Talaycob66b1122010-02-10 22:38:49 -0800153
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700154 if icmp:
Rich Laned0478ff2013-03-11 12:46:58 -0700155 match.ip_proto = 1
156 match.tcp_src = icmp.type
157 match.tcp_dst = icmp.code
Rich Lanef6883512013-03-11 17:00:09 -0700158 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700159
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700160 if arp:
Rich Laned0478ff2013-03-11 12:46:58 -0700161 match.ip_proto = arp.op
Rich Lanef6883512013-03-11 17:00:09 -0700162 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Rich Laned0478ff2013-03-11 12:46:58 -0700163 match.ipv4_src = parse_ip(arp.psrc)
Rich Lanef6883512013-03-11 17:00:09 -0700164 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700165 match.ipv4_dst = parse_ip(arp.pdst)
Rich Lanef6883512013-03-11 17:00:09 -0700166 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Dan Talaycob66b1122010-02-10 22:38:49 -0800167
168 return match
Rich Laneb6255512013-05-07 14:58:43 -0700169
170def packet_to_flow_match_v3(packet):
171 """
172 OpenFlow 1.2 implementation of packet_to_flow_match
173 """
174 import loxi.of12 as ofp
175 return packet_to_flow_match_oxm(packet, ofp)
176
177def packet_to_flow_match_v4(packet):
178 """
179 OpenFlow 1.3 implementation of packet_to_flow_match
180 """
181 import loxi.of13 as ofp
182 return packet_to_flow_match_oxm(packet, ofp)
183
184def packet_to_flow_match_oxm(packet, ofp):
185 def parse_ether_layer(layer, match):
186 assert(type(layer) == scapy.Ether)
187 match.oxm_list.append(ofp.oxm.eth_dst(parse_mac(layer.dst)))
188 match.oxm_list.append(ofp.oxm.eth_src(parse_mac(layer.src)))
189
190 if type(layer.payload) == scapy.Dot1Q:
191 layer = layer.payload
192 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
Rich Lane3f17dbb2013-07-19 13:51:37 -0700193 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT|layer.vlan))
Rich Laneb6255512013-05-07 14:58:43 -0700194 match.oxm_list.append(ofp.oxm.vlan_pcp(layer.prio))
195 else:
196 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
197 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE))
198
199 if type(layer.payload) == scapy.IP:
200 parse_ipv4_layer(layer.payload, match)
201 elif type(layer.payload) == scapy.IPv6:
202 parse_ipv6_layer(layer.payload, match)
203 elif type(layer.payload) == scapy.ARP:
204 parse_arp_layer(layer.payload, match)
205 # TODO MPLS
206
207 def parse_ipv4_layer(layer, match):
208 assert(type(layer) == scapy.IP)
209 match.oxm_list.append(ofp.oxm.ip_proto(layer.proto))
210 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tos >> 2))
211 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tos & 3))
212 match.oxm_list.append(ofp.oxm.ipv4_src(parse_ip(layer.src)))
213 match.oxm_list.append(ofp.oxm.ipv4_dst(parse_ip(layer.dst)))
214
215 if type(layer.payload) == scapy.TCP:
216 parse_tcp_layer(layer.payload, match)
217 elif type(layer.payload) == scapy.UDP:
218 parse_udp_layer(layer.payload, match)
219 elif type(layer.payload) == scapy.ICMP:
220 parse_icmpv4_layer(layer.payload, match)
221 # TODO SCTP
222
223 def parse_tcp_layer(layer, match):
224 assert(type(layer) == scapy.TCP)
225 match.oxm_list.append(ofp.oxm.tcp_src(layer.sport))
226 match.oxm_list.append(ofp.oxm.tcp_dst(layer.dport))
227
228 def parse_udp_layer(layer, match):
229 assert(type(layer) == scapy.UDP)
230 match.oxm_list.append(ofp.oxm.udp_src(layer.sport))
231 match.oxm_list.append(ofp.oxm.udp_dst(layer.dport))
232
233 def parse_icmpv4_layer(layer, match):
234 assert(type(layer) == scapy.ICMP)
235 match.oxm_list.append(ofp.oxm.icmpv4_type(layer.type))
236 match.oxm_list.append(ofp.oxm.icmpv4_code(layer.code))
237
238 def parse_arp_layer(layer, match):
239 assert(type(layer) == scapy.ARP)
240 match.oxm_list.append(ofp.oxm.arp_op(layer.op))
241 match.oxm_list.append(ofp.oxm.arp_spa(parse_ip(layer.psrc)))
242 match.oxm_list.append(ofp.oxm.arp_tpa(parse_ip(layer.pdst)))
243 match.oxm_list.append(ofp.oxm.arp_sha(parse_mac(layer.hwsrc)))
244 match.oxm_list.append(ofp.oxm.arp_tha(parse_mac(layer.hwdst)))
245
246 def parse_ipv6_layer(layer, match):
247 assert(type(layer) == scapy.IPv6)
248 # TODO handle chained headers
249 match.oxm_list.append(ofp.oxm.ip_proto(layer.nh))
250 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tc >> 2))
251 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tc & 3))
252 match.oxm_list.append(ofp.oxm.ipv6_src(parse_ipv6(layer.src)))
253 match.oxm_list.append(ofp.oxm.ipv6_dst(parse_ipv6(layer.dst)))
254 match.oxm_list.append(ofp.oxm.ipv6_flabel(layer.fl))
255
256 if type(layer.payload) == scapy.TCP:
257 parse_tcp_layer(layer.payload, match)
258 elif type(layer.payload) == scapy.UDP:
259 parse_udp_layer(layer.payload, match)
260 elif layer.nh == 0x3a:
261 parse_icmpv6_layer(layer.payload, match)
262 # TODO ND
263 # TODO SCTP
264
265 def parse_icmpv6_layer(layer, match):
266 match.oxm_list.append(ofp.oxm.icmpv6_type(layer.type))
267 match.oxm_list.append(ofp.oxm.icmpv6_code(layer.code))
268
269 if type(packet) == type(""):
270 ether = scapy.Ether(packet)
271 else:
272 ether = packet
273
274 match = ofp.match()
275 parse_ether_layer(packet, match)
276 return match