diff --git a/src/driver/net/l1/net.pas b/src/driver/net/l1/net.pas
index e6fc37db..0256e763 100644
--- a/src/driver/net/l1/net.pas
+++ b/src/driver/net/l1/net.pas
@@ -36,7 +36,7 @@ uses
 var
     CBSend : TNetSendCallback = nil;
     CBNext : TRecvCallback    = nil;
-    MAC     : puint8          = @NULL_MAC[0];
+    MAC    : puint8           = @NULL_MAC[0];
 
 procedure writeToLog(str : pchar);
 var
@@ -92,8 +92,8 @@ end;
 
 procedure send(p_data : void; p_len : uint16);
 begin
-    push_trace('net.send');
-    writeToLogLn('L1: net.send');
+    //push_trace('net.send');
+    //writeToLogLn('L1: net.send');
     if CBSend <> nil then CBSend(p_data, p_len);
     pop_trace;
 end;
@@ -103,8 +103,8 @@ var
     context : PPacketContext;
 
 begin
-    push_trace('net.recv');
-    writeToLogLn('L1: net.recv');
+    //push_trace('net.recv');
+    //writeToLogLn('L1: net.recv');
     context:= newPacketContext;
     if CBNext <> nil then CBNext(p_data, p_len, context);
     freePacketContext(context);
diff --git a/src/driver/net/l2/eth2.pas b/src/driver/net/l2/eth2.pas
index e265ba29..559ee9e6 100644
--- a/src/driver/net/l2/eth2.pas
+++ b/src/driver/net/l2/eth2.pas
@@ -34,7 +34,6 @@ var
 
 procedure registerTypePromisc(eType : uint16; RecvCB : TRecvCallback);
 begin
-    push_trace('eth2.registerType');
     register;
     if EthTypes[eType] = nil then EthTypes[eType]:= RecvCB;
     Promisc[eType]:= true;
@@ -42,7 +41,6 @@ end;
 
 procedure registerType(eType : uint16; RecvCB : TRecvCallback);
 begin
-    push_trace('eth2.registerType');
     register;
     if EthTypes[eType] = nil then EthTypes[eType]:= RecvCB;
 end;
@@ -83,8 +81,7 @@ var
     buf        : puint8;
 
 begin
-    push_trace('eth2.recv');
-    writeToLogLn('    L2: eth2.recv');
+    //writeToLogLn('    L2: eth2.recv');
     buf:= puint8(p_data);
     
     Header:= PEthernetHeader(buf);
@@ -102,7 +99,6 @@ begin
             EthTypes[proto_type](void(buf), p_len - 14, p_context);
         end;    
     end;
-    pop_trace;
 end;
 
 procedure register;
diff --git a/src/driver/net/l3/arp.pas b/src/driver/net/l3/arp.pas
index fe30f770..be26cd1c 100644
--- a/src/driver/net/l3/arp.pas
+++ b/src/driver/net/l3/arp.pas
@@ -80,8 +80,6 @@ var
     hSize, pSize : uint8;
 
 begin
-    push_trace('arp.send');
-    writeToLogLn('        L3: arp.send');
     if p_context <> nil then begin
         buf:= kalloc(sizeof(TARPHeader));
         hdr:= PARPHeader(buf);
@@ -189,10 +187,6 @@ var
     context      : PPacketContext;
 
 begin
-    push_trace('arp.recv');
-    writeToLogLn('        L3: arp.recv');
-    console.redrawWindows;
-    
     { Get our converted Header }
     Header:= PARPHeader(p_data);
     AHeader.Hardware_Type:= (Header^.Hardware_Type_Hi SHL 8) + Header^.Hardware_Type_Lo;
@@ -219,7 +213,6 @@ begin
     if IPEqual(@AHeader.Destination_Protocol[0], @getIPv4Config^.Address[0]) then begin
         case AHeader.Operation of
             $1:begin { ARP Request }
