#pragma once #ifdef __cplusplus extern "C" { #endif #include "utils_internal.h" #include #include #include #include #include #include #include #include #define __FAVOR_BSD 1 #include #include /****************************************************************************** * 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: // PPP:Point-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; } } 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