#pragma once #ifdef __cplusplus extern "C" { #endif #include #include #include #include "eth_utils.h" /* * 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 /****************************************************************************** * get ******************************************************************************/ 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) { 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; } /****************************************************************************** * set ******************************************************************************/ 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); } } /****************************************************************************** * print ******************************************************************************/ 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; } #ifdef __cplusplus } #endif