385 lines
12 KiB
C#
385 lines
12 KiB
C#
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: DhcpFmt.cs
|
|
//
|
|
// Note: The key DHCP RFC is 2131.
|
|
//
|
|
|
|
using NetStack.Common;
|
|
using System;
|
|
using System.Collections;
|
|
|
|
using System.Net.IP;
|
|
using Drivers.Net;
|
|
|
|
namespace NetStack.Protocols
|
|
{
|
|
public class InvalidDhcpFormatException : SystemException
|
|
{
|
|
public InvalidDhcpFormatException(String message)
|
|
: base(message)
|
|
{
|
|
}
|
|
|
|
public InvalidDhcpFormatException(String message,
|
|
Exception innerException)
|
|
: base(message, innerException)
|
|
{
|
|
}
|
|
|
|
public InvalidDhcpFormatException(String format, params object [] args)
|
|
: base (String.Format(format, args))
|
|
{
|
|
}
|
|
}
|
|
|
|
sealed public class DhcpFormat
|
|
{
|
|
#region FormatFields
|
|
internal byte op; // Message Type
|
|
internal byte htype; // Hardware Type
|
|
internal byte hlen; // Hardware Length
|
|
internal byte hops; // Relay hops
|
|
internal uint xid; // Transaction ID (host order)
|
|
internal ushort secs; // Seconds since transaction start (host order)
|
|
internal ushort flags; // Flags (host order)
|
|
internal uint ciaddr; // Client IP if known (host order)
|
|
internal uint yiaddr; // Your IP address (host order)
|
|
internal uint siaddr; // Next Server address (host order)
|
|
internal uint giaddr; // Relay agent IP address (host order)
|
|
internal byte [] chaddr; // Client h/w address (16-bytes)
|
|
internal byte []! sname; // Optional server name (64-bytes null term)
|
|
internal byte [] file; // Boot file name (128-bytes null terminated)
|
|
internal uint cookie; // Mandatory DHCP option
|
|
internal byte [] options; // Variable size
|
|
#endregion // FormatFields
|
|
|
|
internal int optionsUsedLength;
|
|
|
|
// Constants
|
|
public const int MinLength = 240; // Min DHCP packet
|
|
public const int HardwareAddressLength = 16; // chaddr fixed length
|
|
public const int ServerNameLength = 64; // sname fixed length
|
|
public const int BootFileLength = 128; // file length
|
|
public const int MaxOptionsLength = 512; // Really H/W dependent
|
|
private const byte EndMarker = 255;
|
|
private const int EndMarkerLength = 1;
|
|
|
|
internal const uint DhcpCookie = 0x63825363; // DHCP Cookie
|
|
|
|
public const int ClientPort = 68;
|
|
public const int ServerPort = 67;
|
|
|
|
public enum BootType : byte
|
|
{
|
|
NotSpecified = 0,
|
|
Request = 1, // Client to Server Message
|
|
Reply = 2 // Server to Client Message
|
|
}
|
|
|
|
public enum MessageType : byte
|
|
{
|
|
Discover = 1,
|
|
Offer = 2,
|
|
Request = 3,
|
|
Decline = 4,
|
|
Ack = 5,
|
|
Nak = 6,
|
|
Release = 7,
|
|
Inform = 8
|
|
}
|
|
|
|
// http://www.iana.org/assignments/arp-parameters
|
|
public enum HType : byte
|
|
{
|
|
Ethernet = 1,
|
|
ExperimentalEthernet = 2,
|
|
AX25 = 3,
|
|
ProNET = 4,
|
|
Chaos = 5,
|
|
Serial = 20,
|
|
IEEE1394_1995 = 24,
|
|
ARPSec = 30,
|
|
IPSecTunnel = 31,
|
|
Infiniband = 32
|
|
}
|
|
|
|
public DhcpFormat(BootType bootType)
|
|
{
|
|
op = (byte)bootType;
|
|
chaddr = new byte[HardwareAddressLength];
|
|
sname = new byte[ServerNameLength];
|
|
file = new byte[BootFileLength];
|
|
cookie = DhcpCookie;
|
|
options = new byte[MaxOptionsLength];
|
|
flags = 0x8000; // Request broadcast responses
|
|
optionsUsedLength = 0;
|
|
|
|
base();
|
|
}
|
|
|
|
public DhcpFormat(MessageType messageType)
|
|
: this(GetBootType(messageType))
|
|
{
|
|
AddOption(DhcpMessageType.Create((byte)messageType));
|
|
}
|
|
|
|
public int Size
|
|
{
|
|
get { return MinLength + optionsUsedLength + EndMarkerLength; }
|
|
}
|
|
|
|
public BootType BootMessageType
|
|
{
|
|
get { return (BootType)op; }
|
|
}
|
|
|
|
public IPv4 ClientIPAddress
|
|
{
|
|
get { return new IPv4(ciaddr); }
|
|
set { ciaddr = (uint)value; }
|
|
}
|
|
|
|
public IPv4 YourIPAddress
|
|
{
|
|
get { return new IPv4(yiaddr); }
|
|
set { yiaddr = (uint)value; }
|
|
}
|
|
|
|
public IPv4 NextServerIPAddress
|
|
{
|
|
get { return new IPv4(siaddr); }
|
|
set { siaddr = (uint)value; }
|
|
}
|
|
|
|
public IPv4 RelayAgentIPAddress
|
|
{
|
|
get { return new IPv4(giaddr); }
|
|
set { giaddr = (uint)value; }
|
|
}
|
|
|
|
public byte Hops
|
|
{
|
|
get { return hops; }
|
|
set { hops = value; }
|
|
}
|
|
|
|
public uint TransactionID
|
|
{
|
|
get { return xid; }
|
|
set { xid = value; }
|
|
}
|
|
|
|
public ushort TransactionSeconds
|
|
{
|
|
get { return secs; }
|
|
set { secs = value; }
|
|
}
|
|
|
|
public ushort Flags
|
|
{
|
|
get { return flags; }
|
|
set { flags = value; }
|
|
}
|
|
|
|
public string! ServerName
|
|
{
|
|
get { return BytesToString(sname); }
|
|
set { StringToBytes(value, sname); }
|
|
}
|
|
|
|
public string! BootFile
|
|
{
|
|
get { return BytesToString(file); }
|
|
set { StringToBytes(value, file); }
|
|
}
|
|
|
|
public void SetHardwareAddress (EthernetAddress ea)
|
|
{
|
|
hlen = 6;
|
|
htype = (byte)HType.Ethernet;
|
|
chaddr = new byte[HardwareAddressLength];
|
|
ea.CopyOut(chaddr, 0);
|
|
}
|
|
|
|
public EthernetAddress GetHardwareAddress()
|
|
{
|
|
if (htype != (byte)HType.Ethernet) {
|
|
// XXX Throw some nasty exception
|
|
}
|
|
return EthernetAddress.ParseBytes(chaddr, 0);
|
|
}
|
|
|
|
public SortedList! GetOptions()
|
|
{
|
|
return DhcpOptionParser.Parse(options, 0, this.optionsUsedLength);
|
|
}
|
|
|
|
public void AddOption(IDhcpOption! option)
|
|
{
|
|
int newLength = option.PayloadLength + 2 + optionsUsedLength;
|
|
if (newLength > this.options.Length) {
|
|
newLength = (newLength + 64) & ~3;
|
|
byte [] newOptions = new byte [newLength];
|
|
this.options.CopyTo(newOptions, 0);
|
|
this.options = newOptions;
|
|
}
|
|
option.Pack(this.options, ref optionsUsedLength);
|
|
}
|
|
|
|
private static String! BytesToString(byte []! bytes)
|
|
{
|
|
// Poorman's StringBuilder
|
|
char[] chars = new char[bytes.Length];
|
|
int i;
|
|
for (i = 0; i < bytes.Length; i++) {
|
|
if (bytes[i] == 0)
|
|
break;
|
|
chars[i] = (char)bytes[i];
|
|
}
|
|
return new String(chars, 0, i);
|
|
}
|
|
|
|
private static void StringToBytes(string! from, byte []! to)
|
|
{
|
|
int end = Math.Min(from.Length, to.Length);
|
|
for (int i = 0; i < end; i++) {
|
|
to[i] = (byte)from[i];
|
|
}
|
|
// Zero-terminate
|
|
if (end != to.Length) {
|
|
to[end] = 0;
|
|
}
|
|
}
|
|
|
|
public static DhcpFormat! Parse(IBuffer! buffer)
|
|
{
|
|
DhcpFormat p = new DhcpFormat(BootType.NotSpecified);
|
|
p.optionsUsedLength = 0;
|
|
|
|
if (buffer.Available < DhcpFormat.MinLength) {
|
|
throw new InvalidDhcpFormatException("Format less than minimum size");
|
|
}
|
|
|
|
buffer.Read8(out p.op);
|
|
if (p.op != (byte)BootType.Request &&
|
|
p.op != (byte)BootType.Reply)
|
|
{
|
|
throw new InvalidDhcpFormatException("Bad Type (op = {0})", p.op);
|
|
}
|
|
|
|
buffer.Read8(out p.htype);
|
|
// No check
|
|
|
|
buffer.Read8(out p.hlen);
|
|
if (p.hlen > HardwareAddressLength) {
|
|
throw new InvalidDhcpFormatException("Bad address length (hlen {0})",
|
|
p.hlen);
|
|
}
|
|
|
|
buffer.Read8(out p.hops);
|
|
// No check
|
|
|
|
buffer.ReadNet32(out p.xid);
|
|
buffer.ReadNet16(out p.secs);
|
|
buffer.ReadNet16(out p.flags);
|
|
buffer.ReadNet32(out p.ciaddr);
|
|
buffer.ReadNet32(out p.yiaddr);
|
|
buffer.ReadNet32(out p.siaddr);
|
|
buffer.ReadNet32(out p.giaddr);
|
|
|
|
buffer.ReadBytes(p.chaddr, 0, HardwareAddressLength);
|
|
buffer.ReadBytes(p.sname, 0, ServerNameLength);
|
|
buffer.ReadBytes(p.file, 0, BootFileLength);
|
|
|
|
buffer.ReadNet32(out p.cookie);
|
|
if (p.cookie != DhcpCookie) {
|
|
throw new InvalidDhcpFormatException("Bad cookie (0x{0x:x8})",
|
|
p.cookie);
|
|
}
|
|
|
|
if (buffer.Available > p.options.Length) {
|
|
p.options = new byte [buffer.Available];
|
|
}
|
|
p.optionsUsedLength = buffer.Available;
|
|
buffer.ReadBytes(p.options, 0, buffer.Available);
|
|
|
|
return p;
|
|
}
|
|
|
|
private static void Write(byte[]! buffer, ref int offset, byte value)
|
|
{
|
|
buffer[offset++] = value;
|
|
}
|
|
|
|
private static void Write(byte[]! buffer, ref int offset, ushort value)
|
|
{
|
|
buffer[offset++] = (byte)(value >> 8);
|
|
buffer[offset++] = (byte)(value);
|
|
}
|
|
|
|
private static void Write(byte[]! buffer, ref int offset, uint value)
|
|
{
|
|
buffer[offset++] = (byte)(value >> 24);
|
|
buffer[offset++] = (byte)(value >> 16);
|
|
buffer[offset++] = (byte)(value >> 8);
|
|
buffer[offset++] = (byte)(value);
|
|
}
|
|
|
|
private static void Write(byte[]! buffer,
|
|
ref int offset,
|
|
byte[]! values,
|
|
int valuesLength)
|
|
{
|
|
for (int i = 0; i < valuesLength; i++) {
|
|
buffer[offset++] = values[i];
|
|
}
|
|
}
|
|
|
|
public int Write(byte[]! buffer, int offset)
|
|
{
|
|
Write(buffer, ref offset, this.op);
|
|
Write(buffer, ref offset, this.htype);
|
|
Write(buffer, ref offset, this.hlen);
|
|
Write(buffer, ref offset, this.hops);
|
|
Write(buffer, ref offset, this.xid);
|
|
Write(buffer, ref offset, this.secs);
|
|
Write(buffer, ref offset, this.flags);
|
|
Write(buffer, ref offset, this.ciaddr);
|
|
Write(buffer, ref offset, this.yiaddr);
|
|
Write(buffer, ref offset, this.siaddr);
|
|
Write(buffer, ref offset, this.giaddr);
|
|
Write(buffer, ref offset, this.chaddr, this.chaddr.Length);
|
|
Write(buffer, ref offset, this.sname, this.sname.Length);
|
|
Write(buffer, ref offset, this.file, this.file.Length);
|
|
Write(buffer, ref offset, this.cookie);
|
|
|
|
Write(buffer, ref offset, this.options, this.optionsUsedLength);
|
|
Write(buffer, ref offset, EndMarker);
|
|
return offset;
|
|
}
|
|
|
|
public static bool IsRequestMessage(MessageType m)
|
|
{
|
|
return (m == MessageType.Discover || m == MessageType.Request ||
|
|
m == MessageType.Decline || m == MessageType.Release ||
|
|
m == MessageType.Inform);
|
|
}
|
|
|
|
public static bool IsResponseMessage(MessageType m)
|
|
{
|
|
return (m == MessageType.Offer || m == MessageType.Ack ||
|
|
m == MessageType.Nak);
|
|
}
|
|
|
|
public static BootType GetBootType(MessageType m)
|
|
{
|
|
return IsRequestMessage(m) ? BootType.Request : BootType.Reply;
|
|
}
|
|
}
|
|
}
|