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
2024-11-27 19:50:50 +08:00

2874 lines
80 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "utils_internal.h"
#include <stdio.h>
#include <string.h>
#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