-                writeToLogLn('            arp.recv.arp.req');
                 context:= newPacketContext;
                 copyMAC(@AHeader.Source_Hardware[0], @context^.MAC.Destination[0]);
                 copyIPv4(@AHeader.Source_Protocol[0], @context^.IP.Destination[0]);
@@ -229,28 +222,28 @@ begin
                 freePacketContext(context);
             end;
             $2:begin { ARP Reply }
-                writeToLogLn('            arp.recv.arp.rep');
+                
             end;
             $3:begin { RARP Request }
-                writeToLogLn('            arp.recv.rarp.req');
+                
             end;
             $4:begin { RARP Reply }
-                writeToLogLn('            arp.recv.rarp.rep');
+                
             end;
             $5:begin { DRARP Request }
-                writeToLogLn('            arp.recv.drarp.req');
+                
             end;
             $6:begin { DRARP Reply }
-                writeToLogLn('            arp.recv.drarp.rep');
+                
             end;
             $7:begin { DRARP Error }
-                writeToLogLn('            arp.recv.drarp.err');
+                
             end;
             $8:begin { InARP Request }
-                writeToLogLn('            arp.recv.inarp.req');
+                
             end;
             $9:begin { InARP Reply }
-                writeToLogLn('            arp.recv.inarp.rep');
+                
             end;
         end;
     end;
diff --git a/src/driver/net/l3/ipv4.pas b/src/driver/net/l3/ipv4.pas
index c7f70e94..420d98e7 100644
--- a/src/driver/net/l3/ipv4.pas
+++ b/src/driver/net/l3/ipv4.pas
@@ -44,7 +44,6 @@ var
     buffer : void;
 
 begin
-    writeToLogLn('        L3: ipv4.send');  
     inc(CurrentID);
     Header.version:= 4;
     Header.header_len:= 5;
@@ -83,8 +82,6 @@ var
     len     : uint16;
 
 begin
-    push_trace('ipv4.recv');
-    writeToLogLn('        L3: ipv4.recv');
     Header:= PIPV4Header(p_data);
     AHeader.version:= Header^.version;
     AHeader.header_len:= Header^.header_len;
@@ -112,7 +109,7 @@ begin
     copyIPv4(@AHeader.Dst[0], @p_context^.IP.Destination[0]);
 
     if Config.UP then begin
-        if (IPEqual(@Config.Address[0], @AHeader.Dst[0])) OR (AHeader.Dst[3] = 255) then begin
+        if (IPEqual(@Config.Address[0], @AHeader.Dst[0])) OR (AHeader.Dst[3] = 255) OR (IPEqual(@Config.Address[0], @NULL_IP[0])) then begin
             if Protocols[AHeader.Protocol] <> nil then begin
                 Protocols[AHeader.Protocol](void(buf), len, p_context);
             end;
@@ -130,7 +127,6 @@ var
     i : uint32;
 
 begin
-    push_trace('ipv4.terminal_command_ifconfig');
     if paramCount(params) > 1 then begin
         Command:= GetParam(0, Params);
         if StringEquals(Command, 'set') then begin
@@ -177,7 +173,6 @@ begin
         else 
             writestringlnWND('   NetUP:   false', getTerminalHWND);
     end;
-    pop_trace;
 end;
 
 procedure register;
@@ -205,7 +200,6 @@ end;
 
 procedure registerProtocol(Protocol_ID : uint8; recv_callback : TRecvCallback);
 begin
-    push_trace('ipv4.registerProtocol');
     register;
     if Protocols[Protocol_ID] = nil then Protocols[Protocol_ID]:= recv_callback;
     pop_trace;
diff --git a/src/driver/net/l4/icmp.pas b/src/driver/net/l4/icmp.pas
index d0224c3f..42f9aa1c 100644
--- a/src/driver/net/l4/icmp.pas
+++ b/src/driver/net/l4/icmp.pas
@@ -118,12 +118,10 @@ var
     Handle : uint8;
 
 begin
