-
Notifications
You must be signed in to change notification settings - Fork 165
Add Dhcp-Support #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add Dhcp-Support #84
Conversation
return; | ||
} | ||
|
||
switch (random.Next(0, 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this random.NextBool() instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep it because there may be more than two possible payloads in the future
} | ||
else | ||
{ | ||
udpLayer.DestinationPort = 5355; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use port 5355 here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This port was copied from the DnsLayer-Test above because I thought during tests the port is randomly wrong on purpose. I just checked port 5355 and it is used for Link-Local Multicast Name Resolution. So therefor my code with random wrong port doesn't make sense. Was removed by commit 0e40921
else | ||
{ | ||
udpLayer.SourcePort = 5355; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 432-439 seem to repeat lines 424-431.
/// The DHCP message structure is shown below: | ||
/// <pre> | ||
/// 0 1 2 3 | ||
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency, it might be better to format this similar to other layers.
private static class Offset | ||
{ | ||
public const int Op = 0; | ||
public const int Htype = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider not using short names, here and elsewhere.
Op -> Operation
Htype -> HardwareAddressType
Hlen -> HardwareAddressLength
Xid -> TransactionId
Secs -> Seconds
CiAddr -> ClientIpAddress
etc.
public const int Hops = 3; | ||
public const int Xid = 4; | ||
public const int Secs = 8; | ||
public const int Flags = 10; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having a field for "flags", split it to the different flags (RFC 2131 only has Broadcast).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added another property Broadcast in 8a31f33
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, but I meant split it to different fields:
I think it would make the usage easier.
/// +-------------------------------+------------+------------------+
/// | secs (2) | Broadcase | reserved |
/// +-------------------------------+------------+------------------+
/// RFC 2131. | ||
/// Transaction ID, a random number chosen by the client, used by the client and server to associate messages and responses between a client and a server. | ||
/// </summary> | ||
public int TransactionId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be uint unless there's a reason to have signed values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change in 7971a20
{ | ||
} | ||
|
||
private void ParseServerHostName() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe inline in the getter as it's only used once?
|
||
private void ParseServerHostName() | ||
{ | ||
if (_serverHostName == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer if (_serverHostName != null)
return;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it like in DnsDatagram to keep it consistent
{ | ||
//at the moment we only interpret server host name as sname and ignore options | ||
byte[] byteServerHostName = ReadBytes(Offset.Sname, 64); | ||
_serverHostName = Encoding.ASCII.GetString(byteServerHostName).TrimEnd('\0'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this is limited to ASCII?
} | ||
} | ||
|
||
private void ParseBootFileName() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto comments in ParseServerHostName()
{ | ||
//at the moment we only interpret server host name as sname and ignore options | ||
byte[] byteServerHostName = ReadBytes(Offset.Sname, 64); | ||
_serverHostName = Encoding.ASCII.GetString(byteServerHostName).TrimEnd('\0'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding TrimEnd(), what happens if after the null character there's a non null character?
} | ||
} | ||
|
||
private void ParseOptions() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto some of the comments in the other Parse methods.
if (_options == null) | ||
{ | ||
List<DhcpOption> options = new List<DhcpOption>(); | ||
int pos = IsDhcp ? Offset.OptionsWithMagicCookie : Offset.Options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use offset instead of pos.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed in df7dcd2
{ | ||
options.Add(DhcpOption.CreateInstance(this, ref pos)); | ||
} | ||
this._options = new ReadOnlyCollection<DhcpOption>(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using "this." explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed in 2912854
/// <summary> | ||
/// Creates a Layer that represents the datagram to be used with PacketBuilder. | ||
/// </summary> | ||
public override ILayer ExtractLayer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep methods sorted: public, internal, private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed in 5170fe1
|
||
/// <summary> | ||
/// RFC 2131. | ||
/// Optional server host name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
End comments with periods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed in 7e1df41
public const int Op = 0; | ||
public const int Htype = 1; | ||
public const int Hlen = 2; | ||
public const int Hops = 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use relative offsets.
Hops = Hlen + sizeof(byte).
|
||
/// <summary> | ||
/// RFC 2131. | ||
/// true if the magic dhcp-cookie is set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace "true if" with "Whether"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed in ae3f79c
public const int OptionsWithMagicCookie = 240; | ||
} | ||
|
||
internal const int DHCP_MAGIC_COOKIE = 0x63825363; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not uint here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in c1550dd
{ | ||
get | ||
{ | ||
if (Length >= Offset.Options + 4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace "4" with "sizeof(int)" ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in c1550dd
/// RFC 2131. | ||
/// true if the magic dhcp-cookie is set | ||
/// </summary> | ||
public bool IsDhcp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this Datagram if it's false?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation added in ae3f79c
|
||
if (options != null) | ||
{ | ||
length += options.Sum(p => 1 + (!(p is DhcpPadOption || p is DhcpEndOption) ? 1 : 0) + p.Length); //Type + Len? + Option |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace "p" with "option".
Replace "1" with "sizeof(byte)".
Can you rely on p.Op instead of its class type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in c1550dd.
Yes, option.OptionCode makes much more sense.
|
||
if (options != null) | ||
{ | ||
length += options.Sum(p => 1 + (!(p is DhcpPadOption || p is DhcpEndOption) ? 1 : 0) + p.Length); //Type + Len? + Option |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace "//Type" with "// Type".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in c1550dd
|
||
internal static void Write(byte[] buffer, int offset, DhcpMessageType messageType, ArpHardwareType hardwareType, byte hardwareAddressLength, byte hops, int transactionId, ushort secondsElapsed, DhcpFlags flags, IpV4Address clientIpAddress, IpV4Address yourClientIpAddress, IpV4Address nextServerIpAddress, IpV4Address relayAgentIpAddress, DataSegment clientHardwareAddress, string serverHostName, string bootFileName, bool isDhcp, IList<DhcpOption> options) | ||
{ | ||
buffer.Write(ref offset, (byte)messageType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be consistent with the name - either "op" or "messageType".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed that. Changed in 41bc492
internal static void Write(byte[] buffer, int offset, DhcpMessageType messageType, ArpHardwareType hardwareType, byte hardwareAddressLength, byte hops, int transactionId, ushort secondsElapsed, DhcpFlags flags, IpV4Address clientIpAddress, IpV4Address yourClientIpAddress, IpV4Address nextServerIpAddress, IpV4Address relayAgentIpAddress, DataSegment clientHardwareAddress, string serverHostName, string bootFileName, bool isDhcp, IList<DhcpOption> options) | ||
{ | ||
buffer.Write(ref offset, (byte)messageType); | ||
buffer.Write(ref offset, (byte)hardwareType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider relying on the defined offsets instead of using ref.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in 8b2f118
offset += Offset.Sname - Offset.ChAddr; | ||
if (serverHostName != null) | ||
{ | ||
buffer.Write(offset, new DataSegment(Encoding.ASCII.GetBytes(serverHostName))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider also writing the null termination.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in 8b2f118
/// <summary> | ||
/// Hardware address length | ||
/// </summary> | ||
public byte HardwareAddressLength { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the hard address length just be derived from the hardware address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would separate it from the ClientHardwareAddress to allow setting all ClientHardwareAddress-Bytes.
ex. HardwareAddressLength = 6 but the user still wants to set byte 7 - 16
Maybe automatically set the HardwareAddressLength to 6 if the ClientMacAddress is set. What do you think about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, makes sense.
I didn't realize the Hardware Address field has constant size, regardless of the Hardware Address Length field.
It makes sense to set it to 6 (you can use MacAddress.SizeOf instead of 6) when the helping property ClientMacAddress is used, just make sure it's clear from the property comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in cda7273
/// <param name="nextLayer">The layer that comes after this layer. null if this is the last layer.</param> | ||
public override void Finalize(byte[] buffer, int offset, int payloadLength, ILayer nextLayer) | ||
{ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to override this if you don't do anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in 3ccd7fc
namespace PcapDotNet.Packets.Dhcp | ||
{ | ||
/// <summary> | ||
/// RFCs 2131. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace "RFCs" with "RFC".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in f10975f
/// <summary> | ||
/// RFCs 2131. | ||
/// </summary> | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a None value instead of suppressing this.
/// create new DhcpBootfileNameOption | ||
/// </summary> | ||
/// <param name="bootfileName">Bootfilename</param> | ||
public DhcpBootfileNameOption(string bootfileName) : base(bootfileName, DhcpOptionCode.BootfileName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename bootfilename to bootFilename ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always tried to keep the names according the RFC specification. Only if it was not possible (spaces, conflict with existing names,...) I changed the name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, in general in Pcap.Net, it's attempted to have the names readable so they can be used without being familiar with the RFC.
In this case, if "bootfile" is considered a word, this makes sense. Otherwise it makes more sense to have bootFilename.
} | ||
|
||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider solving these maintainability issues by using registration mechanism for the options, similar to what is being done in other protocols.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored in 6d0b4dd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow thanks!
Awesome work!
I didn't go over all the details.
Specifically, I didn't check code matches the RFC and I assume it's ok.
One missing part that can help make sure the code indeed matches the RFC is the Wireshark comparison code. Let me know if you need help with this.
I've written a bunch of comments, many of them should be applied on many places in the code.
Let me know if you can address these comments. Otherwise, I'll have to see if I can take care of them myself.
Hopefully I didn't miss any.
sizeof instead of constant value
(also some changes for "End comments with periods.")
Consider relying on the defined offsets instead of using ref.
…s (RFC 2131 only has Broadcast).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fixes!
Let me know when you want me to make a full pass on the code.
@@ -26,6 +26,7 @@ public DhcpArpCacheTimeoutOption(uint time) : base(time, DhcpOptionCode.ArpCache | |||
{ | |||
} | |||
|
|||
[DhcpOptionReadRegistration(DhcpOptionCode.ArpCacheTimeout)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason not to have this attribute on the classes instead of the creation method?
As long as you don't have two creation methods for the same class, I think putting this on the class is easier to maintain.
Added support for DHCP (RFC 2131) as requested in #55.
Also includes tests.