U0 IPV4Handler(CEthernetFrame *ethernet_frame)
{
        CIPV4Packet packet;

        IPV4PacketParse(&packet, ethernet_frame);

        ARPCachePut(packet.source_ip_address, ethernet_frame->source_address);

        switch (packet.protocol)
        {
                case IP_PROTOCOL_ICMP:
                        NetLog("IPV4 HANDLER: ICMP.");
                        ICMPHandler(&packet);
                        break;

                case IP_PROTOCOL_TCP:
                        NetWarn("IPV4 HANDLER: TCP.");
                        TCPHandler(&packet);
                        break;

                case IP_PROTOCOL_UDP:
                        NetLog("IPV4 HANDLER: UDP.");
                        UDPHandler(&packet);
                        break;

                default:
                        NetErr("IPV4 HANDLER: Unrecognized protocol: 0x%X", packet.protocol);
                        break;
        }

        NetLog("IPV4 HANDLER: Exiting.");
}

U0 NetQueueEntryHandle(CNetQueueEntry *entry)
{
        CEthernetFrame ethernet_frame;

        EthernetFrameParse(&ethernet_frame, entry->frame, entry->packet_length);

        switch (ethernet_frame.ethertype)
        {
                case ETHERTYPE_ARP:
                        NetLog("HANDLE NETQUEUE ENTRY: ARP.");
                        ARPHandler(&ethernet_frame);
                        break;

                case ETHERTYPE_IPV4:
                        NetLog("HANDLE NETQUEUE ENTRY: IPV4.");
                        IPV4Handler(&ethernet_frame);
                        break;

                case ETHERTYPE_IPV6:
                        NetWarn("HANDLE NETQUEUE ENTRY: IPV6. FIXME");
                        break;

                default:
                        NetErr("HANDLE NETQUEUE ENTRY: Unrecognized ethertype: 0x%X", ethernet_frame.ethertype);
                        break;
        }

        NetLog("HANDLE NETQUEUE ENTRY: Exiting.");
}

interrupt U0 NetHandler()
{
        CNetQueueEntry *entry;

        NetLog("$BG,DKGRAY$$FG,BLACK$"
                   "=== NET HANDLER ==="
                   "$BG$$FG$");

        NetLog("$BD,LTGRAY$$FD,WHITE$"
                   "NET HANDLER: Entering interrupt.");

        while (entry = NetQueuePull)
        {
                NetLog("NET HANDLER: Caught NetQueue Entry, handling.");
                NetQueueEntryHandle(entry);
                NetLog("NET HANDLER: Finished handling NetQueue Entry, Freeing.");
                Free(entry);

        }

        NetLog("NET HANDLER: NetQueue empty, exiting interrupt.\n"
                   "$BD,WHITE$$FD,LTGRAY$"
                   "$BG,DKGRAY$$FG,BLACK$"
                   "==================="
                   "$BG$$FG$");

        *(dev.uncached_alias + LAPIC_EOI)(U32*) = 0;
}

U0 NetHandlerInit()
{
        IntEntrySet(I_NETHANDLER, &NetHandler);
}

NetHandlerInit;