- Fixed a bug within `dhcp.pas` - `processPacket_OFFER` in which the client was responding with the client IP value within the DHCP header was incorrectly being filled out with the IP being requested & this value was then being used within the REQUESTED_IP_ADDRESS option. Corrected this to fill out the client IP with the currently configured IP, which will be NULL (0.0.0.0) on boot, and whatever is issued thereafter. - Cascaded the change to use the currently configured IP as opposed to a NULL IP to any other functions that were calling `copyIPv4(@NULL_IP[0], @packetCtx^.IP.Source[0])`. - Allowed `processPacket_OFFER` to process packets addressed to the BROADCAST MAC (WHY COULDN'T IT DO THIS ALREADY?!).
795 lines
26 KiB
ObjectPascal
795 lines
26 KiB
ObjectPascal
// Copyright 2021 Kieron Morris
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
{
|
|
Driver->Net->L5->DHCP - Dynamic Host Configuration Protocol Driver.
|
|
|
|
@author(Kieron Morris <kjm@kieronmorris.me>)
|
|
}
|
|
unit dhcp;
|
|
|
|
interface
|
|
|
|
uses
|
|
lmemorymanager, console,
|
|
nettypes, netutils, udp, netlog, net,
|
|
util, rand, lists, tracer, ipv4, arp;
|
|
|
|
type
|
|
TDHCPOptions = PLinkedListBase;
|
|
PDHCPOptions = ^PLinkedListBase;
|
|
TDHCPOption = record
|
|
Opcode : TDHCPOpCode;
|
|
Size : uint32;
|
|
Value : void;
|
|
Reverse_Endian : boolean;
|
|
end;
|
|
PDHCPOption = ^TDHCPOption;
|
|
|
|
procedure register();
|
|
procedure DHCPDiscover();
|
|
|
|
implementation
|
|
|
|
type
|
|
TFlipExclude = Array[0..255] of boolean;
|
|
PFlipExclude = ^TFlipExclude;
|
|
TDHCPConfiguration = record
|
|
Transaction : uint32;
|
|
Options : array[0..255] of void;
|
|
end;
|
|
PDHCPConfiguation = ^TDHCPConfiguration;
|
|
|
|
var
|
|
Configuration : PDHCPConfiguation;
|
|
Socket : PUDPBindContext = nil;
|
|
FlipExclude : PFlipExclude;
|
|
|
|
procedure nullConfiguration;
|
|
var
|
|
i : uint8;
|
|
|
|
begin
|
|
{ Null the entire configuration ready for a new DISCOVER or REQUEST }
|
|
if configuration <> nil then begin
|
|
Configuration^.Transaction:= 0;
|
|
for i:=0 to 255 do begin
|
|
if Configuration^.Options[i] <> nil then begin
|
|
kfree(Configuration^.Options[i]);
|
|
end;
|
|
Configuration^.Options[i]:= nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function createHeader(): PDHCPHeader;
|
|
var
|
|
result : PDHCPHeader;
|
|
MAC : puint8;
|
|
|
|
begin
|
|
{ Create a basic header with most values nulled, Hardware type & length is prefilled for ethernet }
|
|
tracer.push_trace('dhcp.newHeader');
|
|
result:= PDHCPHeader(kalloc(sizeof(TDHCPHeader)));
|
|
result^.Message_Type:= $01;
|
|
result^.Hardware_Type:= $01;
|
|
result^.Hardware_Address_Length:= $06;
|
|
result^.Hops:= $00;
|
|
result^.Transaction_ID:= Configuration^.Transaction;
|
|
result^.Seconds_Elapsed:= $0000;
|
|
result^.Bootp_Flags:= $0000;
|
|
CopyIPv4(@NULL_IP[0], @result^.Client_IP[0]);
|
|
CopyIPv4(@NULL_IP[0], @result^.Your_IP[0]);
|
|
CopyIPv4(@NULL_IP[0], @result^.Server_IP[0]);
|
|
CopyIPv4(@NULL_IP[0], @result^.Relay_Agent_IP[0]);
|
|
MAC:= getMAC;
|
|
CopyMAC(MAC, @result^.Client_MAC[0]);
|
|
memset(uint32(@result^.Padding[0]), 0, 10);
|
|
memset(uint32(@result^.Server_Hostname[0]), 0, 64);
|
|
memset(uint32(@result^.Boot_File[0]), 0, 128);
|
|
memcpy(uint32(@DHCP_MAGIC[0]), uint32(@result^.Magic_Cookie[0]), 4);
|
|
createHeader:= result;
|
|
end;
|
|
|
|
function newOption(DHCPOptions : PDHCPOptions; Opcode : TDHCPOpCode; Data : void; Length : uint32; SwapEndian : Boolean) : PDHCPOption;
|
|
var
|
|
Option : PDHCPOption;
|
|
|
|
begin
|
|
{ Add an option to the Options linkedlist, copy data[size] to the Option }
|
|
tracer.push_trace('dhcp.newOption');
|
|
Option:= PDHCPOption(LL_Add(PLinkedListBase(DHCPOptions)));
|
|
Option^.Opcode:= Opcode;
|
|
Option^.Size:= Length;
|
|
Option^.Reverse_Endian:= SwapEndian;
|
|
if Length > 0 then begin
|
|
Option^.Value:= kalloc(Length);
|
|
memcpy(uint32(Data), uint32(Option^.Value), Length);
|
|
end else begin
|
|
Option^.Value:= nil;
|
|
end;
|
|
newOption:= Option;
|
|
end;
|
|
|
|
procedure deleteOption(DHCPOptions : PDHCPOptions; idx : uint32);
|
|
var
|
|
Option : PDHCPOption;
|
|
|
|
begin
|
|
{ Remove an option from the linkedlsit }
|
|
tracer.push_trace('dhcp.deleteOptions');
|
|
Option:= PDHCPOption(LL_Get(PLinkedListBase(DHCPOptions), idx));
|
|
if Option <> nil then begin
|
|
if Option^.Value <> nil then begin
|
|
kfree(Option^.Value);
|
|
end;
|
|
LL_Delete(PLinkedListBase(DHCPOptions), idx);
|
|
end;
|
|
end;
|
|
|
|
function getOption(DHCPOptions : PDHCPOptions; idx : uint32) : PDHCPOption;
|
|
begin
|
|
{ Get an option by index from the linkedlist }
|
|
tracer.push_trace('dhcp.getOption');
|
|
getOption:= PDHCPOption(LL_Get(PLinkedListBase(DHCPOptions),idx));
|
|
end;
|
|
|
|
function newOptions : PDHCPOptions;
|
|
begin
|
|
{ Create a new Options LinkedList }
|
|
tracer.push_trace('dhcp.newOptions');
|
|
newOptions:= PDHCPOptions(LL_New(sizeof(TDHCPOption)));
|
|
end;
|
|
|
|
procedure freeOptions(Options : PDHCPOptions);
|
|
begin
|
|
{ Free all options and free their underlying data buffers }
|
|
tracer.push_trace('dhcp.freeOptions');
|
|
if Options <> nil then begin
|
|
while LL_Size(PLinkedListBase(Options)) > 0 do begin
|
|
deleteOption(Options, 0);
|
|
end;
|
|
LL_Free(PLinkedListBase(Options));
|
|
end;
|
|
end;
|
|
|
|
function getOptionsCount(Options : PDHCPOptions) : uint32;
|
|
begin
|
|
{ Get the number of options in the linkedlist }
|
|
tracer.push_trace('dhcp.getOptionsCount');
|
|
getOptionsCount:= LL_Size(PLinkedListBase(Options));
|
|
end;
|
|
|
|
function calculateOptionsSize(Options : PDHCPOptions) : uint16;
|
|
var
|
|
i : uint32;
|
|
Option : PDHCPOption;
|
|
OptionsSize : uint16;
|
|
|
|
begin
|
|
{
|
|
Get the size of all options in the linkedlist
|
|
1 byte for the opcode
|
|
1 byte for the length
|
|
n bytes (specified by length) for the data
|
|
total = foreach(element):(sizeof(opcode)+sizeof(length)+length)
|
|
}
|
|
tracer.push_trace('dhcp.calculateOptionsSize');
|
|
OptionsSize := 0;
|
|
for i:=0 to getOptionsCount(Options)-1 do begin
|
|
Option:= PDHCPOption(LL_Get(PLinkedListBase(Options),i));
|
|
case Option^.Opcode of
|
|
{ PAD & END_VENDOR_OPTIONS are special cases as they are 1 byte in length with no size byte }
|
|
PAD,END_VENDOR_OPTIONS : inc(OptionsSize,1);
|
|
else inc(OptionsSize, 2 + Option^.Size);
|
|
end;
|
|
end;
|
|
calculateOptionsSize:= OptionsSize;
|
|
end;
|
|
|
|
function getEndianCorrectValue16(Option : PDHCPOption) : uint16;
|
|
var
|
|
Value : uint16;
|
|
|
|
begin
|
|
{ Get the value from an Option as an endian corrected (if applicable) uint16 }
|
|
tracer.push_trace('dhcp.getEndianCorrectValue16');
|
|
Value:= PuInt16(Option^.Value)^;
|
|
if Option^.Reverse_Endian then
|
|
getEndianCorrectValue16:= switchendian16(Value)
|
|
else
|
|
getEndianCorrectValue16:= Value;
|
|
end;
|
|
|
|
function getEndianCorrectValue32(Option : PDHCPOption) : uint32;
|
|
var
|
|
Value : uint32;
|
|
begin
|
|
{ Get the value from an Option as an endian corrected (if applicable) uint32 }
|
|
tracer.push_trace('dhcp.getEndianCorrectValue32');
|
|
Value:= puint32(Option^.Value)^;
|
|
if Option^.Reverse_Endian then
|
|
getEndianCorrectValue32:= switchendian32(Value)
|
|
else
|
|
getEndianCorrectValue32:= Value;
|
|
end;
|
|
|
|
function readOption8(Option : PDHCPOption) : uint8;
|
|
var
|
|
read8 : puint8;
|
|
begin
|
|
{ Read the option data as an 8-bit value }
|
|
read8:= puint8(Option^.Value);
|
|
readOption8:= read8^;
|
|
end;
|
|
|
|
function readOption16(Option : PDHCPOption) : uint16;
|
|
var
|
|
read16 : puint16;
|
|
begin
|
|
{ Read the option data as an 16-bit value }
|
|
read16:= puint16(Option^.Value);
|
|
readOption16:= read16^;
|
|
end;
|
|
|
|
function readOption32(Option : PDHCPOption) : uint32;
|
|
var
|
|
read32 : puint32;
|
|
begin
|
|
{ Read the option data as an 32-bit value }
|
|
read32:= puint32(Option^.Value);
|
|
readOption32:= read32^;
|
|
end;
|
|
|
|
function getOptionByOpcode(Options : PDHCPOptions; Opcode : TDHCPOpCode) : PDHCPOption;
|
|
var
|
|
Option : PDHCPOption;
|
|
i : uint16;
|
|
|
|
begin
|
|
{ Search the linked list for a given OpCode and return if found, nil if not. }
|
|
getOptionByOpcode:= nil;
|
|
for i:=0 to getOptionsCount(Options)-1 do begin
|
|
Option:= getOption(Options, i);
|
|
if Option^.Opcode = Opcode then begin
|
|
getOptionByOpcode:= Option;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function writeOptions(Header : PDHCPHeader; Options : PDHCPOptions; newLength : puint16) : PDHCPHeader;
|
|
var
|
|
OptionsSize : uint16;
|
|
TotalSize : uint16;
|
|
NewBuffer : void;
|
|
NewHeader : PDHCPHeader;
|
|
buffer : puint8;
|
|
read8 : puint8;
|
|
read16 : puint16;
|
|
read32 : puint32;
|
|
Option : PDHCPOption;
|
|
i : uint32;
|
|
|
|
begin
|
|
tracer.push_trace('dhcp.writeOptions');
|
|
|
|
{ Make room in our new packet buffer for the header + options }
|
|
OptionsSize := calculateOptionsSize(Options);
|
|
TotalSize:= OptionsSize + SizeOf(TDHCPHeader);
|
|
NewBuffer:= kalloc(TotalSize);
|
|
|
|
{ Copy the header }
|
|
NewHeader:= PDHCPHeader(NewBuffer);
|
|
memcpy(uint32(Header), uint32(NewHeader), sizeof(TDHCPHeader));
|
|
|
|
{ Write Options }
|
|
buffer:= puint8(NewBuffer);
|
|
inc(buffer, SizeOf(TDHCPHeader));
|
|
for i:=0 to getOptionsCount(Options)-1 do begin
|
|
Option:= getOption(Options, i);
|
|
case Option^.Opcode of
|
|
{ PAD & END_VENDOR_OPTIONS have a length of 0 & no length byte }
|
|
PAD,END_VENDOR_OPTIONS:begin
|
|
buffer^:= Ord(Option^.Opcode);
|
|
inc(buffer);
|
|
end;
|
|
{ Everything else follows the format: uint8(OpCode)->uint8(Length)->variable(Data) }
|
|
else begin
|
|
buffer^:= Ord(Option^.OpCode);
|
|
inc(buffer);
|
|
buffer^:= Option^.Size;
|
|
inc(buffer);
|
|
case Option^.Size of
|
|
{ Try to read and write using a 16bit or 32bit pointer for speed, else do a memcpy }
|
|
2:begin
|
|
read16:= puint16(buffer);
|
|
read16^:= getEndianCorrectValue16(Option);
|
|
end;
|
|
4:begin
|
|
read32:= puint32(buffer);
|
|
read32^:= getEndianCorrectValue32(Option);
|
|
end;
|
|
else begin
|
|
memcpy(uint32(Option^.Value), uint32(buffer), Option^.Size);
|
|
end;
|
|
end;
|
|
inc(buffer, Option^.Size);
|
|
end;
|
|
end;
|
|
end;
|
|
{ Return the new packet size a pointer to the packet (Header) }
|
|
newLength^:= TotalSize;
|
|
writeOptions:= PDHCPHeader(NewBuffer);
|
|
end;
|
|
|
|
|
|
procedure readOptions(DHCPOptions : PDHCPOptions; p_data : void; p_len : uint16);
|
|
var
|
|
headerSize : uint32;
|
|
buffer : puint8;
|
|
bufferEnd : puint8;
|
|
bufferStart : puint8;
|
|
HaveOp : boolean;
|
|
HaveLen : boolean;
|
|
Opcode : TDHCPOpCode;
|
|
Length : uint8;
|
|
read32 : puint32;
|
|
read16 : puint16;
|
|
Option : PDHCPOption;
|
|
|
|
begin
|
|
tracer.push_trace('dhcp.register');
|
|
|
|
{ Get the beginning & end of the Options buffer }
|
|
HeaderSize:= sizeOf(TDHCPHeader);
|
|
bufferEnd:= puint8(uint32(p_data) + p_len);
|
|
bufferStart:= puint8(uint32(p_data) + headerSize);
|
|
|
|
{ Buffer is our iterator }
|
|
buffer:= bufferStart;
|
|
|
|
HaveOp:= false;
|
|
HaveLen:= false;
|
|
|
|
while (uint32(buffer) < uint32(bufferEnd)) do begin
|
|
{
|
|
If we have a length, read the value (bytes[length])
|
|
If we have an Opcode read the length (bytes[1])
|
|
If we have neither, read the opcode (bytes[1])
|
|
}
|
|
if HaveLen then begin
|
|
{ Get a new option LinkedList 'object' and store everything inside }
|
|
Option:= newOption(DHCPOptions, Opcode, void(buffer), Length, not FlipExclude^[ord(Opcode)]);
|
|
if Length = 2 then begin
|
|
if Option^.Reverse_Endian then begin
|
|
read16:= puint16(Option^.Value);
|
|
read16^:= switchendian16(read16^);
|
|
end;
|
|
end;
|
|
if Length = 4 then begin
|
|
if Option^.Reverse_Endian then begin
|
|
read32:= puint32(Option^.Value);
|
|
read32^:= switchendian32(read32^);
|
|
end;
|
|
end;
|
|
inc(buffer, Length);
|
|
HaveOp:= false;
|
|
HaveLen:= false;
|
|
end else if HaveOp then begin
|
|
Length:= buffer^;
|
|
HaveLen:= true;
|
|
inc(buffer);
|
|
end else begin
|
|
Opcode:= TDHCPOpCode(buffer^);
|
|
case opcode of
|
|
PAD,END_VENDOR_OPTIONS:begin
|
|
Length:= 0;
|
|
haveLen:= true;
|
|
end;
|
|
end;
|
|
HaveOp:= true;
|
|
inc(buffer)
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure processHeader(Header : PDHCPHeader);
|
|
begin
|
|
{ Switch endianness of any network-byte-order values }
|
|
Header^.Transaction_ID:= switchendian32(Header^.Transaction_ID);
|
|
Header^.Seconds_Elapsed:= switchendian16(Header^.Seconds_Elapsed);
|
|
Header^.Bootp_Flags:= switchendian16(Header^.Bootp_Flags);
|
|
end;
|
|
|
|
procedure processPacket_NAK(Header : PDHCPHeader; Options : PDHCPOptions);
|
|
begin
|
|
console.outputln('DHCP', 'Process NAK.');
|
|
{ Server provided a NAK, NULL configuration ready for next DISCOVER/Request }
|
|
nullConfiguration();
|
|
end;
|
|
|
|
procedure processPacket_ACK(Header : PDHCPHeader; Options : PDHCPOptions);
|
|
var
|
|
i : uint16;
|
|
Option : PDHCPOption;
|
|
read16 : puint16;
|
|
read32 : puint32;
|
|
cfgopt : void;
|
|
|
|
begin
|
|
console.outputln('DHCP', 'Process ACK.');
|
|
getIPv4Config^.UP:= false;
|
|
|
|
//Copy new address
|
|
Configuration^.Options[Ord(TDHCPOpCode.REQUESTED_IP_ADDRESS)]:= kalloc(sizeof(TIPv4Address));
|
|
copyIPv4(@Header^.Your_IP[0], puint8(Configuration^.Options[Ord(TDHCPOpCode.REQUESTED_IP_ADDRESS)]));
|
|
|
|
{ Copy any given configuration data }
|
|
for i:=0 to getOptionsCount(Options)-1 do begin
|
|
Option:= getOption(Options, i);
|
|
case Option^.Opcode of
|
|
PAD,END_VENDOR_OPTIONS:begin
|
|
end;
|
|
else begin
|
|
cfgopt:= kalloc(Option^.Size);
|
|
Configuration^.Options[ord(Option^.Opcode)]:= cfgopt;
|
|
memcpy(uint32(Option^.Value), uint32(cfgopt), Option^.Size);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ Copy into IPv4 Configuration }
|
|
if Configuration^.Options[Ord(TDHCPOpCode.REQUESTED_IP_ADDRESS)] <> nil then
|
|
copyIPv4(puint8(Configuration^.Options[Ord(TDHCPOpCode.REQUESTED_IP_ADDRESS)]), puint8(@getIPv4Config^.address[0]));
|
|
if Configuration^.Options[Ord(TDHCPOpCode.ROUTER)] <> nil then
|
|
copyIpv4(puint8(Configuration^.Options[Ord(TDHCPOpCode.ROUTER)]), puint8(@getIPv4Config^.Gateway[0]));
|
|
if Configuration^.Options[Ord(TDHCPOpCode.SUBNET_MASK)] <> nil then
|
|
copyIPv4(puint8(Configuration^.Options[Ord(TDHCPOpCode.SUBNET_MASK)]), puint8(@getIPv4Config^.Netmask[0]));
|
|
|
|
arp.sendGratuitous;
|
|
arp.sendRequest(@getIPv4Config^.Gateway[0]);
|
|
|
|
getIPv4Config^.UP:= true;
|
|
end;
|
|
|
|
procedure processPacket_OFFER(Header : PDHCPHeader; Options : PDHCPOptions);
|
|
Var
|
|
SendHeader : PDHCPHeader;
|
|
SendOptions : PDHCPOptions;
|
|
SendMsgType : uint8;
|
|
|
|
NewSendHeader : PDHCPHeader;
|
|
NewHeaderSize : uint32;
|
|
|
|
RequestParams : Array[0..3] of uint8;
|
|
|
|
SendCtx : PUDPSendContext;
|
|
PacketCtx : PPacketContext;
|
|
|
|
MAC : puint8;
|
|
|
|
Option : PDHCPOption;
|
|
|
|
begin
|
|
console.outputln('DHCP', 'Process OFFER.');
|
|
|
|
{ Check the Transaction ID matches our stored ID, discard if not. }
|
|
if Header^.Transaction_ID = Configuration^.Transaction then begin
|
|
console.outputln('DHCP', 'XID Match');
|
|
|
|
{ Create a new header to the use in our DHCP REQUEST packet }
|
|
SendHeader:= createHeader();
|
|
CopyIPv4(@getIPv4Config^.Address[0], puint8(@SendHeader^.Client_IP[0]));
|
|
CopyIPv4(puint8(@Header^.Server_IP[0]), puint8(@SendHeader^.Server_IP[0]));
|
|
processHeader(SendHeader);
|
|
|
|
{ Setup Options }
|
|
SendOptions:= newOptions();
|
|
|
|
{ Create a message type option and assign it the value REQUEST }
|
|
SendMsgType:= ord(TDHCPMessageType.REQUEST);
|
|
NewOption(SendOptions, TDHCPOpCode.DHCP_MESSAGE_TYPE, void(@SendMsgType), 1, false);
|
|
|
|
{ Create a Requested IP option and assign it the value from the OFFER packet header }
|
|
NewOption(SendOptions, TDHCPOpCode.REQUESTED_IP_ADDRESS, void(@Header^.Your_IP[0]), 4, false);
|
|
|
|
{ Create a Server Identifier Option and assign it the value from the OFFER packet options }
|
|
Option:= getOptionByOpcode(Options, TDHCPOpCode.SERVER_IDENTIFIER);
|
|
if Option <> nil then begin
|
|
NewOption(SendOptions, TDHCPOpCode.SERVER_IDENTIFIER, void(@Option^.Value[0]), 4, false);
|
|
end;
|
|
|
|
{ Create a Parameter Request List, Request the following: Netmask, Gateway, DNS Name & DNS Server }
|
|
RequestParams[0]:= Ord(TDHCPOpCode.SUBNET_MASK);
|
|
RequestParams[1]:= Ord(TDHCPOpCode.ROUTER);
|
|
RequestParams[2]:= Ord(TDHCPOpCode.DOMAIN_NAME);
|
|
RequestParams[3]:= Ord(TDHCPOpCode.DNS_SERVER);
|
|
NewOption(SendOptions, TDHCPOpCode.PARAMETER_REQUEST_LIST, void(@RequestParams[0]), 4, false);
|
|
NewOption(SendOptions, TDHCPOpCode.END_VENDOR_OPTIONS, nil, 0, false);
|
|
|
|
{ Write the options to the header }
|
|
NewSendHeader:= writeOptions(SendHeader, SendOptions, @NewHeaderSize);
|
|
|
|
{ Setup Packet Context (IPv4 & ETH2) & Copy in the correct details }
|
|
MAC:= getMAC();
|
|
packetCtx:= PPacketContext(Kalloc(sizeof(TPacketContext)));
|
|
packetCtx^.TTL:= 128;
|
|
|
|
{ Copy over MAC - src: broadcast - dst: DHCP Server }
|
|
copyMAC(MAC, @packetCtx^.MAC.Source[0]);
|
|
copyMAC(@BROADCAST_MAC[0], @packetCtx^.MAC.Destination[0]);
|
|
|
|
{ Copy over IP - src: NULL - dst: Broadcast }
|
|
CopyIPv4(@getIPv4Config^.Address[0], @packetCtx^.IP.Source[0]);
|
|
copyIPv4(@BROADCAST_IP[0], @packetCtx^.IP.Destination[0]);
|
|
|
|
{ Setup UDPContext (UDP) & copy in the correct details }
|
|
sendCtx:= PUDPSendContext(Kalloc(sizeof(TUDPSendContext)));
|
|
sendCtx^.DstPort:= 67;
|
|
sendCtx^.context:= packetCtx;
|
|
sendCtx^.socket:= Socket;
|
|
|
|
{ Send the packet }
|
|
udp.send(void(NewSendHeader), NewHeaderSize, sendCtx);
|
|
|
|
{ Free everything we allocated }
|
|
freeOptions(SendOptions);
|
|
kfree(void(NewSendHeader));
|
|
kfree(void(SendHeader));
|
|
kfree(void(packetCtx));
|
|
kfree(void(sendCtx));
|
|
end else begin
|
|
console.outputln('DHCP', 'XID Mismatch');
|
|
end;
|
|
end;
|
|
|
|
procedure processPacket(p_data : void; p_len : uint16; context : PUDPPacketContext);
|
|
var
|
|
Header : PDHCPHeader;
|
|
Options : PDHCPOptions;
|
|
Option : PDHCPOption;
|
|
MAC : puint8;
|
|
i : uint32;
|
|
MsgType : uint8;
|
|
|
|
begin
|
|
tracer.push_trace('dhcp.processPacket.enter');
|
|
Outputln('DHCP','processPacket');
|
|
|
|
{ Give access to header values & process to correct endianness. }
|
|
Header:= PDHCPHeader(p_data);
|
|
processHeader(Header);
|
|
|
|
{ Process Options }
|
|
Options:= newOptions;
|
|
readOptions(Options, p_data, p_len);
|
|
|
|
{ Check the frame is for us and then process }
|
|
MAC:= getMAC;
|
|
if MACEqual(@context^.PacketContext^.MAC.Destination[0], MAC) or MACEqual(@context^.PacketContext^.MAC.Destination[0], BROADCAST_MAC) then begin
|
|
Outputln('DHCP','Frame is addressed to us.');
|
|
{ Check the message type is client specific }
|
|
If Header^.Message_Type = $02 then begin
|
|
Outputln('DHCP','Packet is a client packet.');
|
|
|
|
{ Iterate options to find DHCP_MESSAGE_TYPE }
|
|
Outputln('DHCP','Searching for message type in Options');
|
|
for i:=0 to getOptionsCount(Options)-1 do begin
|
|
Option:= getOption(Options, i);
|
|
if Option^.Opcode = DHCP_MESSAGE_TYPE then begin
|
|
break;
|
|
end else begin
|
|
Option:= nil;
|
|
end;
|
|
end;
|
|
Outputln('DHCP','Done searching for message type in Options');
|
|
|
|
{ Did we successfully get the DHCP_MESSAGE_TYPE option? }
|
|
if Option <> nil then begin
|
|
Outputln('DHCP','Found message type option.');
|
|
{ Read the value of DHCP_MESSAGE_TYPE }
|
|
MsgType:= readOption8(Option);
|
|
case TDHCPMessageType(MsgType) of
|
|
{ Pass to the correct packet processing function }
|
|
TDHCPMessageType.OFFER:processPacket_OFFER(Header, Options);
|
|
TDHCPMessageType.PACK:processPacket_ACK(Header, Options);
|
|
TDHCPMessageType.NAK:processPacket_NAK(Header, Options);
|
|
end;
|
|
end else begin
|
|
{ Could not find the DHCP_MESSAGE_TYPE option }
|
|
Outputln('DHCP','Could not find message type option.');
|
|
end;
|
|
end else begin
|
|
{ Packet is intended for a DHCP server }
|
|
Outputln('DHCP','Packet is a server packet.');
|
|
end;
|
|
end else begin
|
|
Outputln('DHCP', 'Packet is not addressed to us.');
|
|
end;
|
|
|
|
freeOptions(Options);
|
|
tracer.push_trace('dhcp.processPacket.exit');
|
|
end;
|
|
|
|
procedure DHCPDiscover();
|
|
var
|
|
SendCtx : PUDPSendContext;
|
|
PacketCtx : PPacketContext;
|
|
Header : PDHCPHeader;
|
|
NewHeader : PDHCPHeader;
|
|
HeaderSize : uint32;
|
|
Options : PDHCPOptions;
|
|
MsgType : uint8;
|
|
MAC : puint8;
|
|
|
|
begin
|
|
tracer.push_trace('dhcp.DHCPDiscover.begin');
|
|
{ Ensure we have a socket bound. }
|
|
if Socket <> nil then begin
|
|
{ Clear any current configuration }
|
|
nullConfiguration();
|
|
|
|
{ Setup our Transaction ID }
|
|
Configuration^.Transaction:= rand32();
|
|
|
|
{ Setup header }
|
|
Header:= createHeader();
|
|
processHeader(Header);
|
|
|
|
{ Setup options }
|
|
Options:= newOptions;
|
|
MsgType:= Ord(TDHCPMessageType.DISCOVER);
|
|
newOption(Options, DHCP_MESSAGE_TYPE, void(@MsgType), 1, false);
|
|
NewOption(Options, END_VENDOR_OPTIONS, nil, 0, false);
|
|
|
|
{ Write options to header }
|
|
NewHeader:= writeOptions(Header, Options, @HeaderSize);
|
|
|
|
{ Setup Packet Context (IPv4 & ETH2) }
|
|
packetCtx:= PPacketContext(Kalloc(sizeof(TPacketContext)));
|
|
packetCtx^.TTL:= 128;
|
|
copyMAC(@BROADCAST_MAC[0], @packetCtx^.MAC.Destination[0]);
|
|
MAC:= getMAC;
|
|
copyMAC(MAC, @packetCtx^.MAC.Source[0]);
|
|
CopyIPv4(@getIPv4Config^.Address[0], @packetCtx^.IP.Source[0]);
|
|
copyIPv4(@BROADCAST_IP[0], @packetCtx^.IP.Destination[0]);
|
|
|
|
{ Setup UDPContext (UDP) }
|
|
sendCtx:= PUDPSendContext(Kalloc(sizeof(TUDPSendContext)));
|
|
sendCtx^.DstPort:= 67;
|
|
sendCtx^.context:= packetCtx;
|
|
sendCtx^.socket:= Socket;
|
|
|
|
{ NET UP }
|
|
getIPv4Config^.UP:= true;
|
|
|
|
{ Send }
|
|
udp.send(void(NewHeader), HeaderSize, sendCtx);
|
|
|
|
{ Free }
|
|
kfree(void(PacketCtx));
|
|
kfree(void(sendCtx));
|
|
kfree(void(Header));
|
|
kfree(void(NewHeader));
|
|
freeOptions(Options);
|
|
end;
|
|
tracer.push_trace('dhcp.DHCPDiscover.exit');
|
|
end;
|
|
|
|
procedure bind();
|
|
begin
|
|
{ Bind our socket, free the socket in case of failure }
|
|
Socket:= PUDPBindContext(Kalloc(sizeof(TUDPBindContext)));
|
|
Socket^.Port:= 68;
|
|
Socket^.Callback:= @processPacket;
|
|
Socket^.UID:= rand32;
|
|
case UDP.bind(Socket) of
|
|
tueOK:console.outputln('DHCP', 'Successfully bound port 68.');
|
|
else begin
|
|
kfree(void(Socket));
|
|
Socket:= nil;
|
|
console.outputln('DHCP', 'Failed to bind port 68.');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure register();
|
|
var
|
|
i : uint8;
|
|
|
|
begin
|
|
tracer.push_trace('dhcp.register');
|
|
console.outputln('DHCP', 'Register begin.');
|
|
|
|
{ Kalloc our Configuration Data }
|
|
Configuration:= PDHCPConfiguation(kalloc(sizeof(TDHCPConfiguration)));
|
|
|
|
{ Null Existing Coniguration }
|
|
for i:=0 to 255 do begin
|
|
Configuration^.Options[i] := nil;
|
|
end;
|
|
nullConfiguration();
|
|
|
|
{ Clear FlipExclude Table }
|
|
FlipExclude:= PFlipExclude(kalloc(sizeof(TFlipExclude)));
|
|
for i:=0 to 255 do begin
|
|
FlipExclude^[i]:= false;
|
|
end;
|
|
|
|
{ Set FlipExclude Table up to exclude certain OPCode from EndianFlips }
|
|
FlipExclude^[ord(PAD)]:= true;
|
|
FlipExclude^[ord(SUBNET_MASK)]:= true;
|
|
FlipExclude^[ord(ROUTER)]:= true;
|
|
FlipExclude^[ord(TIME_SERVER)]:= true;
|
|
FlipExclude^[ord(NAME_SERVER)]:= true;
|
|
FlipExclude^[ord(DNS_SERVER)]:= true;
|
|
FlipExclude^[ord(LOG_SERVER)]:= true;
|
|
FlipExclude^[ord(COOKIE_SERVER)]:= true;
|
|
FlipExclude^[ord(LPR_SERVER)]:= true;
|
|
FlipExclude^[ord(IMPRESS_SERVER)]:= true;
|
|
FlipExclude^[ord(RESOURCE_LOCATION_SERVER)]:= true;
|
|
FlipExclude^[ord(HOST_NAME)]:= true;
|
|
FlipExclude^[ord(MERIT_DUMP_FILE)]:= true;
|
|
FlipExclude^[ord(DOMAIN_NAME)]:= true;
|
|
FlipExclude^[ord(SWAP_SERVER)]:= true;
|
|
FlipExclude^[ord(ROOT_PATH)]:= true;
|
|
FlipExclude^[ord(EXTENSIONS_PATH)]:= true;
|
|
FlipExclude^[ord(END_VENDOR_OPTIONS)]:= true;
|
|
FlipExclude^[ord(BROADCAST_ADDRESS)]:= true;
|
|
FlipExclude^[ord(ROUTER_SOLICITATION_ADDRESS)]:= true;
|
|
FlipExclude^[ord(STATIC_ROUTE)]:= true;
|
|
FlipExclude^[ord(NETWORK_INFORMATION_SERVICE_DOMAIN)]:= true;
|
|
FlipExclude^[ord(NETWORK_INFORMATION_SERVERS)]:= true;
|
|
FlipExclude^[ord(NTP_SERVERS)]:= true;
|
|
FlipExclude^[ord(VENDOR_SPECIFIC_INFORMATION)]:= true;
|
|
FlipExclude^[ord(NETBIOS_OVER_TCP_NAME_SERVER)]:= true;
|
|
FlipExclude^[ord(NETBIOS_OVER_TCP_DATAGRAM_DISTRIBUTION_SERVER)]:= true;
|
|
FlipExclude^[ord(NETBIOS_OVER_TCP_SCOPE)]:= true;
|
|
FlipExclude^[ord(X_WINDOW_SYSTEM_FONT_SERVER)]:= true;
|
|
FlipExclude^[ord(X_WINDOW_SYSTEM_DISPLAY_MANAGER)]:= true;
|
|
FlipExclude^[ord(NETWORK_INFORMATION_SERVICE_PLUS_DOMAIN)]:= true;
|
|
FlipExclude^[ord(NETWORK_INFORMATION_SERVICE_PLUS_SERVERS)]:= true;
|
|
FlipExclude^[ord(MOBILE_IP_HOME_AGENT)]:= true;
|
|
FlipExclude^[ord(SMTP_SERVER)]:= true;
|
|
FlipExclude^[ord(POP3_SERVER)]:= true;
|
|
FlipExclude^[ord(NNTP_SERVER)]:= true;
|
|
FlipExclude^[ord(DEFAULT_WWW_SERVER)]:= true;
|
|
FlipExclude^[ord(DEFAULT_FINGER_SERVER)]:= true;
|
|
FlipExclude^[ord(DEFAULT_IRC_SERVER)]:= true;
|
|
FlipExclude^[ord(STREETTALK_SERVER)]:= true;
|
|
FlipExclude^[ord(STDA_SERVER)]:= true;
|
|
FlipExclude^[ord(REQUESTED_IP_ADDRESS)]:= true;
|
|
FlipExclude^[ord(SERVER_IDENTIFIER)]:= true;
|
|
FlipExclude^[ord(PARAMETER_REQUEST_LIST)]:= true;
|
|
FlipExclude^[ord(VENDOR_CLASS_IDENTIFIER)]:= true;
|
|
FlipExclude^[ord(CLIENT_IDENTIFIER)]:= true;
|
|
FlipExclude^[ord(TFTP_SERVER_NAME)]:= true;
|
|
FlipExclude^[ord(BOOTFILE_NAME)]:= true;
|
|
FlipExclude^[ord(RELAY_AGENT_INFORMATION)]:= true;
|
|
FlipExclude^[ord(NDS_SERVERS)]:= true;
|
|
FlipExclude^[ord(NDS_TREE_NAME)]:= true;
|
|
FlipExclude^[ord(NDS_CONTEXT)]:= true;
|
|
FlipExclude^[ord(POSIX_TIMEZONE)]:= true;
|
|
FlipExclude^[ord(TZ_TIMEZONE)]:= true;
|
|
FlipExclude^[ord(DOMAIN_SEARCH)]:= true;
|
|
FlipExclude^[ord(CLASSLESS_STATIC_ROUTE)]:= true;
|
|
|
|
{ Bind to port 68 }
|
|
bind();
|
|
|
|
console.outputln('DHCP', 'Register end.');
|
|
end;
|
|
|
|
end. |