-    writeToLogLn('            L4: icmp.recv');
     Header:= PICMPHeader(p_data);
     //writehexlnWND(Header^.ICMP_Type, getTerminalHWND); 
     case Header^.ICMP_Type of
         $08:Begin //Request
-            writeToLogLn('            L4: icmp.request');
             contextMACSwitch(p_context);
             contextIPv4Switch(p_context);
             Header^.ICMP_Type:= 0;
@@ -137,7 +135,6 @@ begin
             ipv4.send(p_data, p_len, p_context);    
         end;
         $00:begin //Reply
-            writeToLogLn('            L4: icmp.reply');
             Handle:= Header^.Identifier;
             if (Handle > 0) and (Handle < 256) then begin
                 If Handlers[Handle].Active then begin
diff --git a/src/driver/net/l4/udp.pas b/src/driver/net/l4/udp.pas
index 01c7e9da..86095bb4 100644
--- a/src/driver/net/l4/udp.pas
+++ b/src/driver/net/l4/udp.pas
@@ -123,20 +123,6 @@ begin
         memcpy(uint32(hdr), uint32(buffer), sizeof(TUDPHeader));
         memcpy(uint32(p_data), uint32(buffer) + sizeof(TUDPHeader), p_len); 
 
-        writehexln(uint32(buffer) + sizeof(TUDPHeader));
-        for i:=0 to p_len-1 do begin
-            writehexpair(puint8(p_data)[i]);
-        end;
-        writestringln(' ');
-        for i:=0 to p_len-1 do begin
-            writechar(pchar(p_data)[i]);
-        end;
-        writestringln(' ');
-        for i:=0 to size-1 do begin
-            writehexpair(puint8(buffer)[i]);
-        end;
-        writestringln(' ');
-
         udpContext^.context^.Protocol.L4:= $11;       
 
         ipv4.send(buffer, size, udpContext^.context);
@@ -203,7 +189,6 @@ var
     size    : uint16;
 
 begin
-    writeToLogLn('              L4: udp.recv');
     header:= PUDPHeader(p_data);
     if Ports[switchendian16(header^.DstPort)] <> nil then begin
         context:= PUDPPacketContext(kalloc(sizeof(TUDPPacketContext)));
diff --git a/src/driver/net/l5/dhcp.pas b/src/driver/net/l5/dhcp.pas
index 4fbb8a4b..d4fda3a6 100644
--- a/src/driver/net/l5/dhcp.pas
+++ b/src/driver/net/l5/dhcp.pas
@@ -1,3 +1,8 @@
+{ 
+	Driver->Net->L5->DHCP - Dynamic Host Configuration Protocol Driver.
+	
+	@author(Kieron Morris <kjm@kieronmorris.me>)
+}
 unit dhcp;
 
 interface
@@ -5,7 +10,7 @@ interface
 uses
     lmemorymanager, console,
     nettypes, netutils, udp, netlog, net,
-    util, rand, lists, tracer, ipv4;
+    util, rand, lists, tracer, ipv4, arp;
 
 type
     TDHCPOptions = PLinkedListBase;
@@ -26,16 +31,59 @@ implementation
 type
     TFlipExclude = Array[0..255] of boolean;
     PFlipExclude = ^TFlipExclude;
+    TDHCPConfiguration = record
+        Transaction : uint32;
+        Options     : array[0..255] of void;
+    end;
+    PDHCPConfiguation = ^TDHCPConfiguration;
 
 var
-    XID         : uint32;
-    Socket      : PUDPBindContext;
-    FlipExclude : PFlipExclude;
+    Configuration : PDHCPConfiguation;
+    Socket        : PUDPBindContext = nil;
+    FlipExclude   : PFlipExclude;
+
+procedure nullConfiguration;
+var
+    i : uint8;
+
+begin
+    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;
 
-function newHeader : PDHCPHeader;
 begin
     tracer.push_trace('dhcp.newHeader');
