feature: add GTP utils, support overwrite message length of GTP header
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user