/**
* Copyright 2013, Big Switch Networks, Inc.
*
* 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;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.floodlightcontroller.core.annotations.LogMessageCategory;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.core.util.AppCookie;
import net.floodlightcontroller.packet.IPv4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.MappingJsonFactory;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionDataLayerDestination;
import org.openflow.protocol.action.OFActionDataLayerSource;
import org.openflow.protocol.action.OFActionEnqueue;
import org.openflow.protocol.action.OFActionNetworkLayerDestination;
import org.openflow.protocol.action.OFActionNetworkLayerSource;
import org.openflow.protocol.action.OFActionNetworkTypeOfService;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.protocol.action.OFActionStripVirtualLan;
import org.openflow.protocol.action.OFActionTransportLayerDestination;
import org.openflow.protocol.action.OFActionTransportLayerSource;
import org.openflow.protocol.action.OFActionVirtualLanIdentifier;
import org.openflow.protocol.action.OFActionVirtualLanPriorityCodePoint;
import org.openflow.util.HexString;
/**
* Represents static flow entries to be maintained by the controller on the
* switches.
*/
@LogMessageCategory("Static Flow Pusher")
public class StaticFlowEntries {
protected static Logger log = LoggerFactory.getLogger(StaticFlowEntries.class);
private static class SubActionStruct {
OFAction action;
int len;
}
private static byte[] zeroMac = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
/**
* This function generates a random hash for the bottom half of the cookie
*
* @param fm
* @param userCookie
* @param name
* @return A cookie that encodes the application ID and a hash
*/
public static long computeEntryCookie(OFFlowMod fm, int userCookie, String name) {
// flow-specific hash is next 20 bits LOOK! who knows if this
int prime = 211;
int flowHash = 2311;
for (int i=0; i < name.length(); i++)
flowHash = flowHash * prime + (int)name.charAt(i);
return AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, flowHash);
}
/**
* Sets defaults for an OFFlowMod
* @param fm The OFFlowMod to set defaults for
* @param entryName The name of the entry. Used to compute the cookie.
*/
public static void initDefaultFlowMod(OFFlowMod fm, String entryName) {
fm.setIdleTimeout((short) 0); // infinite
fm.setHardTimeout((short) 0); // infinite
fm.setBufferId(OFPacketOut.BUFFER_ID_NONE);
fm.setCommand((short) 0);
fm.setFlags((short) 0);
fm.setOutPort(OFPort.OFPP_NONE.getValue());
fm.setCookie(computeEntryCookie(fm, 0, entryName));
fm.setPriority(Short.MAX_VALUE);
}
/**
* Gets the entry name of a flow mod
* @param fmJson The OFFlowMod in a JSON representation
* @return The name of the OFFlowMod, null if not found
* @throws IOException If there was an error parsing the JSON
*/
public static String getEntryNameFromJson(String fmJson) throws IOException{
MappingJsonFactory f = new MappingJsonFactory();
JsonParser jp;
try {
jp = f.createJsonParser(fmJson);
} catch (JsonParseException e) {
throw new IOException(e);
}
jp.nextToken();
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
throw new IOException("Expected START_OBJECT");
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
throw new IOException("Expected FIELD_NAME");
}
String n = jp.getCurrentName();
jp.nextToken();
if (jp.getText().equals(""))
continue;
if (n == "name")
return jp.getText();
}
return null;
}
/**
* Parses an OFFlowMod (and it's inner OFMatch) to the storage entry format.
* @param fm The FlowMod to parse
* @param sw The switch the FlowMod is going to be installed on
* @param name The name of this static flow entry
* @return A Map representation of the storage entry
*/
public static Map<String, Object> flowModToStorageEntry(OFFlowMod fm, String sw, String name) {
Map<String, Object> entry = new HashMap<String, Object>();
OFMatch match = fm.getMatch();
entry.put(StaticFlowEntryPusher.COLUMN_NAME, name);
entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, sw);
entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Short.toString(fm.getPriority()));
entry.put(StaticFlowEntryPusher.COLUMN_WILDCARD, Integer.toString(match.getWildcards()));
if ((fm.getActions() != null) && (fm.getActions().size() > 0))
entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, StaticFlowEntries.flowModActionsToString(fm.getActions()));
if (match.getInputPort() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, Short.toString(match.getInputPort()));
if (!Arrays.equals(match.getDataLayerSource(), zeroMac))
entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, HexString.toHexString(match.getDataLayerSource()));
if (!Arrays.equals(match.getDataLayerDestination(), zeroMac))
entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, HexString.toHexString(match.getDataLayerDestination()));
if (match.getDataLayerVirtualLan() != -1)
entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, Short.toString(match.getDataLayerVirtualLan()));
if (match.getDataLayerVirtualLanPriorityCodePoint() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, Short.toString(match.getDataLayerVirtualLanPriorityCodePoint()));
if (match.getDataLayerType() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, Short.toString(match.getDataLayerType()));
if (match.getNetworkTypeOfService() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, Short.toString(match.getNetworkTypeOfService()));
if (match.getNetworkProtocol() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Short.toString(match.getNetworkProtocol()));
if (match.getNetworkSource() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, IPv4.fromIPv4Address(match.getNetworkSource()));
if (match.getNetworkDestination() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, IPv4.fromIPv4Address(match.getNetworkDestination()));
if (match.getTransportSource() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, Short.toString(match.getTransportSource()));
if (match.getTransportDestination() != 0)
entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, Short.toString(match.getTransportDestination()));
return entry;
}
/**
* Returns a String representation of all the openflow actions.
* @param fmActions A list of OFActions to encode into one string
* @return A string of the actions encoded for our database
*/
@LogMessageDoc(level="ERROR",
message="Could not decode action {action}",
explanation="A static flow entry contained an invalid action",
recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
private static String flowModActionsToString(List<OFAction> fmActions) {
StringBuilder sb = new StringBuilder();
for (OFAction a : fmActions) {
if (sb.length() > 0) {
sb.append(',');
}
switch(a.getType()) {
case OUTPUT:
sb.append("output=" + Short.toString(((OFActionOutput)a).getPort()));
break;
case OPAQUE_ENQUEUE:
int queue = ((OFActionEnqueue)a).getQueueId();
short port = ((OFActionEnqueue)a).getPort();
sb.append("enqueue=" + Short.toString(port) + ":0x" + String.format("%02x", queue));
break;
case STRIP_VLAN:
sb.append("strip-vlan");
break;
case SET_VLAN_ID:
sb.append("set-vlan-id=" +
Short.toString(((OFActionVirtualLanIdentifier)a).getVirtualLanIdentifier()));
break;
case SET_VLAN_PCP:
sb.append("set-vlan-priority=" +
Byte.toString(((OFActionVirtualLanPriorityCodePoint)a).getVirtualLanPriorityCodePoint()));
break;
case SET_DL_SRC:
sb.append("set-src-mac=" +
HexString.toHexString(((OFActionDataLayerSource)a).getDataLayerAddress()));
break;
case SET_DL_DST:
sb.append("set-dst-mac=" +
HexString.toHexString(((OFActionDataLayerDestination)a).getDataLayerAddress()));
break;
case SET_NW_TOS:
sb.append("set-tos-bits=" +
Byte.toString(((OFActionNetworkTypeOfService)a).getNetworkTypeOfService()));
break;
case SET_NW_SRC:
sb.append("set-src-ip=" +
IPv4.fromIPv4Address(((OFActionNetworkLayerSource)a).getNetworkAddress()));
break;
case SET_NW_DST:
sb.append("set-dst-ip=" +
IPv4.fromIPv4Address(((OFActionNetworkLayerDestination)a).getNetworkAddress()));
break;
case SET_TP_SRC:
sb.append("set-src-port=" +
Short.toString(((OFActionTransportLayerSource)a).getTransportPort()));
break;
case SET_TP_DST:
sb.append("set-dst-port=" +
Short.toString(((OFActionTransportLayerDestination)a).getTransportPort()));
break;
default:
log.error("Could not decode action: {}", a);
break;
}
}
return sb.toString();
}
/**
* Turns a JSON formatted Static Flow Pusher string into a storage entry
* Expects a string in JSON along the lines of:
* {
* "switch": "AA:BB:CC:DD:EE:FF:00:11",
* "name": "flow-mod-1",
* "cookie": "0",
* "priority": "32768",
* "ingress-port": "1",
* "actions": "output=2",
* }
* @param fmJson The JSON formatted static flow pusher entry
* @return The map of the storage entry
* @throws IOException If there was an error parsing the JSON
*/
public static Map<String, Object> jsonToStorageEntry(String fmJson) throws IOException {
Map<String, Object> entry = new HashMap<String, Object>();
MappingJsonFactory f = new MappingJsonFactory();
JsonParser jp;
try {
jp = f.createJsonParser(fmJson);
} catch (JsonParseException e) {
throw new IOException(e);
}
jp.nextToken();
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
throw new IOException("Expected START_OBJECT");
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
throw new IOException("Expected FIELD_NAME");
}
String n = jp.getCurrentName();
jp.nextToken();
if (jp.getText().equals(""))
continue;
if (n == "name")
entry.put(StaticFlowEntryPusher.COLUMN_NAME, jp.getText());
else if (n == "switch")
entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, jp.getText());
else if (n == "actions")
entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, jp.getText());
else if (n == "priority")
entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, jp.getText());
else if (n == "active")
entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, jp.getText());
else if (n == "wildcards")
entry.put(StaticFlowEntryPusher.COLUMN_WILDCARD, jp.getText());
else if (n == "ingress-port")
entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, jp.getText());
else if (n == "src-mac")
entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, jp.getText());
else if (n == "dst-mac")
entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, jp.getText());
else if (n == "vlan-id")
entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, jp.getText());
else if (n == "vlan-priority")
entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, jp.getText());
else if (n == "ether-type")
entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, jp.getText());
else if (n == "tos-bits")
entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, jp.getText());
else if (n == "protocol")
entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, jp.getText());
else if (n == "src-ip")
entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, jp.getText());
else if (n == "dst-ip")
entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, jp.getText());
else if (n == "src-port")
entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, jp.getText());
else if (n == "dst-port")
entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, jp.getText());
}
return entry;
}
/**
* Parses OFFlowMod actions from strings.
* @param flowMod The OFFlowMod to set the actions for
* @param actionstr The string containing all the actions
* @param log A logger to log for errors.
*/
@LogMessageDoc(level="ERROR",
message="Unexpected action '{action}', '{subaction}'",
explanation="A static flow entry contained an invalid action",
recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
public static void parseActionString(OFFlowMod flowMod, String actionstr, Logger log) {
List<OFAction> actions = new LinkedList<OFAction>();
int actionsLength = 0;
if (actionstr != null) {
actionstr = actionstr.toLowerCase();
for (String subaction : actionstr.split(",")) {
String action = subaction.split("[=:]")[0];
SubActionStruct subaction_struct = null;
if (action.equals("output")) {
subaction_struct = StaticFlowEntries.decode_output(subaction, log);
}
else if (action.equals("enqueue")) {
subaction_struct = decode_enqueue(subaction, log);
}
else if (action.equals("strip-vlan")) {
subaction_struct = decode_strip_vlan(subaction, log);
}
else if (action.equals("set-vlan-id")) {
subaction_struct = decode_set_vlan_id(subaction, log);
}
else if (action.equals("set-vlan-priority")) {
subaction_struct = decode_set_vlan_priority(subaction, log);
}
else if (action.equals("set-src-mac")) {
subaction_struct = decode_set_src_mac(subaction, log);
}
else if (action.equals("set-dst-mac")) {
subaction_struct = decode_set_dst_mac(subaction, log);
}
else if (action.equals("set-tos-bits")) {
subaction_struct = decode_set_tos_bits(subaction, log);
}
else if (action.equals("set-src-ip")) {
subaction_struct = decode_set_src_ip(subaction, log);
}
else if (action.equals("set-dst-ip")) {
subaction_struct = decode_set_dst_ip(subaction, log);
}
else if (action.equals("set-src-port")) {
subaction_struct = decode_set_src_port(subaction, log);
}
else if (action.equals("set-dst-port")) {
subaction_struct = decode_set_dst_port(subaction, log);
}
else {
log.error("Unexpected action '{}', '{}'", action, subaction);
}
if (subaction_struct != null) {
actions.add(subaction_struct.action);
actionsLength += subaction_struct.len;
}
}
}
log.debug("action {}", actions);
flowMod.setActions(actions);
flowMod.setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLength);
}
@LogMessageDoc(level="ERROR",
message="Invalid subaction: '{subaction}'",
explanation="A static flow entry contained an invalid subaction",
recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
private static SubActionStruct decode_output(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n;
n = Pattern.compile("output=(?:((?:0x)?\\d+)|(all)|(controller)|(local)|(ingress-port)|(normal)|(flood))").matcher(subaction);
if (n.matches()) {
OFActionOutput action = new OFActionOutput();
action.setMaxLength(Short.MAX_VALUE);
short port = OFPort.OFPP_NONE.getValue();
if (n.group(1) != null) {
try {
port = get_short(n.group(1));
}
catch (NumberFormatException e) {
log.debug("Invalid port in: '{}' (error ignored)", subaction);
return null;
}
}
else if (n.group(2) != null)
port = OFPort.OFPP_ALL.getValue();
else if (n.group(3) != null)
port = OFPort.OFPP_CONTROLLER.getValue();
else if (n.group(4) != null)
port = OFPort.OFPP_LOCAL.getValue();
else if (n.group(5) != null)
port = OFPort.OFPP_IN_PORT.getValue();
else if (n.group(6) != null)
port = OFPort.OFPP_NORMAL.getValue();
else if (n.group(7) != null)
port = OFPort.OFPP_FLOOD.getValue();
action.setPort(port);
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionOutput.MINIMUM_LENGTH;
}
else {
log.error("Invalid subaction: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_enqueue(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n;
n = Pattern.compile("enqueue=(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(subaction);
if (n.matches()) {
short portnum = 0;
if (n.group(1) != null) {
try {
portnum = get_short(n.group(1));
}
catch (NumberFormatException e) {
log.debug("Invalid port-num in: '{}' (error ignored)", subaction);
return null;
}
}
int queueid = 0;
if (n.group(2) != null) {
try {
queueid = get_int(n.group(2));
}
catch (NumberFormatException e) {
log.debug("Invalid queue-id in: '{}' (error ignored)", subaction);
return null;
}
}
OFActionEnqueue action = new OFActionEnqueue();
action.setPort(portnum);
action.setQueueId(queueid);
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionEnqueue.MINIMUM_LENGTH;
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_strip_vlan(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("strip-vlan").matcher(subaction);
if (n.matches()) {
OFActionStripVirtualLan action = new OFActionStripVirtualLan();
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionStripVirtualLan.MINIMUM_LENGTH;
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_vlan_id(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction);
if (n.matches()) {
if (n.group(1) != null) {
try {
short vlanid = get_short(n.group(1));
OFActionVirtualLanIdentifier action = new OFActionVirtualLanIdentifier();
action.setVirtualLanIdentifier(vlanid);
log.debug(" action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionVirtualLanIdentifier.MINIMUM_LENGTH;
}
catch (NumberFormatException e) {
log.debug("Invalid VLAN in: {} (error ignored)", subaction);
return null;
}
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_vlan_priority(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction);
if (n.matches()) {
if (n.group(1) != null) {
try {
byte prior = get_byte(n.group(1));
OFActionVirtualLanPriorityCodePoint action = new OFActionVirtualLanPriorityCodePoint();
action.setVirtualLanPriorityCodePoint(prior);
log.debug(" action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionVirtualLanPriorityCodePoint.MINIMUM_LENGTH;
}
catch (NumberFormatException e) {
log.debug("Invalid VLAN priority in: {} (error ignored)", subaction);
return null;
}
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_src_mac(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction);
if (n.matches()) {
byte[] macaddr = get_mac_addr(n, subaction, log);
if (macaddr != null) {
OFActionDataLayerSource action = new OFActionDataLayerSource();
action.setDataLayerAddress(macaddr);
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionDataLayerSource.MINIMUM_LENGTH;
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_dst_mac(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction);
if (n.matches()) {
byte[] macaddr = get_mac_addr(n, subaction, log);
if (macaddr != null) {
OFActionDataLayerDestination action = new OFActionDataLayerDestination();
action.setDataLayerAddress(macaddr);
log.debug(" action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionDataLayerDestination.MINIMUM_LENGTH;
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_tos_bits(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction);
if (n.matches()) {
if (n.group(1) != null) {
try {
byte tosbits = get_byte(n.group(1));
OFActionNetworkTypeOfService action = new OFActionNetworkTypeOfService();
action.setNetworkTypeOfService(tosbits);
log.debug(" action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionNetworkTypeOfService.MINIMUM_LENGTH;
}
catch (NumberFormatException e) {
log.debug("Invalid dst-port in: {} (error ignored)", subaction);
return null;
}
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_src_ip(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction);
if (n.matches()) {
int ipaddr = get_ip_addr(n, subaction, log);
OFActionNetworkLayerSource action = new OFActionNetworkLayerSource();
action.setNetworkAddress(ipaddr);
log.debug(" action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionNetworkLayerSource.MINIMUM_LENGTH;
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_dst_ip(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction);
if (n.matches()) {
int ipaddr = get_ip_addr(n, subaction, log);
OFActionNetworkLayerDestination action = new OFActionNetworkLayerDestination();
action.setNetworkAddress(ipaddr);
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionNetworkLayerDestination.MINIMUM_LENGTH;
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_src_port(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction);
if (n.matches()) {
if (n.group(1) != null) {
try {
short portnum = get_short(n.group(1));
OFActionTransportLayerSource action = new OFActionTransportLayerSource();
action.setTransportPort(portnum);
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionTransportLayerSource.MINIMUM_LENGTH;;
}
catch (NumberFormatException e) {
log.debug("Invalid src-port in: {} (error ignored)", subaction);
return null;
}
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static SubActionStruct decode_set_dst_port(String subaction, Logger log) {
SubActionStruct sa = null;
Matcher n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction);
if (n.matches()) {
if (n.group(1) != null) {
try {
short portnum = get_short(n.group(1));
OFActionTransportLayerDestination action = new OFActionTransportLayerDestination();
action.setTransportPort(portnum);
log.debug("action {}", action);
sa = new SubActionStruct();
sa.action = action;
sa.len = OFActionTransportLayerDestination.MINIMUM_LENGTH;;
}
catch (NumberFormatException e) {
log.debug("Invalid dst-port in: {} (error ignored)", subaction);
return null;
}
}
}
else {
log.debug("Invalid action: '{}'", subaction);
return null;
}
return sa;
}
private static byte[] get_mac_addr(Matcher n, String subaction, Logger log) {
byte[] macaddr = new byte[6];
for (int i=0; i<6; i++) {
if (n.group(i+1) != null) {
try {
macaddr[i] = get_byte("0x" + n.group(i+1));
}
catch (NumberFormatException e) {
log.debug("Invalid src-mac in: '{}' (error ignored)", subaction);
return null;
}
}
else {
log.debug("Invalid src-mac in: '{}' (null, error ignored)", subaction);
return null;
}
}
return macaddr;
}
private static int get_ip_addr(Matcher n, String subaction, Logger log) {
int ipaddr = 0;
for (int i=0; i<4; i++) {
if (n.group(i+1) != null) {
try {
ipaddr = ipaddr<<8;
ipaddr = ipaddr | get_int(n.group(i+1));
}
catch (NumberFormatException e) {
log.debug("Invalid src-ip in: '{}' (error ignored)", subaction);
return 0;
}
}
else {
log.debug("Invalid src-ip in: '{}' (null, error ignored)", subaction);
return 0;
}
}
return ipaddr;
}
// Parse int as decimal, hex (start with 0x or #) or octal (starts with 0)
private static int get_int(String str) {
return Integer.decode(str);
}
// Parse short as decimal, hex (start with 0x or #) or octal (starts with 0)
private static short get_short(String str) {
return (short)(int)Integer.decode(str);
}
// Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0)
private static byte get_byte(String str) {
return Integer.decode(str).byteValue();
}
}