{ 
	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(puint8(@Header^.Your_IP[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(@SendHeader^.Client_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;
        copyMAC(@BROADCAST_MAC[0], @packetCtx^.MAC.Destination[0]);
        copyMAC(MAC, @packetCtx^.MAC.Source[0]);
        copyIPv4(@NULL_IP[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) 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(@NULL_IP[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.