blob: 1f94c453258de305ba77f3a79aaac3930b6c70bb [file] [log] [blame]
Matteo Scandoloa229eca2017-08-08 13:05:28 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Dan Talaycob66b1122010-02-10 22:38:49 -080017"""
Rich Lanef6883512013-03-11 17:00:09 -070018Utility parsing functions
Dan Talaycob66b1122010-02-10 22:38:49 -080019"""
20
21import sys
Rich Lane0f79bee2013-05-13 17:37:43 -070022import socket
Rich Lanea68176f2013-08-09 17:41:05 -070023import packet as scapy
Dan Talaycof75360a2010-02-05 22:22:54 -080024
Dan Talaycob66b1122010-02-10 22:38:49 -080025def parse_mac(mac_str):
Dan Talaycob9cb5482010-02-09 15:23:12 -080026 """
Dan Talaycob66b1122010-02-10 22:38:49 -080027 Parse a MAC address
28
29 Parse a MAC address ':' separated string of hex digits to an
30 array of integer values. '00:d0:05:5d:24:00' => [0, 208, 5, 93, 36, 0]
31 @param mac_str The string to convert
32 @return Array of 6 integer values
33 """
Rich Lanea92f2522012-10-04 18:11:04 -070034 return map(lambda val: int(val, 16), mac_str.split(":"))
Dan Talaycob66b1122010-02-10 22:38:49 -080035
36def parse_ip(ip_str):
37 """
38 Parse an IP address
39
40 Parse an IP address '.' separated string of decimal digits to an
41 host ordered integer. '172.24.74.77' =>
42 @param ip_str The string to convert
43 @return Integer value
44 """
Rich Lanea92f2522012-10-04 18:11:04 -070045 array = map(lambda val: int(val), ip_str.split("."))
Dan Talaycob66b1122010-02-10 22:38:49 -080046 val = 0
47 for a in array:
48 val <<= 8
49 val += a
50 return val
51
Rich Laneb6255512013-05-07 14:58:43 -070052def parse_ipv6(ip_str):
53 """
54 Parse an IPv6 address
55
56 Parse a textual IPv6 address and return a 16 byte binary string.
57 """
Rich Lane0f79bee2013-05-13 17:37:43 -070058 return socket.inet_pton(socket.AF_INET6, ip_str)
Rich Laneb6255512013-05-07 14:58:43 -070059
Dan Talaycob66b1122010-02-10 22:38:49 -080060def packet_type_classify(ether):
61 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080062 dot1q = ether[scapy.Dot1Q]
Dan Talaycob66b1122010-02-10 22:38:49 -080063 except:
64 dot1q = 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 ip = ether[scapy.IP]
Dan Talaycob66b1122010-02-10 22:38:49 -080068 except:
69 ip = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080070
Dan Talaycob66b1122010-02-10 22:38:49 -080071 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080072 tcp = ether[scapy.TCP]
Dan Talaycob66b1122010-02-10 22:38:49 -080073 except:
74 tcp = None
Dan Talayco08d9dfe2010-02-12 23:02:11 -080075
Dan Talaycob66b1122010-02-10 22:38:49 -080076 try:
Dan Talayco958f3b92010-03-12 21:58:57 -080077 udp = ether[scapy.UDP]
Dan Talaycob66b1122010-02-10 22:38:49 -080078 except:
79 udp = None
80
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -070081 try:
82 icmp = ether[scapy.ICMP]
83 except:
84 icmp = None
85
Christian Dickmann8b59b4b2012-09-23 16:48:30 -070086 try:
87 arp = ether[scapy.ARP]
88 except:
89 arp = None
Dan Talaycob66b1122010-02-10 22:38:49 -080090 return (dot1q, ip, tcp, udp, icmp, arp)
91
Rich Laneb6255512013-05-07 14:58:43 -070092def packet_to_flow_match(packet):
Dan Talaycob66b1122010-02-10 22:38:49 -080093 """
94 Create a flow match that matches packet with the given wildcards
Dan Talaycob9cb5482010-02-09 15:23:12 -080095
96 @param packet The packet to use as a flow template
Rich Laneb6255512013-05-07 14:58:43 -070097 @return An loxi.of10.match object
Dan Talaycob66b1122010-02-10 22:38:49 -080098
Dan Talaycoa3b20182010-02-10 22:49:34 -080099 @todo check min length of packet
Dan Talaycob9cb5482010-02-09 15:23:12 -0800100 """
Rich Laneb6255512013-05-07 14:58:43 -0700101 import ofp
102 if ofp.OFP_VERSION == 1:
103 return packet_to_flow_match_v1(packet)
104 elif ofp.OFP_VERSION == 3:
105 return packet_to_flow_match_v3(packet)
106 elif ofp.OFP_VERSION == 4:
107 return packet_to_flow_match_v4(packet)
Rich Lane2eba47a2014-12-09 14:29:08 -0800108 elif ofp.OFP_VERSION == 5:
109 return packet_to_flow_match_v5(packet)
Rich Laneb6255512013-05-07 14:58:43 -0700110 else:
111 raise NotImplementedError()
Dan Talaycob9cb5482010-02-09 15:23:12 -0800112
Rich Laneb6255512013-05-07 14:58:43 -0700113def packet_to_flow_match_v1(packet):
114 """
115 OpenFlow 1.0 implementation of packet_to_flow_match
116 """
117 import loxi.of10 as ofp
Dan Talaycob66b1122010-02-10 22:38:49 -0800118
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800119 if type(packet) == type(""):
Dan Talayco958f3b92010-03-12 21:58:57 -0800120 ether = scapy.Ether(packet)
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800121 else:
122 ether = packet
123
Dan Talaycob66b1122010-02-10 22:38:49 -0800124 # For now, assume ether IP packet and ignore wildcards
Dan Talaycocb6b5d72010-03-10 13:59:33 -0800125 try:
126 (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
127 except:
Rich Laneb6255512013-05-07 14:58:43 -0700128 raise ValueError("could not classify packet")
Dan Talaycob66b1122010-02-10 22:38:49 -0800129
Rich Lane0237baf2013-03-11 22:34:59 -0700130 match = ofp.match()
Rich Lanef6883512013-03-11 17:00:09 -0700131 match.wildcards = ofp.OFPFW_ALL
Dan Talaycob66b1122010-02-10 22:38:49 -0800132 #@todo Check if packet is other than L2 format
Rich Laned0478ff2013-03-11 12:46:58 -0700133 match.eth_dst = parse_mac(ether.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700134 match.wildcards &= ~ofp.OFPFW_DL_DST
Rich Laned0478ff2013-03-11 12:46:58 -0700135 match.eth_src = parse_mac(ether.src)
Rich Lanef6883512013-03-11 17:00:09 -0700136 match.wildcards &= ~ofp.OFPFW_DL_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700137 match.eth_type = ether.type
Rich Lanef6883512013-03-11 17:00:09 -0700138 match.wildcards &= ~ofp.OFPFW_DL_TYPE
Dan Talaycob66b1122010-02-10 22:38:49 -0800139
140 if dot1q:
Rich Laned0478ff2013-03-11 12:46:58 -0700141 match.vlan_vid = dot1q.vlan
142 match.vlan_pcp = dot1q.prio
143 match.eth_type = dot1q.type
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700144 else:
Rich Lanef6883512013-03-11 17:00:09 -0700145 match.vlan_vid = ofp.OFP_VLAN_NONE
Rich Laned0478ff2013-03-11 12:46:58 -0700146 match.vlan_pcp = 0
Rich Lanef6883512013-03-11 17:00:09 -0700147 match.wildcards &= ~ofp.OFPFW_DL_VLAN
148 match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP
Dan Talaycob66b1122010-02-10 22:38:49 -0800149
150 if ip:
Rich Laned0478ff2013-03-11 12:46:58 -0700151 match.ipv4_src = parse_ip(ip.src)
Rich Lanef6883512013-03-11 17:00:09 -0700152 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700153 match.ipv4_dst = parse_ip(ip.dst)
Rich Lanef6883512013-03-11 17:00:09 -0700154 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700155 match.ip_dscp = ip.tos
Rich Lanef6883512013-03-11 17:00:09 -0700156 match.wildcards &= ~ofp.OFPFW_NW_TOS
Dan Talaycob66b1122010-02-10 22:38:49 -0800157
158 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700159 match.ip_proto = 6
Rich Lanef6883512013-03-11 17:00:09 -0700160 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700161 elif not tcp and udp:
162 tcp = udp
Rich Laned0478ff2013-03-11 12:46:58 -0700163 match.ip_proto = 17
Rich Lanef6883512013-03-11 17:00:09 -0700164 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Dan Talayco89d57342010-06-07 16:24:59 -0700165
166 if tcp:
Rich Laned0478ff2013-03-11 12:46:58 -0700167 match.tcp_src = tcp.sport
Rich Lanef6883512013-03-11 17:00:09 -0700168 match.wildcards &= ~ofp.OFPFW_TP_SRC
Rich Laned0478ff2013-03-11 12:46:58 -0700169 match.tcp_dst = tcp.dport
Rich Lanef6883512013-03-11 17:00:09 -0700170 match.wildcards &= ~ofp.OFPFW_TP_DST
Dan Talaycob66b1122010-02-10 22:38:49 -0800171
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700172 if icmp:
Rich Laned0478ff2013-03-11 12:46:58 -0700173 match.ip_proto = 1
174 match.tcp_src = icmp.type
175 match.tcp_dst = icmp.code
Rich Lanef6883512013-03-11 17:00:09 -0700176 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700177
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700178 if arp:
Rich Laned0478ff2013-03-11 12:46:58 -0700179 match.ip_proto = arp.op
Rich Lanef6883512013-03-11 17:00:09 -0700180 match.wildcards &= ~ofp.OFPFW_NW_PROTO
Rich Laned0478ff2013-03-11 12:46:58 -0700181 match.ipv4_src = parse_ip(arp.psrc)
Rich Lanef6883512013-03-11 17:00:09 -0700182 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
Rich Laned0478ff2013-03-11 12:46:58 -0700183 match.ipv4_dst = parse_ip(arp.pdst)
Rich Lanef6883512013-03-11 17:00:09 -0700184 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
Dan Talaycob66b1122010-02-10 22:38:49 -0800185
186 return match
Rich Laneb6255512013-05-07 14:58:43 -0700187
188def packet_to_flow_match_v3(packet):
189 """
190 OpenFlow 1.2 implementation of packet_to_flow_match
191 """
192 import loxi.of12 as ofp
193 return packet_to_flow_match_oxm(packet, ofp)
194
195def packet_to_flow_match_v4(packet):
196 """
197 OpenFlow 1.3 implementation of packet_to_flow_match
198 """
199 import loxi.of13 as ofp
200 return packet_to_flow_match_oxm(packet, ofp)
201
Rich Lane2eba47a2014-12-09 14:29:08 -0800202def packet_to_flow_match_v5(packet):
203 """
204 OpenFlow 1.3 implementation of packet_to_flow_match
205 """
206 import loxi.of14 as ofp
207 return packet_to_flow_match_oxm(packet, ofp)
208
Rich Laneb6255512013-05-07 14:58:43 -0700209def packet_to_flow_match_oxm(packet, ofp):
210 def parse_ether_layer(layer, match):
211 assert(type(layer) == scapy.Ether)
212 match.oxm_list.append(ofp.oxm.eth_dst(parse_mac(layer.dst)))
213 match.oxm_list.append(ofp.oxm.eth_src(parse_mac(layer.src)))
214
215 if type(layer.payload) == scapy.Dot1Q:
216 layer = layer.payload
217 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
Rich Lane3f17dbb2013-07-19 13:51:37 -0700218 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT|layer.vlan))
Rich Laneb6255512013-05-07 14:58:43 -0700219 match.oxm_list.append(ofp.oxm.vlan_pcp(layer.prio))
220 else:
221 match.oxm_list.append(ofp.oxm.eth_type(layer.type))
222 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFP_VLAN_NONE))
223
224 if type(layer.payload) == scapy.IP:
225 parse_ipv4_layer(layer.payload, match)
226 elif type(layer.payload) == scapy.IPv6:
227 parse_ipv6_layer(layer.payload, match)
228 elif type(layer.payload) == scapy.ARP:
229 parse_arp_layer(layer.payload, match)
230 # TODO MPLS
231
232 def parse_ipv4_layer(layer, match):
233 assert(type(layer) == scapy.IP)
234 match.oxm_list.append(ofp.oxm.ip_proto(layer.proto))
235 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tos >> 2))
236 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tos & 3))
237 match.oxm_list.append(ofp.oxm.ipv4_src(parse_ip(layer.src)))
238 match.oxm_list.append(ofp.oxm.ipv4_dst(parse_ip(layer.dst)))
239
240 if type(layer.payload) == scapy.TCP:
241 parse_tcp_layer(layer.payload, match)
242 elif type(layer.payload) == scapy.UDP:
243 parse_udp_layer(layer.payload, match)
244 elif type(layer.payload) == scapy.ICMP:
245 parse_icmpv4_layer(layer.payload, match)
246 # TODO SCTP
247
248 def parse_tcp_layer(layer, match):
249 assert(type(layer) == scapy.TCP)
250 match.oxm_list.append(ofp.oxm.tcp_src(layer.sport))
251 match.oxm_list.append(ofp.oxm.tcp_dst(layer.dport))
252
253 def parse_udp_layer(layer, match):
254 assert(type(layer) == scapy.UDP)
255 match.oxm_list.append(ofp.oxm.udp_src(layer.sport))
256 match.oxm_list.append(ofp.oxm.udp_dst(layer.dport))
257
258 def parse_icmpv4_layer(layer, match):
259 assert(type(layer) == scapy.ICMP)
260 match.oxm_list.append(ofp.oxm.icmpv4_type(layer.type))
261 match.oxm_list.append(ofp.oxm.icmpv4_code(layer.code))
262
263 def parse_arp_layer(layer, match):
264 assert(type(layer) == scapy.ARP)
265 match.oxm_list.append(ofp.oxm.arp_op(layer.op))
266 match.oxm_list.append(ofp.oxm.arp_spa(parse_ip(layer.psrc)))
267 match.oxm_list.append(ofp.oxm.arp_tpa(parse_ip(layer.pdst)))
268 match.oxm_list.append(ofp.oxm.arp_sha(parse_mac(layer.hwsrc)))
269 match.oxm_list.append(ofp.oxm.arp_tha(parse_mac(layer.hwdst)))
270
271 def parse_ipv6_layer(layer, match):
272 assert(type(layer) == scapy.IPv6)
273 # TODO handle chained headers
274 match.oxm_list.append(ofp.oxm.ip_proto(layer.nh))
275 match.oxm_list.append(ofp.oxm.ip_dscp(layer.tc >> 2))
276 match.oxm_list.append(ofp.oxm.ip_ecn(layer.tc & 3))
277 match.oxm_list.append(ofp.oxm.ipv6_src(parse_ipv6(layer.src)))
278 match.oxm_list.append(ofp.oxm.ipv6_dst(parse_ipv6(layer.dst)))
279 match.oxm_list.append(ofp.oxm.ipv6_flabel(layer.fl))
280
281 if type(layer.payload) == scapy.TCP:
282 parse_tcp_layer(layer.payload, match)
283 elif type(layer.payload) == scapy.UDP:
284 parse_udp_layer(layer.payload, match)
285 elif layer.nh == 0x3a:
286 parse_icmpv6_layer(layer.payload, match)
287 # TODO ND
288 # TODO SCTP
289
290 def parse_icmpv6_layer(layer, match):
291 match.oxm_list.append(ofp.oxm.icmpv6_type(layer.type))
292 match.oxm_list.append(ofp.oxm.icmpv6_code(layer.code))
293
294 if type(packet) == type(""):
295 ether = scapy.Ether(packet)
296 else:
297 ether = packet
298
299 match = ofp.match()
300 parse_ether_layer(packet, match)
301 return match