package ejip123;
/**
Implementation of the important parts of RFC 792. Echo requests and replies and different error messages are supported.
For the constants of this class see <a href=http://www.iana.org/assignments/icmp-parameters>iana.org</a>.
*/
public class Icmp{
public static final int PROTOCOL = 1;
private final static int T_ECHO_REP = 0;
//public final static int T_DEST_UNREACH = 3;
private final static int T_ECHO_REQ = 8;
//public final static int T_TIME_EXC = 11;
private static int pingTimeout = 1300;
private static PingReplyHandler pingHandler = null;
private static int timestamp = 0;
private Icmp(){
}
static void loop(int cur){
if(timestamp + pingTimeout - cur < 0){
if(pingHandler != null)
pingHandler.pingTimeout();
pingHandler = null;
}
}
static void receive(Packet p, int off){
// int chksum = p.buf[5]&0xffff;
int type = p.buf[off]>>>24;
// int code = (p.buf[5] >>> 16)&0xff;
int src = p.buf[3];
int dst = p.buf[4];
// Dbg.wr('P');
// Dbg.hexVal(type);
if(Ip.chkSum(p.buf, off, p.len() - (off<<2)) != 0){
/*
Dbg.wr("icmp checksum failed, dropping.");
// p.print(96);
Dbg.wr("should have been ");
Dbg.hexVal(p.buf[off]&0xffff);
Dbg.wr("was ");
p.buf[off] &= 0xffff0000;
Dbg.hexVal(Ip.chkSum(p.buf, off, p.len() - (off<<2)));
*/
p.free();
} else{
/*
| | | | |S| |
| | | | |H| |F
| | | | |O|M|o
| | |S| |U|U|o
| | |H| |L|S|t
| |M|O| |D|T|n
| |U|U|M| | |o
| |S|L|A|N|N|t
| |T|D|Y|O|O|t
FEATURE |SECTION | | | |T|T|e
Included octets same as received |3.2.2 |x| | | | |
TODO? Demux ICMP Error to transport protocol |3.2.2 |x| | | | |
Send ICMP error message with TOS=0 |3.2.2 | |x| | | |
Send ICMP error message for: | | | | | | |
- ICMP error msg |3.2.2 | | | | |x|
- IP b'cast or IP m'cast |3.2.2 | | | | |x|
- Link-layer b'cast |3.2.2 | | | | |x|
- Non-initial fragment |3.2.2 | | | | |x|
- Datagram with non-unique src address |3.2.2 | | | | |x|
Return ICMP error msgs (when not prohibited) |3.3.8 |x| | | | |
| | | | | | |
Dest Unreachable: | | | | | | |
Generate Dest Unreachable (code 2/3) |3.2.2.1 | |x| | | |
TODO? Pass ICMP Dest Unreachable to higher layer |3.2.2.1 |x| | | | |
Redirect: | | | | | | |
TODO? Update route cache when recv Redirect |3.2.2.2 |x| | | | |
TODO? Time Exceeded: pass to higher layer |3.2.2.4 |x| | | | | (when frag reassembly timeout exceeds e.g. frags missing)
TODO? Parameter Problem: | | | | | | | (problems with header parameters preventing processing)
Send Parameter Problem messages |3.2.2.5 | |x| | | |
Pass Parameter Problem to higher layer |3.2.2.5 |x| | | | |
Report Parameter Problem to user |3.2.2.5 | | |x| | |
*/
switch(type){
case T_ECHO_REQ:
// we need to mirror the data back anyway, so we reuse this packet
// just recalculate the checksum and set ICMP type to reply (0)
p.buf[off] = T_ECHO_REP;
p.buf[off] |= Ip.chkSum(p.buf, off, p.len() - (off<<2));
Ip.send(p, dst, src, PROTOCOL);
break;
/*
case T_DEST_UNREACH:
if(code==2 || code==3){
//inform upper, sending layer
}
break;
*/
case T_ECHO_REP:
p.free();
if(pingHandler != null && p.buf[off + 1] == timestamp){
pingHandler.pingReply(((int)System.currentTimeMillis()) - timestamp);
pingHandler = null;
}
break;
default:
p.free();
break;
}
}
}
/**
Sends an ICMP ping to the specified host. On reception of a reply the given handler will be called.
@param dstIp Destination IP address.
@param ph The handler whose {@link PingReplyHandler#pingReply(int)} method should be called.
@return true if a packet was sent to a link layer */
public static boolean ping(int dstIp, PingReplyHandler ph){
Packet p;
if(pingHandler == null && (p = PacketPool.getFreshPacket()) != null){
pingHandler = ph;
int off = Ip.OFFSET;
p.buf[off] = T_ECHO_REQ<<24;
p.buf[off + 2] = 0xDEADBABE;
p.buf[off + 3] = 0x12345678;
timestamp = (int)System.currentTimeMillis();
p.buf[off + 1] = timestamp;
p.setLen((off + 4)<<2);
p.buf[off] |= Ip.chkSum(p.buf, off, p.len() - (off<<2));
return Ip.send(p, 0, dstIp, PROTOCOL);
}
return false;
}
private static void sendError(int type, int code, int[] origBuf, int origOff){
Packet p;
if((p = PacketPool.getFreshPacket()) != null){
int off = Ip.OFFSET;
int[] buf = p.buf;
buf[off] = (type<<24)|(code<<16);
int payOff = off + 2;
for(int i = 0; i < origOff + 2; i++){
buf[payOff + i] = origBuf[i];
}
p.setLen((off + origOff + 4)<<2);
buf[off] |= Ip.chkSum(buf, off, p.len() - (off<<2));
Ip.send(p, 0, origBuf[3], PROTOCOL);
}
}
/**
Sends an ICMP time exceeded error message to the sending host. We support reassembly timeouts only atm (no ttl
exceeded).
@param origBuf The buffer of the original packet.
@param origOff The offset in the original packet. */
public static void sendTimeExceeded(int[] origBuf, int origOff){
sendError(11, 1, origBuf, origOff);
}
/**
Sends an ICMP port unreachable error message to the sending host.
@param origBuf Buffer of the received packet.
@param origOff Offset of ip payload in that buffer. */
public static void sendPortUnreachable(int[] origBuf, int origOff){
sendError(3, 3, origBuf, origOff);
}
/**
Sends an ICMP protocol unreachable error message to the sending host.
@param origBuf Buffer of the received packet.
@param origOff Offset of ip payload in that buffer. */
public static void sendProtocolUnreachable(int[] origBuf, int origOff){
sendError(3, 2, origBuf, origOff);
}
public static void setPingTimeout(int pingTimeout){
Icmp.pingTimeout = pingTimeout;
}
}