singrdk/base/Services/NetStack/Protocol/ICMPFmt.cs

235 lines
7.9 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// 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
}
}
}