Browse Source

commit: DHCP: Address POOL Implement, before assign do check MAC via ARP by libPCAP

HOME 3 years ago
parent
commit
a46e4ee712

+ 27 - 0
DhcpServer/App.config

@@ -10,9 +10,36 @@
   </startup>
   <applicationSettings>
     <DhcpServer.Properties.Settings>
+      <setting name="PoolSize" serializeAs="String">
+        <value>100</value>
+      </setting>
+      <setting name="SubNet" serializeAs="String">
+        <value>255.255.255.0</value>
+      </setting>
+      <setting name="Dns" serializeAs="String">
+        <value>223.5.5.5</value>
+      </setting>
+      <setting name="PxeFileName" serializeAs="String">
+        <value>undionly-http.kpxe</value>
+      </setting>
       <setting name="ListenOn" serializeAs="String">
         <value>192.168.233.233</value>
       </setting>
+      <setting name="PoolStart" serializeAs="String">
+        <value>192.168.233.100</value>
+      </setting>
+      <setting name="Router" serializeAs="String">
+        <value>192.168.233.1</value>
+      </setting>
+      <setting name="BroadcastAddress" serializeAs="String">
+        <value>192.168.233.255</value>
+      </setting>
+      <setting name="PxeTftp" serializeAs="String">
+        <value>192.168.233.233</value>
+      </setting>
+      <setting name="IpxeScriptUri" serializeAs="String">
+        <value>http://192.168.233.233:8233/boot/ipxe</value>
+      </setting>
     </DhcpServer.Properties.Settings>
   </applicationSettings>
 </configuration>

+ 365 - 8
DhcpServer/DhcpProgram.cs