-    newHeader:= PDHCPHeader(kalloc(sizeof(TDHCPHeader)));
+    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;
@@ -145,6 +193,30 @@ begin
         getEndianCorrectValue32:= Value;
 end;
 
+function readOption8(Option : PDHCPOption) : uint8;
+var
+    read8 : puint8;
+begin
+    read8:= puint8(Option^.Value);
+    readOption8:= read8^;
+end;
+
+function readOption16(Option : PDHCPOption) : uint16;
+var
+    read16 : puint16;
+begin
+    read16:= puint16(Option^.Value);
+    readOption16:= read16^;
+end;
+
+function readOption32(Option : PDHCPOption) : uint32;
+var
+    read32 : puint32;
+begin
+    read32:= puint32(Option^.Value);
+    readOption32:= read32^;
+end;
+
 function writeOptions(Header : PDHCPHeader; Options : PDHCPOptions; newLength : puint16) : PDHCPHeader;
 var
     OptionsSize : uint16;
@@ -165,7 +237,7 @@ begin
     NewBuffer:= kalloc(TotalSize);
 
     //Copy over Header
-    NewHeader:= PDHCPHeader(buffer);
+    NewHeader:= PDHCPHeader(NewBuffer);
     memcpy(uint32(Header), uint32(NewHeader), sizeof(TDHCPHeader));
 
     //Write all options
@@ -221,14 +293,26 @@ var
 
 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
@@ -263,57 +347,271 @@ begin
     end;
 end;
 
-procedure processPacket(p_data : void; p_len : uint16; context : PUDPPacketContext);
+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.');  
+    nullConfiguration();  
+end;
+
+procedure processPacket_ACK(Header : PDHCPHeader; Options : PDHCPOptions);
 var
-    Header : PDHCPHeader;
-    Options : PDHCPOptions;
+    i : uint16;
+    Option : PDHCPOption;
+    read16 : puint16;
+    read32 : puint32;
+    cfgopt : void;
 
 begin
-    tracer.push_trace('dhcp.processPacket');
+    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;
+
+    SendCtx     : PUDPSendContext;
+    PacketCtx   : PPacketContext;
+
+    MAC         : puint8;
+
+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');
+        
+        SendHeader:= createHeader();
+        CopyIPv4(puint8(@Header^.Your_IP[0]), puint8(@SendHeader^.Client_IP[0]));
+        CopyIPv4(puint8(@Header^.Server_IP[0]), puint8(@SendHeader^.Server_IP[0]));
+
+        SendOptions:= newOptions();
+        SendMsgType:= ord(TDHCPMessageType.REQUEST);
+        NewOption(SendOptions, TDHCPOpCode.DHCP_MESSAGE_TYPE, void(@SendMsgType), 1, false);
+        NewOption(SendOptions, TDHCPOpCode.REQUESTED_IP_ADDRESS, void(@SendHeader^.Client_IP[0]), 4, false);
+        NewOption(SendOptions, TDHCPOpCode.SERVER_IDENTIFIER, void(@SendHeader^.Server_IP[0]), 4, false);
+        NewOption(SendOptions, TDHCPOpCode.END_VENDOR_OPTIONS, nil, 0, false);
+
+        NewSendHeader:= writeOptions(SendHeader, SendOptions, @NewHeaderSize);
+
+        { Setup Packet Context (IPv4 & ETH2) }
+        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) }
+        sendCtx:= PUDPSendContext(Kalloc(sizeof(TUDPSendContext)));
+        sendCtx^.DstPort:= 67;
+        sendCtx^.context:= packetCtx;
+        sendCtx^.socket:= Socket;
+
+        { Send }
+        udp.send(void(NewSendHeader), NewHeaderSize, sendCtx);
+
+        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.');
+            Outputln('DHCP','Searching for message type in Options');
+            { Iterate options to find DHCP_MESSAGE_TYPE }
+            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;
+
+
+    freeOptions(Options);
+    tracer.push_trace('dhcp.processPacket.exit');
 end;
 
 procedure DHCPDiscover();
 var
