
圖 03:IP 封包組成結構 — 前 20 bytes 為 IP 標頭,其後為選項與資料 (即 ICMP 封包)

圖 04:ICMP 封包組成結構 — Type(1B) Code(1B) Checksum(2B) Identifier(2B) Sequence(2B) Data
public class IcmpPacket
{
private byte _type; // 類型
private byte _subCode; // 代碼
private ushort _checkSum; // 檢驗和
private ushort _identifier; // 識別符
private ushort _sequenceNumber; // 序列號
private byte[] _data; // 選項資料
public IcmpPacket(byte type, byte subCode, ushort checkSum,
ushort identifier, ushort sequenceNumber, int dataSize)
{
_type = type;
_subCode = subCode;
_checkSum = checkSum;
_identifier = identifier;
_sequenceNumber = sequenceNumber;
_data = new byte[dataSize];
for (int i = 0; i < dataSize; i++)
_data[i] = (byte)'#'; // 填充任意資料
}
public ushort CheckSum
{
get { return _checkSum; }
set { _checkSum = value; }
}
// 序列化為位元組陣列
public int CountByte(byte[] buffer)
{
int i = 0;
byte[] b_type = new byte[1] { _type };
byte[] b_code = new byte[1] { _subCode };
byte[] b_cksum = BitConverter.GetBytes(_checkSum);
byte[] b_id = BitConverter.GetBytes(_identifier);
byte[] b_seq = BitConverter.GetBytes(_sequenceNumber);
Array.Copy(b_type, 0, buffer, i, b_type.Length); i += b_type.Length;
Array.Copy(b_code, 0, buffer, i, b_code.Length); i += b_code.Length;
Array.Copy(b_cksum, 0, buffer, i, b_cksum.Length); i += b_cksum.Length;
Array.Copy(b_id, 0, buffer, i, b_id.Length); i += b_id.Length;
Array.Copy(b_seq, 0, buffer, i, b_seq.Length); i += b_seq.Length;
Array.Copy(_data, 0, buffer, i, _data.Length); i += _data.Length;
return i;
}
// Internet 檢驗和演算法 (RFC 1071)
public static ushort SumOfCheck(ushort[] buffer)
{
int cksum = 0;
for (int i = 0; i < buffer.Length; i++)
cksum += buffer[i];
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (ushort)(~cksum);
}
}
_checkSum → 第二次序列化產生最終送出資料。BitConverter.GetBytes 產生 Little-Endian,但在 x86 平台上 Raw Socket 直接發送位元組陣列即可,網路層會處理位序轉換。