This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
stellar-stellar/infra/packet_manager/packet_helper.h

2874 lines
80 KiB
C
Raw Normal View History

#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "utils_internal.h"
#include <stdio.h>
#include <string.h>
2024-08-28 19:00:32 +08:00
#include <stdbool.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <linux/mpls.h>
#define __FAVOR_BSD 1
#include <netinet/tcp.h>
#include <netinet/udp.h>
/******************************************************************************
* ETH
******************************************************************************/
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);
}
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);
}
// /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_8021AD:
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:
case 0x880B: // PPPPoint-to-Point Protoco
return 1;
default:
return 0;
}
}
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_8021AD:
return "ETH_P_8021AD";
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";
case 0x880B:
return "ETH_P_PPP";
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));
}
/******************************************************************************
* GREv0
******************************************************************************/
/*
* GRE Header Format (Version 0)
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |C|R|K|S|s|Recur| Flags | Ver | Protocol Type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Checksum (optional) | Offset (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Key (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Routing (optional)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Family | SRE Offset | SRE Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Routing Information ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* https://datatracker.ietf.org/doc/html/rfc1701
* https://datatracker.ietf.org/doc/html/rfc2890
*/
struct gre0_hdr
{
uint16_t flags;
uint16_t protocol;
};
struct sre
{
uint16_t family;
uint8_t offset;
uint8_t length;
};
#define GRE0_FLAG_CHECKSUM 0x8000
#define GRE0_FLAG_ROUTING 0x4000
#define GRE0_FLAG_KEY 0x2000
#define GRE0_FLAG_SEQUENCE 0x1000
#define GRE0_FLAG_STRICT 0x0800
#define GRE0_FLAG_RECURSION 0x0700
#define GRE0_FLAG_VERSION 0x0007
static inline uint16_t gre0_hdr_get_flags(const struct gre0_hdr *hdr)
{
return ntohs(hdr->flags);
}
static inline uint8_t gre0_hdr_get_checksum_flag(const struct gre0_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE0_FLAG_CHECKSUM) >> 15;
}
static inline uint8_t gre0_hdr_get_routing_flag(const struct gre0_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE0_FLAG_ROUTING) >> 14;
}
static inline uint8_t gre0_hdr_get_key_flag(const struct gre0_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE0_FLAG_KEY) >> 13;
}
static inline uint8_t gre0_hdr_get_seq_flag(const struct gre0_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE0_FLAG_SEQUENCE) >> 12;
}
static inline uint8_t gre0_hdr_get_strict_flag(const struct gre0_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE0_FLAG_STRICT) >> 11;
}
static inline uint8_t gre0_hdr_get_version(const struct gre0_hdr *hdr)
{
return ntohs(hdr->flags) & GRE0_FLAG_VERSION;
}
// ethernet protocol
static inline uint16_t gre0_hdr_get_proto(const struct gre0_hdr *hdr)
{
return ntohs(hdr->protocol);
}
static inline uint16_t gre0_hdr_get_checksum(const struct gre0_hdr *hdr)
{
if (gre0_hdr_get_checksum_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 4;
return ntohs(*(uint16_t *)ptr);
}
else
{
return 0;
}
}
static inline uint16_t gre0_hdr_get_offset(const struct gre0_hdr *hdr)
{
if (gre0_hdr_get_checksum_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 6;
return ntohs(*(uint16_t *)ptr);
}
else
{
return 0;
}
}
#pragma GCC diagnostic warning "-Warray-bounds"
static inline uint32_t gre0_hdr_get_key(const struct gre0_hdr *hdr)
{
if (gre0_hdr_get_key_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 4;
if (gre0_hdr_get_checksum_flag(hdr))
{
ptr += 4;
}
return ntohl(*(uint32_t *)ptr);
}
else
{
return 0;
}
}
static inline uint32_t gre0_hdr_get_seq(const struct gre0_hdr *hdr)
{
if (gre0_hdr_get_seq_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 4;
if (gre0_hdr_get_checksum_flag(hdr))
{
ptr += 4;
}
if (gre0_hdr_get_key_flag(hdr))
{
ptr += 4;
}
return ntohl(*(uint32_t *)ptr);
}
else
{
return 0;
}
}
static inline const char *gre0_hdr_get_routing_data(const struct gre0_hdr *hdr)
{
if (gre0_hdr_get_routing_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 4;
if (gre0_hdr_get_checksum_flag(hdr))
{
ptr += 4;
}
if (gre0_hdr_get_key_flag(hdr))
{
ptr += 4;
}
if (gre0_hdr_get_seq_flag(hdr))
{
ptr += 4;
}
return ptr;
}
else
{
return NULL;
}
}
static inline uint16_t gre0_hdr_get_routing_len(const struct gre0_hdr *hdr)
{
if (gre0_hdr_get_routing_flag(hdr))
{
const char *ptr = gre0_hdr_get_routing_data(hdr);
uint16_t hdr_len = 0;
while (1)
{
struct sre *sre = (struct sre *)(ptr + hdr_len);
if (sre->length == 0)
{
hdr_len += sizeof(struct sre);
break;
}
else
{
hdr_len += sizeof(struct sre) + sre->length;
}
}
return hdr_len;
}
else
{
return 0;
}
}
static inline uint16_t calc_gre0_hdr_len(const char *data, uint32_t len)
{
if (data == NULL || len < sizeof(struct gre0_hdr))
{
return 0;
}
const struct gre0_hdr *hdr = (const struct gre0_hdr *)data;
uint16_t hdr_len = 4;
uint16_t flags = ntohs(hdr->flags);
if ((flags & GRE0_FLAG_CHECKSUM) || (flags & GRE0_FLAG_ROUTING))
{
hdr_len += 4; // skip checksum and offset fields
}
if (flags & GRE0_FLAG_KEY)
{
hdr_len += 4; // skip key field
}
if (flags & GRE0_FLAG_SEQUENCE)
{
hdr_len += 4; // skip sequence number field
}
if (flags & GRE0_FLAG_ROUTING)
{
while (hdr_len + sizeof(struct sre) <= len)
{
struct sre *sre = (struct sre *)((char *)data + hdr_len);
if (sre->length == 0)
{
hdr_len += sizeof(struct sre);
break;
}
else
{
hdr_len += sizeof(struct sre) + sre->length;
}
}
}
return hdr_len;
}
static inline void gre0_hdr_set_flags(struct gre0_hdr *hdr, uint16_t flags)
{
hdr->flags = htons(flags);
}
static inline void gre0_hdr_set_checksum_flag(struct gre0_hdr *hdr, uint8_t flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE0_FLAG_CHECKSUM) | flag << 15);
}
static inline void gre0_hdr_set_routing_flag(struct gre0_hdr *hdr, uint8_t flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE0_FLAG_ROUTING) | flag << 14);
}
static inline void gre0_hdr_set_key_flag(struct gre0_hdr *hdr, uint8_t flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE0_FLAG_KEY) | flag << 13);
}
static inline void gre0_hdr_set_seq_flag(struct gre0_hdr *hdr, uint8_t flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE0_FLAG_SEQUENCE) | flag << 12);
}
static inline void gre0_hdr_set_strict_flag(struct gre0_hdr *hdr, uint8_t flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE0_FLAG_STRICT) | flag << 11);
}
static inline void gre0_hdr_set_version(struct gre0_hdr *hdr, uint8_t version)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE0_FLAG_VERSION) | version);
}
static inline void gre0_hdr_set_proto(struct gre0_hdr *hdr, uint16_t proto)
{
hdr->protocol = htons(proto);
}
static inline void gre0_hdr_set_checksum(struct gre0_hdr *hdr, uint16_t checksum)
{
if (gre0_hdr_get_checksum_flag(hdr))
{
char *ptr = ((char *)hdr) + 4;
*(uint16_t *)ptr = htons(checksum);
}
}
static inline void gre0_hdr_set_offset(struct gre0_hdr *hdr, uint16_t offset)
{
if (gre0_hdr_get_checksum_flag(hdr))
{
char *ptr = ((char *)hdr) + 6;
*(uint16_t *)ptr = htons(offset);
}
}
static inline void gre0_hdr_set_key(struct gre0_hdr *hdr, uint32_t key)
{
if (gre0_hdr_get_key_flag(hdr))
{
char *ptr = ((char *)hdr) + 4;
if (gre0_hdr_get_checksum_flag(hdr))
{
ptr += 4;
}
*(uint32_t *)ptr = htonl(key);
}
}
static inline void gre0_hdr_set_seq(struct gre0_hdr *hdr, uint32_t sequence)
{
if (gre0_hdr_get_seq_flag(hdr))
{
char *ptr = ((char *)hdr) + 4;
if (gre0_hdr_get_checksum_flag(hdr))
{
ptr += 4;
}
if (gre0_hdr_get_key_flag(hdr))
{
ptr += 4;
}
*(uint32_t *)ptr = htonl(sequence);
}
}
static inline void gre0_hdr_set_routing_data(struct gre0_hdr *hdr, const char *data, uint16_t len)
{
if (gre0_hdr_get_routing_flag(hdr))
{
char *ptr = ((char *)hdr) + 4;
if (gre0_hdr_get_checksum_flag(hdr))
{
ptr += 4;
}
if (gre0_hdr_get_key_flag(hdr))
{
ptr += 4;
}
if (gre0_hdr_get_seq_flag(hdr))
{
ptr += 4;
}
memcpy(ptr, data, len);
}
}
static inline int gre0_hdr_to_str(const struct gre0_hdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
int used = 0;
uint16_t proto = gre0_hdr_get_proto(hdr);
used += snprintf(buf + used, size - used, "GRE: flags=0x%04x (checksum_flag=%u, routing_flag=%u, key_flag=%u, seq_flag=%u, strict_flag=%u, version=%u), proto=%s",
gre0_hdr_get_flags(hdr), gre0_hdr_get_checksum_flag(hdr), gre0_hdr_get_routing_flag(hdr), gre0_hdr_get_key_flag(hdr),
gre0_hdr_get_seq_flag(hdr), gre0_hdr_get_strict_flag(hdr), gre0_hdr_get_version(hdr), eth_proto_to_str(proto));
if (gre0_hdr_get_checksum_flag(hdr))
{
used += snprintf(buf + used, size - used, ", checksum=0x%x, offset=%u", gre0_hdr_get_checksum(hdr), gre0_hdr_get_offset(hdr));
}
if (gre0_hdr_get_key_flag(hdr))
{
used += snprintf(buf + used, size - used, ", key=%u", gre0_hdr_get_key(hdr));
}
if (gre0_hdr_get_seq_flag(hdr))
{
used += snprintf(buf + used, size - used, ", seq=%u", gre0_hdr_get_seq(hdr));
}
if (gre0_hdr_get_routing_flag(hdr))
{
used += snprintf(buf + used, size - used, ", routing_len=%u", gre0_hdr_get_routing_len(hdr));
}
return used;
}
/******************************************************************************
* GREv1
******************************************************************************/
/*
* Enhanced GRE header (Version 1)
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Key (HW) Payload Length | Key (LW) Call ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number (Optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Acknowledgment Number (Optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* https://datatracker.ietf.org/doc/html/rfc2637
*/
struct gre1_hdr
{
uint16_t flags;
uint16_t protocol;
uint16_t payload_length;
uint16_t call_id;
};
#define GRE1_FLAG_CHECKSUM 0x8000
#define GRE1_FLAG_ROUTING 0x4000
#define GRE1_FLAG_KEY 0x2000
#define GRE1_FLAG_SEQUENCE 0x1000
#define GRE1_FLAG_STRICT 0x0800
#define GRE1_FLAG_RECURSION 0x0700
#define GRE1_FLAG_ACK 0x0080 /* only in special PPTPized GRE header */
#define GRE1_FLAG_VERSION 0x0007
static inline uint16_t gre1_hdr_get_flags(const struct gre1_hdr *hdr)
{
return ntohs(hdr->flags);
}
static inline uint8_t gre1_hdr_get_seq_flag(const struct gre1_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE1_FLAG_SEQUENCE) >> 12;
}
static inline uint8_t gre1_hdr_get_ack_flag(const struct gre1_hdr *hdr)
{
return (ntohs(hdr->flags) & GRE1_FLAG_ACK) >> 7;
}
static inline uint8_t gre1_hdr_get_version(const struct gre1_hdr *hdr)
{
return ntohs(hdr->flags) & GRE1_FLAG_VERSION;
}
// ethernet protocol type
static inline uint16_t gre1_hdr_get_proto(const struct gre1_hdr *hdr)
{
return ntohs(hdr->protocol);
}
static inline uint16_t gre1_hdr_get_payload_length(const struct gre1_hdr *hdr)
{
return ntohs(hdr->payload_length);
}
static inline uint16_t gre1_hdr_get_call_id(const struct gre1_hdr *hdr)
{
return ntohs(hdr->call_id);
}
static inline uint32_t gre1_hdr_get_seq(const struct gre1_hdr *hdr)
{
if (gre1_hdr_get_seq_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 8;
return ntohl(*((uint32_t *)ptr));
}
else
{
return 0;
}
}
static inline uint32_t gre1_hdr_get_ack(const struct gre1_hdr *hdr)
{
if (gre1_hdr_get_ack_flag(hdr))
{
const char *ptr = ((const char *)hdr) + 8;
if (gre1_hdr_get_seq_flag(hdr))
{
ptr += 4;
}
return ntohl(*((uint32_t *)ptr));
}
else
{
return 0;
}
}
static inline uint16_t calc_gre1_hdr_len(const char *data, uint32_t len)
{
if (data == NULL || len < sizeof(struct gre1_hdr))
{
return 0;
}
const struct gre1_hdr *hdr = (const struct gre1_hdr *)data;
uint16_t hdr_len = 8;
uint16_t flags = gre1_hdr_get_flags(hdr);
if (flags & GRE1_FLAG_SEQUENCE)
{
hdr_len += 4;
}
if (flags & GRE1_FLAG_ACK)
{
hdr_len += 4;
}
return hdr_len;
}
static inline void gre1_hdr_set_flags(struct gre1_hdr *hdr, uint16_t flags)
{
hdr->flags = htons(flags);
}
static inline void gre1_hdr_set_seq_flag(struct gre1_hdr *hdr, uint8_t seq_flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE1_FLAG_SEQUENCE) | seq_flag << 12);
}
static inline void gre1_hdr_set_ack_flag(struct gre1_hdr *hdr, uint8_t ack_flag)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE1_FLAG_ACK) | ack_flag << 7);
}
static inline void gre1_hdr_set_version(struct gre1_hdr *hdr, uint8_t version)
{
hdr->flags = htons((ntohs(hdr->flags) & ~GRE1_FLAG_VERSION) | version);
}
static inline void gre1_hdr_set_proto(struct gre1_hdr *hdr, uint16_t proto)
{
hdr->protocol = htons(proto);
}
static inline void gre1_hdr_set_payload_length(struct gre1_hdr *hdr, uint16_t payload_length)
{
hdr->payload_length = htons(payload_length);
}
static inline void gre1_hdr_set_call_id(struct gre1_hdr *hdr, uint16_t call_id)
{
hdr->call_id = htons(call_id);
}
static inline void gre1_hdr_set_seq(struct gre1_hdr *hdr, uint32_t seq)
{
if (gre1_hdr_get_seq_flag(hdr))
{
char *ptr = ((char *)hdr) + 8;
*((uint32_t *)ptr) = htonl(seq);
}
}
static inline void gre1_hdr_set_ack(struct gre1_hdr *hdr, uint32_t ack)
{
if (gre1_hdr_get_ack_flag(hdr))
{
char *ptr = ((char *)hdr) + 8;
if (gre1_hdr_get_seq_flag(hdr))
{
ptr += 4;
}
*((uint32_t *)ptr) = htonl(ack);
}
}
static inline int gre1_hdr_to_str(const struct gre1_hdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
int used = 0;
uint16_t proto = gre1_hdr_get_proto(hdr);
used += snprintf(buf + used, size - used, "GRE: flags=0x%04x (seq_flag=%u ack_flag=%u version=%u), proto=%s, payload_length=%u, call_id=%u",
gre1_hdr_get_flags(hdr), gre1_hdr_get_seq_flag(hdr), gre1_hdr_get_ack_flag(hdr), gre1_hdr_get_version(hdr),
eth_proto_to_str(proto), gre1_hdr_get_payload_length(hdr), gre1_hdr_get_call_id(hdr));
if (gre1_hdr_get_seq_flag(hdr))
{
used += snprintf(buf + used, size - used, ", seq=%u", gre1_hdr_get_seq(hdr));
}
if (gre1_hdr_get_ack_flag(hdr))
{
used += snprintf(buf + used, size - used, ", ack=%u", gre1_hdr_get_ack(hdr));
}
return used;
}
/******************************************************************************
* GRE
******************************************************************************/
// return GRE version 0 or 1
// return 255 if not GRE
static inline uint8_t peek_gre_version(const char *data, uint32_t len)
{
if (len < MIN(sizeof(struct gre0_hdr), sizeof(struct gre1_hdr)))
{
return 255;
}
uint16_t flag = *(uint16_t *)data;
return ntohs(flag) & 0x0007;
}
static inline uint16_t peek_gre_proto(const char *data, uint32_t len)
{
switch (peek_gre_version(data, len))
{
case 0:
return gre0_hdr_get_proto((const struct gre0_hdr *)data);
case 1:
return gre1_hdr_get_proto((const struct gre1_hdr *)data);
default:
return 0;
}
}
static inline uint16_t calc_gre_hdr_len(const char *data, uint32_t len)
{
switch (peek_gre_version(data, len))
{
case 0:
return calc_gre0_hdr_len(data, len);
case 1:
return calc_gre1_hdr_len(data, len);
default:
return 0;
}
}
static inline int gre_hdr_to_str(const char *hdr, uint16_t len, char *buf, size_t size)
{
switch (peek_gre_version(hdr, len))
{
case 0:
return gre0_hdr_to_str((const struct gre0_hdr *)hdr, buf, size);
case 1:
return gre1_hdr_to_str((const struct gre1_hdr *)hdr, buf, size);
default:
return 0;
}
}
/******************************************************************************
* GTPv1
******************************************************************************/
/*
* https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol
*
* GTP-C: Gateway GPRS Support Nodes (GGSN) <----> Serving GPRS support Nodes (SGSN)
* GTP-U: Radio Access Network <----> Core Network
*
* GTPv1:
* -> GTPv1-C
* -> GTPv1-U
*/
/*
* GTP version 1
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Ver |T|R|E|S|N| Message Type | Message Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | TEID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number(O) |N-PDU Number(O)|Next Ext Hdr(O)| (optional headers)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Message Length:
* a 16-bit field that indicates the length of the payload in bytes
* (rest of the packet following the mandatory 8-byte GTP header).
* Includes the optional fields.
*/
/*
* Next Extension Headers
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Length | Contents |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ... |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Contents | Next Ext Hdr |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Extension length
* an 8-bit field. This field states the length of this extension header,
* including the length, the contents, and the next extension header field,
* in 4-octet units, so the length of the extension must always be a multiple of 4.
*/
struct gtp1_hdr
{
uint8_t flags;
uint8_t msg_type;
uint16_t msg_len;
uint32_t teid;
} __attribute__((packed));
struct gtp1_hdr_long
{
uint8_t flags;
uint8_t msg_type;
uint16_t msg_len;
uint32_t teid;
uint16_t seq;
uint8_t npdu;
uint8_t next_ext_hdr;
} __attribute__((packed));
#define GTP1_FLAG_N_PDU (0x01)
#define GTP1_FLAG_SEQ_NUM (0x02)
#define GTP1_FLAG_EXT_HDR (0x04)
#define GTP1_FLAG_RESERVED (0x08)
#define GTP1_FLAG_PROTOCOL (0x10)
#define GTP1_FLAG_VERSION (0xE0)
static inline uint8_t gtp1_hdr_get_flags(const struct gtp1_hdr *gtp)
{
return gtp->flags;
}
static inline uint8_t gtp1_hdr_get_version(const struct gtp1_hdr *gtp)
{
return (gtp->flags & GTP1_FLAG_VERSION) >> 5;
}
static inline uint8_t gtp1_hdr_get_proto(const struct gtp1_hdr *gtp)
{
return (gtp->flags & GTP1_FLAG_PROTOCOL) >> 4;
}
static inline uint8_t gtp1_hdr_get_reserved(const struct gtp1_hdr *gtp)
{
return (gtp->flags & GTP1_FLAG_RESERVED) >> 3;
}
static inline uint8_t gtp1_hdr_get_ext_flag(const struct gtp1_hdr *gtp)
{
return (gtp->flags & GTP1_FLAG_EXT_HDR) >> 2;
}
static inline uint8_t gtp1_hdr_get_seq_flag(const struct gtp1_hdr *gtp)
{
return (gtp->flags & GTP1_FLAG_SEQ_NUM) >> 1;
}
static inline uint8_t gtp1_hdr_get_npdu_flag(const struct gtp1_hdr *gtp)
{
return (gtp->flags & GTP1_FLAG_N_PDU) >> 0;
}
static inline uint8_t gtp1_hdr_get_msg_type(const struct gtp1_hdr *gtp)
{
return gtp->msg_type;
}
static inline uint16_t gtp1_hdr_get_msg_len(const struct gtp1_hdr *gtp)
{
return ntohs(gtp->msg_len);
}
static inline uint32_t gtp1_hdr_get_teid(const struct gtp1_hdr *gtp)
{
return ntohl(gtp->teid);
}
static inline uint16_t gtp1_hdr_get_seq(const struct gtp1_hdr *gtp)
{
if (gtp1_hdr_get_seq_flag(gtp))
{
const struct gtp1_hdr_long *gtp_long = (const struct gtp1_hdr_long *)gtp;
return ntohs(gtp_long->seq);
}
else
{
return 0;
}
}
static inline uint8_t gtp1_hdr_get_npdu(const struct gtp1_hdr *gtp)
{
if (gtp1_hdr_get_npdu_flag(gtp))
{
const struct gtp1_hdr_long *gtp_long = (const struct gtp1_hdr_long *)gtp;
return gtp_long->npdu;
}
else
{
return 0;
}
}
static inline uint8_t gtp1_hdr_get_next_ext_type(const struct gtp1_hdr *gtp)
{
if (gtp1_hdr_get_ext_flag(gtp))
{
const struct gtp1_hdr_long *gtp_long = (const struct gtp1_hdr_long *)gtp;
return gtp_long->next_ext_hdr;
}
else
{
return 0;
}
}
// include gtp fixed header and optional headers and extension headers
static inline uint16_t calc_gtp1_hdr_len(const char *data, uint16_t len)
{
if (data == NULL || len < sizeof(struct gtp1_hdr))
{
return 0;
}
const struct gtp1_hdr *gtp = (const struct gtp1_hdr *)data;
if (gtp1_hdr_get_version(gtp) != 1)
{
return 0;
}
if (gtp1_hdr_get_flags(gtp) & (GTP1_FLAG_SEQ_NUM | GTP1_FLAG_N_PDU | GTP1_FLAG_EXT_HDR))
{
if (sizeof(struct gtp1_hdr_long) > len)
{
return 0;
}
const struct gtp1_hdr_long *gtp_long = (const struct gtp1_hdr_long *)data;
uint8_t next_ext_hdr = gtp_long->next_ext_hdr;
uint16_t offset = sizeof(struct gtp1_hdr_long);
while (next_ext_hdr)
{
if (offset + 1 > len)
{
return 0;
}
uint16_t ext_hdr_len = *((char *)data + offset) * 4;
if (ext_hdr_len == 0 || offset + ext_hdr_len > len)
{
return 0;
}
offset += ext_hdr_len; // skip extension header
next_ext_hdr = *((char *)data + offset - 1);
}
return offset;
}
else
{
return sizeof(struct gtp1_hdr);
}
}
static inline void gtp1_hdr_set_flags(struct gtp1_hdr *gtp, uint8_t flags)
{
gtp->flags = flags;
}
static inline void gtp1_hdr_set_version(struct gtp1_hdr *gtp, uint8_t version)
{
gtp->flags = (gtp->flags & ~GTP1_FLAG_VERSION) | (version << 5);
}
static inline void gtp1_hdr_set_proto(struct gtp1_hdr *gtp, uint8_t protocol_type)
{
gtp->flags = (gtp->flags & ~GTP1_FLAG_PROTOCOL) | (protocol_type << 4);
}
static inline void gtp1_hdr_set_reserved(struct gtp1_hdr *gtp, uint8_t reserved)
{
gtp->flags = (gtp->flags & ~GTP1_FLAG_RESERVED) | (reserved << 3);
}
static inline void gtp1_hdr_set_ext_flag(struct gtp1_hdr *gtp, uint8_t ext_flag)
{
gtp->flags = (gtp->flags & ~GTP1_FLAG_EXT_HDR) | (ext_flag << 2);
}
static inline void gtp1_hdr_set_seq_flag(struct gtp1_hdr *gtp, uint8_t seq_flag)
{
gtp->flags = (gtp->flags & ~GTP1_FLAG_SEQ_NUM) | (seq_flag << 1);
}
static inline void gtp1_hdr_set_npdu_flag(struct gtp1_hdr *gtp, uint8_t npdu_flag)
{
gtp->flags = (gtp->flags & ~GTP1_FLAG_N_PDU) | (npdu_flag << 0);
}
static inline void gtp1_hdr_set_msg_type(struct gtp1_hdr *gtp, uint8_t msg_type)
{
gtp->msg_type = msg_type;
}
static inline void gtp1_hdr_set_msg_len(struct gtp1_hdr *gtp, uint16_t msg_len)
{
gtp->msg_len = htons(msg_len);
}
static inline void gtp1_hdr_set_teid(struct gtp1_hdr *gtp, uint32_t teid)
{
gtp->teid = htonl(teid);
}
static inline void gtp1_hdr_set_seq(struct gtp1_hdr *gtp, uint16_t seq)
{
if (gtp1_hdr_get_seq_flag(gtp))
{
struct gtp1_hdr_long *gtp_long = (struct gtp1_hdr_long *)gtp;
gtp_long->seq = htons(seq);
}
}
static inline void gtp1_hdr_set_npdu(struct gtp1_hdr *gtp, uint8_t npdu)
{
if (gtp1_hdr_get_npdu_flag(gtp))
{
struct gtp1_hdr_long *gtp_long = (struct gtp1_hdr_long *)gtp;
gtp_long->npdu = npdu;
}
}
static inline void gtp1_hdr_set_next_ext_type(struct gtp1_hdr *gtp, uint8_t next_ext_hdr)
{
if (gtp1_hdr_get_ext_flag(gtp))
{
struct gtp1_hdr_long *gtp_long = (struct gtp1_hdr_long *)gtp;
gtp_long->next_ext_hdr = next_ext_hdr;
}
}
static inline int gtp1_hdr_to_str(const struct gtp1_hdr *gtp, char *buf, size_t len)
{
memset(buf, 0, len);
int used = 0;
used += snprintf(buf + used, len - used, "GTP: flags=0x%02x (version=%u, protocol=%u, reserved=%u, ext_flag=%u, seq_flag=%u, npdu_flag=%u), msg_type=0x%02x, msg_len=%u, teid=%u",
gtp1_hdr_get_flags(gtp),
gtp1_hdr_get_version(gtp),
gtp1_hdr_get_proto(gtp),
gtp1_hdr_get_reserved(gtp),
gtp1_hdr_get_ext_flag(gtp),
gtp1_hdr_get_seq_flag(gtp),
gtp1_hdr_get_npdu_flag(gtp),
gtp1_hdr_get_msg_type(gtp),
gtp1_hdr_get_msg_len(gtp),
gtp1_hdr_get_teid(gtp));
if (gtp1_hdr_get_seq_flag(gtp))
{
used += snprintf(buf + used, len - used, ", seq=%u", gtp1_hdr_get_seq(gtp));
}
if (gtp1_hdr_get_npdu_flag(gtp))
{
used += snprintf(buf + used, len - used, ", npdu=%u", gtp1_hdr_get_npdu(gtp));
}
if (gtp1_hdr_get_ext_flag(gtp))
{
used += snprintf(buf + used, len - used, ", next_ext_hdr=%u", gtp1_hdr_get_next_ext_type(gtp));
}
return used;
}
/******************************************************************************
* GTPv2
******************************************************************************/
/*
* https://en.wikipedia.org/wiki/GPRS_Tunnelling_Protocol
* https://www.etsi.org/deliver/etsi_ts/129200_129299/129274/08.04.00_60/ts_129274v080400p.pdf
*
* GTP-C: Gateway GPRS Support Nodes (GGSN) <----> Serving GPRS support Nodes (SGSN)
* GTP-U: Radio Access Network <----> Core Network
*
* GTPv2:
* -> GTPv2-C
* -> (no GTPv2-U)
*/
/*
* GTP version 2
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Ver |P|T|spare| Message Type | Message Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | TEID (only if T=1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number | Spare |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Message length:
* This field shall indicate the length of the message in octets excluding the mandatory of the GTP-C header (the first 4 octets).
* The TEID (if present) and the Sequence Number shall be included in the length count.
*/
struct gtp2_hdr
{
uint8_t flags;
uint8_t msg_type;
uint16_t msg_len;
uint32_t seq_and_spare;
} __attribute__((packed));
struct gtp2_hdr_long
{
uint8_t flags;
uint8_t msg_type;
uint16_t msg_len;
uint32_t teid;
uint32_t seq_and_spare;
} __attribute__((packed));
#define GPT2_FLAG_SPARE (0x07)
#define GTP2_FLAG_TEID (0x08)
#define GTP2_FLAG_PIGGYBACK (0x10)
#define GTP2_FLAG_VERSION (0xE0)
static inline uint8_t gtp2_hdr_get_flags(const struct gtp2_hdr *gtp)
{
return gtp->flags;
}
static inline uint8_t gtp2_hdr_get_version(const struct gtp2_hdr *gtp)
{
return (gtp->flags & GTP2_FLAG_VERSION) >> 5;
}
static inline uint8_t gtp2_hdr_get_piggyback_flag(const struct gtp2_hdr *gtp)
{
return (gtp->flags & GTP2_FLAG_PIGGYBACK) >> 4;
}
static inline uint8_t gtp2_hdr_get_teid_flag(const struct gtp2_hdr *gtp)
{
return (gtp->flags & GTP2_FLAG_TEID) >> 3;
}
static inline uint8_t gtp2_hdr_get_spare_flag(const struct gtp2_hdr *gtp)
{
return (gtp->flags & GPT2_FLAG_SPARE) >> 0;
}
static inline uint8_t gtp2_hdr_get_msg_type(const struct gtp2_hdr *gtp)
{
return gtp->msg_type;
}
static inline uint16_t gtp2_hdr_get_msg_len(const struct gtp2_hdr *gtp)
{
return ntohs(gtp->msg_len);
}
static inline uint32_t gtp2_hdr_get_teid(const struct gtp2_hdr *gtp)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
const struct gtp2_hdr_long *gtp_long = (const struct gtp2_hdr_long *)gtp;
return ntohl(gtp_long->teid);
}
else
{
return 0;
}
}
static inline uint32_t gtp2_hdr_get_seq(const struct gtp2_hdr *gtp)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
const struct gtp2_hdr_long *gtp_long = (const struct gtp2_hdr_long *)gtp;
return ntohl(gtp_long->seq_and_spare) >> 8;
}
else
{
return ntohl(gtp->seq_and_spare) >> 8;
}
}
static inline uint8_t gtp2_hdr_get_spare(const struct gtp2_hdr *gtp)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
const struct gtp2_hdr_long *gtp_long = (const struct gtp2_hdr_long *)gtp;
return ntohl(gtp_long->seq_and_spare) & 0xFF;
}
else
{
return ntohl(gtp->seq_and_spare) & 0xFF;
}
}
static inline uint16_t calc_gtp2_hdr_len(const char *data, uint16_t len)
{
if (len < sizeof(struct gtp2_hdr))
{
return 0;
}
const struct gtp2_hdr *gtp = (const struct gtp2_hdr *)data;
if (gtp2_hdr_get_version(gtp) == 2)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
return sizeof(struct gtp2_hdr_long);
}
else
{
return sizeof(struct gtp2_hdr);
}
}
return 0;
}
static inline void gtp2_hdr_set_flags(struct gtp2_hdr *gtp, uint8_t flags)
{
gtp->flags = flags;
}
static inline void gtp2_hdr_set_version(struct gtp2_hdr *gtp, uint8_t version)
{
gtp->flags = (gtp->flags & ~GTP2_FLAG_VERSION) | (version << 5);
}
static inline void gtp2_hdr_set_piggyback_flag(struct gtp2_hdr *gtp, uint8_t piggyback)
{
gtp->flags = (gtp->flags & ~GTP2_FLAG_PIGGYBACK) | (piggyback << 4);
}
static inline void gtp2_hdr_set_teid_flag(struct gtp2_hdr *gtp, uint8_t teid_flag)
{
gtp->flags = (gtp->flags & ~GTP2_FLAG_TEID) | (teid_flag << 3);
}
static inline void gtp2_hdr_set_spare_flag(struct gtp2_hdr *gtp, uint8_t spare_flag)
{
gtp->flags = (gtp->flags & ~GPT2_FLAG_SPARE) | (spare_flag << 0);
}
static inline void gtp2_hdr_set_msg_type(struct gtp2_hdr *gtp, uint8_t msg_type)
{
gtp->msg_type = msg_type;
}
static inline void gtp2_hdr_set_msg_len(struct gtp2_hdr *gtp, uint16_t msg_len)
{
gtp->msg_len = htons(msg_len);
}
static inline void gtp2_hdr_set_teid(struct gtp2_hdr *gtp, uint32_t teid)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
struct gtp2_hdr_long *gtp_long = (struct gtp2_hdr_long *)gtp;
gtp_long->teid = htonl(teid);
}
}
static inline void gtp2_hdr_set_seq(struct gtp2_hdr *gtp, uint32_t seq)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
struct gtp2_hdr_long *gtp_long = (struct gtp2_hdr_long *)gtp;
gtp_long->seq_and_spare = htonl((seq << 8) | gtp2_hdr_get_spare(gtp));
}
else
{
gtp->seq_and_spare = htonl((seq << 8) | gtp2_hdr_get_spare(gtp));
}
}
static inline void gtp2_hdr_set_spare(struct gtp2_hdr *gtp, uint8_t spare)
{
if (gtp2_hdr_get_teid_flag(gtp))
{
struct gtp2_hdr_long *gtp_long = (struct gtp2_hdr_long *)gtp;
gtp_long->seq_and_spare = htonl((gtp2_hdr_get_seq(gtp) << 8) | spare);
}
else
{
gtp->seq_and_spare = htonl((gtp2_hdr_get_seq(gtp) << 8) | spare);
}
}
static inline int gtp2_hdr_to_str(const struct gtp2_hdr *gtp, char *buf, size_t len)
{
memset(buf, 0, len);
int used = 0;
used += snprintf(buf + used, len - used, "GTP: flags=0x%02x (version=%u, piggyback=%u, teid_flag=%u, spare_flag=%u), msg_type=0x%02x, msg_len=%u",
gtp2_hdr_get_flags(gtp),
gtp2_hdr_get_version(gtp),
gtp2_hdr_get_piggyback_flag(gtp),
gtp2_hdr_get_teid_flag(gtp),
gtp2_hdr_get_spare_flag(gtp),
gtp2_hdr_get_msg_type(gtp),
gtp2_hdr_get_msg_len(gtp));
if (gtp2_hdr_get_teid_flag(gtp))
{
used += snprintf(buf + used, len - used, ", teid=%u", gtp2_hdr_get_teid(gtp));
}
else
{
used += snprintf(buf + used, len - used, ", seq=%u, spare=%u", gtp2_hdr_get_seq(gtp), gtp2_hdr_get_spare(gtp));
}
return used;
}
/******************************************************************************
* GTP
******************************************************************************/
// return GTP version 0 or 1
// return 255 if not GTP
static inline uint8_t peek_gtp_version(const char *data, uint16_t len)
{
if (data == NULL || len == 0)
{
return 255;
}
return ((*(uint8_t *)data) >> 5) & 0x07;
}
static inline uint16_t calc_gtp_hdr_len(const char *data, uint32_t len)
{
switch (peek_gtp_version(data, len))
{
case 1:
return calc_gtp1_hdr_len(data, len);
case 2:
return calc_gtp2_hdr_len(data, len);
default:
return 0;
}
}
static inline int gtp_hdr_to_str(const char *hdr, uint16_t len, char *buf, size_t size)
{
switch (peek_gtp_version(hdr, len))
{
case 1:
return gtp1_hdr_to_str((const struct gtp1_hdr *)hdr, buf, size);
case 2:
return gtp2_hdr_to_str((const struct gtp2_hdr *)hdr, buf, size);
default:
return 0;
}
}
/******************************************************************************
* IPv4
******************************************************************************/
/*
* Internet Header Format
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |Version| IHL |Type of Service| Total Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Identification |Flags| Fragment Offset |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Time to Live | Protocol | Header Checksum |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Source Address |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Destination Address |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Options | Padding |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static inline uint8_t ip4_hdr_get_version(const struct ip *hdr)
{
return hdr->ip_v;
}
// IP Options are included in the hdr_len field
static inline uint8_t ip4_hdr_get_hdr_len(const struct ip *hdr)
{
return hdr->ip_hl << 2;
}
static inline uint8_t ip4_hdr_get_tos(const struct ip *hdr)
{
return hdr->ip_tos;
}
static inline uint16_t ip4_hdr_get_total_len(const struct ip *hdr)
{
return ntohs(hdr->ip_len);
}
static inline uint16_t ip4_hdr_get_ipid(const struct ip *hdr)
{
return ntohs(hdr->ip_id);
}
static inline uint8_t ip4_hdr_get_flags(const struct ip *hdr)
{
return (ntohs(hdr->ip_off) & (~IP_OFFMASK)) >> 13;
}
static inline bool ip4_hdr_get_rf_flag(const struct ip *hdr)
{
return (ntohs(hdr->ip_off) & IP_RF) != 0;
}
static inline bool ip4_hdr_get_df_flag(const struct ip *hdr)
{
return (ntohs(hdr->ip_off) & IP_DF) != 0;
}
static inline bool ip4_hdr_get_mf_flag(const struct ip *hdr)
{
return (ntohs(hdr->ip_off) & IP_MF) != 0;
}
static inline uint16_t ip4_hdr_get_frag_offset(const struct ip *hdr)
{
return (ntohs(hdr->ip_off) & IP_OFFMASK) << 3;
}
static inline uint8_t ip4_hdr_get_ttl(const struct ip *hdr)
{
return hdr->ip_ttl;
}
static inline uint8_t ip4_hdr_get_proto(const struct ip *hdr)
{
return hdr->ip_p;
}
static inline uint16_t ip4_hdr_get_checksum(const struct ip *hdr)
{
return ntohs(hdr->ip_sum);
}
static inline uint32_t ip4_hdr_get_src_addr(const struct ip *hdr)
{
return ntohl(hdr->ip_src.s_addr);
}
static inline uint32_t ip4_hdr_get_dst_addr(const struct ip *hdr)
{
return ntohl(hdr->ip_dst.s_addr);
}
static inline struct in_addr ip4_hdr_get_src_in_addr(const struct ip *hdr)
{
return hdr->ip_src;
}
static inline struct in_addr ip4_hdr_get_dst_in_addr(const struct ip *hdr)
{
return hdr->ip_dst;
}
static inline uint8_t ip4_hdr_get_opt_len(const struct ip *hdr)
{
return ip4_hdr_get_hdr_len(hdr) - sizeof(struct ip);
}
static inline const char *ip4_hdr_get_opt_data(const struct ip *hdr)
{
if (ip4_hdr_get_opt_len(hdr) == 0)
{
return NULL;
}
return (const char *)hdr + sizeof(struct ip);
}
static inline void ip4_hdr_set_version(struct ip *hdr, uint8_t version)
{
hdr->ip_v = version;
}
static inline void ip4_hdr_set_hdr_len(struct ip *hdr, uint8_t hdr_len)
{
hdr->ip_hl = hdr_len >> 2;
}
static inline void ip4_hdr_set_tos(struct ip *hdr, uint8_t tos)
{
hdr->ip_tos = tos;
}
static inline void ip4_hdr_set_total_len(struct ip *hdr, uint16_t total_len)
{
hdr->ip_len = htons(total_len);
}
static inline void ip4_hdr_set_ipid(struct ip *hdr, uint16_t ipid)
{
hdr->ip_id = htons(ipid);
}
static inline void ip4_hdr_set_flags(struct ip *hdr, uint8_t flags)
{
hdr->ip_off = htons((flags << 13) | (ntohs(hdr->ip_off) & IP_OFFMASK));
}
static inline void ip4_hdr_set_rf_flag(struct ip *hdr, bool flag)
{
if (flag)
{
hdr->ip_off = htons(ntohs(hdr->ip_off) | IP_RF);
}
else
{
hdr->ip_off = htons(ntohs(hdr->ip_off) & ~IP_RF);
}
}
static inline void ip4_hdr_set_df_flag(struct ip *hdr, bool flag)
{
if (flag)
{
hdr->ip_off = htons(ntohs(hdr->ip_off) | IP_DF);
}
else
{
hdr->ip_off = htons(ntohs(hdr->ip_off) & ~IP_DF);
}
}
static inline void ip4_hdr_set_mf_flag(struct ip *hdr, bool flag)
{
if (flag)
{
hdr->ip_off = htons(ntohs(hdr->ip_off) | IP_MF);
}
else
{
hdr->ip_off = htons(ntohs(hdr->ip_off) & ~IP_MF);
}
}
static inline void ip4_hdr_set_frag_offset(struct ip *hdr, uint16_t frag_offset)
{
hdr->ip_off = htons((frag_offset >> 3) | (ntohs(hdr->ip_off) & ~IP_OFFMASK));
}
static inline void ip4_hdr_set_ttl(struct ip *hdr, uint8_t ttl)
{
hdr->ip_ttl = ttl;
}
static inline void ip4_hdr_set_protocol(struct ip *hdr, uint8_t protocol)
{
hdr->ip_p = protocol;
}
static inline void ip4_hdr_set_checksum(struct ip *hdr, uint16_t checksum)
{
hdr->ip_sum = htons(checksum);
}
static inline void ip4_hdr_set_src_addr(struct ip *hdr, uint32_t saddr)
{
hdr->ip_src.s_addr = htonl(saddr);
}
static inline void ip4_hdr_set_dst_addr(struct ip *hdr, uint32_t daddr)
{
hdr->ip_dst.s_addr = htonl(daddr);
}
static inline void ip4_hdr_set_src_in_addr(struct ip *hdr, struct in_addr saddr)
{
hdr->ip_src = saddr;
}
static inline void ip4_hdr_set_dst_in_addr(struct ip *hdr, struct in_addr daddr)
{
hdr->ip_dst = daddr;
}
static inline void ip4_hdr_set_opt_len(struct ip *hdr, uint8_t opt_len)
{
ip4_hdr_set_hdr_len(hdr, opt_len + sizeof(struct ip));
}
// must be called after ip4_hdr_set_opt_len
static inline void ip4_hdr_set_opt_data(struct ip *hdr, const char *opt_data)
{
if (opt_data)
{
memcpy((char *)hdr + sizeof(struct ip), opt_data, ip4_hdr_get_opt_len(hdr));
}
}
// /usr/include/netinet/in.h
static inline int is_ip_proto(uint16_t proto)
{
switch (proto)
{
case IPPROTO_IP:
case IPPROTO_ICMP:
case IPPROTO_IGMP:
case IPPROTO_IPIP:
case IPPROTO_TCP:
case IPPROTO_EGP:
case IPPROTO_PUP:
case IPPROTO_UDP:
case IPPROTO_IDP:
case IPPROTO_TP:
case IPPROTO_DCCP:
case IPPROTO_IPV6:
case IPPROTO_RSVP:
case IPPROTO_GRE:
case IPPROTO_ESP:
case IPPROTO_AH:
case IPPROTO_MTP:
case IPPROTO_BEETPH:
case IPPROTO_ENCAP:
case IPPROTO_PIM:
case IPPROTO_COMP:
case IPPROTO_SCTP:
case IPPROTO_UDPLITE:
case IPPROTO_MPLS:
case IPPROTO_ETHERNET:
case IPPROTO_RAW:
case IPPROTO_MPTCP:
return 1;
default:
return 0;
}
}
static inline const char *ip_proto_to_str(uint16_t proto)
{
switch (proto)
{
case IPPROTO_IP:
return "IPPROTO_IP";
case IPPROTO_ICMP:
return "IPPROTO_ICMP";
case IPPROTO_IGMP:
return "IPPROTO_IGMP";
case IPPROTO_IPIP:
return "IPPROTO_IPIP";
case IPPROTO_TCP:
return "IPPROTO_TCP";
case IPPROTO_EGP:
return "IPPROTO_EGP";
case IPPROTO_PUP:
return "IPPROTO_PUP";
case IPPROTO_UDP:
return "IPPROTO_UDP";
case IPPROTO_IDP:
return "IPPROTO_IDP";
case IPPROTO_TP:
return "IPPROTO_TP";
case IPPROTO_DCCP:
return "IPPROTO_DCCP";
case IPPROTO_IPV6:
return "IPPROTO_IPV6";
case IPPROTO_RSVP:
return "IPPROTO_RSVP";
case IPPROTO_GRE:
return "IPPROTO_GRE";
case IPPROTO_ESP:
return "IPPROTO_ESP";
case IPPROTO_AH:
return "IPPROTO_AH";
case IPPROTO_MTP:
return "IPPROTO_MTP";
case IPPROTO_BEETPH:
return "IPPROTO_BEETPH";
case IPPROTO_ENCAP:
return "IPPROTO_ENCAP";
case IPPROTO_PIM:
return "IPPROTO_PIM";
case IPPROTO_COMP:
return "IPPROTO_COMP";
case IPPROTO_SCTP:
return "IPPROTO_SCTP";
case IPPROTO_UDPLITE:
return "IPPROTO_UDPLITE";
case IPPROTO_MPLS:
return "IPPROTO_MPLS";
case IPPROTO_ETHERNET:
return "IPPROTO_ETHERNET";
case IPPROTO_RAW:
return "IPPROTO_RAW";
case IPPROTO_MPTCP:
return "IPPROTO_MPTCP";
default:
return "IPPROTO_UNKNOWN";
}
}
static inline int ip4_hdr_to_str(const struct ip *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
char src_addr_str[INET6_ADDRSTRLEN] = {0};
char dst_addr_str[INET6_ADDRSTRLEN] = {0};
uint16_t proto = ip4_hdr_get_proto(hdr);
struct in_addr src_addr = ip4_hdr_get_src_in_addr(hdr);
struct in_addr dst_addr = ip4_hdr_get_dst_in_addr(hdr);
inet_ntop(AF_INET, &src_addr, src_addr_str, sizeof(src_addr_str));
inet_ntop(AF_INET, &dst_addr, dst_addr_str, sizeof(dst_addr_str));
return snprintf(buf, size, "IPv4: version=%u hdr_len=%u tos=%u total_len=%u ipid=%u flags=%u(rf=%u df=%u mf=%u) frag_offset=%u ttl=%u proto=%s checksum=0x%x src_addr=%s dst_addr=%s opt_len=%u",
ip4_hdr_get_version(hdr), ip4_hdr_get_hdr_len(hdr), ip4_hdr_get_tos(hdr),
ip4_hdr_get_total_len(hdr), ip4_hdr_get_ipid(hdr), ip4_hdr_get_flags(hdr),
ip4_hdr_get_rf_flag(hdr), ip4_hdr_get_df_flag(hdr), ip4_hdr_get_mf_flag(hdr),
ip4_hdr_get_frag_offset(hdr), ip4_hdr_get_ttl(hdr), ip_proto_to_str(proto),
ip4_hdr_get_checksum(hdr), src_addr_str, dst_addr_str, ip4_hdr_get_opt_len(hdr));
}
/******************************************************************************
* IPv6
******************************************************************************/
/*
* IPv6 Header Format
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |Version| Traffic Class | Flow Label |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Payload Length | Next Header | Hop Limit |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* + +
* | |
* + Source Address +
* | |
* + +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* + +
* | |
* + Destination Address +
* | |
* + +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Fragment Header
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Next Header | Reserved | Fragment Offset |Res|M|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Identification |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static inline uint8_t ip6_hdr_get_version(const struct ip6_hdr *hdr)
{
return (ntohl(hdr->ip6_flow) & 0xf0000000) >> 28;
}
static inline uint8_t ip6_hdr_get_traffic_class(const struct ip6_hdr *hdr)
{
return (ntohl(hdr->ip6_flow) & 0x0ff00000) >> 20;
}
static inline uint32_t ip6_hdr_get_flow_label(const struct ip6_hdr *hdr)
{
return ntohl(hdr->ip6_flow) & 0x000fffff;
}
static inline uint16_t ip6_hdr_get_payload_len(const struct ip6_hdr *hdr)
{
return ntohs(hdr->ip6_plen);
}
static inline uint8_t ip6_hdr_get_next_header(const struct ip6_hdr *hdr)
{
return hdr->ip6_nxt;
}
static inline uint8_t ip6_hdr_get_hop_limit(const struct ip6_hdr *hdr)
{
return hdr->ip6_hlim;
}
static inline struct in6_addr ip6_hdr_get_src_in6_addr(const struct ip6_hdr *hdr)
{
return hdr->ip6_src;
}
static inline struct in6_addr ip6_hdr_get_dst_in6_addr(const struct ip6_hdr *hdr)
{
return hdr->ip6_dst;
}
static inline struct ip6_frag *ip6_hdr_get_frag_ext(const struct ip6_hdr *hdr)
{
if (hdr->ip6_nxt != IPPROTO_FRAGMENT)
{
return NULL;
}
return (struct ip6_frag *)((char *)hdr + sizeof(struct ip6_hdr));
}
static inline void ip6_hdr_set_version(struct ip6_hdr *hdr, uint8_t version)
{
hdr->ip6_flow = htonl((ntohl(hdr->ip6_flow) & 0x0fffffff) | (version << 28));
}
static inline void ip6_hdr_set_traffic_class(struct ip6_hdr *hdr, uint8_t traffic_class)
{
hdr->ip6_flow = htonl((ntohl(hdr->ip6_flow) & 0xf00fffff) | (traffic_class << 20));
}
static inline void ip6_hdr_set_flow_label(struct ip6_hdr *hdr, uint32_t flow_label)
{
hdr->ip6_flow = htonl((ntohl(hdr->ip6_flow) & 0xfff00000) | flow_label);
}
static inline void ip6_hdr_set_payload_len(struct ip6_hdr *hdr, uint16_t payload_len)
{
hdr->ip6_plen = htons(payload_len);
}
static inline void ip6_hdr_set_next_header(struct ip6_hdr *hdr, uint8_t next_header)
{
hdr->ip6_nxt = next_header;
}
static inline void ip6_hdr_set_hop_limit(struct ip6_hdr *hdr, uint8_t hop_limit)
{
hdr->ip6_hlim = hop_limit;
}
static inline void ip6_hdr_set_src_in6_addr(struct ip6_hdr *hdr, struct in6_addr src_addr)
{
hdr->ip6_src = src_addr;
}
static inline void ip6_hdr_set_dst_in6_addr(struct ip6_hdr *hdr, struct in6_addr dst_addr)
{
hdr->ip6_dst = dst_addr;
}
static inline uint8_t ipv6_frag_get_next_header(const struct ip6_frag *frag)
{
return frag->ip6f_nxt;
}
static inline uint16_t ipv6_frag_get_offset(const struct ip6_frag *frag)
{
return ntohs(frag->ip6f_offlg & IP6F_OFF_MASK);
}
static inline uint32_t ipv6_frag_get_ident(const struct ip6_frag *frag)
{
return ntohl(frag->ip6f_ident);
}
static inline bool ipv6_frag_get_more(const struct ip6_frag *frag)
{
return (frag->ip6f_offlg & IP6F_MORE_FRAG);
}
static inline void ipv6_frag_set_next_header(struct ip6_frag *frag, uint8_t next_header)
{
frag->ip6f_nxt = next_header;
}
static inline void ipv6_frag_set_offset(struct ip6_frag *frag, uint16_t offset)
{
frag->ip6f_offlg = (frag->ip6f_offlg & ~IP6F_OFF_MASK) | htons(offset);
}
static inline void ipv6_frag_set_ident(struct ip6_frag *frag, uint32_t ident)
{
frag->ip6f_ident = htonl(ident);
}
static inline void ipv6_frag_set_more(struct ip6_frag *frag, bool more)
{
if (more)
{
frag->ip6f_offlg |= IP6F_MORE_FRAG;
}
else
{
frag->ip6f_offlg &= ~IP6F_MORE_FRAG;
}
}
static inline int ip6_hdr_to_str(const struct ip6_hdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
char src_addr_str[INET6_ADDRSTRLEN] = {0};
char dst_addr_str[INET6_ADDRSTRLEN] = {0};
struct in6_addr src_addr = ip6_hdr_get_src_in6_addr(hdr);
struct in6_addr dst_addr = ip6_hdr_get_dst_in6_addr(hdr);
inet_ntop(AF_INET6, &src_addr, src_addr_str, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &dst_addr, dst_addr_str, INET6_ADDRSTRLEN);
return snprintf(buf, size, "IPv6: version=%u traffic_class=%u flow_label=%u payload_len=%u next_header=%u hop_limit=%u src_addr=%s dst_addr=%s",
ip6_hdr_get_version(hdr), ip6_hdr_get_traffic_class(hdr), ip6_hdr_get_flow_label(hdr), ip6_hdr_get_payload_len(hdr),
ip6_hdr_get_next_header(hdr), ip6_hdr_get_hop_limit(hdr), src_addr_str, dst_addr_str);
}
/******************************************************************************
* L2TP
******************************************************************************/
struct l2tp_hdr
{
uint16_t flags;
// other option fields
};
#define L2TP_CONTROL_BIT 0x8000
#define L2TP_LENGTH_BIT 0x4000
#define L2TP_SEQUENCE_BIT 0x0800
#define L2TP_OFFSET_BIT 0x0200
#define L2TP_PRIORITY_BIT 0x0100
#define L2TP_VERSION 0x000f
static inline uint8_t l2tp_hdr_get_ver(const struct l2tp_hdr *hdr)
{
return ntohs(hdr->flags) & L2TP_VERSION;
}
// 1: control message
// 0: data message
static inline uint8_t l2tp_hdr_get_type(const struct l2tp_hdr *hdr)
{
return (ntohs(hdr->flags) & L2TP_CONTROL_BIT) >> 15;
}
/*
* Layer Two Tunneling Protocol "L2TP" (V2)
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Tunnel ID | Session ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Ns (opt) | Nr (opt) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Offset Size (opt) | Offset pad... (opt)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* https://datatracker.ietf.org/doc/html/rfc2661
*/
static inline uint16_t calc_udp_l2tpv2_hdr_len(const char *data, uint16_t len)
{
const struct l2tp_hdr *hdr = (const struct l2tp_hdr *)data;
uint16_t flags = ntohs(hdr->flags);
// ctrl message
if (flags & L2TP_CONTROL_BIT)
{
if ((flags & L2TP_LENGTH_BIT) == 0 || (flags & L2TP_SEQUENCE_BIT) == 0 ||
(flags & L2TP_OFFSET_BIT) != 0 || (flags & L2TP_PRIORITY_BIT) != 0)
{
return 0;
}
else
{
return ntohs(*((uint16_t *)(data + 2)));
}
}
// data message
else
{
uint16_t skip_len = 2; // skip flags field
if (flags & L2TP_LENGTH_BIT)
{
skip_len += 2; // skip length field
}
skip_len += 2; // skip tunnel id field
skip_len += 2; // skip session id field
if (flags & L2TP_SEQUENCE_BIT)
{
skip_len += 2; // skip ns field
skip_len += 2; // skip nr field
}
if (flags & L2TP_OFFSET_BIT)
{
if (skip_len + 2 > len)
{
return 0;
}
uint16_t offset = ntohs(*((uint16_t *)(data + skip_len)));
if (offset == 0)
{
skip_len += 2; // skip offset field
}
else
{
skip_len = offset;
}
}
return skip_len;
}
}
/*
* Figure 4.1.1.1: L2TPv3 Session Header Over IP
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Session ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Cookie (optional, maximum 64 bits)...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Figure 4.1.1.2: L2TPv3 Control Message Header Over IP
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | (32 bits of zeros) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |T|L|x|x|S|x|x|x|x|x|x|x| Ver | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Control Connection ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Ns | Nr |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Note: Unlike L2TP over UDP, which uses the T bit to distinguish between
* L2TP control and data packets, L2TP over IP uses the reserved Session
* ID of zero (0) when sending control messages.
*
* https://www.rfc-editor.org/rfc/rfc3931.html
*/
static inline uint16_t calc_ip_l2tpv3_hdr_len(const char *data, uint16_t len)
{
if (len < 4)
{
return 0;
}
uint32_t session_id = ntohl(*((uint32_t *)data));
// data message
if (session_id)
{
// TODO The optional Cookie field contains a variable-length value (maximum 64 bits)
// TODO L2-Specific Sublayer 4 bytes
return 4 + 4;
}
// control message
else
{
if (len < 16)
{
return 0;
}
uint16_t flags = ntohs(*((uint16_t *)(data + 4)));
if ((flags & L2TP_LENGTH_BIT) == 0 || (flags & L2TP_SEQUENCE_BIT) == 0)
{
return 0;
}
else
{
return ntohs(*((uint16_t *)(data + 4 + 2)));
}
}
}
/*
* Layer Two Tunneling Protocol - Version 3 (L2TPv3)
*
* Figure 3.2.1: L2TP Control Message Header
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |T|L|x|x|S|x|x|x|x|x|x|x| Ver | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Control Connection ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Ns | Nr |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Figure 4.1.2.1: L2TPv3 Session Header over UDP
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |T|x|x|x|x|x|x|x|x|x|x|x| Ver | Reserved |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Session ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Cookie (optional, maximum 64 bits)...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* https://www.rfc-editor.org/rfc/rfc3931.html
*/
static inline uint16_t calc_udp_l2tpv3_hdr_len(const char *data, uint16_t len)
{
if (len < 8)
{
return 0;
}
const struct l2tp_hdr *hdr = (const struct l2tp_hdr *)data;
uint16_t flags = ntohs(hdr->flags);
// ctrl message
if (flags & L2TP_CONTROL_BIT)
{
if ((flags & L2TP_LENGTH_BIT) == 0 || (flags & L2TP_SEQUENCE_BIT) == 0)
{
return 0;
}
else
{
return ntohs(*((uint16_t *)(data + 2)));
}
}
// data message
else
{
// TODO The optional Cookie field contains a variable-length value (maximum 64 bits)
// TODO L2-Specific Sublayer 4 bytes
return 8 + 4;
}
}
static inline int l2tp_hdr_to_str(const struct l2tp_hdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
return snprintf(buf, size, "L2TP: type=%s version=%u",
l2tp_hdr_get_type(hdr) ? "control" : "data", l2tp_hdr_get_ver(hdr));
}
/******************************************************************************
* MPLS
******************************************************************************/
/*
* 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 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
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);
}
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));
}
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));
}
/******************************************************************************
* TCP
******************************************************************************/
/*
* TCP Header Format
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Source Port | Destination Port |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Acknowledgment Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Data | |U|A|P|R|S|F| |
* | Offset| Reserved |R|C|S|S|Y|I| Window |
* | | |G|K|H|T|N|N| |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Checksum | Urgent Pointer |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Options | Padding |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | data |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static inline uint16_t tcp_hdr_get_src_port(const struct tcphdr *hdr)
{
return ntohs(hdr->th_sport);
}
static inline uint16_t tcp_hdr_get_dst_port(const struct tcphdr *hdr)
{
return ntohs(hdr->th_dport);
}
static inline uint32_t tcp_hdr_get_seq(const struct tcphdr *hdr)
{
return ntohl(hdr->th_seq);
}
static inline uint32_t tcp_hdr_get_ack(const struct tcphdr *hdr)
{
return ntohl(hdr->th_ack);
}
static inline uint8_t tcp_hdr_get_hdr_len(const struct tcphdr *hdr)
{
return hdr->th_off << 2;
}
static inline uint8_t tcp_hdr_get_flags(const struct tcphdr *hdr)
{
return hdr->th_flags;
}
static inline bool tcp_hdr_get_urg_flag(const struct tcphdr *hdr)
{
return hdr->th_flags & TH_URG;
}
static inline bool tcp_hdr_get_ack_flag(const struct tcphdr *hdr)
{
return hdr->th_flags & TH_ACK;
}
static inline bool tcp_hdr_get_push_flag(const struct tcphdr *hdr)
{
return hdr->th_flags & TH_PUSH;
}
static inline bool tcp_hdr_get_rst_flag(const struct tcphdr *hdr)
{
return hdr->th_flags & TH_RST;
}
static inline bool tcp_hdr_get_syn_flag(const struct tcphdr *hdr)
{
return hdr->th_flags & TH_SYN;
}
static inline bool tcp_hdr_get_fin_flag(const struct tcphdr *hdr)
{
return hdr->th_flags & TH_FIN;
}
static inline uint16_t tcp_hdr_get_window(const struct tcphdr *hdr)
{
return ntohs(hdr->th_win);
}
static inline uint16_t tcp_hdr_get_checksum(const struct tcphdr *hdr)
{
return ntohs(hdr->th_sum);
}
static inline uint16_t tcp_hdr_get_urg_ptr(const struct tcphdr *hdr)
{
return ntohs(hdr->th_urp);
}
static inline uint16_t tcp_hdr_get_opt_len(const struct tcphdr *hdr)
{
return tcp_hdr_get_hdr_len(hdr) - sizeof(struct tcphdr);
}
static inline const char *tcp_hdr_get_opt_data(const struct tcphdr *hdr)
{
if (tcp_hdr_get_opt_len(hdr) == 0)
{
return NULL;
}
return ((const char *)hdr) + sizeof(struct tcphdr);
}
static inline void tcp_hdr_set_src_port(struct tcphdr *hdr, uint16_t port)
{
hdr->th_sport = htons(port);
}
static inline void tcp_hdr_set_dst_port(struct tcphdr *hdr, uint16_t port)
{
hdr->th_dport = htons(port);
}
static inline void tcp_hdr_set_seq(struct tcphdr *hdr, uint32_t seq)
{
hdr->th_seq = htonl(seq);
}
static inline void tcp_hdr_set_ack(struct tcphdr *hdr, uint32_t ack)
{
hdr->th_ack = htonl(ack);
}
static inline void tcp_hdr_set_hdr_len(struct tcphdr *hdr, uint8_t offset)
{
hdr->th_off = offset >> 2;
}
static inline void tcp_hdr_set_flags(struct tcphdr *hdr, uint8_t flags)
{
hdr->th_flags = flags;
}
static inline void tcp_hdr_set_urg_flag(struct tcphdr *hdr, bool flag)
{
if (flag)
{
hdr->th_flags |= TH_URG;
}
else
{
hdr->th_flags &= ~TH_URG;
}
}
static inline void tcp_hdr_set_ack_flag(struct tcphdr *hdr, bool flag)
{
if (flag)
{
hdr->th_flags |= TH_ACK;
}
else
{
hdr->th_flags &= ~TH_ACK;
}
}
static inline void tcp_hdr_set_push_flag(struct tcphdr *hdr, bool flag)
{
if (flag)
{
hdr->th_flags |= TH_PUSH;
}
else
{
hdr->th_flags &= ~TH_PUSH;
}
}
static inline void tcp_hdr_set_rst_flag(struct tcphdr *hdr, bool flag)
{
if (flag)
{
hdr->th_flags |= TH_RST;
}
else
{
hdr->th_flags &= ~TH_RST;
}
}
static inline void tcp_hdr_set_syn_flag(struct tcphdr *hdr, bool flag)
{
if (flag)
{
hdr->th_flags |= TH_SYN;
}
else
{
hdr->th_flags &= ~TH_SYN;
}
}
static inline void tcp_hdr_set_fin_flag(struct tcphdr *hdr, bool flag)
{
if (flag)
{
hdr->th_flags |= TH_FIN;
}
else
{
hdr->th_flags &= ~TH_FIN;
}
}
static inline void tcp_hdr_set_window(struct tcphdr *hdr, uint16_t window)
{
hdr->th_win = htons(window);
}
static inline void tcp_hdr_set_checksum(struct tcphdr *hdr, uint16_t checksum)
{
hdr->th_sum = htons(checksum);
}
static inline void tcp_hdr_set_urg_ptr(struct tcphdr *hdr, uint16_t ptr)
{
hdr->th_urp = htons(ptr);
}
static inline void tcp_hdr_set_opt_len(struct tcphdr *hdr, uint16_t len)
{
hdr->th_off = (sizeof(struct tcphdr) + len) >> 2;
}
// must be called after tcp_hdr_set_opt_len
static inline void tcp_hdr_set_opt_data(struct tcphdr *hdr, const char *ptr)
{
memcpy((char *)hdr + sizeof(struct tcphdr), ptr, tcp_hdr_get_opt_len(hdr));
}
static inline int tcp_hdr_to_str(const struct tcphdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
return snprintf(buf, size, "TCP: src_port=%u dst_port=%u seq=%u ack=%u hdr_len=%u flags=0x%02x(%s%s%s%s%s%s) window=%u checksum=0x%x urg_ptr=%u opt_len=%u",
tcp_hdr_get_src_port(hdr), tcp_hdr_get_dst_port(hdr),
tcp_hdr_get_seq(hdr), tcp_hdr_get_ack(hdr),
tcp_hdr_get_hdr_len(hdr), tcp_hdr_get_flags(hdr),
tcp_hdr_get_urg_flag(hdr) ? "URG " : "",
tcp_hdr_get_ack_flag(hdr) ? "ACK " : "",
tcp_hdr_get_push_flag(hdr) ? "PSH " : "",
tcp_hdr_get_rst_flag(hdr) ? "RST " : "",
tcp_hdr_get_syn_flag(hdr) ? "SYN " : "",
tcp_hdr_get_fin_flag(hdr) ? "FIN " : "",
tcp_hdr_get_window(hdr), tcp_hdr_get_checksum(hdr),
tcp_hdr_get_urg_ptr(hdr), tcp_hdr_get_opt_len(hdr));
}
/******************************************************************************
* UDP
******************************************************************************/
/*
* User Datagram Header Format
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Source Port | Destination Port |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Length | Checksum |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Data |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static inline uint16_t udp_hdr_get_src_port(const struct udphdr *hdr)
{
return ntohs(hdr->uh_sport);
}
static inline uint16_t udp_hdr_get_dst_port(const struct udphdr *hdr)
{
return ntohs(hdr->uh_dport);
}
static inline uint16_t udp_hdr_get_total_len(const struct udphdr *hdr)
{
return ntohs(hdr->uh_ulen);
}
static inline uint16_t udp_hdr_get_checksum(const struct udphdr *hdr)
{
return ntohs(hdr->uh_sum);
}
static inline void udp_hdr_set_src_port(struct udphdr *hdr, uint16_t port)
{
hdr->uh_sport = htons(port);
}
static inline void udp_hdr_set_dst_port(struct udphdr *hdr, uint16_t port)
{
hdr->uh_dport = htons(port);
}
static inline void udp_hdr_set_total_len(struct udphdr *hdr, uint16_t len)
{
hdr->uh_ulen = htons(len);
}
static inline void udp_hdr_set_checksum(struct udphdr *hdr, uint16_t sum)
{
hdr->uh_sum = htons(sum);
}
static inline int udp_hdr_to_str(const struct udphdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
return snprintf(buf, size, "UDP: src_port=%u dst_port=%u total_len=%u checksum=0x%x",
udp_hdr_get_src_port(hdr), udp_hdr_get_dst_port(hdr),
udp_hdr_get_total_len(hdr), udp_hdr_get_checksum(hdr));
}
/******************************************************************************
* VLAN
******************************************************************************/
/*
* VLAN Header Format
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Pri |I| VLAN ID | Ethertype |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct vlan_hdr
{
uint16_t vlan_tci;
uint16_t vlan_ethertype;
};
static inline uint8_t vlan_hdr_get_priority(const struct vlan_hdr *hdr)
{
return (ntohs(hdr->vlan_tci) & 0xe000) >> 13;
}
static inline uint8_t vlan_hdr_get_dei(const struct vlan_hdr *hdr)
{
return (ntohs(hdr->vlan_tci) & 0x1000) >> 12;
}
static inline uint16_t vlan_hdr_get_vid(const struct vlan_hdr *hdr)
{
return ntohs(hdr->vlan_tci) & 0x0fff;
}
static inline uint16_t vlan_hdr_get_ethertype(const struct vlan_hdr *hdr)
{
return ntohs(hdr->vlan_ethertype);
}
static inline void vlan_hdr_set_priority(struct vlan_hdr *hdr, uint8_t priority)
{
hdr->vlan_tci = htons((ntohs(hdr->vlan_tci) & ~0xe000) | (priority << 13));
}
static inline void vlan_hdr_set_dei(struct vlan_hdr *hdr, uint8_t dei)
{
hdr->vlan_tci = htons((ntohs(hdr->vlan_tci) & ~0x1000) | (dei << 12));
}
static inline void vlan_hdr_set_vid(struct vlan_hdr *hdr, uint16_t vid)
{
hdr->vlan_tci = htons((ntohs(hdr->vlan_tci) & ~0x0fff) | vid);
}
static inline void vlan_hdr_set_ethertype(struct vlan_hdr *hdr, uint16_t ethertype)
{
hdr->vlan_ethertype = htons(ethertype);
}
static inline int vlan_hdr_to_str(const struct vlan_hdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
uint16_t proto = vlan_hdr_get_ethertype(hdr);
return snprintf(buf, size, "VLAN: priority=%u dei=%u id=%u ethertype=%s",
vlan_hdr_get_priority(hdr), vlan_hdr_get_dei(hdr),
vlan_hdr_get_vid(hdr), eth_proto_to_str(proto));
}
/******************************************************************************
* VXLAN
******************************************************************************/
/*
* VXLAN Header
*
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |R|R|R|R|I|R|R|R| Reserved |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | VXLAN Network Identifier (VNI) | Reserved |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct vxlan_hdr
{
uint32_t vxlan_flags;
uint32_t vxlan_vni;
};
static inline uint8_t vxlan_hdr_get_flags(const struct vxlan_hdr *hdr)
{
return (ntohl(hdr->vxlan_flags) & 0xff000000) >> 24;
}
static inline uint32_t vxlan_hdr_get_vni(const struct vxlan_hdr *hdr)
{
return (ntohl(hdr->vxlan_vni) & 0xffffff00) >> 8;
}
static inline void vxlan_hdr_set_flags(struct vxlan_hdr *hdr, uint8_t flags)
{
hdr->vxlan_flags = htonl((ntohl(hdr->vxlan_flags) & ~0xff000000) | (flags << 24));
}
static inline void vxlan_hdr_set_vni(struct vxlan_hdr *hdr, uint32_t vni)
{
hdr->vxlan_vni = htonl((ntohl(hdr->vxlan_vni) & ~0xffffff00) | (vni << 8));
}
static inline int vxlan_hdr_to_str(const struct vxlan_hdr *hdr, char *buf, size_t size)
{
memset(buf, 0, size);
return snprintf(buf, size, "VXLAN: flags=%u vni=%u",
vxlan_hdr_get_flags(hdr), vxlan_hdr_get_vni(hdr));
}
#ifdef __cplusplus
}
#endif