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