blob: 1f94c453258de305ba77f3a79aaac3930b6c70bb [file] [log] [blame]
Sreeju Sreedhare3fefd92019-04-02 15:57:15 -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
17"""
18Utility parsing functions
19"""
20
21import sys
22import socket
23import packet as scapy
24
25def parse_mac(mac_str):
26 """
27 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 """
34 return map(lambda val: int(val, 16), mac_str.split(":"))
35
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 """
45 array = map(lambda val: int(val), ip_str.split("."))
46 val = 0
47 for a in array:
48 val <<= 8
49 val += a
50 return val
51
52def 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 """
58 return socket.inet_pton(socket.AF_INET6, ip_str)
59
60def packet_type_classify(ether):
61 try:
62 dot1q = ether[scapy.Dot1Q]
63 except:
64 dot1q = None
65
66 try:
67 ip = ether[scapy.IP]
68 except:
69 ip = None
70
71 try:
72 tcp = ether[scapy.TCP]
73 except:
74 tcp = None
75
76 try:
77 udp = ether[scapy.UDP]
78 except:
79 udp = None
80
81 try:
82 icmp = ether[scapy.ICMP]
83 except:
84 icmp = None
85
86 try:
87 arp = ether[scapy.ARP]
88 except:
89 arp = None
90 return (dot1q, ip, tcp, udp, icmp, arp)
91
92def packet_to_flow_match(packet):
93 """
94 Create a flow match that matches packet with the given wildcards
95
96 @param packet The packet to use as a flow template
97 @return An loxi.of10.match object
98
99 @todo check min length of packet
100 """
101 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)
108 elif ofp.OFP_VERSION == 5:
109 return packet_to_flow_match_v5(packet)
110 else:
111 raise NotImplementedError()
112
113def packet_to_flow_match_v1(packet):
114 """
115 OpenFlow 1.0 implementation of packet_to_flow_match
116 """
117 import loxi.of10 as ofp
118
119 if type(packet) == type(""):
120 ether = scapy.Ether(packet)
121 else:
122 ether = packet
123
124 # For now, assume ether IP packet and ignore wildcards
125 try:
126 (dot1q, ip, tcp, udp, icmp, arp) = packet_type_classify(ether)
127 except:
128 raise ValueError("could not classify packet")
129
130 match = ofp.match()
131 match.wildcards = ofp.OFPFW_ALL
132 #@todo Check if packet is other than L2 format
133 match.eth_dst = parse_mac(ether.dst)
134 match.wildcards &= ~ofp.OFPFW_DL_DST
135 match.eth_src = parse_mac(ether.src)
136 match.wildcards &= ~ofp.OFPFW_DL_SRC
137 match.eth_type = ether.type
138 match.wildcards &= ~ofp.OFPFW_DL_TYPE
139
140 if dot1q:
141 match.vlan_vid = dot1q.vlan
142 match.vlan_pcp = dot1q.prio
143 match.eth_type = dot1q.type
144 else:
145 match.vlan_vid = ofp.OFP_VLAN_NONE
146 match.vlan_pcp = 0
147 match.wildcards &= ~ofp.OFPFW_DL_VLAN
148 match.wildcards &= ~ofp.OFPFW_DL_VLAN_PCP
149
150 if ip:
151 match.ipv4_src = parse_ip(ip.src)
152 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
153 match.ipv4_dst = parse_ip(ip.dst)
154 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
155 match.ip_dscp = ip.tos
156 match.wildcards &= ~ofp.OFPFW_NW_TOS
157
158 if tcp:
159 match.ip_proto = 6
160 match.wildcards &= ~ofp.OFPFW_NW_PROTO
161 elif not tcp and udp:
162 tcp = udp
163 match.ip_proto = 17
164 match.wildcards &= ~ofp.OFPFW_NW_PROTO
165
166 if tcp:
167 match.tcp_src = tcp.sport
168 match.wildcards &= ~ofp.OFPFW_TP_SRC
169 match.tcp_dst = tcp.dport
170 match.wildcards &= ~ofp.OFPFW_TP_DST
171
172 if icmp:
173 match.ip_proto = 1
174 match.tcp_src = icmp.type
175 match.tcp_dst = icmp.code
176 match.wildcards &= ~ofp.OFPFW_NW_PROTO
177
178 if arp:
179 match.ip_proto = arp.op
180 match.wildcards &= ~ofp.OFPFW_NW_PROTO
181 match.ipv4_src = parse_ip(arp.psrc)
182 match.wildcards &= ~ofp.OFPFW_NW_SRC_MASK
183 match.ipv4_dst = parse_ip(arp.pdst)
184 match.wildcards &= ~ofp.OFPFW_NW_DST_MASK
185
186 return match
187
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
202def 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
209def 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))
218 match.oxm_list.append(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT|layer.vlan))
219 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