@@ -1,21 +1,40 @@
-using System;
+using DhcpServer.Models;
+using DhcpServer.Properties;
+using SharpPcap;
+using SharpPcap.LibPcap;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
 
 namespace DhcpServer
 {
-    internal class DhcpProgram
+    public static class DhcpProgram
     {
         private static bool _isRunning;
 
+        private static LibPcapLiveDevice _nic;
+        private static IPEndPoint _listenOn;
+        private static IPAddress _poolStart;
+        private static int _poolSize;
+        private static IPAddress _subNet;
+        private static IPAddress _router;
+        private static IPAddress _broadcastAddress;
+        private static IPAddress _dns;
+        private static IPAddress _pxeTftp;
+        private static string _pxeFileName;
+        private static string _ipxeScriptUri;
+
+        private static IReadOnlyCollection<PoolSlot> _pool;
+
         private static void Main(string[] args)
         {
             Console.WriteLine("Starting...");
 
-            if (DhcpEntryManager.CreateDefaultEntryIfNoExist()) Console.WriteLine("Default client entry config created.");
-
-            var tWorker = new Thread(Working);
+            var tWorker = new Thread(Working2);
             _isRunning = true;
             tWorker.Start();
 
@@ -33,9 +52,347 @@ namespace DhcpServer
             Console.ReadLine();
         }
 
-        private static void Working(object obj)
+        //-----------------------------
+
+        private static void Working2()
+        {
+            var upTime = DateTime.Now;
+
+            LoadConfig();
+
+            InitConfig();
+
+            //Listing
+
+            var socket = new Socket(_listenOn.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
+            {
+                EnableBroadcast = true,
+                SendBufferSize = 65536,
+                ReceiveBufferSize = 65536
+            };
+            socket.Bind(_listenOn);
+            var buffer = new byte[65536];
+            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 68);
+            var polling = DateTime.Now;
+
+            var pooled = false;
+            while (_isRunning)
+            {
+                Console.CursorLeft = 0;
+                if (false == socket.Poll(500 * 1000, SelectMode.SelectRead))
+                {
+                    var timeSpan = DateTime.Now - polling;
+                    var up = DateTime.Now - upTime;
+                    Console.Write("Polling sockets..." +
+                                  $" {timeSpan.Days:00}D {timeSpan.Hours:00}H {timeSpan.Minutes:00}M {timeSpan.Seconds:00}S {timeSpan.Milliseconds:000}" +
+                                  $" / UP {up.Days:00}D {up.Hours:00}H {up.Minutes:00}M {up.Seconds:00}S {up.Milliseconds:000}");
+                    pooled = true;
+                }
+                else // polled
+                {
+                    if (pooled)
+                    {
+                        pooled = false;
+                        Console.WriteLine();
+                    }
+
+                    var bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint);
+                    Console.Write($"Receive {bytes} byte From {remoteEndPoint}");
+
+                    try
+                    {
+                        var reply = ProcessRequest(buffer);
+                        if (reply.MessageType != DhcpMessageType.Unknown)
+                        {
+                            bytes = reply.WriteToBuffer(buffer);
+                            var to = new IPEndPoint(IPAddress.Broadcast, 68);
+                            Console.WriteLine($"Send {bytes} bytes to {to} {reply.MessageType} by {reply.ClientMacAddressHex}");
+                            socket.SendTo(buffer, 0, bytes, SocketFlags.None, to);
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine(e);
+                    }
+                    polling = DateTime.Now;
+                }// end if poll
+            }// end while
+
+            socket.Close();
+        }
+
+        private static DhcpPacket ProcessRequest(byte[] buffer)
+        {
+            var packet = new DhcpPacket(buffer);
+
+            var clientMac = packet.ClientMacAddressHex;
+            Console.WriteLine($" {packet.MessageType} by {clientMac}");
+
+            DhcpMessageType reply;
+            switch (packet.MessageType)
+            {
+                default:
+                    reply = DhcpMessageType.Unknown;
+                    break;
+
+                case DhcpMessageType.Discover:
+                    reply = DhcpMessageType.Offer;
+                    break;
+
+                case DhcpMessageType.Request:
+                    reply = DhcpMessageType.Ack;
+                    break;
+            }
+
+            if (reply == DhcpMessageType.Unknown)
+            {
+                packet.MessageType = DhcpMessageType.Unknown;
+            }
+            else
+            {
+                // extract params
+                var requestParameters = packet.Options[DhcpOption.ParameterRequestList];
+                var hostName = packet.HostName;
+                var userClass = packet.UserClass;
+                var ven = packet.Vendor;
+
+                // Allocate ip address
+
+                PoolSlot allocateSlot = null;
+                {
+                    var mSlot = _pool.FirstOrDefault(p => p.Mac == clientMac);
+                    if (mSlot != null)
+                    {
+                        if (CheckMac(mSlot, clientMac))
+                        {
+                            allocateSlot = mSlot;
+                            Console.WriteLine("Prefer last time address");
+                        }
+                    }
+                }
+
+                if (allocateSlot == null)
+                {
+                    foreach (var slot in _pool)
+                    {
+                        if (slot.LastConfirm.HasValue && (DateTime.Now - slot.LastConfirm.Value).TotalSeconds < 600) continue;
+
+                        if (CheckMac(slot))
+                        {
+                            allocateSlot = slot;
+                            break;
+                        }
+                    }
+                }
+
+                // ready for reply
+                packet.Options.Clear();
+
+                if (null == allocateSlot)
+                {
+                    packet.MessageType = DhcpMessageType.Nak;
+                    Console.Write(" *** Allocate fail");
+                }
+                else
+                {
+                    allocateSlot.Mac = clientMac;
+                    allocateSlot.HostName = hostName;
+                    allocateSlot.LastConfirm = DateTime.Now;
+
+                    packet.OpCode = DhcpOpCode.BootReply;
+                    packet.MessageType = reply;
+
+                    //fill dynamic params
+
+                    packet.YourIpAddress = allocateSlot.Address;
+
+                    //fill static params
+
+                    packet.Router = _router;
+                    packet.SubNetMask = _subNet;
+                    packet.BroadcastAddress = _broadcastAddress;
+                    packet.DnsServer = _dns;
+                    packet.LeaseTime = TimeSpan.FromDays(8);
+                    packet.RebindingTime = packet.LeaseTime;
+                    packet.RenewalTime = packet.LeaseTime;
+
+                    packet.DhcpServerIdentifier = _listenOn.Address;
+
+                    // Extend Process
+
+                    if (userClass == "iPXE")
+                    {
+                        packet.BootFileName = _ipxeScriptUri;
+                    }
+                    else if (requestParameters != null && requestParameters.Contains((byte)67))
+                    {
+                        packet.BootFileName = _pxeFileName;
+                        packet.TftpServer = _pxeTftp.ToString();
+                        packet.NextServerIpAddress = _pxeTftp;
+                    }
+                }
+            }
+
+            return packet;
+        }
+
+        private static bool CheckMac(PoolSlot slot, string clientMac = null)
+        {
+            Console.Write($"Checking mac for {slot.Address}...");
+            var mac = GetMac(slot.Address, out var err);
+
+            if (err != null)
+            {
+                Console.WriteLine($"Err:{err}");
+                return false;
+            }
+
+            if (mac == null)
+            {
+                Console.WriteLine("Available");
+                return true;
+            }
+
+            if (mac == clientMac)
+            {
+                Console.WriteLine("DupReq");
+                return true;
+            }
+
+            slot.Mac = mac;
+            slot.LastConfirm = DateTime.Now;
+            Console.WriteLine($"In using by {mac}");
+            return false;
+        }
+
+        private static void InitConfig()
+        {
+            _nic = LibPcapLiveDeviceList.Instance.FirstOrDefault(p => p.Addresses.Any(q => Equals(q.Addr.ipAddress, _listenOn.Address)));
+            if (null == _nic) throw new ConfigurationErrorsException("Device not found");
+
+            var slots = new PoolSlot[_poolSize];
+            slots[0] = new PoolSlot(_poolStart);
+            for (var i = 1; i < _poolSize; i++) slots[i] = new PoolSlot(slots[i - 1].Address.NextAddress());
+
+            //Task.Factory.StartNew(() =>
+            //{
+            //    foreach (var item in slots)
+            //    {
+            //        item.MacStatus = "Requesting...";
+            //        var mac = GetMac(item.Address, out var err);
+            //        if (null != mac)
+            //        {
+            //            item.Mac = mac;
+            //            item.LastConfirm = DateTime.Now;
+            //            item.MacStatus = "OK";
+            //        }
+            //        else
+            //        {
+            //            item.MacStatus = err ?? "Fail";
+            //        }
+            //    }
+
+            //    foreach (var slot in slots)
+            //    {
+            //        slot.HostNameStatus = "Requesting...";
+            //        slot.HostName = GetHostName(slot.Address, out var err);
+            //        slot.HostNameStatus = err ?? "OK";
+            //    }
+            //});
+
+            _pool = slots;
+        }
+
+        private static void LoadConfig()
         {
-            var listenEndPoint = new IPEndPoint(IPAddress.Parse(Properties.Settings.Default.ListenOn), 67);
+            _listenOn = new IPEndPoint(IPAddress.Parse(Settings.Default.ListenOn), 67);
+            _poolStart = IPAddress.Parse(Settings.Default.PoolStart);
+            _poolSize = Settings.Default.PoolSize;
+            _subNet = IPAddress.Parse(Settings.Default.SubNet);
+            _router = IPAddress.Parse(Settings.Default.Router);
+            _dns = IPAddress.Parse(Settings.Default.Dns);
+            _pxeTftp = IPAddress.Parse(Settings.Default.PxeTftp);
+            _pxeFileName = Settings.Default.PxeFileName;
+            _ipxeScriptUri = Settings.Default.IpxeScriptUri;
+            _broadcastAddress = IPAddress.Parse(Settings.Default.BroadcastAddress);
+        }
+
+        public static string GetHostName(IPAddress address, out string error)
+        {
+            try
+            {
+                var ipHostEntry = Dns.GetHostEntry(address);
+                error = null;
+                return ipHostEntry.HostName;
+            }
+            catch (Exception e)
+            {
+                error = e.GetMessageRecursively();
+            }
+
+            return null;
+        }
+
+        public static string GetMac(IPAddress targetAddress, out string error)
+        {
+            try
+            {
+                var arp = new ARP(_nic);
+                var physicalAddress = arp.Resolve(targetAddress);
+                if (null == physicalAddress)
+                {
+                    error = null;
+                    return null;
+                }
+                var mac = string.Join("-", physicalAddress.GetAddressBytes().Select(p => p.ToString("X2")));
+                error = null;
+                return mac;
+            }
+            catch (Exception e)
+            {
+                error = e.GetMessageRecursively();
+            }
+
+            return null;
+        }
+
+        public static IPAddress NextAddress(this IPAddress address, uint increment = 1)
+        {
+            if (address.AddressFamily != AddressFamily.InterNetwork) throw new NotSupportedException();
+
+            var bytes = address.GetAddressBytes();
+
+            var value =
+                (uint)bytes[0] << 24 |
+                (uint)bytes[1] << 16 |
+                (uint)bytes[2] << 8 |
+                (uint)bytes[3];
+
+            value += increment;
+
+            bytes[0] = (byte)(value >> 24);
+            bytes[1] = (byte)(value >> 16);
+            bytes[2] = (byte)(value >> 8);
+            bytes[3] = (byte)value;
+
+            return new IPAddress(bytes);
+        }
+
+        public static string GetMessageRecursively(this Exception exception)
+        {
+            var msg = exception.Message;
+            if (null != exception.InnerException) msg += " --> " + exception.InnerException.GetMessageRecursively();
+
+            return msg;
+        }
+
+        //-----------------------------
+
+        private static void Working()
+        {
+            if (DhcpEntryManager.CreateDefaultEntryIfNoExist()) Console.WriteLine("Default client entry config created.");
+
+            var listenEndPoint = new IPEndPoint(IPAddress.Parse(Settings.Default.ListenOn), 67);
 
             var socket = new Socket(listenEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
             {
@@ -140,4 +497,4 @@ namespace DhcpServer
             socket.Close();
         }
     }
-}
+}

+ 11 - 0
DhcpServer/DhcpServer.csproj

@@ -38,8 +38,18 @@
     <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
       <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
     </Reference>
+    <Reference Include="PacketDotNet, Version=1.0.3.0, Culture=neutral, PublicKeyToken=451414c7667b2a58, processorArchitecture=MSIL">
+      <HintPath>..\packages\PacketDotNet.1.0.3\lib\net45\PacketDotNet.dll</HintPath>
+    </Reference>
+    <Reference Include="SharpPcap, Version=5.1.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\SharpPcap.5.1.0\lib\netstandard2.0\SharpPcap.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -51,6 +61,7 @@
     <Compile Include="App\DhcpEntryManager.cs" />
     <Compile Include="App\DhcpEntry.cs" />
     <Compile Include="App\DhcpPacket.cs" />
+    <Compile Include="Models\PoolSlot.cs" />
     <Compile Include="Properties\Settings.Designer.cs">
       <AutoGen>True</AutoGen>
       <DesignTimeSharedInput>True</DesignTimeSharedInput>

+ 27 - 0
DhcpServer/Models/PoolSlot.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Diagnostics;
+using System.Net;
+
+namespace DhcpServer.Models
+{
+    [DebuggerDisplay("{" + nameof(Address) + "}")]
+    public class PoolSlot
+    {
+        public PoolSlot(IPAddress address)
+        {
+            Address = address;
+        }
+
+        public IPAddress Address { get; }
+
+        public DateTime? LastConfirm { get; set; }
+
+        public string Mac { get; set; }
+
+        public string MacStatus { get; set; }
+
+        public string HostName { get; set; }
+
+        public string HostNameStatus { get; set; }
+    }
+}

+ 82 - 1
DhcpServer/Properties/Settings.Designer.cs

@@ -12,7 +12,7 @@ namespace DhcpServer.Properties {
     
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")]
     internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
         
         private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -25,11 +25,92 @@ namespace DhcpServer.Properties {
         
         [global::System.Configuration.ApplicationScopedSettingAttribute()]
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("100")]
+        public int PoolSize {
+            get {
+                return ((int)(this["PoolSize"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("255.255.255.0")]
+        public string SubNet {
+            get {
+                return ((string)(this["SubNet"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("223.5.5.5")]
+        public string Dns {
+            get {
+                return ((string)(this["Dns"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("undionly-http.kpxe")]
+        public string PxeFileName {
+            get {
+                return ((string)(this["PxeFileName"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
         [global::System.Configuration.DefaultSettingValueAttribute("192.168.233.233")]
         public string ListenOn {
             get {
                 return ((string)(this["ListenOn"]));
             }
         }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("192.168.233.100")]
+        public string PoolStart {
+            get {
+                return ((string)(this["PoolStart"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("192.168.233.1")]
+        public string Router {
+            get {
+                return ((string)(this["Router"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("192.168.233.255")]
+        public string BroadcastAddress {
+            get {
+                return ((string)(this["BroadcastAddress"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("192.168.233.233")]
+        public string PxeTftp {
+            get {
+                return ((string)(this["PxeTftp"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("http://192.168.233.233:8233/boot/ipxe")]
+        public string IpxeScriptUri {
+            get {
+                return ((string)(this["IpxeScriptUri"]));
+            }
+        }
     }
 }

+ 27 - 0
DhcpServer/Properties/Settings.settings

@@ -2,8 +2,35 @@
 <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="DhcpServer.Properties" GeneratedClassName="Settings">
   <Profiles />
   <Settings>
+    <Setting Name="PoolSize" Type="System.Int32" Scope="Application">
+      <Value Profile="(Default)">100</Value>
+    </Setting>
+    <Setting Name="SubNet" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">255.255.255.0</Value>
+    </Setting>
+    <Setting Name="Dns" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">223.5.5.5</Value>
+    </Setting>
+    <Setting Name="PxeFileName" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">undionly-http.kpxe</Value>
+    </Setting>
     <Setting Name="ListenOn" Type="System.String" Scope="Application">
       <Value Profile="(Default)">192.168.233.233</Value>
     </Setting>
+    <Setting Name="PoolStart" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">192.168.233.100</Value>
+    </Setting>
+    <Setting Name="Router" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">192.168.233.1</Value>
+    </Setting>
+    <Setting Name="BroadcastAddress" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">192.168.233.255</Value>
+    </Setting>
+    <Setting Name="PxeTftp" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">192.168.233.233</Value>
+    </Setting>
+    <Setting Name="IpxeScriptUri" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">http://192.168.233.233:8233/boot/ipxe</Value>
+    </Setting>
   </Settings>
 </SettingsFile>

+ 19 - 7
DhcpServer/Protocol/DhcpOption.cs

@@ -2,12 +2,24 @@
 {
     public enum DhcpOption : byte
     {
-        SubNetMask = 1,
-        Router = 3,
-        DnsServer = 6,
-        DhcpMessageType = 53,
-        DhcpServerIdentifier = 54,
-        TftpServer = 66,
-        UserClass = 77,
+        SubNetMask = 1, // 4b ip address
+        Router = 3, // 4b ip address
+        DnsServer = 6, // 4b ip address
+        HostName = 12, // var ascii
+        BroadcastAddress = 28, // 4b ip address
+        DhcpMessageType = 53,  // 1b enum
+        RequestedIpAddress = 50, // 4b ip address
+        LeaseTime = 51, // 4b seconds BE
+        ParameterRequestList = 55, // var byte array
+        ServerIdentifier = 54, // 4b ip address
+        RenewalTime = 58, // 4b seconds BE
+        RebindingTime = 59, // 4b seconds BE
+        DhcpServerIdentifier = 54, // 4b ip address
+        Vendor = 60, // var ascii
+        ClientIdentifier = 61, // var MAC
+        TftpServer = 66, // 4b ip address
+        UserClass = 77, // var ascii
+
+        End = 255,
     }
 }

+ 57 - 2
DhcpServer/Protocol/DhcpPacket.cs

@@ -65,6 +65,18 @@ namespace DhcpServer
             set => Options[DhcpOption.TftpServer] = Encoding.ASCII.GetBytes(value);
         }
 
+        public IPAddress BroadcastAddress
+        {
+            get => Options.MapValue(DhcpOption.BroadcastAddress, value => new IPAddress(value));
+            set => Options[DhcpOption.BroadcastAddress] = value.GetAddressBytes();
+        }
+
+        public IPAddress ServerIdentifier
+        {
+            get => Options.MapValue(DhcpOption.ServerIdentifier, value => new IPAddress(value));
+            set => Options[DhcpOption.ServerIdentifier] = value.GetAddressBytes();
+        }
+
         public IPAddress DhcpServerIdentifier
         {
             get => Options.MapValue(DhcpOption.DhcpServerIdentifier, value => new IPAddress(value));
@@ -77,6 +89,34 @@ namespace DhcpServer
             set => Options[DhcpOption.UserClass] = Encoding.ASCII.GetBytes(value);
         }
 
+        public string HostName => Options.MapValue(DhcpOption.HostName, v => Encoding.ASCII.GetString(v));
+        public string Vendor => Options.MapValue(DhcpOption.Vendor, v => Encoding.ASCII.GetString(v));
+
+        public TimeSpan LeaseTime
+        {
+            get => Options.MapValue(DhcpOption.LeaseTime, p => TimeSpan.FromSeconds(ToBeUint(p)));
+            set => Options[DhcpOption.LeaseTime] = ToBytes((uint)value.TotalSeconds);
+        }
+
+        public TimeSpan RenewalTime
+        {
+            get => Options.MapValue(DhcpOption.RenewalTime, p => TimeSpan.FromSeconds(ToBeUint(p)));
+            set => Options[DhcpOption.RenewalTime] = ToBytes((uint)value.TotalSeconds);
+        }
+
+        public TimeSpan RebindingTime
+        {
+            get => Options.MapValue(DhcpOption.RebindingTime, p => TimeSpan.FromSeconds(ToBeUint(p)));
+            set => Options[DhcpOption.RebindingTime] = ToBytes((uint)value.TotalSeconds);
+        }
+
+        public byte[] ClientIdentifier
+        {
+            get => Options.MapValue(DhcpOption.ClientIdentifier, p => p);
+            set => Options[DhcpOption.ClientIdentifier] = value;
+        }
+
+
         public DhcpPacket(byte[] buffer)
         {
             using var stream = new MemoryStream(buffer);
@@ -151,9 +191,24 @@ namespace DhcpServer
         {
         }
 
-        public int GetByteCount()
+        private static uint ToBeUint(byte[] buf)
+        {
+            return (uint)buf[0] << 24 |
+                   (uint)buf[1] << 16 |
+                   (uint)buf[2] << 8 |
+                   (uint)buf[3];
+        }
+
+        private static byte[] ToBytes(uint value)
         {
-            throw new NotImplementedException();
+            var buf = new byte[4];
+            buf[0] = (byte)(value >> 24);
+            buf[1] = (byte)(value >> 16);
+            buf[2] = (byte)(value >> 8);
+            buf[3] = (byte)(value);
+            return buf;
         }
+
+
     }
 }

+ 3 - 0
DhcpServer/packages.config

@@ -1,4 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
+  <package id="PacketDotNet" version="1.0.3" targetFramework="net472" />
+  <package id="SharpPcap" version="5.1.0" targetFramework="net472" />
+  <package id="System.Runtime.CompilerServices.Unsafe" version="4.6.0" targetFramework="net472" />
 </packages>

+ 6 - 0
NetBootServer.sln

@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "Utils\Utils.csproj
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpServer", "HttpServer\HttpServer.csproj", "{9FBE44BC-56DA-4F12-BA69-DEA537693014}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IscsiServer", "IscsiServer\IscsiServer.csproj", "{978DF635-5160-4513-9796-676E2864CA39}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
 		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Release|Any CPU.Build.0 = Release|Any CPU
+		{978DF635-5160-4513-9796-676E2864CA39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{978DF635-5160-4513-9796-676E2864CA39}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{978DF635-5160-4513-9796-676E2864CA39}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{978DF635-5160-4513-9796-676E2864CA39}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 2 - 0
NetBootServer.sln.DotSettings

@@ -2,8 +2,10 @@
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Dhcp/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Bootp/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=hexhyp/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=isid/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Tftp/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=blksize/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=tsih/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=tsize/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=ipxe/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Iscsi/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>