/* * 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.linklayer; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import de.tud.kom.p2psim.api.analyzer.LinklayerAnalyzer; import de.tud.kom.p2psim.api.analyzer.MessageAnalyzer.Reason; import de.tud.kom.p2psim.api.common.SimHost; import de.tud.kom.p2psim.api.linklayer.LinkLayer; import de.tud.kom.p2psim.api.linklayer.LinkLayerMessage; 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.MacAddress; import de.tud.kom.p2psim.api.linklayer.mac.MacLayer; import de.tud.kom.p2psim.api.linklayer.mac.PhyType; import de.tud.kom.p2psim.api.network.BandwidthImpl; import de.tud.kom.p2psim.api.scenario.ConfigurationException; import de.tud.kom.p2psim.impl.network.IPv4NetID; import de.tud.kom.p2psim.impl.util.LiveMonitoring; import de.tud.kom.p2psim.impl.util.LiveMonitoring.ProgressValue; import de.tud.kom.p2psim.impl.util.toolkits.NumberFormatToolkit; 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.NetID; import de.tudarmstadt.maki.simonstrator.api.component.network.NetInterface; /** * Basic building-block for a LinkLayer. It may contain multiple MAC-Layers as * foreseen by 802.xx. There should be no need to specify additional link * layers, as all the magic happens inside the MAC. This is just a simple * dispatcher to select the right MAC. * * LinkLayers are created and configured using the {@link LinkLayerFactory}. * This LinkLayer is to be extended for more advanced functionality or in-depth * analyzing to keep the main code sleek and fast. Methods declared as final are * not to be altered to ensure correct functionality. * * @author Bjoern Richerzhagen * @version 1.0, 21.02.2012 */ public class ModularLinkLayer implements LinkLayer, LinkMessageListener { /** * All Mac-Layers of this Host */ private Map macLayers; /** * All MessageListeners */ private List messageListeners; /** * All MacStateLIsteners */ private List macStateListeners; /** * All ConnectivityListeners */ // private List connectivityListeners; private static Map addressResolution = new HashMap(); /** * Host this LinkLayer belongs to. */ private SimHost host; /* * Analyzing */ public static long _linkBroadcastSent, _linkUnicastSent, _linkUnicastRcvd, _linkBroadcastRcvd, _linkDropped = 0; public static boolean _analyzersInitialized = false; /** * */ public ModularLinkLayer(SimHost host) { this.host = host; macLayers = new LinkedHashMap(); messageListeners = new LinkedList(); macStateListeners = new LinkedList(); if (!_analyzersInitialized) { LiveMonitoring .addProgressValueIfNotThere(new LinkMessagesSentProgress()); LiveMonitoring .addProgressValueIfNotThere(new LinkMessagesReceivedProgress()); LiveMonitoring .addProgressValueIfNotThere(new LinkMessagesDroppedProgress()); _analyzersInitialized = true; } } @Override public void initialize() { /* * Initialize MACs */ for (MacLayer mac : macLayers.values()) { mac.initialize(); /* * ARP */ NetInterface net = host.getNetworkComponent().getByName( mac.getPhyType().getNetInterfaceName()); assert !addressResolution.containsKey(net.getLocalInetAddress()); addressResolution.put(net.getLocalInetAddress(), mac.getMacAddress()); } } @Override public void shutdown() { throw new AssertionError( "You are not supposed to shutdown this component."); } @Override public MacAddress addressResolution(NetID netID, PhyType phy) { /* * This implementation does not really use ARP, but instead relies on a * global table of all MacAddresses */ if (netID.equals(IPv4NetID.LOCAL_BROADCAST)) { return MacAddress.BROADCAST; } return addressResolution.get(netID); } @Override public final void addMacLayer(MacLayer macLayer) { // prevent duplicate MACs for one PHY if (macLayers.containsKey(macLayer.getPhyType())) { throw new ConfigurationException( "You configured two MACs with the same PhyType!"); } // we register this LinkLayer as a Listener for the MAC macLayer.setMessageListener(this); macLayers.put(macLayer.getPhyType(), macLayer); } @Override public final SimHost getHost() { return host; } @Override public boolean hasPhy(PhyType phyType) { return macLayers.containsKey(phyType); } @Override public MacLayer getMac(PhyType phyType) { return macLayers.get(phyType); } @Override public void goOffline(PhyType phyType) { MacLayer activeMac = macLayers.get(phyType); if (activeMac == null) { throw new UnsupportedOperationException("There is no MAC for " + phyType.toString() + " on this Host. Your NetLayer messed something up!"); } if (activeMac.isOnline()) { activeMac.goOffline(); for (MacStateListener listener : macStateListeners) { listener.goneOffline(phyType); } } else { Monitor.log(ModularLinkLayer.class, Level.DEBUG, "Mac was already offline..."); } } @Override public void goOnline(PhyType phyType) { MacLayer activeMac = macLayers.get(phyType); if (activeMac == null) { throw new UnsupportedOperationException("There is no MAC for " + phyType.toString() + " on this Host. Your NetLayer messed something up!"); } if (!activeMac.isOnline()) { activeMac.goOnline(); for (MacStateListener listener : macStateListeners) { listener.goneOnline(phyType); } } else { Monitor.log(ModularLinkLayer.class, Level.DEBUG, "Mac was already online..."); } } @Override public boolean isOnline(PhyType phyType) { MacLayer activeMac = macLayers.get(phyType); if (activeMac == null) { Monitor.log(ModularLinkLayer.class, Level.WARN, "A MAC with the type " + phyType.toString() + " is not configured."); return false; } return activeMac.isOnline(); } @Override public void send(PhyType phyType, MacAddress destination, Message data) { MacLayer activeMac = macLayers.get(phyType); if (activeMac != null) { /* * here we might add Analyzers/Inform the Monitor */ LinkLayerMessage lMsg = new DefaultLinkLayerMessage(data, activeMac.getMacAddress(), destination); if (destination.isBroadcast()) { _linkBroadcastSent++; } else { _linkUnicastSent++; } activeMac.send(destination, lMsg); } else { throw new UnsupportedOperationException("There is no MAC for " + phyType.toString() + " on this Host. Your NetLayer messed something up!"); } } @Override public void messageArrived(LinkMessageEvent linkMsgEvent) { /* * Notify Listeners. Every message that is received at a MAC and is not * a pure MAC-Message is dispatched through this function! Here we might * add Analyzers ;) */ if (linkMsgEvent.isBroadcast()) { _linkBroadcastRcvd++; } else { _linkUnicastRcvd++; } _linkMsgEvent(linkMsgEvent.getLinkLayerMessage(), getHost(), Reason.RECEIVE); for (LinkMessageListener listener : messageListeners) { listener.messageArrived(linkMsgEvent); } } @Override public final void addLinkMessageListener(LinkMessageListener listener) { messageListeners.add(listener); } @Override public final void removeLinkMessageListener(LinkMessageListener listener) { messageListeners.remove(listener); } @Override public BandwidthImpl getCurrentBandwidth(PhyType phy) { return getMac(phy).getCurrentBandwidth(); } @Override public BandwidthImpl getMaxBandwidth(PhyType phy) { return getMac(phy).getMaxBandwidth(); } // @Override // public final void addConnectivityListener(ConnectivityListener listener) // { // connectivityListeners.add(listener); // } // // @Override // public final void removeConnectivityListener(ConnectivityListener // listener) { // connectivityListeners.remove(listener); // } public class LinkMessagesSentProgress implements ProgressValue { @Override public String getName() { return "Link sent (U/B/T)"; } @Override public String getValue() { return Long.toString(_linkUnicastSent) + " / " + Long.toString(_linkBroadcastSent) + " / " + Long.toString(_linkUnicastSent + _linkBroadcastSent); } } public class LinkMessagesReceivedProgress implements ProgressValue { @Override public String getName() { return "Link rcvd (U/B/T), drop (U)"; } @Override public String getValue() { return Long.toString(_linkUnicastRcvd) + " / " + Long.toString(_linkBroadcastRcvd) + " / " + Long.toString(_linkUnicastRcvd + _linkBroadcastRcvd) + ", drop: " + NumberFormatToolkit.formatPercentage(_linkDropped / (double) _linkUnicastSent, 3); } } public class LinkMessagesDroppedProgress implements ProgressValue { @Override public String getName() { return "Link dropped"; } @Override public String getValue() { return Long.toString(_linkDropped); } } @Override public void addMacStateListener(MacStateListener listener) { macStateListeners.add(listener); } @Override public void removeMacStateListener(MacStateListener listener) { macStateListeners.remove(listener); } private static boolean _linkAnalyzerInitialized = false; private static LinklayerAnalyzer _linkAnalyzer = null; protected static void _linkMsgEvent(LinkLayerMessage msg, SimHost host, Reason reason) { if (!_linkAnalyzerInitialized) { try { _linkAnalyzer = Monitor.get(LinklayerAnalyzer.class); } catch (AnalyzerNotAvailableException e) { _linkAnalyzer = null; } _linkAnalyzerInitialized = true; } if (_linkAnalyzerInitialized && _linkAnalyzer != null) { _linkAnalyzer.linkMsgEvent(msg, host, reason); } } }