blob: 2810817c49591b67c3b236c6bd7657ce8021620a [file] [log] [blame]
ChetanGaonker720ea612016-06-21 17:54:25 -07001#
Chetan Gaonkercfcce782016-05-10 10:10:42 -07002# Copyright 2016-present Ciena Corporation
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
ChetanGaonker720ea612016-06-21 17:54:25 -07007#
Chetan Gaonkercfcce782016-05-10 10:10:42 -07008# http://www.apache.org/licenses/LICENSE-2.0
ChetanGaonker720ea612016-06-21 17:54:25 -07009#
Chetan Gaonkercfcce782016-05-10 10:10:42 -070010# 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#
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070016import json
17import requests
18import os,sys,time
19from nose.tools import *
20from scapy.all import *
21from OnosCtrl import OnosCtrl
Chetan Gaonker3533faa2016-04-25 17:50:14 -070022import fcntl, socket, struct
23
24def get_mac(iface = 'ovsbr0', pad = 4):
25 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Chetan Gaonkerfb3cb5e2016-05-06 11:55:44 -070026 try:
27 info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15]))
28 except:
29 info = ['0'] * 24
Chetan Gaonker3533faa2016-04-25 17:50:14 -070030 return '0'*pad + ''.join(['%02x' %ord(char) for char in info[18:24]])
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070031
32class OnosFlowCtrl:
33
34 auth = ('karaf', 'karaf')
35 controller = os.getenv('ONOS_CONTROLLER_IP') or 'localhost'
36 cfg_url = 'http://%s:8181/onos/v1/flows/' %(controller)
ChetanGaonker720ea612016-06-21 17:54:25 -070037
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070038 def __init__( self,
39 deviceId,
40 appId=0,
41 ingressPort="",
42 egressPort="",
43 ethType="",
44 ethSrc="",
45 ethDst="",
46 vlan="",
47 ipProto="",
48 ipSrc=(),
49 ipDst=(),
50 tcpSrc="",
51 tcpDst="",
52 udpDst="",
53 udpSrc="",
ChetanGaonker720ea612016-06-21 17:54:25 -070054 mpls="",
55 dscp="",
56 icmpv4_type="",
57 icmpv4_code="",
58 icmpv6_type="",
59 icmpv6_code="",
60 ipv6flow_label="",
61 ecn="",
62 ipv6_target="",
63 ipv6_sll="",
64 ipv6_tll="",
65 ipv6_extension=""):
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070066 self.deviceId = deviceId
67 self.appId = appId
68 self.ingressPort = ingressPort
69 self.egressPort = egressPort
70 self.ethType = ethType
71 self.ethSrc = ethSrc
72 self.ethDst = ethDst
73 self.vlan = vlan
74 self.ipProto = ipProto
75 self.ipSrc = ipSrc
76 self.ipDst = ipDst
77 self.tcpSrc = tcpSrc
78 self.tcpDst = tcpDst
79 self.udpDst = udpDst
80 self.udpSrc = udpSrc
81 self.mpls = mpls
ChetanGaonker720ea612016-06-21 17:54:25 -070082 self.dscp = dscp
83 self.icmpv4_type = icmpv4_type
84 self.icmpv4_code = icmpv4_code
85 self.icmpv6_type = icmpv6_type
86 self.icmpv6_code = icmpv6_code
87 self.ipv6flow_label = ipv6flow_label
88 self.ecn = ecn
89 self.ipv6_target = ipv6_target
90 self.ipv6_sll = ipv6_sll
91 self.ipv6_tll = ipv6_tll
92 self.ipv6_extension = ipv6_extension
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070093
94 @classmethod
95 def get_flows(cls, device_id):
96 return OnosCtrl.get_flows(device_id)
97
98 def addFlow(self):
99 """
100 Description:
101 Creates a single flow in the specified device
102 Required:
103 * deviceId: id of the device
104 Optional:
105 * ingressPort: port ingress device
106 * egressPort: port of egress device
107 * ethType: specify ethType
108 * ethSrc: specify ethSrc ( i.e. src mac addr )
109 * ethDst: specify ethDst ( i.e. dst mac addr )
110 * ipProto: specify ip protocol
111 * ipSrc: specify ip source address with mask eg. ip#/24
112 as a tuple (type, ip#)
113 * ipDst: specify ip destination address eg. ip#/24
114 as a tuple (type, ip#)
115 * tcpSrc: specify tcp source port
116 * tcpDst: specify tcp destination port
117 Returns:
118 True for successful requests;
119 False for failure/error on requests
120 """
121 flowJson = { "priority":100,
122 "isPermanent":"true",
123 "timeout":0,
124 "deviceId":self.deviceId,
125 "treatment":{"instructions":[]},
126 "selector": {"criteria":[]}}
127 if self.appId:
128 flowJson[ "appId" ] = self.appId
ChetanGaonker720ea612016-06-21 17:54:25 -0700129
Chetan Gaonker7ab338c2016-04-15 17:23:17 -0700130 if self.egressPort:
131 flowJson[ 'treatment' ][ 'instructions' ].append( {
132 "type":"OUTPUT",
133 "port":self.egressPort } )
134 if self.ingressPort:
135 flowJson[ 'selector' ][ 'criteria' ].append( {
136 "type":"IN_PORT",
137 "port":self.ingressPort } )
138 if self.ethType:
139 flowJson[ 'selector' ][ 'criteria' ].append( {
140 "type":"ETH_TYPE",
141 "ethType":self.ethType } )
142 if self.ethSrc:
143 flowJson[ 'selector' ][ 'criteria' ].append( {
144 "type":"ETH_SRC",
145 "mac":self.ethSrc } )
146 if self.ethDst:
147 flowJson[ 'selector' ][ 'criteria' ].append( {
148 "type":"ETH_DST",
149 "mac":self.ethDst } )
150 if self.vlan:
151 flowJson[ 'selector' ][ 'criteria' ].append( {
152 "type":"VLAN_VID",
153 "vlanId":self.vlan } )
154 if self.mpls:
155 flowJson[ 'selector' ][ 'criteria' ].append( {
156 "type":"MPLS_LABEL",
157 "label":self.mpls } )
158 if self.ipSrc:
159 flowJson[ 'selector' ][ 'criteria' ].append( {
160 "type":self.ipSrc[0],
161 "ip":self.ipSrc[1] } )
162 if self.ipDst:
163 flowJson[ 'selector' ][ 'criteria' ].append( {
164 "type":self.ipDst[0],
165 "ip":self.ipDst[1] } )
166 if self.tcpSrc:
167 flowJson[ 'selector' ][ 'criteria' ].append( {
168 "type":"TCP_SRC",
169 "tcpPort": self.tcpSrc } )
170 if self.tcpDst:
171 flowJson[ 'selector' ][ 'criteria' ].append( {
172 "type":"TCP_DST",
173 "tcpPort": self.tcpDst } )
174 if self.udpSrc:
175 flowJson[ 'selector' ][ 'criteria' ].append( {
176 "type":"UDP_SRC",
177 "udpPort": self.udpSrc } )
178 if self.udpDst:
179 flowJson[ 'selector' ][ 'criteria' ].append( {
180 "type":"UDP_DST",
181 "udpPort": self.udpDst } )
182 if self.ipProto:
183 flowJson[ 'selector' ][ 'criteria' ].append( {
184 "type":"IP_PROTO",
185 "protocol": self.ipProto } )
ChetanGaonker720ea612016-06-21 17:54:25 -0700186 if self.dscp:
187 flowJson[ 'selector' ][ 'criteria' ].append( {
188 "type":"IP_DSCP",
189 "ipDscp": self.dscp } )
190
191 if self.icmpv4_type:
192 flowJson[ 'selector' ][ 'criteria' ].append( {
193 "type":'ICMPV4_TYPE',
194 "icmpType":self.icmpv4_type } )
195
196 if self.icmpv6_type:
197 flowJson[ 'selector' ][ 'criteria' ].append( {
198 "type":'ICMPV6_TYPE',
199 "icmpv6Type":self.icmpv6_type } )
200
201 if self.icmpv4_code:
202 flowJson[ 'selector' ][ 'criteria' ].append( {
203 "type":'ICMPV4_CODE',
204 "icmpCode": self.icmpv4_code } )
205
206 if self.icmpv6_code:
207 flowJson[ 'selector' ][ 'criteria' ].append( {
208 "type":'ICMPV6_CODE',
209 "icmpv6Code": self.icmpv6_code } )
210
211 if self.ipv6flow_label:
212 flowJson[ 'selector' ][ 'criteria' ].append( {
213 "type":'IPV6_FLABEL',
214 "flowLabel": self.ipv6flow_label } )
215
216 if self.ecn:
217 flowJson[ 'selector' ][ 'criteria' ].append( {
218 "type":"IP_ECN",
219 "ipEcn": self.ecn } )
220
221 if self.ipv6_target:
222 flowJson[ 'selector' ][ 'criteria' ].append( {
223 "type":'IPV6_ND_TARGET',
224 "targetAddress": self.ipv6_target } )
225
226 if self.ipv6_sll:
227 flowJson[ 'selector' ][ 'criteria' ].append( {
228 "type":'IPV6_ND_SLL',
229 "mac": self.ipv6_sll } )
230
231 if self.ipv6_tll:
232 flowJson[ 'selector' ][ 'criteria' ].append( {
233 "type":'IPV6_ND_TLL',
234 "mac": self.ipv6_tll } )
235
236
237 if self.ipv6_extension:
238 flowJson[ 'selector' ][ 'criteria' ].append( {
239 "type":'IPV6_EXTHDR',
240 "exthdrFlags": self.ipv6_extension } )
241
242
243
Chetan Gaonker7ab338c2016-04-15 17:23:17 -0700244
245 return self.sendFlow( deviceId=self.deviceId, flowJson=flowJson)
246
247 def removeFlow(self, deviceId, flowId):
248 """
249 Description:
250 Remove specific device flow
251 Required:
252 str deviceId - id of the device
253 str flowId - id of the flow
254 Return:
255 Returns True if successfully deletes flows, otherwise False
256 """
257 # NOTE: REST url requires the intent id to be in decimal form
258 query = self.cfg_url + str( deviceId ) + '/' + str( int( flowId ) )
259 response = requests.delete(query, auth = self.auth)
260 if response:
261 if 200 <= response.status_code <= 299:
262 return True
263 else:
264 return False
265
266 return True
267
268 def findFlow(self, deviceId, **criterias):
269 flows = self.get_flows(deviceId)
270 match_keys = criterias.keys()
271 matches = len(match_keys)
272 num_matched = 0
273 for f in flows:
274 criteria = f['selector']['criteria']
275 for c in criteria:
276 if c['type'] not in match_keys:
277 continue
278 match_key, match_val = criterias.get(c['type'])
279 val = c[match_key]
280 if val == match_val:
281 num_matched += 1
ChetanGaonker720ea612016-06-21 17:54:25 -0700282 if num_matched == matches:
Chetan Gaonker7ab338c2016-04-15 17:23:17 -0700283 return f['id']
284 return None
ChetanGaonker720ea612016-06-21 17:54:25 -0700285
Chetan Gaonker7ab338c2016-04-15 17:23:17 -0700286 def sendFlow(self, deviceId, flowJson):
287 """
288 Description:
289 Sends a single flow to the specified device. This function exists
290 so you can bypass the addFLow driver and send your own custom flow.
291 Required:
292 * The flow in json
293 * the device id to add the flow to
294 Returns:
295 True for successful requests
296 False for error on requests;
297 """
298 url = self.cfg_url + str(deviceId)
299 response = requests.post(url, auth = self.auth, data = json.dumps(flowJson) )
300 if response.ok:
301 if response.status_code in [200, 201]:
302 log.info('Successfully POSTED flow for device %s' %str(deviceId))
303 return True
304 else:
ChetanGaonker720ea612016-06-21 17:54:25 -0700305 log.info('Post flow for device %s failed with status %d' %(str(deviceId),
Chetan Gaonker7ab338c2016-04-15 17:23:17 -0700306 response.status_code))
307 return False
308 else:
309 log.error('Flow post request returned with status %d' %response.status_code)
310
311 return False
ChetanGaonker720ea612016-06-21 17:54:25 -0700312