package net.floodlightcontroller.qos; /** * Copyright 2012 Marist College, New York * Author Ryan Wallner (ryan.wallner1@marist.edu) * * 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. * * Implementation adopted from Firewall * **/ import java.io.IOException; import java.util.Iterator; import java.util.List; import net.floodlightcontroller.packet.IPv4; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.codehaus.jackson.map.MappingJsonFactory; import org.openflow.util.U16; import org.restlet.resource.Delete; import org.restlet.resource.Post; import org.restlet.resource.Get; import org.restlet.resource.ServerResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QoSPoliciesResource extends ServerResource { public static Logger logger = LoggerFactory.getLogger(QoSPoliciesResource.class); @Get("json") public Object handleRequest(){ IQoSService qos = (IQoSService)getContext().getAttributes(). get(IQoSService.class.getCanonicalName()); String status = null; if(qos.isEnabled()){ // gets the list of policies currently being implemented return qos.getPolicies(); } else{ status = "Please enable Quality of Service"; return ("{\"status\" : \"" + status + "\"}"); } } /** * Takes a QoS Policy Rule string in JSON format and parses it into * our firewall rule data structure, then adds it to the qos polcies storage. * @param fmJson The qos policy entry in JSON format. * @return A string status message */ @Post public String add(String qosJson) { IQoSService qos = (IQoSService)getContext().getAttributes(). get(IQoSService.class.getCanonicalName()); //dummy policy QoSPolicy policy; try{ policy = jsonToPolicy(qosJson); } catch(IOException e){ logger.error("Error Parsing Quality of Service Policy to JSON: {}, Error: {}", qosJson, e); e.printStackTrace(); return "{\"status\" : \"Error! Could not parse policy, see log for details.\"}"; } String status = null; if(checkIfPolicyExists(policy,qos.getPolicies())){ status = "Error!, This policy already exists!"; logger.error(status); } else{ //Only add if enabled ?needed? if(qos.isEnabled()){ /**NOTE: the check for how its added happens * inside addPolicy:(AROUND QoS.java:467)**/ if(policy.name == null){ return status = "Bad Policy, No Name";} else if(policy.service == null && policy.enqueueport != -1 && policy.queue != -1){ status = "Adding Policy: " + policy.name;//add service //basic checks on validity qos.addPolicy(policy); }else if(checkIfServiceExists(policy.service, qos.getServices()) && policy.enqueueport == -1 && policy.queue == -1){ status = "Adding Policy: " + policy.name;//add service //basic checks on validity qos.addPolicy(policy); }else{status = "Service Policy or a Queuing Policy not defined. Check if Service Exists";} } else{ status = "Please enable Quality of Service"; } } return ("{\"status\" : \"" + status + "\"}"); } /** * Deletes a policy * @param qosJson * @return status **/ @Delete public String delete(String qosJson) { IQoSService qos = (IQoSService)getContext().getAttributes(). get(IQoSService.class.getCanonicalName()); //dummy service QoSPolicy policy; try{ policy = jsonToPolicy(qosJson); } catch(IOException e){ logger.debug("Error Parsing QoS Policy to JSON: {}, Error: {}", qosJson, e); e.printStackTrace(); return "{\"status\" : \"Error! Could not parse policy, see log for details.\"}"; } String status = null; if(qos.isEnabled()){ boolean found = false; Iterator<QoSPolicy> sIter = qos.getPolicies().iterator(); while(sIter.hasNext()){ QoSPolicy p = sIter.next(); if(p.policyid == policy.policyid){ policy = p; //returned the entire policy found = true; break; } } if(!found){ status = "Error! Cannot delete a rule with this ID or NAME, does not exist."; logger.error(status); } else{ qos.deletePolicy(policy); status = "Type Of Service Service-ID: "+policy.policyid+" Deleted"; } } else{ status = "Please enable Quality of Service"; } return ("{\"status\" : \"" + status + "\"}"); } /** * Turns POST json data into policy * @param pJson * @return * @throws IOException */ public static QoSPolicy jsonToPolicy(String pJson) throws IOException{ QoSPolicy policy = new QoSPolicy(); //initialize needs json tools MappingJsonFactory jf = new MappingJsonFactory(); JsonParser jp; try{ jp = jf.createJsonParser(pJson); }catch(JsonParseException e){ throw new IOException(e); } JsonToken tkn = jp.getCurrentToken(); if(tkn != JsonToken.START_OBJECT){ jp.nextToken(); if(jp.getCurrentToken() != JsonToken.START_OBJECT){ logger.error("Did not recieve json start token, current " + "token is: {}",jp.getCurrentToken()); } } while(jp.nextToken() != JsonToken.END_OBJECT){ if(jp.getCurrentToken() != JsonToken.FIELD_NAME){ throw new IOException("FIELD_NAME expected"); } try{ String tmpS = jp.getCurrentName(); jp.nextToken(); /** may be worth: jsonText = jp.getText(); to avoid over * use of jp.getText() method call **/ //get current text of the FIELD_NAME logger.info("Current text is "+ jp.getText()); //debug for dev if(jp.getText().equals("")){ //back to beginning of loop continue; } if (tmpS == "policy-id"){ policy.policyid = Long.parseLong(jp.getText()); //logger.info("[JSON PARSER]Policy Name: {}" , jp.getText()); } if (tmpS == "name"){ policy.name = jp.getText(); //logger.info("[JSON PARSER]Policy Name: {}" , jp.getText()); } else if(tmpS == "protocol"){ // i.e "protocol": "6" policy.protocol = Byte.parseByte(jp.getText()); //logger.info("[JSON PARSER]Policy Protocol: {}", jp.getText()); } else if(tmpS == "eth-type"){ // i.e if "eth-type":"0x0800" if (jp.getText().startsWith("0x")) { policy.ethtype = U16.t(Integer.valueOf (jp.getText().replaceFirst("0x",""),16)); } //return the short value of number i.e 8 else{policy.ethtype = (short) Integer.parseInt(jp.getText());} //logger.info("[JSON PARSER]Policy Eth-type: {}", jp.getText()); } else if(tmpS == "ingress-port"){ policy.ingressport = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy Ingress-Port: {}", jp.getText()); } else if(tmpS == "ip-src"){ policy.ipsrc = IPv4.toIPv4Address(jp.getText()); //logger.info("[JSON PARSER]Policy IP-Src: {}", IPv4.fromIPv4Address(policy.ipsrc)); } else if(tmpS == "ip-dst"){ policy.ipdst = IPv4.toIPv4Address(jp.getText()); //logger.info("[JSON PARSER]Policy IP-Dst: {}", IPv4.fromIPv4Address(policy.ipdst)); } else if(tmpS == "tos"){ //This is so you can enter a binary number or a integer number. //It will be stored as a Byte try{ //Try to get binary number first Integer tmpInt = Integer.parseInt(jp.getText(),2); policy.tos = tmpInt.byteValue(); }catch(NumberFormatException e){ //logger.debug("Number entered was not binary, processing as int..."); //Must be entered as 0-64 Integer tmpInt = Integer.parseInt(jp.getText()); policy.tos = tmpInt.byteValue(); } //logger.info("[JSON PARSER]Policy TOS Bits: {}", jp.getText()); } else if(tmpS == "vlan-id"){ policy.vlanid = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy VLAN-ID: {}", jp.getText()); } else if(tmpS == "eth-src"){ policy.ethsrc = jp.getText(); //logger.info("[JSON PARSER]Policy Eth-src: {}", jp.getText()); } else if(tmpS == "eth-dst"){ policy.ethdst = jp.getText(); //logger.info("[JSON PARSER]Policy Eth-dst: {}", jp.getText()); } else if(tmpS == "src-port"){ policy.tcpudpsrcport = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy Src-Port: {}", jp.getText()); } else if(tmpS == "dst-port"){ policy.tcpudpdstport = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy Dst-Port: {}", jp.getText()); } //TODO morph this to use a String[] of Switches else if(tmpS == "sw"){ policy.sw = jp.getText(); //logger.info("[JSON PARSER]Policy Switch: {}", jp.getText()); } else if(tmpS == "queue"){ policy.queue = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy QUEUE: {}", jp.getText()); } else if(tmpS == "enqueue-port"){ policy.enqueueport = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy ENQUEUE-PORT: {}", jp.getText()); } else if(tmpS == "service"){ policy.service = jp.getText(); //logger.info("[JSON PARSER]Policy Service: {}", jp.getText()); } else if(tmpS == "priority"){ policy.priority = Short.parseShort(jp.getText()); //logger.info("[JSON PARSER]Policy Priority: {}", jp.getText()); } }catch(JsonParseException e){ logger.debug("Error getting current FIELD_NAME {}", e); }catch(IOException e){ logger.debug("Error procession Json {}", e); } } return policy; } /** * * @param policy * @param policies * @return */ private static boolean checkIfPolicyExists(QoSPolicy policy, List<QoSPolicy> policies) { Iterator<QoSPolicy> pIter = policies.iterator(); while(pIter.hasNext()){ QoSPolicy p = pIter.next(); if(policy.isSameAs(p) || policy.name.equals(p.name)){ return true; } } return false; } /** * Needed for when ToS Policies define a Service. * Needs to be checked. See Line 94 * @param service * @param services * @return */ private static boolean checkIfServiceExists(String service, List<QoSTypeOfService> services) { Iterator<QoSTypeOfService> sIter = services.iterator(); while(sIter.hasNext()){ QoSTypeOfService s = sIter.next(); if(s.name.equals(service)){ return true; } } return false; } }