/*
** This file is part of Filius, a network construction and simulation software.
**
** Originally created at the University of Siegen, Institute "Didactics of
** Informatics and E-Learning" by a students' project group:
** members (2006-2007):
** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding,
** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer
** supervisors:
** Stefan Freischlad (maintainer until 2009), Peer Stechert
** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de>
** and Stefan Freischlad
** Filius 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 2 of the License, or
** (at your option) version 3.
**
** Filius 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 Filius. If not, see <http://www.gnu.org/licenses/>.
*/
package filius.software.vermittlungsschicht;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map.Entry;
import filius.Main;
import filius.exception.VerbindungsException;
import filius.hardware.NetzwerkInterface;
import filius.hardware.Verbindung;
import filius.hardware.knoten.InternetKnoten;
import filius.software.netzzugangsschicht.EthernetFrame;
import filius.software.system.InternetKnotenBetriebssystem;
import filius.software.system.SystemSoftware;
import filius.rahmenprogramm.I18n;
/**
* This class implements the ICMP protocol -- at least for echo request/response.
*/
public class ICMP extends VermittlungsProtokoll implements I18n {
/**
*
*/
private ICMPThread thread;
/**
* Standard-Konstruktor zur Initialisierung der zugehoerigen Systemsoftware
*
* @param systemAnwendung
*/
public ICMP(SystemSoftware systemAnwendung) {
super(systemAnwendung);
Main.debug.println("INVOKED-2 ("+this.hashCode()+") "+getClass()+" (ICMP), constr: ICMP("+systemAnwendung+")");
}
public void starten() {
Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (ICMP), starten()");
thread = new ICMPThread(this);
thread.starten();
}
public void beenden() {
Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (ICMP), beenden()");
if (thread != null) thread.beenden();
}
private void placeLocalICMPPacket(IcmpPaket icmpPacket) {
Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (ICMP), placeLocalICMPPacket("+icmpPacket.toString()+")");
LinkedList<IcmpPaket> icmpPakete = ((InternetKnotenBetriebssystem) holeSystemSoftware()).holeEthernet().holeICMPPuffer();
synchronized(icmpPakete) {
icmpPakete.add(icmpPacket);
icmpPakete.notify();
}
}
/** Hilfsmethode zum Versenden eines ICMP Echo Requests */
public void sendEchoRequest(String destIp, int seqNr) {
sendeICMP(8, 0, seqNr, destIp);
}
/** Hilfsmethode zum Versenden eines ICMP Echo Reply */
public void sendEchoReply(IcmpPaket rcvPacket) {
sendeICMP(0, 0, rcvPacket.getSeqNr(), rcvPacket.getQuellIp());
}
public void sendeICMP(int typ, int code, String zielIP) {
sendeICMP(typ, code, 0, zielIP);
}
public void sendeICMP(int typ, int code, int seqNr, String zielIP) {
sendeICMP(typ, code, 64, seqNr, zielIP);
}
public void sendeICMP(int typ, int code, int ttl, int seqNr, String zielIP) {
IcmpPaket icmpPaket = new IcmpPaket();
icmpPaket.setProtokollTyp(EthernetFrame.IP);
icmpPaket.setQuellIp(((InternetKnotenBetriebssystem)
holeSystemSoftware()).holeIPAdresse());
icmpPaket.setQuellMacAdresse(((InternetKnotenBetriebssystem)
holeSystemSoftware()).holeMACAdresse());
icmpPaket.setZielIp(zielIP);
icmpPaket.setIcmpType(typ);
icmpPaket.setIcmpCode(code);
icmpPaket.setSeqNr(seqNr);
icmpPaket.setTtl(ttl);
sendeUnicast(icmpPaket);
}
/**
* Hilfsmethode zum versenden eines Unicast-Pakets. Hier wird unterschieden,
* ob sich die Ziel-IP-Adresse im lokalen Rechnernetz befindet oder ueber
* das Gateway verschickt werden muss.
*
* @param paket
* das zu versendende IP-Paket
* @throws VerbindungsException
*/
private void sendeUnicast(IcmpPaket paket) {
if (this.isLocal(paket.getZielIp())) {
placeLocalICMPPacket(paket);
return;
}
InternetKnotenBetriebssystem bs =
(InternetKnotenBetriebssystem) holeSystemSoftware();
String[] route = bs.getWeiterleitungstabelle()
.holeWeiterleitungsZiele(paket.getZielIp());
if (route == null) {
// Es wurde keine Route gefunden, ueber die das Paket versendet
// werden koennte.
// Falls das weiterzuleitende Paket ein ICMP Echo Request ist,
// muss ein ICMP Destination Unreachable: Network Unreachable (3/0)
// zurueckgesendet werden. Andere ICMP-Paket muessen verworfen
// werden.
if (paket.getIcmpType() == 8 && paket.getIcmpCode() == 0) {
sendeICMP(3, 0, paket.getSeqNr(), paket.getQuellIp());
}
return;
}
String gateway = route[0];
String schnittstelle = route[1];
InternetKnoten knoten = (InternetKnoten) bs.getKnoten();
NetzwerkInterface nic = knoten.getNetzwerkInterfaceByIp(schnittstelle);
String netzmaske = nic.getSubnetzMaske();
if (gleichesRechnernetz(paket.getZielIp(), schnittstelle, netzmaske)) {
// adressierter Knoten befindet sich im lokalen Rechnernetz
sendeUnicastLokal(paket, paket.getZielIp(), nic);
} else {
// adressierter Knoten ist ueber Gateway zu erreichen
sendeUnicastLokal(paket, gateway, nic);
}
}
/**
* Hilfsmethode zum Versenden eines Unicast-Pakets im lokalen Rechnernetz.
*
* @param paket
* das zu versendende IP-Paket
* @param ziel
* die Ziel-IP
* @param nic
* die Schnittstelle, ueber die das IP-Paket verschickt werden muss
*/
private void sendeUnicastLokal(IcmpPaket paket, String ziel, NetzwerkInterface nic) {
InternetKnotenBetriebssystem bs =
(InternetKnotenBetriebssystem)holeSystemSoftware();
String zielMacAdresse = bs.holeARP().holeARPTabellenEintrag(ziel);
if (zielMacAdresse != null) {
// MAC-Adresse konnte bestimmt werden
paket.setZielMacAdresse(zielMacAdresse);
bs.holeEthernet().senden(paket, nic.getMac(), zielMacAdresse,
EthernetFrame.IP);
} else {
// Es konnte keine MAC-Adresse bestimmt werden.
// Falls das weiterzuleitende Paket ein ICMP Echo Request ist,
// muss ein ICMP Destination Unreachable: Host Unreachable (3/1)
// zurueckgesendet werden. Andere ICMP-Paket muessen verworfen
// werden.
if (paket.getIcmpType() == 8 && paket.getIcmpCode() == 0) {
sendeICMP(3, 1, paket.getSeqNr(), paket.getQuellIp());
}
}
}
/**
* Methode zum Weiterleiten eines ICMP-Pakets. Zunaechst wird geprueft, ob das
* Feld Time-to-Live-Feld (TTL) noch nicht abgelaufen ist (d. h. TTL
* groesser 0). Wenn diese Bedingung erfuellt ist, wird zunaechst geprueft,
* ob es sich um einen Broadcast handelt. Ein solches Paket wird nicht
* weitergeleitet sondern nur an die Transportschicht weitergegeben. Sonst
* wird die Weiterleitungstabelle nach einem passenden Eintrag abgefragt.
* Anhand des zurueckgegebenen Eintrags wird geprueft, ob das Paket fuer den
* eigenen Rechner ist oder ob ein Unicast-Paket verschickt werden muss.
*
* @param icmpPaket
* das zu versendende ICMP-Paket
*/
public void weiterleitenPaket(IcmpPaket icmpPaket) {
if (icmpPaket.getTtl() <= 0) {
// TTL ist abgelaufen.
// (wird in ICMPThread.verarbeiteDatenEinheit()
// dekrementiert, bevor diese Funktion aufgerufen
// wird)
// ICMP Timeout Expired In Transit (11/0) zuruecksenden:
sendeICMP(11, 0, icmpPaket.getSeqNr(), icmpPaket.getQuellIp());
} else {
// TTL ist nicht abgelaufen.
// Paket weiterleiten:
sendeUnicast(icmpPaket);
}
}
public int startSinglePing(String destIp, int seqNr) throws java.util.concurrent.TimeoutException {
return thread.startSinglePing(destIp, seqNr);
}
public IcmpPaket sendProbe(String destIp, int ttl, int seqNr) {
return thread.sendProbe(destIp, ttl, seqNr);
}
public ICMPThread getICMPThread() {
return thread;
}
}