/** * 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.staticentry.web; import java.io.IOException; import java.util.Map; import org.projectfloodlight.openflow.protocol.OFInstructionType; import org.projectfloodlight.openflow.protocol.match.MatchFields; import org.projectfloodlight.openflow.types.DatapathId; import org.restlet.resource.Delete; import org.restlet.resource.Post; import org.restlet.resource.ServerResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.floodlightcontroller.staticentry.StaticEntries; import net.floodlightcontroller.staticentry.StaticEntryPusher; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.util.MatchUtils; /** * Pushes a static flow entry to the storage source */ public class StaticEntryPusherResource extends ServerResource { protected static Logger log = LoggerFactory.getLogger(StaticEntryPusherResource.class); /** * Validates if all the mandatory fields are set properly while adding an IPv6 flow * @param Map containing the fields of the flow * @return state indicating whether a flow is valid or not */ private int checkFlow(Map<String, Object> rows) { //Declaring & Initializing flags int state = 0; boolean dl_type = false; boolean nw_proto = false; boolean nw_layer = false; boolean icmp6_type = false; boolean icmp6_code = false; boolean nd_target = false; boolean nd_sll = false; boolean nd_tll = false; boolean ip6 = false; boolean ip4 = false; int eth_type = -1; int nw_protocol = -1; int icmp_type = -1; //Determine the dl_type if set if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))) { if (((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))).startsWith("0x")) { eth_type = Integer.parseInt(((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))).replaceFirst("0x", ""), 16); dl_type = true; } else { eth_type = Integer.parseInt((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))); dl_type = true; } if (eth_type == 0x86dd) { /* or 34525 */ ip6 = true; dl_type = true; } else if (eth_type == 0x800 || /* or 2048 */ eth_type == 0x806 || /* or 2054 */ eth_type == 0x8035) { /* or 32821*/ ip4 = true; dl_type = true; } //else { // state = 2; // return state; //} } final String ipv4_dst_str = StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_DST); final String ipv4_src_str = StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_SRC); if (rows.containsKey(ipv4_dst_str) || rows.containsKey(ipv4_src_str)) { nw_layer = true; ip4 = true; } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV4_CODE)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV4_TYPE)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_THA)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_SHA)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_TPA)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_SPA)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_OP))) { ip4 = true; } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_FLABEL)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_SRC)) || rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_DST))) { nw_layer = true; ip6 = true; } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO))) { nw_proto = true; if (((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO))).startsWith("0x")) { nw_protocol = Integer.parseInt(((String) rows.get(StaticEntryPusher .matchFieldToColumnName(MatchFields.IP_PROTO))).replaceFirst("0x", ""), 16); } else { nw_protocol = Integer.parseInt((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO))); } } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_CODE))) { icmp6_code = true; ip6 = true; } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_TYPE))) { icmp6_type = true; ip6 = true; if (((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_TYPE))).startsWith("0x")) { icmp_type = Integer.parseInt(((String) rows.get(StaticEntryPusher .matchFieldToColumnName(MatchFields.ICMPV6_TYPE))).replaceFirst("0x", ""), 16); } else { icmp_type = Integer.parseInt((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_TYPE))); } } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_ND_SLL))) { nd_sll = true; ip6 = true; } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_ND_TLL))) { nd_tll = true; ip6 = true; } if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_ND_TARGET))) { nd_target = true; ip6 = true; } if (nw_layer == true || nw_proto == true) { if (dl_type == true) { if (!(ip4 == true || ip6 == true)) { //invalid dl_type state = 2; return state; } } else { //dl_type not set state = 1; return state; } } if (icmp6_type == true || icmp6_code == true ) { if (nw_proto == true) { if (nw_protocol != 0x3A) { /* or 58 */ //invalid nw_proto state = 4; return state; } } else { //nw_proto not set state = 3; return state; } } if (nd_sll == true || nd_tll == true || nd_target == true) { if (icmp6_type == true) { //icmp_type must be set to 135/136 to set ipv6_nd_target if (nd_target == true) { if (!(icmp_type == 135 || icmp_type == 136)) { /* or 0x87 / 0x88 */ //invalid icmp6_type state = 6; return state; } } //icmp_type must be set to 136 to set ipv6_nd_tll else if (nd_tll == true) { if (!(icmp_type == 136)) { //invalid icmp6_type state = 6; return state; } } //icmp_type must be set to 135 to set ipv6_nd_sll else if (nd_sll == true) { if (!(icmp_type == 135)) { //invalid icmp6_type state = 6; return state; } } } else { //icmp6_type not set state = 5; return state; } } int result = checkActions(rows); if ((ip4 == true && ip6 == true) || (result == -1) || (result == 1 && ip6 == true) || (result == 2 && ip4 == true)) { //ipv4 & ipv6 conflict state = 7; return state; } if (rows.containsKey(StaticEntryPusher.Columns.COLUMN_SWITCH)) { try { DatapathId.of((String) rows.get(StaticEntryPusher.Columns.COLUMN_SWITCH)); } catch (Exception e) { state = 9; } } else { state = 8; } return state; } /** * Validates actions/instructions * * -1 --> IPv4/IPv6 conflict * 0 --> no IPv4 or IPv6 actions * 1 --> IPv4 only actions * 2 --> IPv6 only actions * * @param Map containing the fields of the flow * @return state indicating whether a flow is valid or not */ public static int checkActions(Map<String, Object> entry) { boolean ip6 = false; boolean ip4 = false; String actions = null; if (entry.containsKey(StaticEntryPusher.Columns.COLUMN_ACTIONS) || entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS)) || entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS))) { if (entry.containsKey(StaticEntryPusher.Columns.COLUMN_ACTIONS)) { actions = (String) entry.get(StaticEntryPusher.Columns.COLUMN_ACTIONS); } else if (entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS))) { actions = (String) entry.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS)); } else if (entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS))) { actions = (String) entry.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS)); } if (actions.contains(MatchUtils.STR_ICMPV6_CODE) || actions.contains(MatchUtils.STR_ICMPV6_TYPE) || actions.contains(MatchUtils.STR_IPV6_DST) || actions.contains(MatchUtils.STR_IPV6_SRC) || actions.contains(MatchUtils.STR_IPV6_FLOW_LABEL) || actions.contains(MatchUtils.STR_IPV6_ND_SLL) || actions.contains(MatchUtils.STR_IPV6_ND_TARGET) || actions.contains(MatchUtils.STR_IPV6_ND_TLL)) { ip6 = true; } if (actions.contains(MatchUtils.STR_NW_SRC) || actions.contains(MatchUtils.STR_NW_DST) || actions.contains(MatchUtils.STR_ARP_OPCODE) || actions.contains(MatchUtils.STR_ARP_SHA) || actions.contains(MatchUtils.STR_ARP_DHA) || actions.contains(MatchUtils.STR_ARP_SPA) || actions.contains(MatchUtils.STR_ARP_DPA) || actions.contains(MatchUtils.STR_ICMP_CODE) || actions.contains(MatchUtils.STR_ICMP_TYPE)) { ip4 = true; } } if (ip6 == false && ip4 == false) { return 0; // no actions involving ipv4 or ipv6 } else if (ip6 == false && ip4 == true) { return 1; //ipv4 } else if (ip6 == true && ip4 == false) { return 2; //ipv6 } else { return -1; // conflict of ipv4 and ipv6 actions } } /** * Takes a Static Entry 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 public String store(String json) { IStorageSourceService storageSource = (IStorageSourceService)getContext().getAttributes(). get(IStorageSourceService.class.getCanonicalName()); Map<String, Object> rowValues; try { rowValues = StaticEntries.jsonToStorageEntry(json); String status = null; int state = checkFlow(rowValues); if (state == 1) { status = "Warning! Must specify eth_type of IPv4/IPv6 to " + "match on IPv4/IPv6 fields! The flow has been discarded."; log.error(status); } else if (state == 2) { status = "Warning! eth_type not recognized! The flow has been discarded."; log.error(status); } else if (state == 3) { status = "Warning! Must specify ip_proto to match! The flow has been discarded."; log.error(status); } else if (state == 4) { status = "Warning! ip_proto invalid! The flow has been discarded."; log.error(status); } else if (state == 5) { status = "Warning! Must specify icmp6_type to match! The flow has been discarded."; log.error(status); } else if (state == 6) { status = "Warning! icmp6_type invalid! The flow has been discarded."; log.error(status); } else if (state == 7) { status = "Warning! IPv4 & IPv6 fields cannot be specified in the same flow! The flow has been discarded."; log.error(status); } else if (state == 8) { status = "Warning! Must specify switch DPID in flow. The flow has been discarded."; log.error(status); } else if (state == 9) { status = "Warning! Switch DPID invalid! The flow has been discarded."; log.error(status); } else if (state == 0) { status = "Entry pushed"; storageSource.insertRowAsync(StaticEntryPusher.TABLE_NAME, rowValues); } return ("{\"status\" : \"" + status + "\"}"); } catch (IOException e) { log.error("Error parsing push flow mod request: " + json, e); return "{\"status\" : \"Error! Could not parse flow mod, see log for details.\"}"; } } @Delete public String del(String json) { IStorageSourceService storageSource = (IStorageSourceService)getContext().getAttributes(). get(IStorageSourceService.class.getCanonicalName()); String fmName = null; if (json == null) { return "{\"status\" : \"Error! No data posted.\"}"; } try { fmName = StaticEntries.getEntryNameFromJson(json); if (fmName == null) { return "{\"status\" : \"Error deleting entry, no name provided\"}"; } } catch (IOException e) { log.error("Error deleting flow mod request: " + json, e); return "{\"status\" : \"Error deleting entry, see log for details\"}"; } storageSource.deleteRowAsync(StaticEntryPusher.TABLE_NAME, fmName); return "{\"status\" : \"Entry " + fmName + " deleted\"}"; } }