/* * Copyright 2010 NCHOVY * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.pcap.util; import java.io.IOException; import java.net.InetAddress; import java.util.Date; import java.util.concurrent.TimeoutException; import org.krakenapps.pcap.decoder.ethernet.EthernetFrame; import org.krakenapps.pcap.decoder.ethernet.EthernetHeader; import org.krakenapps.pcap.decoder.ethernet.EthernetType; import org.krakenapps.pcap.decoder.ethernet.MacAddress; import org.krakenapps.pcap.decoder.icmp.IcmpPacket; import org.krakenapps.pcap.decoder.icmp.IcmpProcessor; import org.krakenapps.pcap.decoder.ip.InternetProtocol; import org.krakenapps.pcap.decoder.ip.Ipv4Packet; import org.krakenapps.pcap.live.PcapDevice; import org.krakenapps.pcap.live.PcapDeviceManager; import org.krakenapps.pcap.live.PcapDeviceMetadata; import org.krakenapps.pcap.routing.RoutingEntry; import org.krakenapps.pcap.routing.RoutingTable; /** * @author xeraph */ public class Ping { public static PingResponse ping(InetAddress targetIp, int timeout) throws IOException, TimeoutException { return ping(targetIp, 1, timeout); } public static PingResponse ping(InetAddress targetIp, int seq, int timeout) throws IOException, TimeoutException { MacAddress dstMac = Arping.query(targetIp, timeout); return ping(dstMac, targetIp, seq, timeout); } public static PingResponse ping(MacAddress dstMac, InetAddress targetIp, int seq, int timeout) throws IOException, TimeoutException { if (dstMac == null) throw new IllegalArgumentException("destination mac should be not null"); RoutingEntry entry = RoutingTable.findRoute(targetIp); PcapDeviceMetadata metadata = PcapDeviceManager.getDeviceMetadata(entry.getInterfaceName()); if (metadata == null) throw new IllegalStateException("route not found for " + targetIp); return ping(metadata, entry, dstMac, targetIp, seq, timeout); } private static PingResponse ping(PcapDeviceMetadata metadata, RoutingEntry entry, MacAddress dstMac, InetAddress targetIp, int seq, int timeout) throws IOException, TimeoutException { InetAddress sourceIp = metadata.getInet4Address(); MacAddress srcMac = metadata.getMacAddress(); Buffer buf = buildFrame(entry, srcMac, dstMac, sourceIp, targetIp, seq); PcapDevice device = PcapDeviceManager.open(metadata.getName(), timeout); try { device.setFilter("icmp"); PcapLiveRunner runner = new PcapLiveRunner(device); IcmpCallback callback = new IcmpCallback(); runner.getIcmpDecoder().register(callback); // send icmp packet device.write(buf); long begin = new Date().getTime(); IcmpPacket last = null; while (true) { try { runner.runOnce(); } catch (IOException e) { if (e.getMessage().equals("Timeout")) throw new TimeoutException("ping timeout for " + targetIp.getHostAddress()); else throw e; } last = callback.getLast(); if (last.getType() == 0 && last.getCode() == 0 && last.getId() == 1 && last.getSeq() == seq) break; long end = new Date().getTime(); if (end - begin >= timeout) break; } if (last == null) throw new TimeoutException("ping timeout for " + targetIp.getHostAddress()); return new PingResponse(last, (int) (System.currentTimeMillis() - begin)); } finally { device.close(); } } private static Buffer buildFrame(RoutingEntry entry, MacAddress srcMac, MacAddress dstMac, InetAddress sourceIp, InetAddress targetIp, int seq) { IcmpPacket.Builder icmp = new IcmpPacket.Builder().seq(seq).data( new ChainBuffer("abcdefghijklmnopqrstuvwabcdefghi".getBytes())); Ipv4Packet ipPacket = new Ipv4Packet.Builder().dst(targetIp).proto(InternetProtocol.ICMP).data(icmp).build(); EthernetHeader ethernetHeader = new EthernetHeader(srcMac, dstMac, EthernetType.IPV4); EthernetFrame frame = new EthernetFrame(ethernetHeader, ipPacket.getBuffer()); return frame.getBuffer(); } private static class IcmpCallback implements IcmpProcessor { private IcmpPacket last; @Override public void process(IcmpPacket p) { last = p; } public IcmpPacket getLast() { return last; } } public static class PingResponse { private IcmpPacket packet; private int bytes; private int time; public PingResponse(IcmpPacket packet, int time) { this.packet = packet; this.time = (time + 50) / 100; this.bytes = packet.getBuffer().readableBytes() - 8; } public IcmpPacket getPacket() { return packet; } public int getBytes() { return bytes; } public int getTime() { return time; } } }