/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity / Netstack // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: TestDhcpClient.cs // using NetStack.Common; using System; using System.Threading; using System.Collections; using System.Net.IP; using Drivers.Net; using NetStack.Protocols; using NetStack.NetDrivers; namespace NetStack.Runtime.TestDhcpClient { public class FakeDhcpServer : IDebugAdapterClient { #region Constants public enum ServerState : byte { Running = 1, Failed = 2, Finished = 3 }; private const int NonDhcpHeaderSize = EthernetFormat.Size + IPFormat.Size + UdpFormat.Size; private readonly EthernetAddress ServerMac = EthernetAddress.Parse("00:50:f2:fe:dc:0d"); private readonly IPv4 ServerAddress = IPv4.Parse("10.0.0.99"); private readonly IPv4 HostAddress = IPv4.Parse("10.0.0.100"); private readonly IPv4 NetMask = IPv4.NetMask(19); private readonly IPv4 [] Routers = { IPv4.Parse("10.0.0.1") }; private readonly IPv4 [] DnsServers = { IPv4.Parse("10.0.0.10"), IPv4.Parse("10.0.1.10") }; private const uint RenewalTime = 20; private const uint RebindingTime = 40; private readonly String DomainName = "microsoft.com"; // // pairings. // // NB If action is not a DHCP Response, no response should be sent. // private DhcpFormat.MessageType [,] expectActions = { // Expect Discover and do nothing with it. { DhcpFormat.MessageType.Discover, DhcpFormat.MessageType.Discover }, // Expect Discover and make an offer { DhcpFormat.MessageType.Discover, DhcpFormat.MessageType.Offer }, // Expect request then cruelly reject it { DhcpFormat.MessageType.Request, DhcpFormat.MessageType.Nak }, // Expect Discover and make another offer { DhcpFormat.MessageType.Discover, DhcpFormat.MessageType.Offer }, // Expect request and fickly accept { DhcpFormat.MessageType.Request, DhcpFormat.MessageType.Ack }, // Live happily ever after }; #endregion Constants #region InstanceState private DebugAdapter adapter; private ServerState state; private IPv4 assignedAddress = IPv4.Zero; private int expectIndex = 0; #endregion InstanceState public ServerState State { get { return state; } } private void SetState(ServerState newState) { state = newState; } public void RegisterAdapter(DebugAdapter adapter) { this.adapter = adapter; } /// /// Check Ethernet, IP, and UDP headers match those expected. /// private bool ValidHeaders(NetPacket packet) { if (packet.Length < NonDhcpHeaderSize) { Console.WriteLine("Packet too short"); return false; } // // Check EthernetHeader // EthernetAddress originMac; EthernetAddress targetMac; EthernetFormat.Protocol protocol; EthernetFormat.Read(packet, out originMac, out targetMac, out protocol); if (originMac != adapter.HardwareAddress) { Console.WriteLine("Bad origin mac: {0}", originMac); return false; } else if (targetMac != EthernetAddress.Broadcast && targetMac != ServerMac) { Console.WriteLine("Bad target mac: {0}", targetMac); return false; } else if (protocol != EthernetFormat.Protocol.IP) { Console.WriteLine("Bad encapsulated protocol: {0}", protocol); return false; } // // Check IP Header // IPFormat.IPHeader ipHeader; if (IPFormat.ReadIPHeader(packet, out ipHeader) == false) { Console.WriteLine("Failed to read IP Header"); return false; } else if (ipHeader.Protocol != IPFormat.Protocol.UDP) { Console.WriteLine("Bad encapsulated IP protocol: {0}", ipHeader.Protocol); return false; } else if (ipHeader.Source != IPv4.Zero && ipHeader.Source != assignedAddress) { Console.WriteLine("Bad IP source address: {0}", ipHeader.Source); return false; } else if (ipHeader.Destination != IPv4.AllOnes && ipHeader.Destination != ServerAddress) { Console.WriteLine("Bad IP destination address: {0}", ipHeader.Destination); return false; } Console.WriteLine("{0} {1}", ipHeader.Source, ipHeader.Destination); // // Check UDP Header // UdpFormat.UdpHeader udpHeader; if (UdpFormat.ReadUdpHeader(packet, out udpHeader) == false) { Console.WriteLine("Failed to read UDP Header"); return false; } else if (udpHeader.srcPort != 68) { Console.WriteLine("Bad UDP source port: {0}", udpHeader.srcPort); return false; } else if (udpHeader.dstPort != 67) { Console.WriteLine("Bad UDP destination port: {0}", udpHeader.dstPort); return false; } return true; } public void Receive(NetPacket packet) { packet.Reset(); Console.WriteLine("Received {0} bytes", packet.Length); if (ValidHeaders(packet) == false) { SetState(ServerState.Failed); return; } try { DhcpFormat dhcp = DhcpFormat.Parse(packet); SortedList options = dhcp.GetOptions(); DhcpByteOption message = options[DhcpMessageType.OptionCode] as DhcpByteOption; if (message == null) { Console.WriteLine("MessageType option not found"); SetState(ServerState.Failed); return; } DhcpFormat.MessageType messageType = (DhcpFormat.MessageType) message.Value; if (DhcpFormat.IsRequestMessage(messageType) == false) { Console.WriteLine("Inappropriate message type: {0}", message.Value); SetState(ServerState.Failed); return; } DhcpFormat.MessageType expected = expectActions[expectIndex,0]; DhcpFormat.MessageType action = expectActions[expectIndex,1]; expectIndex++; if (messageType != expected) { Console.WriteLine("Unexpected message type: {0} != {1}", messageType, expected); SetState(ServerState.Failed); return; } if (DhcpFormat.IsResponseMessage(action)) { Respond(dhcp, action); } if (expectIndex == expectActions.Length / expectActions.Rank) { SetState(ServerState.Finished); } } catch (Exception e) { Console.WriteLine("Bad Dhcp packet: {0}", e); SetState(ServerState.Failed); } } private void FillOptions(DhcpFormat request, DhcpFormat response) { SortedList requestOptions = request.GetOptions(); response.AddOption(DhcpServerID.Create(ServerAddress)); response.AddOption(DhcpRenewalTime.Create(RenewalTime)); response.AddOption(DhcpRebindingTime.Create(RebindingTime)); foreach (IDhcpOption option in requestOptions.Values) { byte optionCode = option.OptionCode; Console.WriteLine("({0}) {1}", optionCode, DhcpOptionParser.GetOptionName(optionCode)); } DhcpMultiByteOption parameters = requestOptions[DhcpParameterRequest.OptionCode] as DhcpMultiByteOption; if (parameters == null) return; foreach (byte parameter in parameters.Values) { switch (parameter) { case DhcpSubnetMask.OptionCode: response.AddOption(DhcpSubnetMask.Create(NetMask)); break; case DhcpDomainName.OptionCode: response.AddOption( DhcpDomainName.Create(DomainName.ToCharArray()) ); break; case DhcpRouter.OptionCode: response.AddOption(DhcpRouter.Create(Routers)); break; case DhcpDomainNameServer.OptionCode: response.AddOption( DhcpDomainNameServer.Create(DnsServers) ); break; } } } private void Respond(DhcpFormat request, DhcpFormat.MessageType m) { DhcpFormat response = new DhcpFormat(m); response.TransactionID = request.TransactionID; EthernetAddress hwAddress = request.GetHardwareAddress(); response.SetHardwareAddress(hwAddress); switch (m) { case DhcpFormat.MessageType.Offer: response.NextServerIPAddress = ServerAddress; response.AddOption( DhcpIPAddressLeaseTime.Create(RebindingTime) ); goto case DhcpFormat.MessageType.Ack; case DhcpFormat.MessageType.Ack: response.YourIPAddress = HostAddress; assignedAddress = HostAddress; FillOptions(request, response); break; case DhcpFormat.MessageType.Nak: // Nothing to do break; default: return; } SendResponsePacket(response); } private void SendResponsePacket(DhcpFormat dhcp) { int packetSize = dhcp.Size + UdpFormat.Size + IPFormat.Size + EthernetFormat.Size; byte [] packet = new byte [packetSize]; // Write out DHCP packet int dhcpSize = dhcp.Size; int dhcpOffset = packet.Length - dhcpSize; dhcp.Write(packet, dhcpOffset); // Create UDP Header UdpFormat.UdpHeader udpHeader = new UdpFormat.UdpHeader(); udpHeader.srcPort = DhcpFormat.ServerPort; udpHeader.dstPort = DhcpFormat.ClientPort; udpHeader.length = (ushort)(UdpFormat.Size + dhcpSize); // Create IP Header IPFormat.IPHeader ipHeader = new NetStack.Protocols.IPFormat.IPHeader(); ipHeader.SetDefaults(IPFormat.Protocol.UDP); IPFormat.SetDontFragBit(ipHeader); ipHeader.Source = ServerAddress; ipHeader.Destination = IPv4.Broadcast; ipHeader.totalLength = (ushort)(IPFormat.Size + UdpFormat.Size + dhcpSize); // Write out IP and Header int udpOffset = packet.Length - dhcp.Size - UdpFormat.Size; int ipOffset = udpOffset - IPFormat.Size; UdpFormat.WriteUdpPacket(packet, ipOffset, ref ipHeader, ref udpHeader, packet, dhcpOffset, dhcpSize); // Add Ethernet Header EthernetFormat.Write(packet, 0, ServerMac, EthernetAddress.Broadcast, EthernetFormat.Protocol.IP); NetPacket np = new NetPacket(packet); if (adapter.ReceivePacket(np) == false) { Console.WriteLine("Failed to send packet"); SetState(ServerState.Failed); } } } public class Test { static int Main() { StaticConfiguration.Initialize(); StaticConfiguration.Start(); FakeDhcpServer fakeDhcpServer = new FakeDhcpServer(); DebugAdapter adapter = new DebugAdapter(fakeDhcpServer); Console.WriteLine("Created Debug Adapter {0}", adapter.HardwareAddress); Core.Instance().RegisterAdapter(adapter, 64); DhcpClient dc = new DhcpClient(adapter); dc.Start(); while (fakeDhcpServer.State == FakeDhcpServer.ServerState.Running) { Thread.Sleep(TimeSpan.FromSeconds(1)); } dc.Stop(); Console.WriteLine("Removing Adapter."); Core.Instance().DeregisterAdapter(adapter); StaticConfiguration.Stop(); if (fakeDhcpServer.State == FakeDhcpServer.ServerState.Failed) { return 1; } return 0; } } }