/* * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab * * This file is part of PeerfactSim.KOM. * * PeerfactSim.KOM 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 * any later version. * * PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see . * */ package de.tud.kom.p2psim.impl.network.routed; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import de.tud.kom.p2psim.api.analyzer.ConnectivityAnalyzer; import de.tud.kom.p2psim.api.analyzer.MessageAnalyzer.Reason; import de.tud.kom.p2psim.api.analyzer.NetlayerAnalyzer; import de.tud.kom.p2psim.api.common.SimHost; import de.tud.kom.p2psim.api.common.SimHostComponent; import de.tud.kom.p2psim.api.linklayer.LinkLayer; import de.tud.kom.p2psim.api.linklayer.LinkMessageEvent; import de.tud.kom.p2psim.api.linklayer.LinkMessageListener; import de.tud.kom.p2psim.api.linklayer.MacStateListener; import de.tud.kom.p2psim.api.linklayer.mac.PhyType; import de.tud.kom.p2psim.api.network.NetMessage; import de.tud.kom.p2psim.api.network.NetMessageEvent; import de.tud.kom.p2psim.api.network.NetMessageListener; import de.tud.kom.p2psim.api.network.NetProtocol; import de.tud.kom.p2psim.api.network.SimNetInterface; import de.tud.kom.p2psim.api.network.SimNetworkComponent; import de.tud.kom.p2psim.api.network.routing.RoutingAlgorithm; import de.tud.kom.p2psim.api.network.routing.RoutingListener; import de.tud.kom.p2psim.api.network.routing.RoutingMessage; import de.tud.kom.p2psim.impl.network.DefaultNetMessageEvent; import de.tud.kom.p2psim.impl.network.IPv4NetID; import de.tud.kom.p2psim.impl.util.LiveMonitoring; import de.tud.kom.p2psim.impl.util.livemon.AvgAccumulatorDouble; import de.tudarmstadt.maki.simonstrator.api.Message; import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Monitor.Level; import de.tudarmstadt.maki.simonstrator.api.component.core.MonitorComponent.AnalyzerNotAvailableException; import de.tudarmstadt.maki.simonstrator.api.component.network.Bandwidth; import de.tudarmstadt.maki.simonstrator.api.component.network.NetID; import de.tudarmstadt.maki.simonstrator.api.component.network.NetInterface; import de.tudarmstadt.maki.simonstrator.api.component.network.NetworkComponent; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; import de.tudarmstadt.maki.simonstrator.api.component.transport.ConnectivityListener; /** * This NetLayer supports a {@link LinkLayer} and provides more realistic * simulations for multi-hop message propagation and energy consumption. It is * therefore very different from the other NetLayers. It provides some IP-like * functionality (most important: a routing protocol and fragmenting). * * TODO: due to the introduction of the {@link NetInterface} and * {@link NetworkComponent} API, this NetLayer has to be altered conceptionally * to support multiple NetIDs for different PHYs. * * @author Bjoern Richerzhagen * @version 1.0, 23.02.2012 */ public class RoutedNetLayer implements SimNetworkComponent, NetworkComponent, SimHostComponent { protected final SimHost host; /* * Analyzing */ public static AvgAccumulatorDouble _avgHops = new AvgAccumulatorDouble( "Net avg. hops", 1000); public static AvgAccumulatorDouble _avgFragments = new AvgAccumulatorDouble( "Net avg. fragments", 1000); private final Map nets = new LinkedHashMap(); public static boolean _analyzersInitialized = false; /** * If this is set to "true", hosts will start offline. */ public static boolean START_HOSTS_OFFLINE = false; private boolean enableFragmenting = false; private long fragmentSize = 0; /** * Create a new NetLayer for the given NetID with fragmenting disabled. * * @param netID */ public RoutedNetLayer(SimHost host) { this(host, false, 0); } /** * Create a new Netlayer with support for Message fragmenting. * * @param netID * @param enableFragmenting * @param fragmentSize */ public RoutedNetLayer(SimHost host, boolean enableFragmenting, long fragmentSize) { this.host = host; this.enableFragmenting = enableFragmenting; this.fragmentSize = fragmentSize; if (!_analyzersInitialized) { LiveMonitoring.addProgressValueIfNotThere(_avgHops); if (enableFragmenting) LiveMonitoring.addProgressValueIfNotThere(_avgFragments); _analyzersInitialized = true; } } @Override public void initialize() { /* * Create the NetinterfaceImpls for all specified PHYs in the LinkLayer * * TODO maybe get rid of PhyType and use NetInterfaceName throughout the * Simulator */ for (NetInterface netI : nets.values()) { ((NetInterfaceImpl) netI).initialize(); } } @Override public void shutdown() { throw new AssertionError( "You are not supposed to shutdown this component."); } /** * Configures the NetLayer with the provided routing Algorithm for a PHY * * @param routing */ public void addRoutingAlgorithm(RoutingAlgorithm routing, NetID netId) { if (nets.containsKey(routing.getNetInterfaceName())) { throw new AssertionError( "Another routing algorithm for " + routing.getNetInterfaceName() + " has already been configured. Ensure, that routing is only set in the Hostbuilder-Section of your configuration!!"); } nets.put(routing.getNetInterfaceName(), new NetInterfaceImpl(routing.getNetInterfaceName(), netId, routing.getPhyType(), routing, enableFragmenting, fragmentSize)); } @Override public SimHost getHost() { return host; } /** * Adapter to the MacLayer (now, a Netlayer corresponds to a single * NetInterface - i.e., a host may have multiple NetLayers that are managed * by a single NetComponent. * * @author Bjoern Richerzhagen * @version 1.0, May 16, 2013 */ private class NetInterfaceImpl implements SimNetInterface, MacStateListener, LinkMessageListener, RoutingListener { private final boolean enableFragmenting; /** * Size in byte for one fragment */ private final long fragmentSize; private final NetInterfaceName name; private final NetID localNetId; private final PhyType underlyingPhy; private final RoutingAlgorithm routing; private final List netListeners; private final List connListeners = new LinkedList(); private NetlayerAnalyzer netAnalyzerProxy; private boolean hasAnalyzer = false; public NetInterfaceImpl(NetInterfaceName name, NetID localNetId, PhyType phy, RoutingAlgorithm routing, boolean enableFragmenting, long fragmentSize) { this.enableFragmenting = enableFragmenting; this.fragmentSize = fragmentSize; this.localNetId = localNetId; this.name = name; this.underlyingPhy = phy; this.routing = routing; this.netListeners = new LinkedList(); } /** * Called by the NetComponent-wrapper */ public void initialize() { getHost().getLinkLayer().addMacStateListener(this); getHost().getLinkLayer().addLinkMessageListener(this); routing.setMessageListener(this); routing.setNetInterface(this); /* * For convenience, all hosts are assumed to be online at the * beginning of the Simulation. If you do not want this behavior, * you can set START_HOSTS_OFFLINE in the configuration of the * factory. */ if (!START_HOSTS_OFFLINE) { goOnline(); } try { netAnalyzerProxy = Monitor.get(NetlayerAnalyzer.class); hasAnalyzer = true; } catch (AnalyzerNotAvailableException e) { // No analyzer, no problem. } } @Override public void send(Message msg, NetID receiver, NetProtocol protocol) { /* * TODO Lateron, the protocol should not be needed anymore, instead * we rely on the NetInterface (our local IP), as soon as multiple * IPs per Host are available. */ if (routing == null) { throw new UnsupportedOperationException( "There is no handler (routing algorithm) for the NetProtocol " + protocol.toString() + " defined."); } /* * Fragmenting */ if (enableFragmenting) { long messageSize = msg.getSize(); int numberOfFragments = (int) Math.ceil((double) messageSize / (double) fragmentSize); long lastFragmentSize = messageSize % fragmentSize; NetMessage nMsg = null; FragmentReceivedInfo fragmentInfo = new FragmentReceivedInfo(); for (int i = 1; i <= numberOfFragments; i++) { if (i == numberOfFragments) { nMsg = new RoutedNetMessage(msg, receiver, getNetID(), protocol, lastFragmentSize, i, numberOfFragments, fragmentInfo); /* * Inform the monitor only once (after the last fragment * has been sent), but with the complete Message */ RoutedNetMessage complete = new RoutedNetMessage(msg, receiver, getNetID(), protocol); if (hasAnalyzer) { netAnalyzerProxy.netMsgEvent(complete, getHost(), Reason.SEND); } } else { nMsg = new RoutedNetMessage(msg, receiver, getNetID(), protocol, fragmentSize, i, numberOfFragments, fragmentInfo); } routing.route(nMsg); } _avgFragments.newVal(numberOfFragments); } else { NetMessage nMsg = new RoutedNetMessage(msg, receiver, getNetID(), protocol); /* * Notify NetLayer Monitor */ if (hasAnalyzer) { netAnalyzerProxy.netMsgEvent(nMsg, getHost(), Reason.SEND); } /* * let the RoutingAlgorithm do the heavy lifting. */ routing.route(nMsg); } } @Override public void deliverMessage(NetMessage msg) { /* * Specified by the RoutingListener */ if (!msg.getReceiver().equals(localNetId) && !msg.getReceiver().equals(IPv4NetID.LOCAL_BROADCAST)) { throw new UnsupportedOperationException( "The message was intended for another NetLayer: " + msg.getReceiver()); } assert !(msg.getPayload() instanceof RoutingMessage) : "Routing messages are not allowed!"; assert msg instanceof RoutedNetMessage; RoutedNetMessage rMsg = (RoutedNetMessage) msg; if (!rMsg.getReceiver().equals(IPv4NetID.LOCAL_BROADCAST)) { // analyze average hop count _avgHops.newVal(rMsg.getRoutedNetMessageHopCount()); } /* * Re-Assemble partitioned messages! */ RoutedNetMessage completeMessage; if (enableFragmenting) { /* * Check, if the message is complete */ if (!rMsg.getFragmentReceiverInfo().receivedFragment( getNetID(), rMsg.getFragmentNumber(), rMsg.getTotalNumberOfFragments())) { // only a fragment arrived, not yet complete return; } completeMessage = new RoutedNetMessage(msg.getPayload(), msg.getReceiver(), msg.getSender(), msg.getNetProtocol()); } else { completeMessage = rMsg; } /* * Notify NetLayer Monitor with the COMPLETE message */ NetMessageEvent nme = new DefaultNetMessageEvent( completeMessage.getNetProtocol(), completeMessage.getSender(), localNetId, completeMessage.getPayload()); if (hasAnalyzer) { netAnalyzerProxy.netMsgEvent(completeMessage, getHost(), Reason.RECEIVE); } for (NetMessageListener listener : netListeners) { listener.messageArrived(nme); } } @Override public void messageArrived(LinkMessageEvent linkMsgEvent) { if (linkMsgEvent.getPhyType() == underlyingPhy) { /* * This is triggered by the LinkLayer, we dispatch to the * correct routing algorithm */ if (linkMsgEvent.getPayload() instanceof RoutedNetMessage) { RoutedNetMessage nMsg = (RoutedNetMessage) linkMsgEvent .getPayload(); /* * increment hop-count for Unicast-Msgs */ if (!linkMsgEvent.isBroadcast()) { nMsg.incrementRoutedNetMessageHopCount(); } if (routing == null) { throw new UnsupportedOperationException( "There is no handler (routing algorithm) for the NetProtocol " + nMsg.getNetProtocol().toString() + " defined."); } /* * DEBUG: trace the path through the network */ // nMsg.traceHop(getNetID()); routing.handleMessage(nMsg, linkMsgEvent.getPhyType(), linkMsgEvent.getSender()); } else { throw new UnsupportedOperationException( "The RoutedNetLayer only accepts RoutedNetMessages from the LinkLayer."); } } } @Override public NetInterfaceName getName() { return name; } @Override public int getMTU() { return underlyingPhy.getDefaultMTU(); } @Override public NetID getLocalInetAddress() { return localNetId; } @Override public NetID getBroadcastAddress() { return IPv4NetID.LOCAL_BROADCAST; } @Override public NetID getByName(String name) { return new IPv4NetID(name); } @Override public boolean isUp() { return (isOnline() && getHost().getLinkLayer() .getMac(underlyingPhy).isOnline()); } @Override public void addConnectivityListener(ConnectivityListener listener) { connListeners.add(listener); } @Override public void removeConnectivityListener(ConnectivityListener listener) { connListeners.remove(listener); } @Override public void goneOffline(PhyType phy) { if (phy == underlyingPhy) { for (ConnectivityListener listener : connListeners) { listener.wentOffline(getHost(), this); } } } @Override public void goneOnline(PhyType phy) { if (phy == underlyingPhy) { for (ConnectivityListener listener : connListeners) { listener.wentOnline(getHost(), this); } } } @Override public Bandwidth getCurrentBandwidth() { return getHost().getLinkLayer().getCurrentBandwidth(underlyingPhy); } @Override public Bandwidth getMaxBandwidth() { return getHost().getLinkLayer().getMaxBandwidth(underlyingPhy); } @Override public void addNetMsgListener(NetMessageListener listener) { netListeners.add(listener); } @Override public void removeNetMsgListener(NetMessageListener listener) { netListeners.remove(listener); } @Override public void goOnline() { boolean wentOnline = false; if (host.getLinkLayer().hasPhy(underlyingPhy) && !host.getLinkLayer().isOnline(underlyingPhy)) { host.getLinkLayer().goOnline(underlyingPhy); wentOnline |= host.getLinkLayer().isOnline(underlyingPhy); } if (wentOnline) { try { Monitor.log(RoutedNetLayer.class, Level.INFO, "Netlayer %s of host %s went online.", name, host); Monitor.get(ConnectivityAnalyzer.class).wentOnline(host); } catch (AnalyzerNotAvailableException e) { // No analyzer, no problem. } } } @Override public void goOffline() { boolean wentOffline = false; if (host.getLinkLayer().hasPhy(underlyingPhy) && host.getLinkLayer().isOnline(underlyingPhy)) { host.getLinkLayer().goOffline(underlyingPhy); wentOffline |= !host.getLinkLayer().isOnline(underlyingPhy); } if (wentOffline) { try { Monitor.log(RoutedNetLayer.class, Level.INFO, "Netlayer %s of host %s went offline.", name, host); Monitor.get(ConnectivityAnalyzer.class).wentOffline(host); } catch (AnalyzerNotAvailableException e) { // No analyzer, no problem. } } } @Override public boolean isOnline() { /* * Online, if one+ PHY is online */ if (host.getLinkLayer().hasPhy(underlyingPhy) && host.getLinkLayer().isOnline(underlyingPhy)) { return true; } return false; } @Override public boolean isOffline() { return !isOnline(); } @Override public NetID getNetID() { return localNetId; } @Override public Location getNetPosition() { return host.getTopologyComponent().getRealPosition(); } @Override public SimHost getHost() { return RoutedNetLayer.this.getHost(); } } @Override public Iterable getNetworkInterfaces() { return new LinkedList(nets.values()); } @Override public Iterable getSimNetworkInterfaces() { return nets.values(); } @Override public SimNetInterface getByNetId(NetID netID) { for (SimNetInterface net : nets.values()) { if (net.getLocalInetAddress().equals(netID)) { return net; } } return null; } @Override public SimNetInterface getByName(NetInterfaceName name) { return nets.get(name); } }