singrdk/base/Services/NetStack/Runtime/TestDhcpClient.cs

404 lines
14 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// 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";
//
// <Expect,Action> 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;
}
/// <summary>
/// Check Ethernet, IP, and UDP headers match those expected.
/// </summary>
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;
}
}
}