All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.sdnwiselab.sdnwise.controller.AbstractController Maven / Gradle / Ivy

The newest version!
/*
 * 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()); } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy