void DescribePacket(const u_int8_t* data, const char* io)
{
    struct ether_header* header = (struct ether_header*)data;
    unsigned int type = ntohs(header->ether_type);
    char summary[256];
    MacAddress mac;

    switch (type)
    {
        case ETHERTYPE_IP:
        case ETHERTYPE_ARP:
            if (type == ETHERTYPE_IP)
            {
                GetIPv4PacketSummary(data, summary);
                log_info("%s IPv4: %s", io, summary);
            }
            else
            {
                struct ether_arp* arpreq = (struct ether_arp*)(data + sizeof(struct ether_header));
                mac = MacAddress::CopyFrom(arpreq->arp_sha);
                log_info("%s ARP: op: %d - sender: %s", io, ntohs(arpreq->arp_op), mac.ToString().c_str());
                char srcip[INET_ADDRSTRLEN], dstip[INET_ADDRSTRLEN];
                strcpy(srcip, inet_ntoa(*(in_addr*)arpreq->arp_spa));
                strcpy(dstip, inet_ntoa(*(in_addr*)arpreq->arp_tpa));
                log_info("       For: %s->%s", srcip, dstip);
            }
            break;
        case ETHERTYPE_IPV6:
            GetIPv6PacketSummary(data, summary);
            log_info("%s IPv6: %s", io, summary);
            break;
        default:
            log_info("%s 0x%X packet", io, type);
            break;
    }
    
    mac = MacAddress::CopyFrom(header->ether_shost);
    log_info("    Source: %s", mac.ToString().c_str());
    mac = MacAddress::CopyFrom(header->ether_dhost);
    log_info("    Target: %s", mac.ToString().c_str());

    printf("\n");
}