#ifndef _ETH_UTILS_H
#define _ETH_UTILS_H

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>

/******************************************************************************
 * get
 ******************************************************************************/

static inline int eth_hdr_get_dest(const struct ethhdr *hdr, char *buff, int size)
{
    return snprintf(buff, size, "%02x:%02x:%02x:%02x:%02x:%02x",
                    hdr->h_dest[0], hdr->h_dest[1], hdr->h_dest[2],
                    hdr->h_dest[3], hdr->h_dest[4], hdr->h_dest[5]);
}

static inline int eth_hdr_get_source(const struct ethhdr *hdr, char *buff, int size)
{
    return snprintf(buff, size, "%02x:%02x:%02x:%02x:%02x:%02x",
                    hdr->h_source[0], hdr->h_source[1], hdr->h_source[2],
                    hdr->h_source[3], hdr->h_source[4], hdr->h_source[5]);
}

static inline uint16_t eth_hdr_get_proto(const struct ethhdr *hdr)
{
    return ntohs(hdr->h_proto);
}

/******************************************************************************
 * set
 ******************************************************************************/

static inline void eth_hdr_set_dest(struct ethhdr *hdr, const char *dest)
{
    sscanf(dest, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
           &hdr->h_dest[0], &hdr->h_dest[1], &hdr->h_dest[2],
           &hdr->h_dest[3], &hdr->h_dest[4], &hdr->h_dest[5]);
}

static inline void eth_hdr_set_source(struct ethhdr *hdr, const char *source)
{
    sscanf(source, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
           &hdr->h_source[0], &hdr->h_source[1], &hdr->h_source[2],
           &hdr->h_source[3], &hdr->h_source[4], &hdr->h_source[5]);
}

static inline void eth_hdr_set_proto(struct ethhdr *hdr, uint16_t proto)
{
    hdr->h_proto = htons(proto);
}

/******************************************************************************
 * identify
 ******************************************************************************/

// /usr/include/linux/if_ether.h
static inline int is_eth_proto(uint16_t proto)
{
    switch (proto)
    {
    case ETH_P_LOOP:
    case ETH_P_PUP:
    case ETH_P_PUPAT:
    case ETH_P_IP:
    case ETH_P_X25:
    case ETH_P_ARP:
    case ETH_P_BPQ:
    case ETH_P_IEEEPUP:
    case ETH_P_IEEEPUPAT:
    case ETH_P_DEC:
    case ETH_P_DNA_DL:
    case ETH_P_DNA_RC:
    case ETH_P_DNA_RT:
    case ETH_P_LAT:
    case ETH_P_DIAG:
    case ETH_P_CUST:
    case ETH_P_SCA:
    case ETH_P_TEB:
    case ETH_P_RARP:
    case ETH_P_ATALK:
    case ETH_P_AARP:
    case ETH_P_8021Q:
    case ETH_P_IPX:
    case ETH_P_IPV6:
    case ETH_P_PAUSE:
    case ETH_P_SLOW:
    case ETH_P_WCCP:
    case ETH_P_PPP_DISC:
    case ETH_P_PPP_SES:
    case ETH_P_MPLS_UC:
    case ETH_P_MPLS_MC:
    case ETH_P_ATMMPOA:
    case ETH_P_ATMFATE:
    case ETH_P_PAE:
    case ETH_P_AOE:
    case ETH_P_TIPC:
    case ETH_P_1588:
    case ETH_P_FCOE:
    case ETH_P_FIP:
    case ETH_P_EDSA:
    case ETH_P_802_3:
    case ETH_P_AX25:
    case ETH_P_ALL:
    case ETH_P_802_2:
    case ETH_P_SNAP:
    case ETH_P_DDCMP:
    case ETH_P_WAN_PPP:
    case ETH_P_PPP_MP:
    case ETH_P_LOCALTALK:
    case ETH_P_CAN:
    case ETH_P_PPPTALK:
    case ETH_P_TR_802_2:
    case ETH_P_MOBITEX:
    case ETH_P_CONTROL:
    case ETH_P_IRDA:
    case ETH_P_ECONET:
    case ETH_P_HDLC:
    case ETH_P_ARCNET:
    case ETH_P_DSA:
    case ETH_P_TRAILER:
    case ETH_P_PHONET:
    case ETH_P_IEEE802154:
        return 1;
    default:
        return 0;
    }
}

/******************************************************************************
 * print
 ******************************************************************************/

static inline const char *eth_proto_to_str(uint16_t proto)
{
    switch (proto)
    {
    case ETH_P_LOOP:
        return "ETH_P_LOOP";
    case ETH_P_PUP:
        return "ETH_P_PUP";
    case ETH_P_PUPAT:
        return "ETH_P_PUPAT";
    case ETH_P_IP:
        return "ETH_P_IP";
    case ETH_P_X25:
        return "ETH_P_X25";
    case ETH_P_ARP:
        return "ETH_P_ARP";
    case ETH_P_BPQ:
        return "ETH_P_BPQ";
    case ETH_P_IEEEPUP:
        return "ETH_P_IEEEPUP";
    case ETH_P_IEEEPUPAT:
        return "ETH_P_IEEEPUPAT";
    case ETH_P_DEC:
        return "ETH_P_DEC";
    case ETH_P_DNA_DL:
        return "ETH_P_DNA_DL";
    case ETH_P_DNA_RC:
        return "ETH_P_DNA_RC";
    case ETH_P_DNA_RT:
        return "ETH_P_DNA_RT";
    case ETH_P_LAT:
        return "ETH_P_LAT";
    case ETH_P_DIAG:
        return "ETH_P_DIAG";
    case ETH_P_CUST:
        return "ETH_P_CUST";
    case ETH_P_SCA:
        return "ETH_P_SCA";
    case ETH_P_TEB:
        return "ETH_P_TEB";
    case ETH_P_RARP:
        return "ETH_P_RARP";
    case ETH_P_ATALK:
        return "ETH_P_ATALK";
    case ETH_P_AARP:
        return "ETH_P_AARP";
    case ETH_P_8021Q:
        return "ETH_P_8021Q";
    case ETH_P_IPX:
        return "ETH_P_IPX";
    case ETH_P_IPV6:
        return "ETH_P_IPV6";
    case ETH_P_PAUSE:
        return "ETH_P_PAUSE";
    case ETH_P_SLOW:
        return "ETH_P_SLOW";
    case ETH_P_WCCP:
        return "ETH_P_WCCP";
    case ETH_P_PPP_DISC:
        return "ETH_P_PPP_DISC";
    case ETH_P_PPP_SES:
        return "ETH_P_PPP_SES";
    case ETH_P_MPLS_UC:
        return "ETH_P_MPLS_UC";
    case ETH_P_MPLS_MC:
        return "ETH_P_MPLS_MC";
    case ETH_P_ATMMPOA:
        return "ETH_P_ATMMPOA";
    case ETH_P_ATMFATE:
        return "ETH_P_ATMFATE";
    case ETH_P_PAE:
        return "ETH_P_PAE";
    case ETH_P_AOE:
        return "ETH_P_AOE";
    case ETH_P_TIPC:
        return "ETH_P_TIPC";
    case ETH_P_1588:
        return "ETH_P_1588";
    case ETH_P_FCOE:
        return "ETH_P_FCOE";
    case ETH_P_FIP:
        return "ETH_P_FIP";
    case ETH_P_EDSA:
        return "ETH_P_EDSA";
    case ETH_P_802_3:
        return "ETH_P_802_3";
    case ETH_P_AX25:
        return "ETH_P_AX25";
    case ETH_P_ALL:
        return "ETH_P_ALL";
    case ETH_P_802_2:
        return "ETH_P_802_2";
    case ETH_P_SNAP:
        return "ETH_P_SNAP";
    case ETH_P_DDCMP:
        return "ETH_P_DDCMP";
    case ETH_P_WAN_PPP:
        return "ETH_P_WAN_PPP";
    case ETH_P_PPP_MP:
        return "ETH_P_PPP_MP";
    case ETH_P_LOCALTALK:
        return "ETH_P_LOCALTALK";
    case ETH_P_CAN:
        return "ETH_P_CAN";
    case ETH_P_PPPTALK:
        return "ETH_P_PPPTALK";
    case ETH_P_TR_802_2:
        return "ETH_P_TR_802_2";
    case ETH_P_MOBITEX:
        return "ETH_P_MOBITEX";
    case ETH_P_CONTROL:
        return "ETH_P_CONTROL";
    case ETH_P_IRDA:
        return "ETH_P_IRDA";
    case ETH_P_ECONET:
        return "ETH_P_ECONET";
    case ETH_P_HDLC:
        return "ETH_P_HDLC";
    case ETH_P_ARCNET:
        return "ETH_P_ARCNET";
    case ETH_P_DSA:
        return "ETH_P_DSA";
    case ETH_P_TRAILER:
        return "ETH_P_TRAILER";
    case ETH_P_PHONET:
        return "ETH_P_PHONET";
    case ETH_P_IEEE802154:
        return "ETH_P_IEEE802154";
    default:
        return "ETH_P_UNKNOWN";
    }
}

static inline int eth_hdr_to_str(const struct ethhdr *hdr, char *buf, size_t size)
{
    memset(buf, 0, size);
    char dest[18] = {0};
    char source[18] = {0};
    uint16_t proto = eth_hdr_get_proto(hdr);
    eth_hdr_get_dest(hdr, dest, sizeof(dest));
    eth_hdr_get_source(hdr, source, sizeof(source));
    return snprintf(buf, size, "ETH: source=%s dest=%s proto=%s",
                    source, dest, eth_proto_to_str(proto));
}

#ifdef __cplusplus
}
#endif

#endif
