404 lines
14 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|