From 51909d18f4167e3ec79ded247e64ae52146a59f9 Mon Sep 17 00:00:00 2001 From: femsci Date: Wed, 24 May 2023 00:38:13 +0200 Subject: [PATCH] Use Generic Host model. Refactor server implementations. --- src/IPMeow.Dhcp/IPMeow.Dhcp.csproj | 4 +- src/IPMeow.Dhcp/Program.cs | 13 +- src/IPMeow.Dhcp/Server/Dhcp4/DhcpPacket.cs | 10 +- src/IPMeow.Dhcp/Server/Dhcp4/DhcpServer.cs | 55 ++++++--- src/IPMeow.Dhcp/Server/Dhcp6/DhcpOption.cs | 116 +++++++++++++++++- src/IPMeow.Dhcp/Server/Dhcp6/DhcpPacket.cs | 88 +++++++++++-- src/IPMeow.Dhcp/Server/Dhcp6/DhcpServer.cs | 49 +++++--- src/IPMeow.Dhcp/Server/Dhcp6/DhcpStatus.cs | 27 ++++ src/IPMeow.Dhcp/Server/Dhcp6/Duid.cs | 34 ++++- src/IPMeow.Dhcp/Server/DhcpEvent.cs | 6 +- src/IPMeow.Dhcp/Server/IDhcpServer.cs | 8 +- .../Standalone/DecoupledAddressProvider.cs | 42 +------ src/IPMeow.Lib/Address/IAddressProvider.cs | 25 ---- src/IPMeow.Lib/Address/MacAddress.cs | 4 +- src/IPMeow.Lib/BitEnumerator.cs | 14 ++- src/IPMeow.Lib/Host/Host.cs | 12 +- src/IPMeow.Lib/Host/HostInterface.cs | 15 +++ .../Request/AddressRequestContext.cs | 9 ++ src/IPMeow.Lib/Request/IAddressProvider.cs | 8 ++ .../Request/IRequestClientIdentifier.cs | 6 + .../Controllers/AddressController.cs | 21 ++++ src/IPMeow.Server/Data/IpamContext.cs | 26 ++++ .../Data/Model/AddressAssignment.cs | 13 ++ src/IPMeow.Server/Data/Model/AddressBlock.cs | 9 ++ src/IPMeow.Server/Data/Model/AddressPolicy.cs | 6 + src/IPMeow.Server/IPMeow.Server.csproj | 9 ++ src/IPMeow.Server/Program.cs | 5 + test/IPMeow.Test/StructTests.cs | 1 - 28 files changed, 501 insertions(+), 134 deletions(-) delete mode 100644 src/IPMeow.Lib/Address/IAddressProvider.cs create mode 100644 src/IPMeow.Lib/Host/HostInterface.cs create mode 100644 src/IPMeow.Lib/Request/AddressRequestContext.cs create mode 100644 src/IPMeow.Lib/Request/IAddressProvider.cs create mode 100644 src/IPMeow.Lib/Request/IRequestClientIdentifier.cs create mode 100644 src/IPMeow.Server/Controllers/AddressController.cs create mode 100644 src/IPMeow.Server/Data/IpamContext.cs create mode 100644 src/IPMeow.Server/Data/Model/AddressAssignment.cs create mode 100644 src/IPMeow.Server/Data/Model/AddressBlock.cs create mode 100644 src/IPMeow.Server/Data/Model/AddressPolicy.cs diff --git a/src/IPMeow.Dhcp/IPMeow.Dhcp.csproj b/src/IPMeow.Dhcp/IPMeow.Dhcp.csproj index 499b6e0..835ae55 100644 --- a/src/IPMeow.Dhcp/IPMeow.Dhcp.csproj +++ b/src/IPMeow.Dhcp/IPMeow.Dhcp.csproj @@ -4,8 +4,8 @@ - - + + diff --git a/src/IPMeow.Dhcp/Program.cs b/src/IPMeow.Dhcp/Program.cs index 50d605c..cde91b0 100644 --- a/src/IPMeow.Dhcp/Program.cs +++ b/src/IPMeow.Dhcp/Program.cs @@ -1,5 +1,8 @@ using IPMeow.Dhcp.Server.Dhcp4; -using IPMeow.Lib.Host; +using IPMeow.Dhcp.Server.Dhcp6; +using IPMeow.Dhcp.Standalone; +using IPMeow.Lib.Request; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace IPMeow.Dhcp @@ -8,7 +11,13 @@ namespace IPMeow.Dhcp { public static async Task Main(string[] args) { - var app = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args).Build(); + var app = Host.CreateDefaultBuilder(args).ConfigureServices(s => + { + s.AddScoped(); + s.AddSingleton, DecoupledIPAddressProvider>(); + s.AddHostedService(); + s.AddHostedService(); + }).Build(); Console.WriteLine("Listening on DHCP..."); diff --git a/src/IPMeow.Dhcp/Server/Dhcp4/DhcpPacket.cs b/src/IPMeow.Dhcp/Server/Dhcp4/DhcpPacket.cs index 539cfb6..b4c7a5f 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp4/DhcpPacket.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp4/DhcpPacket.cs @@ -1,6 +1,7 @@ using System.Net; using System.Text; using IPMeow.Lib; +using IPMeow.Lib.Address; namespace IPMeow.Dhcp.Server.Dhcp4; @@ -20,7 +21,12 @@ public class DhcpPacket public required byte[] ClientHWAddress { get; init; } public required string ServerHostName { get; init; } public required string BootFileName { get; init; } - public bool? IsValidated { get; set; } + public bool IsValidated { get; set; } + + public override string ToString() + { + return $"{OpType}, htype: {HType}, hlen: {HLen}, hops: {Hops}\nXid: {Xid}, Seconds: {Seconds}, Flags: {Flags:x}\nci: {ClientAddress}\nyi: {YourAddress}, ns: {NextServerAddress}, ra: {RelayAgentAddress}\nhw: {MacAddress.Parse(GetMacAddress())}\nhost: {ServerHostName}\nboot: {BootFileName}"; + } public byte[] GetClientHwAddress() { @@ -36,6 +42,8 @@ public class DhcpPacket return buf; } + public byte[] GetMacAddress() => ClientHWAddress[0..6]; + public byte[] GetSnameBytes() { var buf = new byte[64]; diff --git a/src/IPMeow.Dhcp/Server/Dhcp4/DhcpServer.cs b/src/IPMeow.Dhcp/Server/Dhcp4/DhcpServer.cs index fb9e642..6a381cc 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp4/DhcpServer.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp4/DhcpServer.cs @@ -1,6 +1,6 @@ using System.Net; using System.Net.Sockets; -using IPMeow.Lib.Address; +using IPMeow.Lib.Request; namespace IPMeow.Dhcp.Server.Dhcp4; @@ -8,27 +8,20 @@ public class Dhcp4Server : IDhcpServer, IDisposable { public const int clientPort = 68, serverPort = 67; - public Dhcp4Server(IAddressProvider addressProvider) + public Dhcp4Server(IAddressProvider addressProvider) { _addrProvider = addressProvider; _udp = new UdpClient(serverPort, AddressFamily.InterNetwork); } - private readonly IAddressProvider _addrProvider; + private readonly IAddressProvider _addrProvider; - public event EventHandler DhcpRequestedEvent; + public event EventHandler DhcpRequestedEvent = delegate { }; - public async Task Run() + protected async Task Receive(CancellationToken token) { - while (true) - { - await Receive(); - } - } - - protected async Task Receive() - { - var datagram = await _udp.ReceiveAsync(); + var datagram = await _udp.ReceiveAsync(token); + Console.WriteLine($"New v4 request: {datagram.Buffer.Length}"); if (datagram.Buffer.Length < 236 || datagram.Buffer.Length > 576) { @@ -36,19 +29,23 @@ public class Dhcp4Server : IDhcpServer, IDisposable } var packet = DhcpPacket.Deserialize(datagram.Buffer); - packet.IsValidated = ValidateIncoming(packet); - if (!packet.IsValidated.Value) + if (!ValidateIncoming(packet)) { return; } + packet.IsValidated = true; + Console.WriteLine("Solicitation: "); Console.WriteLine(packet.ToString()); + + //TODO } public bool ValidateIncoming(DhcpPacket packet) { + Console.WriteLine(packet.ToString()); if (packet.OpType != DhcpOpType.BOOTREQUEST) { return false; @@ -72,7 +69,31 @@ public class Dhcp4Server : IDhcpServer, IDisposable public void Dispose() { - throw new NotImplementedException(); + GC.SuppressFinalize(this); + _udp.Dispose(); + } + + async Task Run(CancellationToken token) + { + while (true) + { + if (token.IsCancellationRequested) + { + return; + } + await Receive(token); + } + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + new Thread(async () => await Run(cancellationToken)).Start(); + Console.WriteLine("V4 started."); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; } private readonly UdpClient _udp; diff --git a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpOption.cs b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpOption.cs index 01cc8bb..a8a826e 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpOption.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpOption.cs @@ -15,8 +15,18 @@ public enum DhcpOptionCode : short ElapsedTime = 8, ServerUnicast = 12, StatusCode = 13, - - + VendorClass = 16, + VendorOptions = 17, + IfaceId = 18, + DnsServers = 23, + DomainList = 24, + SntpServers = 31, + SubscriberId = 38, + ClientFqdn = 39, + V6Lost = 51, + NtpServer = 56, + Addrsel = 84, + AddrselTable = 85, } public interface IDhcpOption @@ -37,7 +47,7 @@ public class ClientIdentifierOption : IDhcpOption public object GetData() { - return new Duid(Data); + return Duid.Parse(Data); } } @@ -51,7 +61,7 @@ public class ServerIdentifierOption : IDhcpOption public object GetData() { - return new Duid(Data); + return Duid.Parse(Data); } } @@ -155,3 +165,101 @@ public class StatusCodeOption : IDhcpOption return new DhcpStatus(code, message); } } + +public class VendorClassOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} + +public class InterfaceIdOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} + +public class SubscriberIdOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} + +public class V6LostOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} + +public class ClientFqdnOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} + +public class DnsServersOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} + +public class NtpServerOption : IDhcpOption +{ + public DhcpOptionCode Code => DhcpOptionCode.VendorClass; + + public required byte[] Data { get; set; } + + public Type DataType => typeof(byte[]); + + public object GetData() + { + return Data; + } +} diff --git a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpPacket.cs b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpPacket.cs index f15a17c..065d40a 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpPacket.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpPacket.cs @@ -16,14 +16,69 @@ public class DhcpPacket { Data = data }, - DhcpOptionCode.ServerId => throw new NotImplementedException(), - DhcpOptionCode.IANonTempAssoc => throw new NotImplementedException(), - DhcpOptionCode.IATempAssoc => throw new NotImplementedException(), - DhcpOptionCode.IAAddr => throw new NotImplementedException(), - DhcpOptionCode.OptionRequest => throw new NotImplementedException(), + DhcpOptionCode.ServerId => new ServerIdentifierOption() + { + Data = data + }, + DhcpOptionCode.IANonTempAssoc => new NonTempIdentityAssociationOption() + { + Data = data + }, + DhcpOptionCode.IATempAssoc => new TempIdentityAssociationOption() + { + Data = data + }, + DhcpOptionCode.IAAddr => new IAAddressOption() + { + Data = data + }, + DhcpOptionCode.OptionRequest => new OptionRequestOption() + { + Data = data + }, DhcpOptionCode.Preference => throw new NotImplementedException(), - DhcpOptionCode.ElapsedTime => throw new NotImplementedException(), - DhcpOptionCode.StatusCode => throw new NotImplementedException(), + DhcpOptionCode.ElapsedTime => new ElapsedTimeOption() + { + Data = data + }, + DhcpOptionCode.StatusCode => new StatusCodeOption() + { + Data = data + }, + DhcpOptionCode.ServerUnicast => new ServerUnicastOption() + { + Data = data + }, + DhcpOptionCode.VendorClass => new VendorClassOption() + { + Data = data + }, + DhcpOptionCode.IfaceId => new InterfaceIdOption() + { + Data = data + }, + DhcpOptionCode.DnsServers => new DnsServersOption() + { + Data = data + }, + DhcpOptionCode.SubscriberId => new SubscriberIdOption() + { + Data = data + }, + DhcpOptionCode.ClientFqdn => new ClientFqdnOption() + { + Data = data + }, + DhcpOptionCode.V6Lost => new V6LostOption() + { + Data = data + }, + DhcpOptionCode.NtpServer => new NtpServerOption() + { + Data = data + }, + DhcpOptionCode.Addrsel => throw new NotImplementedException(), + DhcpOptionCode.AddrselTable => throw new NotImplementedException(), _ => throw new InvalidOperationException($"Invalid option type {type}") }; } @@ -36,13 +91,22 @@ public class DhcpPacket DhcpMessageType MessageType = (DhcpMessageType)header[0]; - uint Xid = BitConverter.ToUInt32(header, 1); + header[0] = 0; + uint Xid = BitConverter.ToUInt32(header); + + Console.WriteLine("Preview:\n{0}", string.Join(' ', data.Select(b => b.ToString("x2")))); + + Console.WriteLine($"v6: {Xid}, {enu.Position}, {MessageType}, {data.Length}"); + + List options = new(); while (enu.Position < data.Length) { - short optionCode = enu.TakeInt16(); + DhcpOptionCode optionCode = (DhcpOptionCode)enu.TakeInt16(); short optionLen = enu.TakeInt16(); + Console.WriteLine($"D: {data.Length}, {enu.Position}, {optionCode}, {optionLen}"); + if (enu.Position + optionLen > data.Length) { throw new DataMisalignedException("Invalid option length."); @@ -50,13 +114,17 @@ public class DhcpPacket byte[] optionData = enu.TakeBytes(optionLen); + Console.WriteLine(string.Join(' ', optionData.Select(b => b.ToString("x2")))); + IDhcpOption option = GetOption(optionCode, optionData); + options.Add(option); } return new() { MessageType = MessageType, - Xid = Xid + Xid = Xid, + Options = options }; } } diff --git a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpServer.cs b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpServer.cs index 7ee2bcb..16dd7b1 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpServer.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpServer.cs @@ -1,6 +1,6 @@ using System.Net; using System.Net.Sockets; -using IPMeow.Lib.Address; +using IPMeow.Lib.Request; namespace IPMeow.Dhcp.Server.Dhcp6; @@ -8,17 +8,18 @@ public class Dhcp6Server : IDhcpServer, IDisposable { public const int clientPort = 546, serverPort = 547; - public Dhcp6Server(IAddressProvider addressProvider) + public Dhcp6Server(IAddressProvider addressProvider) { _addrProvider = addressProvider; - _listener = new(new IPEndPoint(IPAddress.Parse("ff02::1:2"), serverPort)); + _listener = new(serverPort, AddressFamily.InterNetworkV6); + _listener.JoinMulticastGroup(IPAddress.Parse("ff02::1:2")); _sender = new(clientPort, AddressFamily.InterNetworkV6); } - private readonly IAddressProvider _addrProvider; + private readonly IAddressProvider _addrProvider; private readonly UdpClient _listener, _sender; - public event EventHandler DhcpRequestedEvent; + public event EventHandler DhcpRequestedEvent = delegate { }; public void Dispose() { @@ -27,24 +28,44 @@ public class Dhcp6Server : IDhcpServer, IDisposable _sender.Dispose(); } - public async Task Run() + protected async Task Receive(CancellationToken token) { - while (true) - { - await Receive(); - } - } + var datagram = await _listener.ReceiveAsync(token); - protected async Task Receive() - { - var datagram = await _listener.ReceiveAsync(); + Console.WriteLine($"From v6: {datagram.RemoteEndPoint}, len: {datagram.Buffer.Length}"); var packet = DhcpPacket.Deserialize(datagram.Buffer); + Console.WriteLine($"New v6 packet: {packet.Xid}, len: {datagram.Buffer.Length}"); + if (packet.MessageType is DhcpMessageType.Invalid or DhcpMessageType.Solicit or DhcpMessageType.Confirm or DhcpMessageType.Rebind) { return; } + //TODO + } + + public async Task Run(CancellationToken token) + { + while (true) + { + if (token.IsCancellationRequested) + { + return; + } + await Receive(token); + } + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + new Thread(async () => await Run(cancellationToken)).Start(); + Console.WriteLine("V6 started."); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; } } diff --git a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpStatus.cs b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpStatus.cs index 7f86dea..5ce8c3c 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp6/DhcpStatus.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp6/DhcpStatus.cs @@ -2,6 +2,33 @@ using System.Text; namespace IPMeow.Dhcp.Server.Dhcp6; +public enum DhcpStatusCode : short +{ + Success = 0, + UnspecifiedFail = 1, + NoAddrAvailable = 2, + NoBinding = 3, + NotOnLink = 4, + UseMulticast = 5, + NoPrefixAvailable = 6, + UnknownQueryType = 7, + MalformedQuery = 8, + NotConfigured = 9, + NotAllowed = 10, + QueryTerminated = 11, + DataMissing = 12, + CatchUpComplete = 13, + NotSupported = 14, + TlsConnRefused = 15, + AddrInUse = 16, + ConfigConflict = 17, + MissingBindingInfo = 18, + OutdatedBindingInfo = 19, + ServerShuttingDown = 20, + DnsUpdateNotSupported = 21, + ExcessiveTimeSkew = 22 +} + public struct DhcpStatus { public DhcpStatus(ushort code, string? message) diff --git a/src/IPMeow.Dhcp/Server/Dhcp6/Duid.cs b/src/IPMeow.Dhcp/Server/Dhcp6/Duid.cs index 29ffb0d..0b34700 100644 --- a/src/IPMeow.Dhcp/Server/Dhcp6/Duid.cs +++ b/src/IPMeow.Dhcp/Server/Dhcp6/Duid.cs @@ -1,3 +1,5 @@ +using IPMeow.Lib.Request; + namespace IPMeow.Dhcp.Server.Dhcp6; public enum DuidType : short @@ -8,12 +10,36 @@ public enum DuidType : short Uuid = 4 } -public readonly struct Duid +public readonly struct Duid : IRequestClientIdentifier { - public Duid(byte[] data) + private Duid(DuidType type, byte[] data) { - Type = (DuidType)BitConverter.ToInt16(data.AsSpan()[0..2]); - Data = data[2..]; + Type = type; + Data = data; + } + + private static readonly Duid Invalid = new(0, Array.Empty()); + + public static bool TryParse(byte[] data, out Duid duid) + { + short dType = BitConverter.ToInt16(data.AsSpan()[0..2]); + + if (!Enum.IsDefined(typeof(DuidType), dType)) + { + duid = Invalid; + return false; + } + + byte[] duidData = data[2..]; + + duid = new((DuidType)dType, duidData); + + return true; + } + + public static Duid Parse(byte[] data) + { + return TryParse(data, out Duid duid) ? duid : throw new InvalidDataException("Invalid data supplied."); } public DuidType Type { get; } diff --git a/src/IPMeow.Dhcp/Server/DhcpEvent.cs b/src/IPMeow.Dhcp/Server/DhcpEvent.cs index 1dc3548..402eaa8 100644 --- a/src/IPMeow.Dhcp/Server/DhcpEvent.cs +++ b/src/IPMeow.Dhcp/Server/DhcpEvent.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Threading.Tasks; using IPMeow.Lib.Address; namespace IPMeow.Dhcp.Server; -public class DhcpRequestEvent +public class DhcpRequestEventArgs : EventArgs { public bool IsV6 { get; init; } public IPAddress? RequestOrigin { get; init; } diff --git a/src/IPMeow.Dhcp/Server/IDhcpServer.cs b/src/IPMeow.Dhcp/Server/IDhcpServer.cs index 9d5977f..ce49f35 100644 --- a/src/IPMeow.Dhcp/Server/IDhcpServer.cs +++ b/src/IPMeow.Dhcp/Server/IDhcpServer.cs @@ -1,8 +1,8 @@ +using Microsoft.Extensions.Hosting; + namespace IPMeow.Dhcp.Server; -public interface IDhcpServer +public interface IDhcpServer : IHostedService { - Task Run(); - - event EventHandler DhcpRequestedEvent; + event EventHandler DhcpRequestedEvent; } diff --git a/src/IPMeow.Dhcp/Standalone/DecoupledAddressProvider.cs b/src/IPMeow.Dhcp/Standalone/DecoupledAddressProvider.cs index 37aa955..bf375b3 100644 --- a/src/IPMeow.Dhcp/Standalone/DecoupledAddressProvider.cs +++ b/src/IPMeow.Dhcp/Standalone/DecoupledAddressProvider.cs @@ -1,50 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Threading.Tasks; -using IPMeow.Lib.Address; +using IPMeow.Lib.Request; namespace IPMeow.Dhcp.Standalone; -public class DecoupledIPv6AddressProvider : IPv6AddressProvider + +public class DecoupledIPAddressProvider : IAddressProvider { - public DecoupledIPv6AddressProvider(HttpClient http) + public DecoupledIPAddressProvider(HttpClient http) { _http = http; } private readonly HttpClient _http; - public IPv6Address GetAddress() - { - _http.GetAsync("/api/request/6/free"); - throw new NotImplementedException(); - } - - public IPv6Address GetAddress(MacAddress mac) + public IPAddress GetAddress(AddressRequestContext ctx) { - throw new NotImplementedException(); - } -} - -public class DecoupledIPv4AddressProvider : IPv4AddressProvider -{ - public DecoupledIPv4AddressProvider(HttpClient http) - { - _http = http; - } - - private readonly HttpClient _http; - - public IPv4Address GetAddress() - { - _http.GetAsync("/api/request/4/free"); - throw new NotImplementedException(); - } - - public IPv4Address GetAddress(MacAddress mac) - { - _http.GetAsync($"/api/request/4/mac/{mac}"); + _http.GetAsync($"/api/request/{(ctx.IsV6 ? "6" : "4")}/free"); throw new NotImplementedException(); } } diff --git a/src/IPMeow.Lib/Address/IAddressProvider.cs b/src/IPMeow.Lib/Address/IAddressProvider.cs deleted file mode 100644 index b2f4e3a..0000000 --- a/src/IPMeow.Lib/Address/IAddressProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace IPMeow.Lib.Address; - -public interface IAddressProvider -{ - public IPAddress GetAddress(); - public IPAddress GetAddress(MacAddress mac); -} - -public interface IPv4AddressProvider -{ - public IPv4Address GetAddress(); - public IPv4Address GetAddress(MacAddress mac); -} - -public interface IPv6AddressProvider -{ - public IPv6Address GetAddress(); - public IPv6Address GetAddress(MacAddress mac); -} diff --git a/src/IPMeow.Lib/Address/MacAddress.cs b/src/IPMeow.Lib/Address/MacAddress.cs index d01e2f4..befe296 100644 --- a/src/IPMeow.Lib/Address/MacAddress.cs +++ b/src/IPMeow.Lib/Address/MacAddress.cs @@ -22,7 +22,7 @@ public readonly struct MacAddress return mac.Value; } - throw new ArgumentException("Invalid address byte length. Expected 6 octets."); + throw new ArgumentException($"Invalid address byte length. Expected 6 octets. Got {addr.Length} octets."); } public static MacAddress Parse(string addr) @@ -32,7 +32,7 @@ public readonly struct MacAddress return mac.Value; } - throw new FormatException("Invalid address format."); + throw new FormatException($"Invalid address format: {addr}."); } public static bool TryParse(byte[] addr, [NotNullWhen(true)] out MacAddress? mac) diff --git a/src/IPMeow.Lib/BitEnumerator.cs b/src/IPMeow.Lib/BitEnumerator.cs index 0fe95d4..81e2dd0 100644 --- a/src/IPMeow.Lib/BitEnumerator.cs +++ b/src/IPMeow.Lib/BitEnumerator.cs @@ -1,3 +1,5 @@ +using System.Buffers.Binary; + namespace IPMeow.Lib; public class BitEnumerator @@ -26,7 +28,7 @@ public class BitEnumerator public short TakeInt16() { - short num = BitConverter.ToInt16(_data.AsSpan()[_position..(_position + 2)]); + short num = BinaryPrimitives.ReadInt16BigEndian(_data.AsSpan()[_position..(_position + 2)]); _position += 2; @@ -35,7 +37,7 @@ public class BitEnumerator public int TakeInt32() { - int num = BitConverter.ToInt32(_data.AsSpan()[_position..(_position + 4)]); + int num = BinaryPrimitives.ReadInt32BigEndian(_data.AsSpan()[_position..(_position + 4)]); _position += 4; @@ -44,7 +46,7 @@ public class BitEnumerator public long TakeInt64() { - long num = BitConverter.ToInt64(_data.AsSpan()[_position..(_position + 8)]); + long num = BinaryPrimitives.ReadInt64BigEndian(_data.AsSpan()[_position..(_position + 8)]); _position += 8; @@ -53,7 +55,7 @@ public class BitEnumerator public ushort TakeUInt16() { - ushort num = BitConverter.ToUInt16(_data.AsSpan()[_position..(_position + 2)]); + ushort num = BinaryPrimitives.ReadUInt16BigEndian(_data.AsSpan()[_position..(_position + 2)]); _position += 2; @@ -62,7 +64,7 @@ public class BitEnumerator public uint TakeUInt32() { - uint num = BitConverter.ToUInt32(_data.AsSpan()[_position..(_position + 4)]); + uint num = BinaryPrimitives.ReadUInt32BigEndian(_data.AsSpan()[_position..(_position + 4)]); _position += 4; @@ -71,7 +73,7 @@ public class BitEnumerator public ulong TakeUInt64() { - ulong num = BitConverter.ToUInt64(_data.AsSpan()[_position..(_position + 8)]); + ulong num = BinaryPrimitives.ReadUInt64BigEndian(_data.AsSpan()[_position..(_position + 8)]); _position += 8; diff --git a/src/IPMeow.Lib/Host/Host.cs b/src/IPMeow.Lib/Host/Host.cs index f450c47..2d55843 100644 --- a/src/IPMeow.Lib/Host/Host.cs +++ b/src/IPMeow.Lib/Host/Host.cs @@ -8,7 +8,17 @@ public class Host : IBindable { [Key] public required string Id { get; set; } - public DateTime? LastPing { get; set; } + [MaxLength(16)] + public byte[] Duid { get; set; } = default!; + public HostType HostType { get; set; } public required IPAddressCollection Addresses { get; set; } public required MacAddress Mac { get; set; } } + +public enum HostType : byte +{ + Physical = 0b1, + Virtual = 0b10, + Container = 0b100, + Other = 0b1000000, +} diff --git a/src/IPMeow.Lib/Host/HostInterface.cs b/src/IPMeow.Lib/Host/HostInterface.cs new file mode 100644 index 0000000..5b98788 --- /dev/null +++ b/src/IPMeow.Lib/Host/HostInterface.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace IPMeow.Lib.Host; + +public class HostInterface +{ + [Key] + public required string Id { get; set; } + public byte[] IAID { get; set; } = default!; + public DateTime LastPinged { get; set; } + public DateTime LastAssignment { get; set; } + + public string HostId { get; set; } = default!; + public virtual Host Host { get; set; } = default!; +} diff --git a/src/IPMeow.Lib/Request/AddressRequestContext.cs b/src/IPMeow.Lib/Request/AddressRequestContext.cs new file mode 100644 index 0000000..9a16c57 --- /dev/null +++ b/src/IPMeow.Lib/Request/AddressRequestContext.cs @@ -0,0 +1,9 @@ +using IPMeow.Lib.Address; + +namespace IPMeow.Lib.Request; + +public class AddressRequestContext +{ + public bool IsV6 { get; set; } + public MacAddress? MacAddress { get; set; } +} diff --git a/src/IPMeow.Lib/Request/IAddressProvider.cs b/src/IPMeow.Lib/Request/IAddressProvider.cs new file mode 100644 index 0000000..0f36726 --- /dev/null +++ b/src/IPMeow.Lib/Request/IAddressProvider.cs @@ -0,0 +1,8 @@ +using System.Net; + +namespace IPMeow.Lib.Request; + +public interface IAddressProvider where T : AddressRequestContext +{ + public IPAddress GetAddress(T ctx); +} diff --git a/src/IPMeow.Lib/Request/IRequestClientIdentifier.cs b/src/IPMeow.Lib/Request/IRequestClientIdentifier.cs new file mode 100644 index 0000000..497ee7e --- /dev/null +++ b/src/IPMeow.Lib/Request/IRequestClientIdentifier.cs @@ -0,0 +1,6 @@ +namespace IPMeow.Lib.Request; + +public interface IRequestClientIdentifier +{ + +} diff --git a/src/IPMeow.Server/Controllers/AddressController.cs b/src/IPMeow.Server/Controllers/AddressController.cs new file mode 100644 index 0000000..85cd294 --- /dev/null +++ b/src/IPMeow.Server/Controllers/AddressController.cs @@ -0,0 +1,21 @@ +using IPMeow.Lib.Request; +using Microsoft.AspNetCore.Mvc; + +namespace IPMeow.Server.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class AddressController : ControllerBase +{ + [HttpPost("request")] + public async Task RequestAddress(AddressRequestContext ctx) + { + throw new NotImplementedException(); + } + + [HttpGet] + public async Task ConfirmRequest() + { + throw new NotImplementedException(); + } +} diff --git a/src/IPMeow.Server/Data/IpamContext.cs b/src/IPMeow.Server/Data/IpamContext.cs new file mode 100644 index 0000000..dbd6b6c --- /dev/null +++ b/src/IPMeow.Server/Data/IpamContext.cs @@ -0,0 +1,26 @@ +using IPMeow.Lib.Host; +using IPMeow.Server.Data.Model; +using Microsoft.EntityFrameworkCore; + +namespace IPMeow.Server.Data; + +public class IpamContext : DbContext +{ + public DbSet Hosts => Set(); + public DbSet HostInterfaces => Set(); + public DbSet Assignments => Set(); + + protected override void OnConfiguring(DbContextOptionsBuilder opt) + { + opt.LogTo(t => Console.WriteLine(t)); + opt.EnableDetailedErrors(); + opt.EnableSensitiveDataLogging(); + base.OnConfiguring(opt); + } + + protected override void OnModelCreating(ModelBuilder mb) + { + mb.Entity().HasKey(ass => new { Address = ass.Address.GetAddressBytes(), HostId = ass.HostInterfaceId }); + base.OnModelCreating(mb); + } +} diff --git a/src/IPMeow.Server/Data/Model/AddressAssignment.cs b/src/IPMeow.Server/Data/Model/AddressAssignment.cs new file mode 100644 index 0000000..b2a348f --- /dev/null +++ b/src/IPMeow.Server/Data/Model/AddressAssignment.cs @@ -0,0 +1,13 @@ +using System.Net; +using IPMeow.Lib.Host; + +namespace IPMeow.Server.Data.Model; + +public class AddressAssignment +{ + public required string HostInterfaceId { get; set; } + public required IPAddress Address { get; set; } + public DateTime LastAssigned { get; set; } + + public virtual HostInterface HostInterface { get; set; } = default!; +} diff --git a/src/IPMeow.Server/Data/Model/AddressBlock.cs b/src/IPMeow.Server/Data/Model/AddressBlock.cs new file mode 100644 index 0000000..f8af6c5 --- /dev/null +++ b/src/IPMeow.Server/Data/Model/AddressBlock.cs @@ -0,0 +1,9 @@ +using System.Net; + +namespace IPMeow.Server.Data.Model; + +public class AddressBlock +{ + public required IPAddress Address { get; set; } + public required int Prefix { get; set; } +} diff --git a/src/IPMeow.Server/Data/Model/AddressPolicy.cs b/src/IPMeow.Server/Data/Model/AddressPolicy.cs new file mode 100644 index 0000000..e15f8c2 --- /dev/null +++ b/src/IPMeow.Server/Data/Model/AddressPolicy.cs @@ -0,0 +1,6 @@ +namespace IPMeow.Server.Data.Model; + +public class AddressPolicy +{ + public bool IsTemporary { get; set; } +} diff --git a/src/IPMeow.Server/IPMeow.Server.csproj b/src/IPMeow.Server/IPMeow.Server.csproj index 72a1294..752d874 100644 --- a/src/IPMeow.Server/IPMeow.Server.csproj +++ b/src/IPMeow.Server/IPMeow.Server.csproj @@ -1,5 +1,14 @@ + + + + + + + + + net7.0 enable diff --git a/src/IPMeow.Server/Program.cs b/src/IPMeow.Server/Program.cs index 0244463..0c96491 100644 --- a/src/IPMeow.Server/Program.cs +++ b/src/IPMeow.Server/Program.cs @@ -1,4 +1,9 @@ +using IPMeow.Server.Data; + var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDbContext(); + var app = builder.Build(); app.Run(); diff --git a/test/IPMeow.Test/StructTests.cs b/test/IPMeow.Test/StructTests.cs index b4579ed..ea26132 100644 --- a/test/IPMeow.Test/StructTests.cs +++ b/test/IPMeow.Test/StructTests.cs @@ -1,4 +1,3 @@ -using IPMeow.Lib; using IPMeow.Lib.Address; namespace IPMeow.Test;