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

336 lines
11 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;
using System.Diagnostics;
#if !SINGULARITY
using System.Net;
// using System.Net.Sockets; // for AddressFamily
#endif
using System.Net.IP;
using Drivers.Net;
namespace NetStack.Protocols
{
public class IPFormat
{
// size of minimum IP header (wo options)
public const int Size = 20;
// all header fields are given in host endianness
public class IPHeader
{
public byte verLen; // 0:3 Version, 4:7 Header length in DWORDs.
public byte tos;
public ushort totalLength;
public ushort id;
public ushort offset;
public byte ttl;
public byte protocol;
public ushort checksum;
internal IPv4 srcAddr;
internal IPv4 destAddr;
// some wrappers
public IPv4 Source
{
get { return srcAddr; }
set { srcAddr = value; }
}
public IPv4 Destination
{
get { return destAddr; }
set { destAddr = value; }
}
public Protocol Protocol
{
get { return (Protocol) protocol; }
set { protocol = (byte) value; }
}
// set default values
public void SetDefaults(Protocol type)
{
verLen = 0x45; // version=4, header_len=5
tos = 0;
totalLength = 0;
id = 0;
offset = 0;
ttl = DefaultTTL;
protocol = (byte)type;
checksum = 0;
srcAddr = IPv4.Zero;
destAddr = IPv4.Zero;
}
}
// construct an IP header from a packet
// if something is wrong, return the same start value
public static bool ReadIPHeader(IBuffer! buf, out IPHeader! iphdr)
{
// initialize an IPHeader struct
iphdr = new IPHeader();
bool b;
uint addr;
b = buf.Read8(out iphdr.verLen);
b &= buf.Read8(out iphdr.tos);
b &= buf.ReadNet16(out iphdr.totalLength);
b &= buf.ReadNet16(out iphdr.id);
b &= buf.ReadNet16(out iphdr.offset);
b &= buf.Read8(out iphdr.ttl);
b &= buf.Read8(out iphdr.protocol);
b &= buf.ReadNet16(out iphdr.checksum);
b &= buf.ReadNet32(out addr);
iphdr.srcAddr = new IPv4(addr);
b &= buf.ReadNet32(out addr);
iphdr.destAddr = new IPv4(addr);
return b;
}
// TTL default value
public const byte DefaultTTL = 128;
// IP protocols identifiers
public enum Protocol : byte
{
ICMP = 1,
IGMP = 2,
TCP = 6,
IGRP = 9,
UDP = 17,
GRE = 47,
ESP = 50,
AH = 51,
SKIP = 57,
EIGRP = 88,
OSPF = 89,
L2TP = 115
}
// some getters...
public static ushort GetVersion(IPHeader! hdr)
{
return (ushort)(hdr.verLen & 0xF0);
}
// minimum value is 20, if it is 60 bytes, then
// Options field is valid.
public static uint GetHeaderLength(IPHeader! hdr)
{
return ((uint)(hdr.verLen & 0xF));
}
public static bool IsMoreFragSet(IPHeader! hdr)
{
ushort flags = (ushort)(((ushort)hdr.offset) >> 13);
return ((flags & 0x01)==0x01);
}
public static bool IsDontFragSet(IPHeader! hdr)
{
ushort flags = (ushort)(((ushort)hdr.offset) >> 13);
return ((flags & 0x02)==0x02);
}
public static void SetDontFragBit(IPHeader! hdr)
{
hdr.offset=(ushort)(hdr.offset|0x4000);
}
public static ushort GetFragOffset(IPHeader! hdr)
{
return (ushort)(hdr.offset & 0x1FFF);
}
// calculate's one's complement
public static ushort SumShortValues(byte[]! buffer,
int offset,
int length)
{
//generate an IP checksum based on a given data buffer
uint chksum = 0;
int end = offset + (length & ~1);
int i = offset;
while (i != end)
{
chksum += (uint)((((ushort)buffer[i++]) << 8) +
(ushort)buffer[i++]);
}
if (i != offset + length)
{
// padding at the end!
chksum += (uint)(((ushort)buffer[i]) << 8);
}
// Double-wrap chksum
chksum = (chksum & 0xFFFF) + (chksum >> 16);
chksum = (chksum & 0xFFFF) + (chksum >> 16);
return (ushort)chksum;
}
public static ushort SumShortValues(ushort currentChecksum,
ushort valueToAdd)
{
uint chksum = currentChecksum;
chksum += valueToAdd;
// Double-wrap chksum
chksum = (chksum & 0xFFFF) + (chksum >> 16);
chksum = (chksum & 0xFFFF) + (chksum >> 16);
return (ushort)chksum;
}
public static bool IsCheckSumOK(IPHeader! hdr)
{
ushort sum = (ushort) ((((int)hdr.verLen) << 8) + ((int)hdr.tos));
sum = SumShortValues(sum, hdr.totalLength);
sum = SumShortValues(sum, hdr.id);
sum = SumShortValues(sum, hdr.offset);
sum = SumShortValues(sum, (ushort) ((((int)hdr.ttl) << 8) + ((int)hdr.protocol)));
sum = SumShortValues(sum, (ushort) (((uint)hdr.srcAddr) >> 16));
sum = SumShortValues(sum, (ushort) (((uint)hdr.srcAddr) & 0xFFFFU));
sum = SumShortValues(sum, (ushort) (((uint)hdr.destAddr) >> 16));
sum = SumShortValues(sum, (ushort) (((uint)hdr.destAddr) & 0xFFFFU));
sum = (ushort)~sum;
if (sum != hdr.checksum)
{
Console.WriteLine("Bad IP Checksum {0:x4} != {1:x4}",
hdr.checksum, sum);
}
return (sum == hdr.checksum);
}
// calculate the ip header checksum
public static ushort Checksum(byte[]! buffer,
int ipHeaderOffset,
int ipHeaderLength)
{
return (ushort)~SumShortValues(buffer,
ipHeaderOffset,
ipHeaderLength);
}
// calc the sum of the 16bits pseudo header fields
// (to be later used for transport level checksum calculation)
// offset points to the start of the IP header
// also return the protocolLength=header+data
public static ushort SumPseudoHeader(byte[]! pkt,
int offset,
ref ushort protocolLength)
{
// source IP + destination IP
uint sum = SumShortValues(pkt, offset + 12, 8);
// add the protocol field (with zero MSB)
sum += pkt[offset + 9];
// add udp length = ip_total_length-ip_header_len
ushort ipTotalLen = (ushort)(((ushort)(pkt[offset + 2] << 8)) |
((ushort)pkt[offset + 3]));
// ushort ipSize = (ushort)((pkt[offset] & 0x0f) * 4);
protocolLength = (ushort)(ipTotalLen - IPFormat.Size);
sum += protocolLength;
// Double-wrap the sum
sum = (sum & 0xFFFF) + (sum >> 16);
sum = (sum & 0xFFFF) + (sum >> 16);
return (ushort)sum;
}
public static ushort SumPseudoHeader(IPHeader! ipHeader)
{
uint sum = 0;
uint origin = (uint)ipHeader.srcAddr;
uint target = (uint)ipHeader.destAddr;
sum = (origin & 0xffff) + ((origin >> 16) & 0xffff);
sum += (target & 0xffff) + ((target >> 16) & 0xffff);
sum += ipHeader.protocol;
sum += (ipHeader.totalLength - (ipHeader.verLen & 0xfu) * 4u);
// Double-wrap the sum
sum = (sum & 0xFFFF) + (sum >> 16);
sum = (sum & 0xFFFF) + (sum >> 16);
return (ushort)sum;
}
// writes an IP header to a packet
// return the next place to write to, fixes the checksum field
public static int WriteIPHeader(byte[]! pkt, int offset, IPHeader! hdr)
{
// check we have enough packet space
if (pkt.Length - offset < Size)
return offset;
int o = offset;
pkt[o++] = hdr.verLen;
pkt[o++] = hdr.tos;
pkt[o++] = (byte) (((ushort)hdr.totalLength) >> 8);
pkt[o++] = (byte) (((ushort)hdr.totalLength) & 0xff);
pkt[o++] = (byte) (((ushort)hdr.id) >> 8);
pkt[o++] = (byte) (((ushort)hdr.id) & 0xff);
pkt[o++] = (byte) (((ushort)hdr.offset) >> 8);
pkt[o++] = (byte) (((ushort)hdr.offset) & 0xff);
pkt[o++] = hdr.ttl;
pkt[o++] = hdr.protocol;
pkt[o++] = 0;
pkt[o++] = 0;
// set the ip addresses
hdr.srcAddr.CopyOut(pkt, o);
o += IPv4.Length;
hdr.destAddr.CopyOut(pkt, o);
o += IPv4.Length;
// calculate checksum
ushort chk = Checksum(pkt, offset, Size);
pkt[offset + 10] = (byte) (((ushort)chk) >> 8);
pkt[offset + 11] = (byte) (((ushort)chk) & 0xff);
// save the header checksum
hdr.checksum = (ushort)chk;
return o;
}
/// <summary>
/// Update checksum when a 16-bit value that composed the original
/// checksum changes. Useful for recomputing checksum when
/// TTL changes when acting as a router.
/// </summary>
public static ushort UpdateChecksum(ushort checksum,
ushort oldValue,
ushort newValue)
{
// From RFC1624
ushort newChecksum = (ushort)~checksum;
newChecksum += (ushort)~oldValue;
newChecksum += newValue;
return (ushort)~newChecksum;
}
}
}