+    SendCtx     : PUDPSendContext;
+    PacketCtx   : PPacketContext;
     Header      : PDHCPHeader;
     NewHeader   : PDHCPHeader;
-    HeaderSize  : Puint32; 
+    HeaderSize  : uint32; 
     Options     : PDHCPOptions;
     MsgType     : uint8;
+    MAC         : puint8;
 
 begin
-    tracer.push_trace('dhcp.DHCPDiscover');
-    Header:= newHeader;
-    Options:= newOptions;
-    XID:= rand32();
-    
-    //Setup header
-    Header^.Message_Type:= $01;
-    Header^.Hardware_Type:= $01;
-    Header^.Hardware_Address_Length:= $06;
-    Header^.Hops:= $00;
-    Header^.Transaction_ID:= switchendian32(XID);
-    Header^.Seconds_Elapsed:= $0000;
-    CopyIPv4(@NULL_IP[0], @Header^.Client_IP[0]);
-    CopyIPv4(@NULL_IP[0], @Header^.Your_IP[0]);
-    CopyIPv4(@NULL_IP[0], @Header^.Server_IP[0]);
-    CopyIPv4(@NULL_IP[0], @Header^.Relay_Agent_IP[0]);
-    CopyMAC(@getMAC[0], @Header^.Client_MAC[0]);
-    memset(uint32(@Header^.Padding[0]), 0, 10);
-    memset(uint32(@Header^.Server_Hostname[0]), 0, 64);
-    memset(uint32(@Header^.Boot_File[0]), 0, 128);
-    memcpy(uint32(@DHCP_MAGIC[0]), uint32(@Header^.Magic_Cookie[0]), 4);
+    tracer.push_trace('dhcp.DHCPDiscover.begin');
+    { Ensure we have a socket bound. }
+    if Socket <> nil then begin    
+        { Clear any current configuration }
+        nullConfiguration();
 
-    //Setup options
-    MsgType:= Ord(TDHCPMessageType.DISCOVER);
-    newOption(Options, DHCP_MESSAGE_TYPE, void(@MsgType), 1, false);
+        { Setup our Transaction ID }
+        Configuration^.Transaction:= rand32();
+        
+        { Setup header }
+        Header:= createHeader();
+        processHeader(Header);
 
-    NewOption(Options, END_VENDOR_OPTIONS, nil, 0, false);
+        { 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);
 
