blob: f1b72cfd838da31c8d54538c52ce1997fc34eacb [file] [log] [blame]
#
# Copyright 2016-present Ciena Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import json
import requests
import os,sys,time
from nose.tools import *
from scapy.all import *
from OnosCtrl import OnosCtrl
import fcntl, socket, struct
def get_mac(iface = 'ovsbr0', pad = 4):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15]))
except:
info = ['0'] * 24
s.close()
sep = ''
if pad == 0:
sep = ':'
return '0'*pad + sep.join(['%02x' %ord(char) for char in info[18:24]])
class OnosFlowCtrl:
auth = ('karaf', 'karaf')
controller = os.getenv('ONOS_CONTROLLER_IP') or 'localhost'
cfg_url = 'http://%s:8181/onos/v1/flows/' %(controller)
def __init__( self,
deviceId,
appId=0,
ingressPort="",
egressPort="",
ethType="",
ethSrc="",
ethDst="",
vlan="",
ipProto="",
ipSrc=(),
ipDst=(),
tcpSrc="",
tcpDst="",
udpDst="",
udpSrc="",
mpls="",
dscp="",
icmpv4_type="",
icmpv4_code="",
icmpv6_type="",
icmpv6_code="",
ipv6flow_label="",
ecn="",
ipv6_target="",
ipv6_sll="",
ipv6_tll="",
ipv6_extension=""):
self.deviceId = deviceId
self.appId = appId
self.ingressPort = ingressPort
self.egressPort = egressPort
self.ethType = ethType
self.ethSrc = ethSrc
self.ethDst = ethDst
self.vlan = vlan
self.ipProto = ipProto
self.ipSrc = ipSrc
self.ipDst = ipDst
self.tcpSrc = tcpSrc
self.tcpDst = tcpDst
self.udpDst = udpDst
self.udpSrc = udpSrc
self.mpls = mpls
self.dscp = dscp
self.icmpv4_type = icmpv4_type
self.icmpv4_code = icmpv4_code
self.icmpv6_type = icmpv6_type
self.icmpv6_code = icmpv6_code
self.ipv6flow_label = ipv6flow_label
self.ecn = ecn
self.ipv6_target = ipv6_target
self.ipv6_sll = ipv6_sll
self.ipv6_tll = ipv6_tll
self.ipv6_extension = ipv6_extension
@classmethod
def get_flows(cls, device_id):
return OnosCtrl.get_flows(device_id)
def addFlow(self):
"""
Description:
Creates a single flow in the specified device
Required:
* deviceId: id of the device
Optional:
* ingressPort: port ingress device
* egressPort: port of egress device
* ethType: specify ethType
* ethSrc: specify ethSrc ( i.e. src mac addr )
* ethDst: specify ethDst ( i.e. dst mac addr )
* ipProto: specify ip protocol
* ipSrc: specify ip source address with mask eg. ip#/24
as a tuple (type, ip#)
* ipDst: specify ip destination address eg. ip#/24
as a tuple (type, ip#)
* tcpSrc: specify tcp source port
* tcpDst: specify tcp destination port
Returns:
True for successful requests;
False for failure/error on requests
"""
flowJson = { "priority":100,
"isPermanent":"true",
"timeout":0,
"deviceId":self.deviceId,
"treatment":{"instructions":[]},
"selector": {"criteria":[]}}
if self.appId:
flowJson[ "appId" ] = self.appId
if self.egressPort:
flowJson[ 'treatment' ][ 'instructions' ].append( {
"type":"OUTPUT",
"port":self.egressPort } )
if self.ingressPort:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"IN_PORT",
"port":self.ingressPort } )
if self.ethType:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"ETH_TYPE",
"ethType":self.ethType } )
if self.ethSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"ETH_SRC",
"mac":self.ethSrc } )
if self.ethDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"ETH_DST",
"mac":self.ethDst } )
if self.vlan:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"VLAN_VID",
"vlanId":self.vlan } )
if self.mpls:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"MPLS_LABEL",
"label":self.mpls } )
if self.ipSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":self.ipSrc[0],
"ip":self.ipSrc[1] } )
if self.ipDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":self.ipDst[0],
"ip":self.ipDst[1] } )
if self.tcpSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"TCP_SRC",
"tcpPort": self.tcpSrc } )
if self.tcpDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"TCP_DST",
"tcpPort": self.tcpDst } )
if self.udpSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"UDP_SRC",
"udpPort": self.udpSrc } )
if self.udpDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"UDP_DST",
"udpPort": self.udpDst } )
if self.ipProto:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"IP_PROTO",
"protocol": self.ipProto } )
if self.dscp:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"IP_DSCP",
"ipDscp": self.dscp } )
if self.icmpv4_type:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'ICMPV4_TYPE',
"icmpType":self.icmpv4_type } )
if self.icmpv6_type:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'ICMPV6_TYPE',
"icmpv6Type":self.icmpv6_type } )
if self.icmpv4_code:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'ICMPV4_CODE',
"icmpCode": self.icmpv4_code } )
if self.icmpv6_code:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'ICMPV6_CODE',
"icmpv6Code": self.icmpv6_code } )
if self.ipv6flow_label:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'IPV6_FLABEL',
"flowLabel": self.ipv6flow_label } )
if self.ecn:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"IP_ECN",
"ipEcn": self.ecn } )
if self.ipv6_target:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'IPV6_ND_TARGET',
"targetAddress": self.ipv6_target } )
if self.ipv6_sll:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'IPV6_ND_SLL',
"mac": self.ipv6_sll } )
if self.ipv6_tll:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'IPV6_ND_TLL',
"mac": self.ipv6_tll } )
if self.ipv6_extension:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":'IPV6_EXTHDR',
"exthdrFlags": self.ipv6_extension } )
return self.sendFlow( deviceId=self.deviceId, flowJson=flowJson)
def removeFlow(self, deviceId, flowId):
"""
Description:
Remove specific device flow
Required:
str deviceId - id of the device
str flowId - id of the flow
Return:
Returns True if successfully deletes flows, otherwise False
"""
# NOTE: REST url requires the intent id to be in decimal form
query = self.cfg_url + str( deviceId ) + '/' + str( int( flowId ) )
response = requests.delete(query, auth = self.auth)
if response:
if 200 <= response.status_code <= 299:
return True
else:
return False
return True
def findFlow(self, deviceId, **criterias):
flows = self.get_flows(deviceId)
match_keys = criterias.keys()
matches = len(match_keys)
num_matched = 0
for f in flows:
criteria = f['selector']['criteria']
for c in criteria:
if c['type'] not in match_keys:
continue
match_key, match_val = criterias.get(c['type'])
val = c[match_key]
if val == match_val:
num_matched += 1
if num_matched == matches:
return f['id']
return None
def sendFlow(self, deviceId, flowJson):
"""
Description:
Sends a single flow to the specified device. This function exists
so you can bypass the addFLow driver and send your own custom flow.
Required:
* The flow in json
* the device id to add the flow to
Returns:
True for successful requests
False for error on requests;
"""
url = self.cfg_url + str(deviceId)
response = requests.post(url, auth = self.auth, data = json.dumps(flowJson) )
if response.ok:
if response.status_code in [200, 201]:
log.info('Successfully POSTED flow for device %s' %str(deviceId))
return True
else:
log.info('Post flow for device %s failed with status %d' %(str(deviceId),
response.status_code))
return False
else:
log.error('Flow post request returned with status %d' %response.status_code)
return False