feature: add GTP utils, support overwrite message length of GTP header

This commit is contained in:
luwenpeng
2024-07-09 11:17:03 +08:00
parent b435ec2ea1
commit c87ad330e0
28 changed files with 1676 additions and 648 deletions

View File

@@ -9,8 +9,10 @@
#include "gre_utils.h"
#include "udp_utils.h"
#include "tcp_utils.h"
#include "ipv4_utils.h"
#include "ipv6_utils.h"
#include "ip4_utils.h"
#include "ip6_utils.h"
#include "gtp1_utils.h"
#include "gtp2_utils.h"
#include "mpls_utils.h"
#include "l2tp_utils.h"
#include "vlan_utils.h"
@@ -33,20 +35,20 @@
#define PACKET_LOG_UNSUPPORT_PROTO(pkt, layer, next_proto) \
{ \
PACKET_PARSE_LOG_WARN("pkt: %p, layer: %s, unsupport next proto %d", \
PACKET_PARSE_LOG_WARN("pkt: %p, layer: %s, unsupport next proto: %d", \
(pkt), layer_proto_to_str(layer), (next_proto)); \
}
#define PACKET_LOG_UNSUPPORT_ETHPROTO(pkt, next_proto) \
{ \
PACKET_PARSE_LOG_WARN("pkt: %p, layer: l3, unsupport next eth proto %s", \
(pkt), eth_proto_to_str(next_proto)); \
#define PACKET_LOG_UNSUPPORT_ETHPROTO(pkt, next_proto) \
{ \
PACKET_PARSE_LOG_WARN("pkt: %p, layer: L3, unsupport next proto: %d %s", \
(pkt), (next_proto), eth_proto_to_str(next_proto)); \
}
#define PACKET_LOG_UNSUPPORT_IPPROTO(pkt, next_proto) \
{ \
PACKET_PARSE_LOG_WARN("pkt: %p, layer: l4, unsupport next ip proto %s", \
(pkt), ip_proto_to_str(next_proto)); \
#define PACKET_LOG_UNSUPPORT_IPPROTO(pkt, next_proto) \
{ \
PACKET_PARSE_LOG_WARN("pkt: %p, layer: L4, unsupport next proto: %d %s", \
(pkt), (next_proto), ip_proto_to_str(next_proto)); \
}
/******************************************************************************
@@ -55,7 +57,6 @@
static inline const char *layer_proto_to_str(enum layer_proto proto);
static inline struct raw_layer *get_free_layer(struct packet *pkt);
static inline uint16_t get_gtp_hdr_len(const char *data, uint16_t len);
// 数据链路层
static inline const char *parse_ether(struct packet *pkt, const char *data, uint16_t len);
@@ -82,7 +83,8 @@ static inline const char *parse_icmp(struct packet *pkt, const char *data, uint1
static inline const char *parse_icmp6(struct packet *pkt, const char *data, uint16_t len);
// 传输层 -- 隧道
static inline const char *parse_vxlan(struct packet *pkt, const char *data, uint16_t len);
static inline const char *parse_gtpv1_u(struct packet *pkt, const char *data, uint16_t len);
static inline const char *parse_gtp_u(struct packet *pkt, const char *data, uint16_t len);
static inline const char *parse_gtp_c(struct packet *pkt, const char *data, uint16_t len);
// L3/L4
static inline const char *parse_l3(struct packet *pkt, uint16_t next_proto, const char *data, uint16_t len);
static inline const char *parse_l4(struct packet *pkt, uint8_t next_proto, const char *data, uint16_t len);
@@ -127,8 +129,10 @@ static inline const char *layer_proto_to_str(enum layer_proto proto)
return "ICMP6";
case LAYER_PROTO_VXLAN:
return "VXLAN";
case LAYER_PROTO_GTP:
return "GTP";
case LAYER_PROTO_GTP_C:
return "GTP-C";
case LAYER_PROTO_GTP_U:
return "GTP-U";
default:
return "UNKNOWN";
}
@@ -163,74 +167,6 @@ static inline struct raw_layer *get_free_layer(struct packet *pkt)
* Private API -- Parses
******************************************************************************/
static inline uint16_t get_gtp_hdr_len(const char *data, uint16_t len)
{
#define GTP_HDR_VER (0xE0)
#define GTP_HDR_FLAG_N_PDU (0x01)
#define GTP_HDR_FLAG_SEQ_NUM (0x02)
#define GTP_HDR_FLAG_EXT_HDR (0x04)
struct gtp_hdr
{
uint8_t flags;
uint8_t msg_type;
uint16_t msg_len;
uint32_t teid;
} __attribute__((__packed__));
struct gtp_opt
{
uint16_t seq_num;
uint8_t npdu;
uint8_t next_ext_hdr;
} __attribute__((__packed__));
uint16_t hdr_offset = 0;
if (len < sizeof(struct gtp_hdr))
{
return 0;
}
const struct gtp_hdr *gtp = (const struct gtp_hdr *)data;
hdr_offset += sizeof(struct gtp_hdr); // skip gre hdr
// GTPv0 Not Supported
if (((gtp->flags & GTP_HDR_VER) >> 5) != 1)
{
return 0;
}
if (gtp->flags & (GTP_HDR_FLAG_SEQ_NUM | GTP_HDR_FLAG_N_PDU | GTP_HDR_FLAG_EXT_HDR))
{
if (hdr_offset + sizeof(struct gtp_opt) > len)
{
return 0;
}
struct gtp_opt *opt_hdr = (struct gtp_opt *)((char *)data + hdr_offset);
uint8_t next_ext_hdr = opt_hdr->next_ext_hdr;
hdr_offset += sizeof(struct gtp_opt); // skip gre opt
while (next_ext_hdr)
{
if (hdr_offset + 1 > len)
{
return 0;
}
uint8_t length = *((char *)data + hdr_offset) * 4 - 2;
hdr_offset += 1; // skip length field
if (hdr_offset + length + 1 > len)
{
return 0;
}
hdr_offset += length; // skip data field
next_ext_hdr = *((char *)data + hdr_offset);
hdr_offset += 1; // skip next ext hdr field
}
}
return hdr_offset;
}
static inline const char *parse_ether(struct packet *pkt, const char *data, uint16_t len)
{
if (unlikely(len < sizeof(struct ethhdr)))
@@ -564,14 +500,14 @@ static inline const char *parse_ipv4(struct packet *pkt, const char *data, uint1
return data;
}
const struct ip *hdr = (const struct ip *)data;
uint16_t hdr_len = ipv4_hdr_get_hdr_len(hdr);
uint16_t hdr_len = ip4_hdr_get_hdr_len(hdr);
if (unlikely(hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV4);
return data;
}
uint16_t total_len = ipv4_hdr_get_total_len(hdr);
uint16_t total_len = ip4_hdr_get_total_len(hdr);
if (unlikely(total_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV4);
@@ -586,14 +522,14 @@ static inline const char *parse_ipv4(struct packet *pkt, const char *data, uint1
SET_LAYER(pkt, layer, LAYER_PROTO_IPV4, hdr_len, data, len, trim_len);
// ip fragmented
if (ipv4_hdr_get_mf_flag(hdr) || ipv4_hdr_get_frag_offset(hdr))
if (ip4_hdr_get_mf_flag(hdr) || ip4_hdr_get_frag_offset(hdr))
{
PACKET_PARSE_LOG_WARN("packet %p ip layer %p is fragmented", pkt, layer);
pkt->frag_layer = layer;
return layer->pld_ptr;
}
uint8_t next_proto = ipv4_hdr_get_proto(hdr);
uint8_t next_proto = ip4_hdr_get_proto(hdr);
return parse_l4(pkt, next_proto, layer->pld_ptr, layer->pld_len);
}
@@ -630,13 +566,13 @@ static inline const char *parse_ipv6(struct packet *pkt, const char *data, uint1
return data;
}
const struct ip6_hdr *hdr = (const struct ip6_hdr *)data;
uint16_t pld_len = ipv6_hdr_get_payload_len(hdr);
uint16_t pld_len = ip6_hdr_get_payload_len(hdr);
if (unlikely(pld_len + sizeof(struct ip6_hdr) > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_IPV6);
return data;
}
uint8_t next_proto = ipv6_hdr_get_next_header(hdr);
uint8_t next_proto = ip6_hdr_get_next_header(hdr);
uint16_t hdr_len = sizeof(struct ip6_hdr);
uint16_t trim_len = len - pld_len - sizeof(struct ip6_hdr);
const char *next_hdr_ptr = data + hdr_len;
@@ -786,17 +722,14 @@ static inline const char *parse_udp(struct packet *pkt, const char *data, uint16
if (dst_port == 2152 || src_port == 2152)
{
// TODO
// check V1 or V2
// GTP1U_PORT 2152
return parse_gtpv1_u(pkt, layer->pld_ptr, layer->pld_len);
// only GTPv1-U, no GTPv2-U
return parse_gtp_u(pkt, layer->pld_ptr, layer->pld_len);
}
if (dst_port == 2123 || src_port == 2123)
{
// TODO
// GTP-C - GTP control 2123
// GTPv1-C or GTPv2-C
return parse_gtp_c(pkt, layer->pld_ptr, layer->pld_len);
}
if (dst_port == 1701 || src_port == 1701)
@@ -826,7 +759,7 @@ static inline const char *parse_udp(struct packet *pkt, const char *data, uint16
return layer->pld_ptr;
}
const struct ip6_hdr *hdr = (const struct ip6_hdr *)layer->pld_ptr;
if (ipv6_hdr_get_version(hdr) != 6)
if (ip6_hdr_get_version(hdr) != 6)
{
return layer->pld_ptr;
}
@@ -914,12 +847,19 @@ static inline const char *parse_vxlan(struct packet *pkt, const char *data, uint
return parse_ether(pkt, layer->pld_ptr, layer->pld_len);
}
static inline const char *parse_gtpv1_u(struct packet *pkt, const char *data, uint16_t len)
static inline const char *parse_gtp_u(struct packet *pkt, const char *data, uint16_t len)
{
uint16_t hdr_len = get_gtp_hdr_len(data, len);
// only GTPv1-U, no GTPv2-U
uint8_t version = peek_gtp_version(data, len);
if (unlikely(version != 1))
{
return data;
}
uint16_t hdr_len = calc_gtp1_hdr_len(data, len);
if (unlikely(hdr_len == 0 || hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_GTP);
PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_GTP_U);
return data;
}
@@ -929,7 +869,7 @@ static inline const char *parse_gtpv1_u(struct packet *pkt, const char *data, ui
return data;
}
uint8_t next_proto = (((const uint8_t *)(data + hdr_len))[0]) >> 4;
SET_LAYER(pkt, layer, LAYER_PROTO_GTP, hdr_len, data, len, 0);
SET_LAYER(pkt, layer, LAYER_PROTO_GTP_U, hdr_len, data, len, 0);
switch (next_proto)
{
@@ -938,11 +878,44 @@ static inline const char *parse_gtpv1_u(struct packet *pkt, const char *data, ui
case 6:
return parse_ipv6(pkt, layer->pld_ptr, layer->pld_len);
default:
PACKET_LOG_UNSUPPORT_PROTO(pkt, LAYER_PROTO_GTP, next_proto);
PACKET_LOG_UNSUPPORT_PROTO(pkt, LAYER_PROTO_GTP_U, next_proto);
return layer->pld_ptr;
}
}
static inline const char *parse_gtp_c(struct packet *pkt, const char *data, uint16_t len)
{
// GTPv1-C or GTPv2-C
uint16_t hdr_len = 0;
uint8_t version = peek_gtp_version(data, len);
switch (version)
{
case 1:
hdr_len = calc_gtp1_hdr_len(data, len);
break;
case 2:
hdr_len = calc_gtp2_hdr_len(data, len);
break;
default:
return data;
}
if (unlikely(hdr_len == 0 || hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(pkt, LAYER_PROTO_GTP_C);
return data;
}
struct raw_layer *layer = get_free_layer(pkt);
if (unlikely(layer == NULL))
{
return data;
}
SET_LAYER(pkt, layer, LAYER_PROTO_GTP_C, hdr_len, data, len, 0);
return layer->pld_ptr;
}
static inline const char *parse_l3(struct packet *pkt, uint16_t next_proto, const char *data, uint16_t len)
{
switch (next_proto)
@@ -1052,10 +1025,10 @@ void packet_print(const struct packet *pkt)
break;
break;
case LAYER_PROTO_IPV4:
used = ipv4_hdr_to_str((const struct ip *)layer->hdr_ptr, buffer, sizeof(buffer));
used = ip4_hdr_to_str((const struct ip *)layer->hdr_ptr, buffer, sizeof(buffer));
break;
case LAYER_PROTO_IPV6:
used = ipv6_hdr_to_str((const struct ip6_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
used = ip6_hdr_to_str((const struct ip6_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
break;
case LAYER_PROTO_IPAH:
break;
@@ -1075,7 +1048,16 @@ void packet_print(const struct packet *pkt)
case LAYER_PROTO_VXLAN:
used = vxlan_hdr_to_str((const struct vxlan_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
break;
case LAYER_PROTO_GTP:
case LAYER_PROTO_GTP_C:
case LAYER_PROTO_GTP_U:
if (peek_gtp_version(layer->hdr_ptr, layer->hdr_len) == 1)
{
used = gtp1_hdr_to_str((const struct gtp1_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
}
if (peek_gtp_version(layer->hdr_ptr, layer->hdr_len) == 2)
{
used = gtp2_hdr_to_str((const struct gtp2_hdr *)layer->hdr_ptr, buffer, sizeof(buffer));
}
break;
default:
break;