Use Generic Host model. Refactor server implementations.
This commit is contained in:
parent
dbffe9e4cc
commit
51909d18f4
28 changed files with 501 additions and 134 deletions
|
@ -4,8 +4,8 @@
|
|||
<ProjectReference Include="..\IPMeow.Lib\IPMeow.Lib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -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<HttpClient>();
|
||||
s.AddSingleton<IAddressProvider<AddressRequestContext>, DecoupledIPAddressProvider>();
|
||||
s.AddHostedService<Dhcp4Server>();
|
||||
s.AddHostedService<Dhcp6Server>();
|
||||
}).Build();
|
||||
|
||||
Console.WriteLine("Listening on DHCP...");
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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<AddressRequestContext> addressProvider)
|
||||
{
|
||||
_addrProvider = addressProvider;
|
||||
_udp = new UdpClient(serverPort, AddressFamily.InterNetwork);
|
||||
}
|
||||
|
||||
private readonly IAddressProvider _addrProvider;
|
||||
private readonly IAddressProvider<AddressRequestContext> _addrProvider;
|
||||
|
||||
public event EventHandler<DhcpRequestEvent> DhcpRequestedEvent;
|
||||
public event EventHandler<DhcpRequestEventArgs> 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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<IDhcpOption> 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<AddressRequestContext> 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<AddressRequestContext> _addrProvider;
|
||||
private readonly UdpClient _listener, _sender;
|
||||
|
||||
public event EventHandler<DhcpRequestEvent> DhcpRequestedEvent;
|
||||
public event EventHandler<DhcpRequestEventArgs> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<byte>());
|
||||
|
||||
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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace IPMeow.Dhcp.Server;
|
||||
|
||||
public interface IDhcpServer
|
||||
public interface IDhcpServer : IHostedService
|
||||
{
|
||||
Task Run();
|
||||
|
||||
event EventHandler<DhcpRequestEvent> DhcpRequestedEvent;
|
||||
event EventHandler<DhcpRequestEventArgs> DhcpRequestedEvent;
|
||||
}
|
||||
|
|
|
@ -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<AddressRequestContext>
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
15
src/IPMeow.Lib/Host/HostInterface.cs
Normal file
15
src/IPMeow.Lib/Host/HostInterface.cs
Normal file
|
@ -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!;
|
||||
}
|
9
src/IPMeow.Lib/Request/AddressRequestContext.cs
Normal file
9
src/IPMeow.Lib/Request/AddressRequestContext.cs
Normal file
|
@ -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; }
|
||||
}
|
8
src/IPMeow.Lib/Request/IAddressProvider.cs
Normal file
8
src/IPMeow.Lib/Request/IAddressProvider.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Net;
|
||||
|
||||
namespace IPMeow.Lib.Request;
|
||||
|
||||
public interface IAddressProvider<T> where T : AddressRequestContext
|
||||
{
|
||||
public IPAddress GetAddress(T ctx);
|
||||
}
|
6
src/IPMeow.Lib/Request/IRequestClientIdentifier.cs
Normal file
6
src/IPMeow.Lib/Request/IRequestClientIdentifier.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace IPMeow.Lib.Request;
|
||||
|
||||
public interface IRequestClientIdentifier
|
||||
{
|
||||
|
||||
}
|
21
src/IPMeow.Server/Controllers/AddressController.cs
Normal file
21
src/IPMeow.Server/Controllers/AddressController.cs
Normal file
|
@ -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<IActionResult> RequestAddress(AddressRequestContext ctx)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ConfirmRequest()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
26
src/IPMeow.Server/Data/IpamContext.cs
Normal file
26
src/IPMeow.Server/Data/IpamContext.cs
Normal file
|
@ -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<Lib.Host.Host> Hosts => Set<Lib.Host.Host>();
|
||||
public DbSet<HostInterface> HostInterfaces => Set<HostInterface>();
|
||||
public DbSet<AddressAssignment> Assignments => Set<AddressAssignment>();
|
||||
|
||||
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<AddressAssignment>().HasKey(ass => new { Address = ass.Address.GetAddressBytes(), HostId = ass.HostInterfaceId });
|
||||
base.OnModelCreating(mb);
|
||||
}
|
||||
}
|
13
src/IPMeow.Server/Data/Model/AddressAssignment.cs
Normal file
13
src/IPMeow.Server/Data/Model/AddressAssignment.cs
Normal file
|
@ -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!;
|
||||
}
|
9
src/IPMeow.Server/Data/Model/AddressBlock.cs
Normal file
9
src/IPMeow.Server/Data/Model/AddressBlock.cs
Normal file
|
@ -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; }
|
||||
}
|
6
src/IPMeow.Server/Data/Model/AddressPolicy.cs
Normal file
6
src/IPMeow.Server/Data/Model/AddressPolicy.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace IPMeow.Server.Data.Model;
|
||||
|
||||
public class AddressPolicy
|
||||
{
|
||||
public bool IsTemporary { get; set; }
|
||||
}
|
|
@ -1,5 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IPMeow.Lib\IPMeow.Lib.csproj" />
|
||||
<ProjectReference Include="..\IPMeow.Dhcp\IPMeow.Dhcp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
using IPMeow.Server.Data;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDbContext<IpamContext>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.Run();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using IPMeow.Lib;
|
||||
using IPMeow.Lib.Address;
|
||||
|
||||
namespace IPMeow.Test;
|
||||
|
|
Loading…
Reference in a new issue