123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- using DhcpServer.Models;
- using DhcpServer.Properties;
- using SharpPcap;
- using SharpPcap.LibPcap;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- namespace DhcpServer
- {
- public static class DhcpProgram
- {
- private const int PoolEntryKeepHours = 10;
- 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 string _u64FileName;
- private static string _u86FileName;
- private static string _uHttpFileName;
- private static IReadOnlyCollection<PoolSlot> _pool;
- private static IPAddress _ipxeRouter;
- private static IPAddress _pxeRouter;
- private static void Main(string[] args)
- {
- Console.WriteLine("Starting...");
- var tWorker = new Thread(Working2);
- _isRunning = true;
- tWorker.Start();
- Console.WriteLine("Press ENTER to Stop.");
- Console.ReadLine();
- Console.Write("Shutting down...");
- _isRunning = false;
- tWorker.Join();
- Console.Write("Stopped.");
- Console.WriteLine();
- Console.Write("Press ENTER to Exit.");
- Console.ReadLine();
- }
- //-----------------------------
- private static void Working2()
- {
- var upTime = DateTime.Now;
- LoadConfig();
- InitConfig();
- //Listing
- var socket = new Socket(_listenOn.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
- {
- EnableBroadcast = true,
- SendBufferSize = 1500,
- ReceiveBufferSize = 1500
- };
- socket.Bind(_listenOn);
- var buffer = new byte[1500];
- 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}");
- var sent = 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}");
- Console.WriteLine($" {packet.Vendor ?? "Unknown Vendor"} / {packet.UserClass ?? "Unknown Class"} / {packet.HostName ?? "Unknown HostName"}");
- 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 hostName = packet.HostName;
- var userClass = packet.UserClass;
- var vendor = 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).Hours < PoolEntryKeepHours) 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;
- // vendor spec
- packet.Router = _pxeRouter;
- packet.TftpServer = _pxeTftp.ToString();
- packet.NextServerIpAddress = _pxeTftp;
- if (userClass == "iPXE")
- {
- packet.BootFileName = _ipxeScriptUri;
- packet.Router = _ipxeRouter;
- }
- else
- {
- if (true == vendor?.StartsWith("PXEClient:Arch:00000"))
- {
- //Legacy
- packet.BootFileName = _pxeFileName;
- }
- else if (true == vendor?.StartsWith("PXEClient:Arch:00002")
- || true == vendor?.StartsWith("PXEClient:Arch:00006"))
- {
- //UEFI x86
- packet.BootFileName = _u86FileName;
- }
- else if (true == vendor?.StartsWith("PXEClient:Arch:00007")
- || true == vendor?.StartsWith("PXEClient:Arch:00008")
- || true == vendor?.StartsWith("PXEClient:Arch:00009")
- )
- {
- //UEFI x64
- packet.BootFileName = _u64FileName;
- }
- }
- }
- }
- 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());
- _pool = slots;
- }
- private static void LoadConfig()
- {
- _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);
- _pxeRouter = IPAddress.Parse(Settings.Default.PxeRouter);
- _pxeTftp = IPAddress.Parse(Settings.Default.PxeTftp);
- _pxeFileName = Settings.Default.FileName;
- _u64FileName = Settings.Default.FileName_UEFI_x64;
- _u86FileName = Settings.Default.FileName_UEFI_I386;
- _ipxeScriptUri = Settings.Default.FileName_iPXE_HTTP;
- _ipxeRouter = IPAddress.Parse(Settings.Default.IpxeRouter);
- _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;
- }
- }
- }
|