-    getIPv4Config^.UP:= true;
+        { 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();
@@ -323,10 +621,23 @@ var
 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;
@@ -383,15 +694,10 @@ begin
     FlipExclude^[ord(TZ_TIMEZONE)]:= true;
     FlipExclude^[ord(DOMAIN_SEARCH)]:= true;
     FlipExclude^[ord(CLASSLESS_STATIC_ROUTE)]:= true;
-    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 console.outputln('DHCP', 'Failed to bind port 68.');
-    end;
-    DHCPDiscover;
+
+    { Bind to port 68 }
+    bind();
+
     console.outputln('DHCP', 'Register end.');
 end;
 
diff --git a/src/driver/netdev/E1000.pas b/src/driver/netdev/E1000.pas
index 7f6ed044..5d0e63bb 100644
--- a/src/driver/netdev/E1000.pas
+++ b/src/driver/netdev/E1000.pas
@@ -165,7 +165,6 @@ var
     mem : puint32;
 
 begin
-    push_trace('E1000.writeCommand');
     if (bar_type = 0) then begin
         mem:= puint32(mem_base + p_address);
         mem^:= p_value;
@@ -173,7 +172,6 @@ begin
         outl(io_base + 0, p_address);
         outl(io_base + 4, p_address)
     end;
-    pop_trace;
 end;
 
 function readCommand(p_address : uint16) : uint32;
@@ -181,7 +179,6 @@ var
     mem : puint32;
 
 begin
-    push_trace('E1000.readCommand');
     if (bar_type = 0) then begin
         mem:= puint32(mem_base + p_address);
         readCommand:= mem^;
@@ -189,7 +186,6 @@ begin
         outl(io_base, p_address);
         readCommand:= inl(io_base + 4);
     end;   
-    pop_trace; 
 end;
 
 function readStatus : uint32;
@@ -202,7 +198,6 @@ var
     val, i : uint32;
 
 begin
-    push_trace('E1000.detectEEPROM');
     val:= 0;
     writeCommand(REG_EEPROM, $1);
     for i:=0 to 1000 do begin
@@ -211,7 +206,6 @@ begin
         if (val and $10) > 0 then eeprom_exists:= true else eeprom_exists:= false;
     end;
     detectEEPROM:= eeprom_exists;
-    pop_trace;
 end;
 
 function EEPROMRead( address : uint8 ) : uint32;
@@ -220,7 +214,6 @@ var
     tmp  : uint32;
 
 begin
-    push_trace('E1000.EEPROMRead');
     tmp:= 0;
     if (eeprom_exists) then begin
         writeCommand( REG_EEPROM, 1 OR (uint32(address) SHL 8) );
@@ -235,7 +228,6 @@ begin
     end;
     data:= uint16( (tmp SHR 16) AND ($FFFF) );
     EEPROMRead:= data;
-    pop_trace;
 end;
 
 function readMACAddress() : boolean;
@@ -271,7 +263,6 @@ begin
         end;
     end;
     readMACAddress:= res;
-    pop_trace;
 end;
 
 procedure startLink();
@@ -279,10 +270,8 @@ var
     val : uint32;
 
 begin
-    push_trace('E1000.startLink');
     val:= readCommand(REG_CTRL);
     writeCommand(REG_CTRL, val OR ECTRL_SLU);
-    pop_trace;
 end;
 
 procedure rxinit();
@@ -293,7 +282,6 @@ var
     i      : uint32;
 
 begin
-    push_trace('E1000.rxinit');
     ptr:= puint8(kalloc(sizeof(TE1000_rx_desc) * E1000_NUM_RX_DESC + 16));
     descs:= PE1000_rx_desc(ptr);
     for i:=0 to E1000_NUM_RX_DESC do begin
@@ -323,7 +311,6 @@ begin
     rx_curr:= 0;
 
     writeCommand(REG_RCTRL, RCTL_EN OR RCTL_SBP OR RCTL_UPE OR RCTL_MPE OR RCTL_LBM_NONE OR RTCL_RDMTS_HALF OR RCTL_BAM OR RCTL_SECRC OR RCTL_BSIZE_2048);
-    pop_trace;
 end;
 
 procedure txinit();
@@ -334,7 +321,6 @@ var
     i      : uint32;
 
 begin
-    push_trace('E1000.txinit');
     ptr:= puint8(kalloc(sizeof(TE1000_tx_desc) * (E1000_NUM_TX_DESC + 16))); 
     descs:= PE1000_tx_desc(ptr);
     for i:=0 to E1000_NUM_TX_DESC do begin
@@ -346,9 +332,7 @@ begin
     
     outptr:= puint8(vtop(uint32(ptr))); //puint8(uint32(ptr) - KERNEL_VIRTUAL_BASE);
 
-    console.output('E1000 Driver', 'TX VMem: ');
     console.writehexln(uint32(ptr));
-    console.output('E1000 Driver', 'TX Mem: ');
     console.writehexln(uint32(outptr));
 
     writeCommand(REG_TXDESCHI, 0);
@@ -366,16 +350,13 @@ begin
         writeCommand(REG_TCTRL, $3003F0FA);
         writeCommand(REG_TIPG, $0060200A);
     end;
-    pop_trace;
 end;
 
 procedure enableInturrupt();
 begin
-    push_trace('E1000.enableInterrupt');
     writeCommand(REG_IMASK, $1F6DC);
     writeCommand(REG_IMASK, $FF AND NOT(4));
     readCommand($C0);
-    pop_trace;
 end;
 
 procedure handleReceive();
@@ -387,7 +368,6 @@ var
     i          : uint16;
     
 begin
-    push_trace('E1000.handleReceive');
     while (rx_descs[rx_curr]^.status AND $1) > 0 do begin
         got_packet:= true;
         buf:= rx_buffs[rx_curr];
@@ -401,7 +381,6 @@ begin
         rx_curr:= (rx_curr + 1) mod E1000_NUM_RX_DESC;
         writeCommand(REG_RXDESCTAIL, old_cur);
     end;
-    pop_trace;
 end;
 
 procedure writeCardType();
@@ -421,8 +400,6 @@ var
     data     : uint32;
 
 begin
-    push_trace('E1000.fire');
-    writeToLogLn('L0: E1000 Fire');
     status:= readCommand($C0);
     if (status AND $04) > 0 then begin
         startLink();
@@ -439,9 +416,7 @@ end;
 
 procedure console_command_mac(params : PParamList);
 begin
-    push_trace('E1000.console_command_mac');
     writeMACAddress(@mac[0], getTerminalHWND);
-    pop_trace;
 end;
 
 procedure console_command_sendtest(params : PParamList);
@@ -461,7 +436,6 @@ var
                                          );
 
 begin
