/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // /////////////////////////////////////////////////////////////////////////////// /** * Microsoft Research, Cambridge * author: Yaron Weinsberg, Richard Black */ using NetStack.Common; using System; #if !SINGULARITY using System.Net; #endif using System.Net.IP; using Drivers.Net; namespace NetStack.Protocols { /** * declaration of the ICMP header (RFC 792) */ public class IcmpFormat { // size of common ICMP header public const int Size = 8; // ICMP Types public enum IcmpType { ECHO_REPLY=0, DESTINATION_UNREACHABLE=3, SOURCE_QUENCH=4, REDIRECT_MESSAGE=5, ECHO_REQUEST=8, TIME_EXCEEDED=11, PARAMETER_PROBLEM=12, TIMESTAMP_REQUEST=13, TIMESTAMP_REPLY=14, INFO_REQUEST=15, INFO_REPLY=16, ADD_MASK_REQUEST=17, ADD_MASK_REPLY=18 } // Destination Unreachable Codes public enum Type3Code { NET_UNREACHABLE=0, HOST_UNREACHABLE=1, PROT_UNREACHABLE=2 /* and some more, let's add as needed */ } // the common ICMP header public struct IcmpHeader { public byte type; public byte code; public ushort chksum; public ushort id; public ushort seq; } // write common ICMP header // (ignores the checksum field, notice that the checksum is // calculated for the whole packet!) public static int WriteIcmpHeader(byte[]! pkt,int offset,ref IcmpHeader hdr) { int o = offset; pkt[o++] = hdr.type; pkt[o++] = hdr.code; pkt[o++] = 0; pkt[o++] = 0; pkt[o++] = (byte)(hdr.id>>8); pkt[o++] = (byte)hdr.id; pkt[o++] = (byte)(hdr.seq>>8); pkt[o++] = (byte)hdr.seq; return o; } // construct an ICMP header from a packet public static bool ReadIcmpHeader(IBuffer! buf, out IcmpHeader hdr) { bool b; b = buf.Read8(out hdr.type); b &= buf.Read8(out hdr.code); b &= buf.ReadNet16(out hdr.chksum); b &= buf.ReadNet16(out hdr.id); b &= buf.ReadNet16(out hdr.seq); return b; } // Timestamp or Timestamp Reply Message public struct TimeStamps { public IcmpHeader header; public uint origTimeStamp; public uint rcvTimeStamp; public uint txTimeStamp; } // various ICMP messages // IcmpEchoRequest public struct IcmpEchoRequest { public IcmpHeader header; public byte[] data; // get the size of the message (depends on the received data) public int GetSize() { return (IcmpFormat.Size+data.Length); } // ctor public IcmpEchoRequest(ref IcmpHeader hdr,byte[]! msgData) { header=hdr; // set the data data = new byte[msgData.Length]; Array.Copy(msgData,0,data,0,msgData.Length); } public IcmpEchoRequest(ushort id,ushort seq,byte[]! msgData) { header.type = (byte)IcmpFormat.IcmpType.ECHO_REQUEST; header.code = 0; header.chksum = 0; header.seq = seq; header.id = id; // set the data data = new byte[msgData.Length]; Array.Copy(msgData,0,data,0,msgData.Length); } // write an ICMP echo request public static int WriteIcmpEchoRequest(byte[]! pkt,int offset,ref IcmpEchoRequest request) { int o = offset; // write the common header o = WriteIcmpHeader(pkt,offset,ref request.header); // dump the data (endian doesn't matter, since it is a user data) Array.Copy(request.data,0,pkt,o,request.data.Length); // calc the checksum ushort chk = IPFormat.Checksum(pkt, offset, request.GetSize()); pkt[offset+2] = (byte) (((ushort)chk) >> 8); pkt[offset+3] = (byte) (((ushort)chk) & 0xff); request.header.chksum = chk; return o; } // write an ICMP echo reply for an ICMP Echo request public static int WriteIcmpEchoReply(byte[]! pkt,int offset,ref IcmpEchoRequest request) { int o = offset; // change the request header code to 0 request.header.type = (byte)IcmpFormat.IcmpType.ECHO_REPLY; // write the common header o = WriteIcmpHeader(pkt,offset,ref request.header); // fix it again request.header.type = (byte)IcmpFormat.IcmpType.ECHO_REQUEST; // dump the data (endian doesn't matter, since it is a user data) Array.Copy(request.data,0,pkt,o,request.data.Length); // calc the checksum ushort chk = IPFormat.Checksum(pkt,offset,request.GetSize()); pkt[offset + 2] = (byte) (((ushort)chk) >> 8); pkt[offset + 3] = (byte) (((ushort)chk) & 0xff); request.header.chksum = chk; return o; } } // a fast method that uses that modified the ECHO request // packet to an ECHO reply. // return true if success public static void CreateFastEchoReply(byte[]! pkt,int icmpPacketSize) { // 1. switch the source IP and destination IP // 2. change ICMP code to IcmpFormat.IcmpType.ECHO_REPLY // 3. should use ARP table to find out the mac address of the target // (if it is on the local network) // CURRENTLY: just switch MAC addresses as well. // 4. recompute ICMP and IP checksums int ipStart = EthernetFormat.Size; int icmpStart = ipStart + IPFormat.Size; pkt[icmpStart] = (byte)IcmpFormat.IcmpType.ECHO_REPLY; // zero checksum pkt[icmpStart + 2] = 0; pkt[icmpStart + 3] = 0; ushort chk = IPFormat.Checksum(pkt, icmpStart, icmpPacketSize); pkt[icmpStart + 2] = (byte)(chk>>8); pkt[icmpStart + 3] = (byte)chk; // switch IPs byte[] tmp = new byte[6]; Array.Copy(pkt, ipStart + 16, tmp, 0, 4); // dest->tmp Array.Copy(pkt, ipStart + 12, pkt, ipStart + 16, 4); // src->dest Array.Copy(tmp, 0, pkt, ipStart + 12, 4); // tmp->src // zero the checksum pkt[ipStart + 10] = 0; pkt[ipStart + 11] = 0; // calculate checksum chk = IPFormat.Checksum(pkt, ipStart, IPFormat.Size); pkt[ipStart + 10] = (byte) (((ushort)chk) >> 8); pkt[ipStart + 11] = (byte) (((ushort)chk) & 0xff); // TBC: currently just switch mac addresses Array.Copy(pkt, 6, tmp, 0, 6); // src -> temp Array.Copy(pkt, 0, pkt, 6, 6); // dest -> src Array.Copy(tmp, 0, pkt, 0, 6); // tmp -> dest } } }