#ifndef _MPLS_UTILS_H
#define _MPLS_UTILS_H

#ifdef __cplusplus
extern "C"
{
#endif

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

/* Reference: RFC 5462, RFC 3032
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                Label                  | TC  |S|       TTL     |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	Label:  Label Value, 20 bits
 *	TC:     Traffic Class, 3 bits
 *	S:      Bottom of Stack, 1 bit
 *	TTL:    Time to Live, 8 bits
 */

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

static inline uint32_t mpls_label_get_label(const struct mpls_label *hdr)
{
    return ((ntohl(hdr->entry) & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT);
}

static inline uint8_t mpls_label_get_tc(const struct mpls_label *hdr)
{
    return ((ntohl(hdr->entry) & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT);
}

static inline uint8_t mpls_label_get_bos(const struct mpls_label *hdr)
{
    return ((ntohl(hdr->entry) & MPLS_LS_S_MASK) >> MPLS_LS_S_SHIFT);
}

static inline uint8_t mpls_label_get_ttl(const struct mpls_label *hdr)
{
    return ((ntohl(hdr->entry) & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT);
}

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

static inline void mpls_label_set_label(struct mpls_label *hdr, uint32_t label)
{
    hdr->entry = htonl((ntohl(hdr->entry) & ~MPLS_LS_LABEL_MASK) | (label << MPLS_LS_LABEL_SHIFT));
}

static inline void mpls_label_set_tc(struct mpls_label *hdr, uint8_t tc)
{
    hdr->entry = htonl((ntohl(hdr->entry) & ~MPLS_LS_TC_MASK) | (tc << MPLS_LS_TC_SHIFT));
}

static inline void mpls_label_set_bos(struct mpls_label *hdr, uint8_t bos)
{
    hdr->entry = htonl((ntohl(hdr->entry) & ~MPLS_LS_S_MASK) | (bos << MPLS_LS_S_SHIFT));
}

static inline void mpls_label_set_ttl(struct mpls_label *hdr, uint8_t ttl)
{
    hdr->entry = htonl((ntohl(hdr->entry) & ~MPLS_LS_TTL_MASK) | (ttl << MPLS_LS_TTL_SHIFT));
}

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

static inline int mpls_label_to_str(const struct mpls_label *hdr, char *buf, size_t size)
{
    return snprintf(buf, size, "MPLS: label=%u tc=%u bos=%u ttl=%u",
                    mpls_label_get_label(hdr), mpls_label_get_tc(hdr),
                    mpls_label_get_bos(hdr), mpls_label_get_ttl(hdr));
}

#ifdef __cplusplus
}
#endif

#endif