-    push_trace('E1000.console_command_sendtest');
     TestPacket[6]:= mac[0];
     TestPacket[7]:= mac[1];
     TestPacket[8]:= mac[2];
@@ -579,35 +553,29 @@ end;
 
 function loadE1000(ptr : void) : boolean;
 begin
-    push_trace('E1000.loadE1000');
     loadE1000:= false;
     if not Loaded then begin
         card_type:= ctE1000;
         loadE1000:= load(ptr);
     end;
-    pop_trace;
 end;
 
 function load82577LM(ptr : void) : boolean;
 begin
-    push_trace('E1000.load82577LM');
     load82577LM:= false;
     if not Loaded then begin
         card_type:= ct82577LM;
         load82577LM:= load(ptr);
     end;
-    pop_trace;
 end;
 
 function loadI217(ptr : void) : boolean;
 begin
-    push_trace('E1000.loadI217');
     loadI217:= false;
     if not Loaded then begin
         card_type:= ctI217;
         loadI217:= load(ptr);
     end;
-    pop_trace;
 end;
 
 procedure init();
@@ -643,7 +611,6 @@ var
     timeout : uint32;
 
 begin
-    push_trace('E1000.sendPacket');
     tx_descs[tx_curr]^.address:= uint32(vtop(uint32(p_data)));
     tx_descs[tx_curr]^.length:= p_len;
     tx_descs[tx_curr]^.cmd:= CMD_EOP OR CMD_IFCS OR CMD_RS OR CMD_RPS;
@@ -657,7 +624,6 @@ begin
     end;
     sendPacket:= 1;
     if timeout > 0 then sendPacket:= 0;
-    pop_trace;
 end;
 
 end.
\ No newline at end of file
diff --git a/src/include/rand.pas b/src/include/rand.pas
index 95f55ec3..f76f3584 100644
--- a/src/include/rand.pas
+++ b/src/include/rand.pas
@@ -20,7 +20,7 @@ end;
 
 function rand32 : uint32;
 begin
-    rand32:= (rand SHL 16) AND rand;
+    rand32:= (rand SHL 16) OR rand;
 end;
 
 function rand16 : uint16;
@@ -35,7 +35,7 @@ end;
 
 procedure srand(seed : uint32);
 begin
-    next:= seed;
+    next:= next + seed;
 end;
 
 end.
