Package org.jnode.command.net

Source Code of org.jnode.command.net.PingCommand

/*
* $Id$
*
* Copyright (C) 2003-2014 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.command.net;

import java.io.PrintWriter;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.jnode.net.SocketBuffer;
import org.jnode.net.ethernet.EthernetConstants;
import org.jnode.net.ipv4.IPv4Address;
import org.jnode.net.ipv4.IPv4Constants;
import org.jnode.net.ipv4.IPv4Header;
import org.jnode.net.ipv4.icmp.ICMPEchoHeader;
import org.jnode.net.ipv4.icmp.ICMPListener;
import org.jnode.net.ipv4.icmp.ICMPProtocol;
import org.jnode.net.ipv4.layer.IPv4NetworkLayer;
import org.jnode.net.util.NetUtils;
import org.jnode.shell.AbstractCommand;
import org.jnode.shell.syntax.Argument;
import org.jnode.shell.syntax.HostNameArgument;

/**
* @author JPG
*/
public class PingCommand extends AbstractCommand implements ICMPListener {
   
    private static final String help_host = "the target host";
    private static final String help_super = "Ping the specified host";
    private static final String fmt_unknown_host = "Unknown host: %s";
    private static final String fmt_ping = "Ping %s attempt %d%n";
    private static final String fmt_reply = "Reply from %s: %d bytes of data ttl=%d seq=%d time=%dms%n";
    private static final String fmt_stats = "-> Packet statistics%n%s%n";
    private static final String fmt_get_stats = "%d packets transmitted, %d packets received%nround-trip min/avg/max" +
                                                " = %d/%.3f/%dms";
   
    // FIXME Some of the following could be command parameters ...
    private final Statistics stat = new Statistics();
    private boolean wait = true;
    private int count = 4;
    private boolean dontFragment = false;
    private IPv4Address dst;
    private boolean flood = false;
    private int interval = 6000;
    private int size = 64;
    private long timeout = 5000;
    private int ttl = 255;

    private final HostNameArgument argHost;

    public PingCommand() {
        super(help_super);
        argHost = new HostNameArgument("host", Argument.MANDATORY, help_host);
        registerArguments(argHost);
    }

    public static void main(String[] args) throws Exception {
        new PingCommand().execute(args);
    }
   
    public void execute() throws SocketException, InterruptedException {
        try {
            this.dst = new IPv4Address(argHost.getAsInetAddress());
        } catch (UnknownHostException ex) {
            getError().getPrintWriter().format(fmt_unknown_host, ex.getLocalizedMessage());
            exit(1);
        }
        final PrintWriter out = getOutput().getPrintWriter();
       
        final IPv4Header netHeader =
                new IPv4Header(0, this.ttl, IPv4Constants.IPPROTO_ICMP, this.dst, 8);
        netHeader.setDontFragment(this.dontFragment);

        final IPv4NetworkLayer netLayer =
                (IPv4NetworkLayer) NetUtils.getNLM().getNetworkLayer(EthernetConstants.ETH_P_IP);
        final ICMPProtocol icmpProtocol =
                (ICMPProtocol) netLayer.getProtocol(ICMPProtocol.IPPROTO_ICMP);
        icmpProtocol.addListener(this);
        try {
            int id_count = 0;
            int seq_count = 0;
            while (this.count != 0) {
                out.format(fmt_ping, dst, seq_count);

                if (!this.flood) {
                    this.wait = true;
                }

                SocketBuffer packet = new SocketBuffer();
                packet.insert(this.size);
                ICMPEchoHeader transportHeader = new ICMPEchoHeader(id_count, seq_count);
                transportHeader.prefixTo(packet);

                Request r =
                        new Request(this.stat, this.timeout, System.currentTimeMillis(), id_count,
                                seq_count);
                registerRequest(r);
                netLayer.transmit(netHeader, packet);

                while (this.wait) {
                    long time = System.currentTimeMillis() - r.getTimestamp();
                    if (time > this.interval) {
                        this.wait = false;
                    }
                    Thread.sleep(500);
                    synchronized (this) {
                        if (response) {
                            out.format(fmt_reply,
                                dst.toString(), hdr1.getDataLength(), hdr1.getTtl(), hdr2.getSeqNumber(), roundt);
                            response = false;
                        }
                    }
                }
                this.count--;
                seq_count++;
            }

            while (!isEmpty()) {
                Thread.sleep(100);
            }
        } finally {
            icmpProtocol.removeListener(this);
        }
       
        out.format(fmt_stats, this.stat.getStatistics());
    }
   
    private long match(int id, int seq, Request r) {
        if (r != null && id == r.getId()) {
            return r.getTimestamp();
        } else {
            return -1;
        }
    }

    public void packetReceived(SocketBuffer skbuf) {
        long received = System.currentTimeMillis();

        IPv4Header hdr1 = (IPv4Header) skbuf.getNetworkLayerHeader();
        ICMPEchoHeader hdr2 = (ICMPEchoHeader) skbuf.getTransportLayerHeader();

        int seq = hdr2.getSeqNumber();
        Request r = removeRequest(seq);
        if (r == null || r.Obsolete()) {
            return;
        }

        long timestamp = match(hdr2.getIdentifier(), seq, r);

        long roundtrip = received - timestamp;
        gotResponse(timestamp, hdr1, hdr2, roundtrip);
    }

    private synchronized void gotResponse(long timestamp, IPv4Header hdr1, ICMPEchoHeader hdr2,
            long roundtrip) {
        if (timestamp != -1) {
            this.hdr1 = hdr1;
            this.hdr2 = hdr2;
            this.roundt = roundtrip;
            response = true;
        }
        wait = false;
        this.stat.recordPacket(roundtrip);
    }

    // response data
    private boolean response;
    private long roundt;
    private IPv4Header hdr1;
    private ICMPEchoHeader hdr2;

    // requests are tracked here
    private Map<Integer, Request> requests = new HashMap<Integer, Request>();

    private void registerRequest(Request r) {
        requests.put(r.seq, r);
    }

    private Request removeRequest(int seq) {
        return requests.remove(seq);
    }

    private boolean isEmpty() {
        return requests.isEmpty();
    }

    class Request extends TimerTask {
        private Timer timer = new Timer();
        private boolean obsolete = false;
        private Statistics stat;
        private long timestamp;
        private int id, seq;

        Request(Statistics stat, long timeout, long timestamp, int id, int seq) {
            this.stat = stat;
            this.timestamp = timestamp;
            this.id = id;
            this.seq = seq;

            timer.schedule(this, timeout);
        }

        public void run() {
            if (!this.Obsolete()) {
                stat.recordLost();
                removeRequest(this.seq);
            }
        }

        synchronized boolean Obsolete() {
            if (!obsolete) {
                this.obsolete = true;
                this.timer.cancel();
                return false;
            } else {
                return true;
            }
        }

        long getTimestamp() {
            return timestamp;
        }

        int getId() {
            return id;
        }

        int getSeq() {
            return seq;
        }
    }

    private class Statistics {
        private int received = 0, lost = 0;
        private long min = Integer.MAX_VALUE, max = 0;
        private long sum;

        void recordPacket(long roundtrip) {
            received++;
            if (roundtrip < min) {
                min = roundtrip;
            }
            if (roundtrip > max) {
                max = roundtrip;
            }
            sum += roundtrip;
        }

        void recordLost() {
            lost++;
        }

        String getStatistics() {
            int packets = received + lost;
            float avg = sum / packets;
            return String.format(fmt_get_stats, packets, received, min, avg, max);
        }
    }
}
TOP

Related Classes of org.jnode.command.net.PingCommand

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.