![JAR search and dependency download from the Maven repository](/logo.png)
com.github.sdnwiselab.sdnwise.controller.AbstractController Maven / Gradle / Ivy
Show all versions of sdn-wise-ctrl Show documentation
/*
* Copyright (C) 2015 SDN-WISE
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.github.sdnwiselab.sdnwise.controller;
import com.github.sdnwiselab.sdnwise.adapter.AbstractAdapter;
import com.github.sdnwiselab.sdnwise.controlplane.ControlPlaneLayer;
import com.github.sdnwiselab.sdnwise.controlplane.ControlPlaneLogger;
import com.github.sdnwiselab.sdnwise.flowtable.FlowTableEntry;
import com.github.sdnwiselab.sdnwise.function.FunctionInterface;
import com.github.sdnwiselab.sdnwise.packet.ConfigPacket;
import com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.ADD_ALIAS;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.ADD_FUNCTION;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.BEACON_PERIOD;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.GET_ALIAS;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.GET_RULE;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.MY_ADDRESS;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.MY_NET;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.PACKET_TTL;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.REM_ALIAS;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.REM_FUNCTION;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.REM_RULE;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.REPORT_PERIOD;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.RESET;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.RSSI_MIN;
import static com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty.RULE_TTL;
import com.github.sdnwiselab.sdnwise.packet.NetworkPacket;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.CONFIG;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.DFLT_HDR_LEN;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.MAX_PACKET_LENGTH;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.REG_PROXY;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.REPORT;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.REQUEST;
import com.github.sdnwiselab.sdnwise.packet.OpenPathPacket;
import com.github.sdnwiselab.sdnwise.packet.ReportPacket;
import com.github.sdnwiselab.sdnwise.packet.RequestPacket;
import com.github.sdnwiselab.sdnwise.packet.ResponsePacket;
import com.github.sdnwiselab.sdnwise.topology.NetworkGraph;
import com.github.sdnwiselab.sdnwise.util.NodeAddress;
import static com.github.sdnwiselab.sdnwise.util.Utils.mergeBytes;
import static com.github.sdnwiselab.sdnwise.util.Utils.splitInteger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jodah.expiringmap.ExpiringMap;
/**
* Representation of the sensor network and resolver all the routing requests
* coming from the network itself. This abstract class has two main methods.
* manageRoutingRequest and graphUpdate. The first is called when a request is
* coming from the network while the latter is called when something in the
* topology of the network changes.
*
* There are two main implementation of this class: ControllerDijkstra and
* AbstractController Static.
*
* This class also offers methods to send messages and configure the nodes in
* the network.
*
* @author Sebastiano Milardo
*/
public abstract class AbstractController extends ControlPlaneLayer implements
ControllerInterface {
/**
* Function buffer size.
*/
private static final int BUFF_SIZE = 16384;
/**
* First request delay.
*/
private static final int DELAY = 200;
/**
* Fields and lengths.
*/
private static final int FUNCTION_HEADER_LEN = 3, CONFIG_HEADER_LEN = 1,
FUNCTION_PAYLOAD_LEN = MAX_PACKET_LENGTH - (DFLT_HDR_LEN
+ FUNCTION_HEADER_LEN + CONFIG_HEADER_LEN);
/**
* Maximum number of parts for a function.
*/
private static final int PARTS_MAX = 256;
/**
* Packet queue size.
*/
private static final int QUEUE_SIZE = 1000;
/**
* Timeout for requests in cache.
*/
protected static final int CACHE_EXP_TIME = 5;
/**
* To avoid garbage collection of the logger.
*/
protected static final Logger LOGGER = Logger.getLogger("CTRL");
/**
* Timeout for a node request. Increase when using COOJA.
*/
protected static final int RESPONSE_TIMEOUT = 300;
/**
* Incoming queue.
*/
private final ArrayBlockingQueue bQ
= new ArrayBlockingQueue<>(QUEUE_SIZE);
/**
* Query cache.
*/
private final Map configCache = ExpiringMap
.builder().expiration(CACHE_EXP_TIME, TimeUnit.SECONDS).build();
/**
* Identificator of the controller.
*/
private final InetSocketAddress myId;
/**
* Network representation.
*/
private final NetworkGraph networkGraph;
/**
* Request cache.
*/
private final Map requestCache = ExpiringMap
.builder().expiration(CACHE_EXP_TIME, TimeUnit.SECONDS).build();
/**
* Computed paths cache.
*/
private final HashMap> results
= new HashMap<>();
/**
* Sink Address.
*/
private NodeAddress sinkAddress;
/**
* Converts a Function into a series of ConfigPackets.
*
* @param net Network ID of the packet
* @param src source address of the packet
* @param dst destination address of the packet
* @param id if of the function
* @param buf the function itself as a byte array
* @return a list of ConfigPackets
*/
public static List createConfigFunctionPackets(
final byte net,
final NodeAddress src,
final NodeAddress dst,
final byte id,
final byte[] buf) {
LinkedList ll = new LinkedList<>();
int packetNumber = buf.length / FUNCTION_PAYLOAD_LEN;
int remaining = buf.length % FUNCTION_PAYLOAD_LEN;
int totalPackets = packetNumber;
if (remaining > 0) {
totalPackets++;
}
int pointer = 0;
int i = 0;
if (packetNumber < PARTS_MAX) {
if (packetNumber > 0) {
for (i = 0; i < packetNumber; i++) {
byte[] payload = ByteBuffer.allocate(FUNCTION_PAYLOAD_LEN
+ FUNCTION_HEADER_LEN)
.put(id)
.put((byte) (i + 1))
.put((byte) totalPackets)
.put(Arrays.copyOfRange(buf, pointer, pointer
+ FUNCTION_PAYLOAD_LEN)).array();
pointer += FUNCTION_PAYLOAD_LEN;
ConfigPacket np = new ConfigPacket(net, src, dst,
ADD_FUNCTION, payload);
ll.add(np);
}
}
if (remaining > 0) {
byte[] payload = ByteBuffer.allocate(remaining
+ FUNCTION_HEADER_LEN)
.put(id)
.put((byte) (i + 1))
.put((byte) totalPackets)
.put(Arrays.copyOfRange(buf, pointer, pointer
+ remaining)).array();
ConfigPacket np = new ConfigPacket(net, src, dst, ADD_FUNCTION,
payload);
ll.add(np);
}
}
return ll;
}
/**
* Constructor Method for the Controller Class.
*
* @param id ControllerId object.
* @param lower Lower Adpater object.
* @param network NetworkGraph object.
*/
AbstractController(final InetSocketAddress id,
final AbstractAdapter lower,
final NetworkGraph network) {
super("CTRL", lower, null);
sinkAddress = new NodeAddress("0.1");
ControlPlaneLogger.setupLogger(getLayerShortName());
myId = id;
networkGraph = network;
}
@Override
public final void addNodeAlias(final byte net, final NodeAddress dst,
final NodeAddress newAddr) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, ADD_ALIAS,
newAddr.getArray());
sendNetworkPacket(cp);
}
@Override
public final void addNodeFunction(final byte net, final NodeAddress dst,
final byte id, final String className) {
try {
InputStream is = FunctionInterface.class.getResourceAsStream(
className);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[BUFF_SIZE];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
List ll = createConfigFunctionPackets(
net, sinkAddress, dst, id, buffer.toByteArray());
Iterator llIterator = ll.iterator();
if (llIterator.hasNext()) {
sendNetworkPacket(llIterator.next());
Thread.sleep(DELAY);
while (llIterator.hasNext()) {
sendNetworkPacket(llIterator.next());
}
}
} catch (IOException | InterruptedException ex) {
log(Level.SEVERE, ex.toString());
}
}
@Override
public final void addNodeRule(final byte net, final NodeAddress destination,
final FlowTableEntry rule) {
ResponsePacket rp = new ResponsePacket(
net, sinkAddress, destination, rule);
sendNetworkPacket(rp);
}
@Override
public final InetSocketAddress getId() {
return myId;
}
/**
* Gets the topological representation of the network.
*
* @return a NetworkGraph object
*/
@Override
public final NetworkGraph getNetworkGraph() {
return networkGraph;
}
@Override
public final NodeAddress getNodeAddress(final byte net,
final NodeAddress dst) {
return new NodeAddress(getNodeValue(net, dst, MY_ADDRESS));
}
@Override
public final NodeAddress getNodeAlias(final byte net, final NodeAddress dst,
final byte index) {
try {
ConfigPacket cp = new ConfigPacket(
net, sinkAddress, dst, GET_ALIAS);
cp.setParams(new byte[]{(byte) index}, GET_RULE.getSize());
ConfigPacket response = sendQuery(cp);
byte[] rule = Arrays.copyOfRange(
response.getParams(), 1, response.getPayloadSize() - 1);
return new NodeAddress(rule);
} catch (TimeoutException ex) {
return null;
}
}
@Override
public final List getNodeAliases(final byte net,
final NodeAddress dst) {
List list = new LinkedList<>();
NodeAddress na;
int i = 0;
while ((na = getNodeAlias(net, dst, (byte) i)) != null) {
list.add(i, na);
i++;
}
return list;
}
@Override
public final int getNodeBeaconPeriod(final byte net,
final NodeAddress dst) {
return getNodeValue(net, dst, BEACON_PERIOD);
}
@Override
public final int getNodeEntryTtl(final byte net,
final NodeAddress dst) {
return getNodeValue(net, dst, RULE_TTL);
}
@Override
public final int getNodeNet(final byte net,
final NodeAddress dst) {
return getNodeValue(net, dst, MY_NET);
}
@Override
public final int getNodePacketTtl(final byte net,
final NodeAddress dst) {
return getNodeValue(net, dst, PACKET_TTL);
}
@Override
public final int getNodeReportPeriod(final byte net,
final NodeAddress dst) {
return getNodeValue(net, dst, REPORT_PERIOD);
}
@Override
public final int getNodeRssiMin(final byte net,
final NodeAddress dst) {
return getNodeValue(net, dst, RSSI_MIN);
}
@Override
public final FlowTableEntry getNodeRule(final byte net,
final NodeAddress dst, final int index) {
try {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, GET_RULE);
cp.setParams(new byte[]{(byte) index}, GET_RULE.getSize());
ConfigPacket response = sendQuery(cp);
byte[] rule = Arrays.copyOfRange(
response.getParams(), 1, response.getPayloadSize() - 1);
if (rule.length > 0) {
return new FlowTableEntry(rule);
} else {
return null;
}
} catch (TimeoutException ex) {
return null;
}
}
@Override
public final List getNodeRules(final byte net,
final NodeAddress dst) {
List list = new ArrayList<>();
FlowTableEntry fte;
int i = 0;
while ((fte = getNodeRule(net, dst, i)) != null) {
list.add(i, fte);
i++;
}
return list;
}
/**
* Gets an HashMap with the already computed path.
*
* @return an hash map with the computed results
*/
public final HashMap> getResults() {
return results;
}
@Override
public final NodeAddress getSinkAddress() {
return sinkAddress;
}
/**
* Manages the packets coming from the network.
*
* @param data an incoming NetworkPacket
*/
public final void managePacket(final NetworkPacket data) {
switch (data.getTyp()) {
case REPORT:
networkGraph.updateMap(new ReportPacket(data));
break;
case REQUEST:
RequestPacket req = new RequestPacket(data);
NetworkPacket p = putInRequestCache(req);
if (p != null) {
manageRoutingRequest(req, p);
}
break;
case CONFIG:
ConfigPacket cp = new ConfigPacket(data);
String key;
if (cp.getConfigId() == (GET_RULE)) {
key = cp.getNet() + " " + cp.getSrc() + " "
+ cp.getConfigId() + " " + cp.getParams()[0];
} else {
key = cp.getNet() + " " + cp.getSrc() + " "
+ cp.getConfigId();
}
configCache.put(key, cp);
break;
case REG_PROXY:
sinkAddress = data.getSrc();
break;
default:
break;
}
}
@Override
public final void removeNodeAlias(final byte net, final NodeAddress dst,
final byte index) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, REM_ALIAS,
new byte[]{index});
sendNetworkPacket(cp);
}
@Override
public final void removeNodeFunction(final byte net, final NodeAddress dst,
final byte index) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, REM_FUNCTION,
new byte[]{index});
sendNetworkPacket(cp);
}
@Override
public final void removeNodeRule(final byte net, final NodeAddress dst,
final byte index) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, REM_RULE,
new byte[]{index});
sendNetworkPacket(cp);
}
@Override
public final void resetNode(final byte net, final NodeAddress dst) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, RESET);
sendNetworkPacket(cp);
}
@Override
public final void sendPath(final byte net, final NodeAddress dst,
final List path) {
OpenPathPacket op = new OpenPathPacket(net, sinkAddress, dst, path);
sendNetworkPacket(op);
}
@Override
public final void setNodeAddress(final byte net, final NodeAddress dst,
final NodeAddress newAddress) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, MY_ADDRESS,
newAddress.getArray());
sendNetworkPacket(cp);
}
@Override
public final void setNodeBeaconPeriod(final byte net, final NodeAddress dst,
final short period) {
ConfigPacket cp = new ConfigPacket(
net, sinkAddress, dst, BEACON_PERIOD, splitInteger(period));
sendNetworkPacket(cp);
}
@Override
public final void setNodeEntryTtl(final byte net, final NodeAddress dst,
final short period) {
//TODO TTL should be in seconds
ConfigPacket cp = new ConfigPacket(
net, sinkAddress, dst, RULE_TTL, splitInteger(period));
sendNetworkPacket(cp);
}
@Override
public final void setNodeNet(final byte net, final NodeAddress dst,
final byte newNet) {
ConfigPacket cp = new ConfigPacket(
net, sinkAddress, dst, MY_NET, new byte[]{newNet});
sendNetworkPacket(cp);
}
@Override
public final void setNodePacketTtl(final byte net, final NodeAddress dst,
final byte newTtl) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, PACKET_TTL,
new byte[]{newTtl});
sendNetworkPacket(cp);
}
@Override
public final void setNodeReportPeriod(final byte net, final NodeAddress dst,
final short period) {
ConfigPacket cp = new ConfigPacket(
net, sinkAddress, dst, REPORT_PERIOD, splitInteger(period));
sendNetworkPacket(cp);
}
@Override
public final void setNodeRssiMin(final byte net, final NodeAddress dst,
final byte newRssi) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, RSSI_MIN,
new byte[]{newRssi});
sendNetworkPacket(cp);
}
@Override
public final void setupLayer() {
new Thread(new Worker()).start();
networkGraph.addObserver(this);
register();
setupNetwork();
}
/**
* This methods manages updates coming from the lower adapter or the network
* representation. When a message is received from the lower adapter it is
* inserted in a ArrayBlockingQueue and then the method managePacket it is
* called on it. While for updates coming from the network representation
* the method graphUpdate is invoked.
*
* @param o the source of the event.
* @param arg Object sent by Observable.
*/
@Override
public final void update(final Observable o, final Object arg) {
if (o.equals(getLower())) {
try {
bQ.put(new NetworkPacket((byte[]) arg));
} catch (InterruptedException ex) {
log(Level.SEVERE, ex.toString());
}
} else if (o.equals(networkGraph)) {
graphUpdate();
}
}
/**
* Gets a property from a node.
*
* @param net Network ID of the packet
* @param dst destination address of the packet
* @param cfp the property to configure
* @return the value from the node
*/
private int getNodeValue(final byte net, final NodeAddress dst,
final ConfigProperty cfp) {
ConfigPacket cp = new ConfigPacket(net, sinkAddress, dst, cfp);
try {
byte[] res = sendQuery(cp).getParams();
if (cfp.getSize() == 1) {
return Byte.toUnsignedInt(res[0]);
} else {
return mergeBytes(res[0], res[1]);
}
} catch (TimeoutException ex) {
log(Level.SEVERE, ex.toString());
return -1;
}
}
/**
* Adds a Request packet in the cache. If the cache already contains a
* request with the same key, it means that this is a response, therefore it
* returns the packet, otherwise returns null
*
* @param rp an incoming Request packet
* @return null if it sent by the controller, the RequestPacket if sent by a
* node
*/
private NetworkPacket putInRequestCache(final RequestPacket rp) {
if (rp.getTotal() == 1) {
return new NetworkPacket(rp.getData());
}
String key = rp.getSrc() + "." + rp.getId();
if (requestCache.containsKey(key)) {
RequestPacket p0 = requestCache.remove(key);
return RequestPacket.mergePackets(p0, rp);
} else {
requestCache.put(key, rp);
}
return null;
}
/**
* Will be used in the future to implement security polcies.
*/
private void register() {
//TODO we need to implement same sort of security check/auth.
}
/**
* Sends a ConfigPacket to query the node.
*
* @param cp the Config packet to be sent
* @return the response from the node
* @throws TimeoutException if the node does not respond
*/
private ConfigPacket sendQuery(final ConfigPacket cp)
throws TimeoutException {
sendNetworkPacket(cp);
try {
Thread.sleep(RESPONSE_TIMEOUT);
} catch (InterruptedException ex) {
log(Level.SEVERE, ex.toString());
}
String key;
if (cp.getConfigId() == (GET_RULE)) {
key = cp.getNet() + " "
+ cp.getDst() + " "
+ cp.getConfigId() + " "
+ cp.getParams()[0];
} else {
key = cp.getNet() + " "
+ cp.getDst() + " "
+ cp.getConfigId();
}
if (configCache.containsKey(key)) {
return configCache.remove(key);
} else {
throw new TimeoutException("No answer from the node");
}
}
/**
* This method sends a generic message to a node. The message is represented
* by a NetworkPacket.
*
* @param packet the packet to be sent.
*/
protected final void sendNetworkPacket(final NetworkPacket packet) {
packet.setNxh(sinkAddress);
getLower().send(packet.toByteArray());
}
/**
* Manages the queue of incoming packets.
*/
private class Worker implements Runnable {
@Override
public void run() {
while (true) {
try {
managePacket(bQ.take());
} catch (InterruptedException ex) {
Logger.getGlobal().log(Level.SEVERE, ex.toString());
}
}
}
}
}