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

433 lines
14 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Netstack / Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: DhcpClientFSM.cs
//
// Description: DHCP Client Finite State machine.
//
// Note: This file does not yet implement the RENEWING or
// REBINDING states.
2008-11-17 18:29:00 -05:00
using Microsoft.Singularity;
2008-03-05 09:52:00 -05:00
using System;
using System.Diagnostics;
using System.Collections;
using System.Net.IP;
using Drivers.Net;
namespace NetStack.Runtime
{
using Protocols;
class DhcpClientState
{
protected DhcpClient client;
protected string stateName;
internal DhcpClientState(DhcpClient client, string stateName)
{
this.client = client;
this.stateName = stateName;
}
internal string Name
{
get { return stateName; }
}
/// <summary>
/// State should perform processing as DhcpClient recognizes it
/// as the currently active event.
/// </summary>
internal virtual void EnterEvent()
{
Core.Log("Entering State {0}\n", stateName);
}
/// <summary>
/// State should process that has arrived.
/// </summary>
internal virtual void ReceiveEvent(DhcpFormat! df)
{
DhcpClient.DebugPrint("FSM Ignored DHCP packet: {0}\n", stateName);
}
/// <summary>
/// Renewal timer expiry event.
/// </summary>
internal virtual void RenewalTimeoutEvent()
{
DhcpClient.DebugPrint("FSM Renewal Timeout: {0}\n", stateName);
}
/// <summary>
/// Rebind timer expiry event.
/// </summary>
internal virtual void RebindTimeoutEvent()
{
DhcpClient.DebugPrint("FSM Rebind Timeout: {0}\n", stateName);
}
/// <summary>
/// General purpose timer expiry event.
/// </summary>
internal virtual void StateTimeoutEvent()
{
DhcpClient.DebugPrint("FSM State Specific Timeout: {0}\n",
stateName);
}
};
internal class DhcpClientStateInitialize : DhcpClientState
{
internal DhcpClientStateInitialize(DhcpClient client)
: base(client, "Initialize")
{
}
internal override void EnterEvent()
{
client.StartNewTransaction();
DhcpFormat dhcp =
new DhcpFormat(DhcpFormat.MessageType.Discover);
dhcp.TransactionID = client.TransactionID;
dhcp.SetHardwareAddress(client.MacAddress);
#if ADVERTISE_CLIENT_ID
// Add Client Identifier for self
//
// This is disabled because the Windows
// DHCP server allocates us a different address with the
// client id present if we networked booted. Thus having the
// identifier breaks static DHCP entries which we use
// for test machines.
EthernetAddress macAddress = client.MacAddress;
dhcp.AddOption(DhcpClientID.Create(macAddress.GetAddressBytes()));
#endif
// Add parameters we'd like to know about
dhcp.AddOption(DhcpParameterRequest.Create(
DhcpClient.StandardRequestParameters
)
);
// dhcp.AddOption(DhcpAutoConfigure.Create(0));
client.Send(EthernetAddress.Broadcast, dhcp);
client.ChangeState(new DhcpClientStateSelecting(client));
}
}
internal class DhcpClientStateSelecting : DhcpClientState
{
private readonly TimeSpan StateTimeout = TimeSpan.FromSeconds(5);
internal DhcpClientStateSelecting(DhcpClient client)
: base(client, "Selecting")
{
}
internal override void EnterEvent()
{
client.SetStateTimeout(DateTime.Now + StateTimeout);
}
internal override void StateTimeoutEvent()
{
// Received no valid offers
client.ChangeState(new DhcpClientStateInitialize(client));
}
internal override void ReceiveEvent(DhcpFormat! dhcp)
{
// Check if message is in response to our request
if (dhcp.BootMessageType != DhcpFormat.BootType.Reply ||
dhcp.TransactionID != client.TransactionID ||
dhcp.GetHardwareAddress() != client.MacAddress)
{
return;
}
IPv4 serverAddress = dhcp.NextServerIPAddress;
// Check if offered address is valid (ie not zero
// and below class E)
IPv4 offeredAddress = dhcp.YourIPAddress;
2008-11-17 18:29:00 -05:00
if (offeredAddress == IPv4.Any || offeredAddress.IsMulticast()) {
2008-03-05 09:52:00 -05:00
return;
}
// Check if message is an offer
SortedList offeredOptions = dhcp.GetOptions();
DhcpByteOption messageType
= offeredOptions[DhcpMessageType.OptionCode] as DhcpByteOption;
if (messageType == null ||
messageType.Value != (byte)DhcpFormat.MessageType.Offer)
{
return;
}
// Must have parameters
byte [] parameters = new byte [] {
DhcpSubnetMask.OptionCode,
DhcpRouter.OptionCode,
DhcpDomainNameServer.OptionCode
};
2008-11-17 18:29:00 -05:00
foreach (byte p in parameters) {
2008-03-05 09:52:00 -05:00
IDhcpOption ido = offeredOptions[p] as IDhcpOption;
2008-11-17 18:29:00 -05:00
if (ido == null) {
2008-03-05 09:52:00 -05:00
return;
}
}
client.CancelStateTimeout();
client.ChangeState(new DhcpClientStateRequesting(client,
serverAddress,
offeredAddress,
offeredOptions));
}
}
internal class DhcpClientStateRequesting : DhcpClientState
{
IPv4 serverAddress;
IPv4 offeredAddress;
SortedList offeredOptions;
private readonly TimeSpan StateTimeout = TimeSpan.FromSeconds(5);
internal DhcpClientStateRequesting(DhcpClient client,
IPv4 serverAddress,
IPv4 offeredAddress,
SortedList offeredOptions)
: base(client, "Requesting")
{
this.serverAddress = serverAddress;
this.offeredAddress = offeredAddress;
this.offeredOptions = offeredOptions;
}
internal override void EnterEvent()
{
client.SetStateTimeout(DateTime.Now + StateTimeout);
DhcpFormat dhcp =
new DhcpFormat(DhcpFormat.MessageType.Request);
dhcp.TransactionID = client.TransactionID;
dhcp.TransactionSeconds = client.TransactionSeconds;
dhcp.SetHardwareAddress(client.MacAddress);
#if ADVERTISE_CLIENT_ID
dhcp.AddOption(
DhcpClientID.Create(client.MacAddress.GetAddressBytes())
);
#endif
dhcp.AddOption(
DhcpRequestedIPAddress.Create(offeredAddress)
);
// Add parameters we'd like to know about
dhcp.AddOption(DhcpParameterRequest.Create(
DhcpClient.StandardRequestParameters
)
);
client.Send(EthernetAddress.Broadcast, dhcp);
}
private static void TakeOption(SortedList! offeredOptions,
byte optionCode,
DhcpFormat! dhcpFormat)
{
IDhcpOption option = offeredOptions[optionCode] as IDhcpOption;
2008-11-17 18:29:00 -05:00
if (option != null) {
2008-03-05 09:52:00 -05:00
dhcpFormat.AddOption(option);
}
}
internal override void StateTimeoutEvent()
{
client.ChangeState(new DhcpClientStateInitialize(client));
}
internal override void ReceiveEvent(DhcpFormat! dhcp)
{
// Check if message is in response to our request
if (dhcp.BootMessageType != DhcpFormat.BootType.Reply ||
dhcp.TransactionID != client.TransactionID ||
dhcp.GetHardwareAddress() != client.MacAddress)
{
return;
}
IPv4 serverAddress = dhcp.NextServerIPAddress;
// Check if offered address is valid (ie not zero
// and below class E)
IPv4 offeredAddress = dhcp.YourIPAddress;
2008-11-17 18:29:00 -05:00
if (offeredAddress == IPv4.Any || offeredAddress.IsMulticast()) {
2008-03-05 09:52:00 -05:00
return;
}
// Check if message is an ack
SortedList offeredOptions = dhcp.GetOptions();
DhcpByteOption messageType
= offeredOptions[DhcpMessageType.OptionCode] as DhcpByteOption;
2008-11-17 18:29:00 -05:00
if (messageType == null) {
2008-03-05 09:52:00 -05:00
return;
}
2008-11-17 18:29:00 -05:00
switch (messageType.Value) {
2008-03-05 09:52:00 -05:00
case (byte) DhcpFormat.MessageType.Ack:
break;
case (byte) DhcpFormat.MessageType.Nak:
client.ChangeState(new DhcpClientStateInitialize(client));
return;
default:
return;
}
// Must have parameters
byte [] parameters = new byte [] {
DhcpSubnetMask.OptionCode,
DhcpRouter.OptionCode,
DhcpDomainNameServer.OptionCode
};
2008-11-17 18:29:00 -05:00
foreach (byte p in parameters) {
2008-03-05 09:52:00 -05:00
IDhcpOption ido = offeredOptions[p] as IDhcpOption;
2008-11-17 18:29:00 -05:00
if (ido == null) {
2008-03-05 09:52:00 -05:00
return;
}
}
client.CancelStateTimeout();
client.ChangeState(new DhcpClientStateBound(client,
serverAddress,
offeredAddress,
offeredOptions));
}
}
internal class DhcpClientStateBound : DhcpClientState
{
private IPv4 serverAddress;
private IPv4 hostAddress;
private SortedList hostOptions;
internal DhcpClientStateBound(DhcpClient client,
IPv4 serverAddress,
IPv4 hostAddress,
SortedList hostOptions)
: base(client, "Bound")
{
this.serverAddress = serverAddress;
this.hostAddress = hostAddress;
this.hostOptions = hostOptions;
}
internal override void EnterEvent()
{
// Yay! We got DHCP configuration information
DateTime now = DateTime.Now;
// Set up renewal timer
DhcpDWordOption renewalOption =
hostOptions[DhcpRenewalTime.OptionCode] as DhcpDWordOption;
uint renewalSeconds = 3600;
2008-11-17 18:29:00 -05:00
if (renewalOption != null) {
2008-03-05 09:52:00 -05:00
renewalSeconds = renewalOption.Value;
}
client.SetRenewalTimeout(now + TimeSpan.FromSeconds(renewalSeconds));
// Set up rebinding timer
DhcpDWordOption rebindOption =
hostOptions[DhcpRebindingTime.OptionCode] as DhcpDWordOption;
uint rebindSeconds = renewalSeconds + 60;
2008-11-17 18:29:00 -05:00
if (rebindOption != null) {
2008-03-05 09:52:00 -05:00
rebindSeconds = Math.Max(rebindOption.Value,
renewalSeconds + 1);
}
client.SetRebindTimeout(now + TimeSpan.FromSeconds(rebindSeconds));
// Add host address as a dhcp option and then get client
// to install settings.
hostOptions[DhcpRequestedIPAddress.OptionCode] =
DhcpRequestedIPAddress.Create(hostAddress);
2008-11-17 18:29:00 -05:00
if (client.InstallDhcpOptions(hostOptions) == false) {
2008-03-05 09:52:00 -05:00
client.ChangeState(new DhcpClientStateInitialize(client));
}
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("\nDHCP client acquired lease of {0} for {1} seconds.",
__arglist(hostAddress.ToString(), rebindSeconds));
2008-03-05 09:52:00 -05:00
}
internal override void ReceiveEvent(DhcpFormat! dhcp)
{
// ignore
}
internal override void RenewalTimeoutEvent()
{
#if NOTYET
client.ChangeState(new DhcpClientStateRenewing(client,
hostOptions));
#endif
}
}
internal class DhcpClientStateRenewing : DhcpClientState
{
SortedList hostOptions;
internal DhcpClientStateRenewing(DhcpClient client,
SortedList hostOptions)
: base(client, "Renewing")
{
this.hostOptions = hostOptions;
}
internal override void EnterEvent()
{
}
internal override void ReceiveEvent(DhcpFormat! dhcp)
{
// ignore
}
internal override void RebindTimeoutEvent()
{
// NB We use Debug.Assert here rather than Debug.Fail
// as Singularity doesn't support the latter.
Debug.Assert(false, "Rebind timeout in renewal state");
}
}
internal class DhcpClientStateRebinding : DhcpClientState
{
internal DhcpClientStateRebinding(DhcpClient client)
: base(client, "Rebinding")
{
}
internal override void EnterEvent()
{
}
internal override void ReceiveEvent(DhcpFormat! dhcp)
{
}
internal override void RebindTimeoutEvent()
{
}
}
}