Skip to content
Snippets Groups Projects
Commit 81d5a556 authored by Henrique Rodrigues's avatar Henrique Rodrigues
Browse files

Makes Floodlight's Static Flow Pusher compliant with RFC 2616

Floodlight's Static Flow Pusher REST interface expects "posted data"
for DELETE requests. It uses this posted data to identify which entry
should be deleted. However, the RFC 2616 states that resources
to be deleted using the DELETE method should be identified by a
URI (not by posted data):
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7

This is a problem when you try to use a spec-compliant framework to
communicate with the REST interface provided by StaticFlowEntryPusher
module. The Restlet framework, that is being used in the implementation
of the module itself, is an example. It prevents you from posting data with
a DELETE method, according to what is specified in the HTTP1.1 RFC.
parent 95ba314b
No related branches found
No related tags found
No related merge requests found
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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.
**/
package net.floodlightcontroller.staticflowentry.web;
import java.io.IOException;
import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.annotations.LogMessageCategory;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.staticflowentry.StaticFlowEntries;
import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
import net.floodlightcontroller.storage.IStorageSourceService;
/**
* Deletes a static flow entry to the storage source
* @author alexreimers
*
* @author Henrique Rodrigues <hsr@cs.ucsd.edu>
* Contributed with splitting StaticFlowEntryPusherResource into
* two, to make Floodlight's StaticFlowEntryPusher restlet resource
* REST compliant.
*
*/
@LogMessageCategory("Static Flow Pusher Delete Resource")
public class StaticFlowEntryDeleteResource extends ServerResource {
protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryDeleteResource.class);
@Post
@LogMessageDoc(level="ERROR",
message="Error deleting flow mod request: {request}",
explanation="An invalid delete request was sent to static flow pusher",
recommendation="Fix the format of the static flow mod request")
public String del(String fmJson) {
IStorageSourceService storageSource =
(IStorageSourceService)getContext().getAttributes().
get(IStorageSourceService.class.getCanonicalName());
String fmName = null;
if (fmJson == null) {
return "{\"status\" : \"Error! No data posted.\"}";
}
try {
fmName = StaticFlowEntries.getEntryNameFromJson(fmJson);
if (fmName == null) {
return "{\"status\" : \"Error deleting entry, no name provided\"}";
}
} catch (IOException e) {
log.error("Error deleting flow mod request: " + fmJson, e);
return "{\"status\" : \"Error deleting entry, see log for details\"}";
}
storageSource.deleteRowAsync(StaticFlowEntryPusher.TABLE_NAME, fmName);
return "{\"status\" : \"Entry " + fmName + " deleted\"}";
}
}
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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.
**/
package net.floodlightcontroller.staticflowentry.web;
import java.io.IOException;
import java.util.Map;
import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.annotations.LogMessageCategory;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.staticflowentry.StaticFlowEntries;
import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
import net.floodlightcontroller.storage.IStorageSourceService;
/**
* Pushes a static flow entry to the storage source
* @author alexreimers
*
* @author Henrique Rodrigues <hsr@cs.ucsd.edu>
* Contributed with splitting StaticFlowEntryPusherResource into
* two, to make Floodlight's StaticFlowEntryPusher restlet resource
* REST compliant.
*
*/
@LogMessageCategory("Static Flow Pusher")
public class StaticFlowEntryStoreResource extends ServerResource {
protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryStoreResource.class);
/**
* Checks to see if the user matches IP information without
* checking for the correct ether-type (2048).
* @param rows The Map that is a string representation of
* the static flow.
* @reutrn True if they checked the ether-type, false otherwise
*/
private boolean checkMatchIp(Map<String, Object> rows) {
boolean matchEther = false;
String val = (String) rows.get(StaticFlowEntryPusher.COLUMN_DL_TYPE);
if (val != null) {
int type = 0;
// check both hex and decimal
if (val.startsWith("0x")) {
type = Integer.parseInt(val.substring(2), 16);
} else {
try {
type = Integer.parseInt(val);
} catch (NumberFormatException e) { /* fail silently */}
}
if (type == 2048) matchEther = true;
}
if ((rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_DST) ||
rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_SRC) ||
rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_PROTO) ||
rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_TOS)) &&
(matchEther == false))
return false;
return true;
}
/**
* Takes a Static Flow Pusher string in JSON format and parses it into
* our database schema then pushes it to the database.
* @param fmJson The Static Flow Pusher entry in JSON format.
* @return A string status message
*/
@Post
@LogMessageDoc(level="ERROR",
message="Error parsing push flow mod request: {request}",
explanation="An invalid request was sent to static flow pusher",
recommendation="Fix the format of the static flow mod request")
public String store(String fmJson) {
IStorageSourceService storageSource =
(IStorageSourceService)getContext().getAttributes().
get(IStorageSourceService.class.getCanonicalName());
Map<String, Object> rowValues;
try {
rowValues = StaticFlowEntries.jsonToStorageEntry(fmJson);
String status = null;
if (!checkMatchIp(rowValues)) {
status = "Warning! Pushing a static flow entry that matches IP " +
"fields without matching for IP payload (ether-type 2048) will cause " +
"the switch to wildcard higher level fields.";
log.error(status);
} else {
status = "Entry pushed";
}
storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, rowValues);
return ("{\"status\" : \"" + status + "\"}");
} catch (IOException e) {
log.error("Error parsing push flow mod request: " + fmJson, e);
return "{\"status\" : \"Error! Could not parse flod mod, see log for details.\"}";
}
}
}
......@@ -30,6 +30,8 @@ public class StaticFlowEntryWebRoutable implements RestletRoutable {
public Restlet getRestlet(Context context) {
Router router = new Router(context);
router.attach("/json", StaticFlowEntryPusherResource.class);
router.attach("/json/store", StaticFlowEntryStoreResource.class);
router.attach("/json/delete", StaticFlowEntryDeleteResource.class);
router.attach("/clear/{switch}/json", ClearStaticFlowEntriesResource.class);
router.attach("/list/{switch}/json", ListStaticFlowEntriesResource.class);
return router;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment