/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.net.ipv4.icmp; import java.net.DatagramSocketImplFactory; import java.net.SocketException; import java.net.SocketImplFactory; import java.util.Vector; import org.apache.log4j.Logger; import org.jnode.net.SocketBuffer; import org.jnode.net.ipv4.IPv4Constants; import org.jnode.net.ipv4.IPv4Header; import org.jnode.net.ipv4.IPv4Protocol; import org.jnode.net.ipv4.IPv4Service; import org.jnode.util.Queue; import org.jnode.util.QueueProcessor; import org.jnode.util.QueueProcessorThread; import org.jnode.vm.objects.Statistics; /** * Protocol handler of the ICMP protocol. * * @author Ewout Prangsma (epr@users.sourceforge.net) */ public class ICMPProtocol implements IPv4Protocol, IPv4Constants, ICMPConstants, QueueProcessor<SocketBuffer> { private static final String IPNAME_ICMP = "icmp"; /** * My logger */ private Logger log = Logger.getLogger(getClass()); /** * The IP service we're a part of */ private final IPv4Service ipService; /** * The statistics */ private final ICMPStatistics stat = new ICMPStatistics(); /** * Queue<SocketBuffer> for requests that need a reply */ private final Queue<SocketBuffer> replyRequestQueue = new Queue<SocketBuffer>(); private final QueueProcessorThread<SocketBuffer> replyRequestsThread; /** * ICMP packet listeners */ private final Vector<ICMPListener> listeners = new Vector<ICMPListener>(); /** * Create a new instance * * @param ipService */ public ICMPProtocol(IPv4Service ipService) { this.ipService = ipService; this.replyRequestsThread = new QueueProcessorThread<SocketBuffer>("icmp-reply", replyRequestQueue, this); replyRequestsThread.start(); } /** * @see org.jnode.net.ipv4.IPv4Protocol#getName() */ public String getName() { return IPNAME_ICMP; } /** * @see org.jnode.net.ipv4.IPv4Protocol#getProtocolID() */ public int getProtocolID() { return IPPROTO_ICMP; } /** * @see org.jnode.net.ipv4.IPv4Protocol#receive(org.jnode.net.SocketBuffer) */ public void receive(SocketBuffer skbuf) throws SocketException { // Update statistics stat.ipackets.inc(); try { final ICMPHeader hdr = ICMPHeaderFactory.createHeader(skbuf); skbuf.setTransportLayerHeader(hdr); skbuf.pull(hdr.getLength()); if (!hdr.isChecksumOk()) { stat.badsum.inc(); return; } // TODO Process ICMP messages switch (hdr.getType()) { case ICMP_ECHO: postReplyRequest(skbuf); break; case ICMP_ECHOREPLY: notifyListeners(skbuf); break; default: log.debug("GOT ICMP type " + hdr.getType() + ", code " + hdr.getCode()); } } catch (SocketException ex) { // TODO fix me // Ignore for now } } /** * Process an ICMP error message that has been received and matches this * protocol. The skbuf is position directly after the ICMP header (thus * contains the error IP header and error transport layer header). The * transportLayerHeader property of skbuf is set to the ICMP message header. * * @param skbuf * @throws SocketException */ public void receiveError(SocketBuffer skbuf) throws SocketException { // Ignore errors here } /** * Gets the SocketImplFactory of this protocol. * * @throws SocketException If this protocol is not Socket based. */ public SocketImplFactory getSocketImplFactory() throws SocketException { throw new SocketException("ICMP is packet based"); } /** * Gets the DatagramSocketImplFactory of this protocol. * * @throws SocketException If this protocol is not DatagramSocket based. */ public DatagramSocketImplFactory getDatagramSocketImplFactory() throws SocketException { throw new SocketException("Not implemented yet"); } /** * Send an ICMP packet * * @param skbuf */ protected void send(IPv4Header ipHdr, ICMPHeader icmpHdr, SocketBuffer skbuf) throws SocketException { stat.opackets.inc(); skbuf.setTransportLayerHeader(icmpHdr); icmpHdr.prefixTo(skbuf); ipService.transmit(ipHdr, skbuf); } /** * Send a reply on an ICMP echo header. * * @param hdr * @param skbuf */ private void sendEchoReply(ICMPEchoHeader hdr, SocketBuffer skbuf) throws SocketException { final IPv4Header ipHdr = (IPv4Header) skbuf.getNetworkLayerHeader(); final IPv4Header ipReplyHdr = new IPv4Header(ipHdr); ipReplyHdr.swapAddresses(); ipReplyHdr.setTtl(0xFF); send(ipReplyHdr, hdr.createReplyHeader(), new SocketBuffer(skbuf)); } /** * @see org.jnode.net.ipv4.IPv4Protocol#getStatistics() */ public Statistics getStatistics() { return stat; } /** * Post a request that needs a reply in the reply queue. * * @param skbuf */ private void postReplyRequest(SocketBuffer skbuf) { replyRequestQueue.add(skbuf); } /** * Process a request that needs a reply * * @param skbuf */ private void processReplyRequest(SocketBuffer skbuf) { final ICMPHeader hdr = (ICMPHeader) skbuf.getTransportLayerHeader(); try { if (hdr.getType() == ICMPType.ICMP_ECHO) { sendEchoReply((ICMPEchoHeader) hdr, skbuf); } } catch (SocketException ex) { log.error("Error in ICMP reply", ex); } } /** * @see org.jnode.util.QueueProcessor#process(java.lang.Object) */ public void process(SocketBuffer object) throws Exception { processReplyRequest(object); } /** * ICMP packet listeners methods */ private void notifyListeners(SocketBuffer skbuf) { for (ICMPListener l : listeners) { l.packetReceived(skbuf); } } public void addListener(ICMPListener listener) { listeners.add(listener); } public void removeListener(ICMPListener listener) { listeners.remove(listener); } }