\ No newline at end of file
diff --git a/src/kernel.pas b/src/kernel.pas
index d499b639..c9f17e77 100644
--- a/src/kernel.pas
+++ b/src/kernel.pas
@@ -19,8 +19,8 @@ uses
      tracer,
      drivermanagement,
      scheduler,
+     progmanager,
      PCI,
-     Terminal,
      strings,
      USB,
      testdriver,
@@ -35,22 +35,12 @@ uses
      fonts,
      RTC,
      serial,
-     shell,
-     memview,
-     splash,
-     themer,
-     netlog,
-     vmlog,
      vm,
-     vmstate,
-     edit,
-     udpcat,
      cpu,
      md5,
-     md5sum,
      base64,
-     base64_prog,
-     rand;
+     rand,
+     terminal;
  
 procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall;
  
@@ -159,7 +149,7 @@ begin
      isrmanager.init();
      faults.init();
      RTC.init();
-     rand.srand((getDateTime.Seconds SHR 24) OR (getDateTime.Minutes SHR 16) OR (getDateTime.Hours SHR 8) OR (getDateTime.Day));     
+          
      pmemorymanager.init();
      vmemorymanager.init();
      lmemorymanager.init();
@@ -214,24 +204,7 @@ begin
      //vm.init();
 
      { Init Progs }
-     tracer.push_trace('kmain.SHELLINIT');
-     shell.init();
-     tracer.push_trace('kmain.MEMVIEWINIT');
-     memview.init();
-     tracer.push_trace('kmain.THEMERINIT');
-     themer.init();
-     tracer.push_trace('kmain.NETLOGINIT');
-     netlog.init();
-     tracer.push_trace('kmain.VMLOGINIT');
-     vmlog.init();
-     tracer.push_trace('kmain.VMSTATEINIT');
-     vmstate.init();
-     tracer.push_trace('kmain.EDIT');
-     edit.init();
-     udpcat.init();
-     md5sum.init();
-     base64_prog.init();
-     terminal.run();
+     progmanager.init();
 
      { Init Splash }
      //tracer.push_trace('kmain.SPLASHINIT');
@@ -244,8 +217,10 @@ begin
      console.setdefaultattribute(console.combinecolors($17E0, $0000));
      console.writestringln('Asuro Booted Correctly!');
      console.setdefaultattribute(console.combinecolors($FFFF, $0000));
+     writestringln(' ');
 
      tracer.push_trace('kmain.END');
+     rand.srand((getDateTime.Seconds SHR 24) OR (getDateTime.Minutes SHR 16) OR (getDateTime.Hours SHR 8) OR (getDateTime.Day));
 
      tracer.push_trace('kmain.TICK');
      while true do begin
diff --git a/src/prog/md5sum.pas b/src/prog/md5sum.pas
index 1f0cf8f3..2d8bbdb2 100644
--- a/src/prog/md5sum.pas
+++ b/src/prog/md5sum.pas
@@ -1,5 +1,5 @@
 { 
-	Prog->VMLog - Virtual Machine Event Log.
+	Prog->MD5Sum - MD5 Checksum of a given string.
 	
 	@author(Kieron Morris <kjm@kieronmorris.me>)
 }
diff --git a/src/tracer.pas b/src/tracer.pas
index 18233228..5db519d1 100644
--- a/src/tracer.pas
+++ b/src/tracer.pas
@@ -20,6 +20,14 @@ implementation
 uses
     console, lmemorymanager, util, strings, serial, terminal;
 
+type
+    PTracerEntry = ^TTracerEntry;
+    TTracerEntry = record
+        Next        : PTracerEntry;
+        Data        : pchar;
+        Previous    : PTracerEntry;
+    end;
+
 const
     MAX_TRACE = 40;
 
@@ -29,6 +37,9 @@ var
     Traces      : Array[0..MAX_TRACE-1] of PChar;
     c_lock      : Boolean = false;
 
+var
+    head : PTracerEntry;
+    tail : PTracerEntry;
 
 procedure terminal_command_tracer(Params : PParamList);
 var