Skip to content

Commit bb01dcb

Browse files
committed
Add support for GTP de-tunneling
Close #6
1 parent 9b5b90b commit bb01dcb

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

pl7m.c

+182
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,47 @@ struct gre_header {
159159
__u16 protocol;
160160
};
161161

162+
#define GTP_MSG_TPDU 0xFF
163+
164+
struct gtp_header {
165+
#if defined(__LITTLE_ENDIAN_BITFIELD)
166+
u_int16_t n_pdu:1,
167+
sequence:1,
168+
extension:1,
169+
reserved:1,
170+
protocol:1,
171+
version:3,
172+
type:8;
173+
#elif defined(__BIG_ENDIAN_BITFIELD)
174+
u_int16_t version:3,
175+
protocol:1,
176+
reserved:1,
177+
extension:1,
178+
sequence:1,
179+
n_pdu:1,
180+
type:8;
181+
#else
182+
#error "Adjust your <asm/byteorder.h> defines"
183+
#endif
184+
u_int16_t total_length;
185+
u_int32_t teid;
186+
};
187+
188+
struct gtp_header_optional {
189+
u_int16_t sn;
190+
u_int8_t n_pdu_nbr;
191+
u_int8_t next_hdr;
192+
};
193+
194+
162195
struct m_pkt {
163196
unsigned char *raw_data;
164197
struct pcap_pkthdr header;
165198

166199
int l2_offset;
167200
int prev_l3_offset;
168201
u_int16_t prev_l3_proto;
202+
int gtp_offset;
169203
int l3_offset;
170204
u_int16_t l3_proto;
171205
int l4_offset;
@@ -708,6 +742,136 @@ static int dissect_l4(struct m_pkt *p)
708742
}
709743
return 0;
710744
}
745+
static int is_gtp_u(unsigned char *gtp_buffer, int gtp_buffer_len,
746+
const struct m_pkt *p, u_int16_t *l3_proto)
747+
{
748+
struct gtp_header *gtp_h;
749+
struct udphdr *udp_h;
750+
uint16_t new_layer_len = 0;
751+
unsigned char sub_proto;
752+
753+
if (p->l4_proto != IPPROTO_UDP ||
754+
gtp_buffer_len < (int)sizeof(struct gtp_header))
755+
return 0;
756+
757+
/* Only default port */
758+
udp_h = (struct udphdr *)(p->raw_data + p->l4_offset);
759+
if(udp_h->source != htons(2152) &&
760+
udp_h->dest != htons(2152))
761+
return 0;
762+
763+
gtp_h = (struct gtp_header *)gtp_buffer;
764+
765+
if (gtp_h->version != 1 ||
766+
gtp_h->type != GTP_MSG_TPDU ||
767+
gtp_h->reserved != 0 ||
768+
ntohs(gtp_h->total_length) > (gtp_buffer_len - sizeof(struct gtp_header))) {
769+
ddbg("Invalid gtp header: %d, 0x%x, 0x%0x, %d vs %d\n",
770+
gtp_h->version, gtp_h->type, gtp_h->reserved,
771+
ntohs(gtp_h->total_length), gtp_buffer_len);
772+
return 0;
773+
}
774+
775+
new_layer_len = sizeof(struct gtp_header);
776+
777+
/* Optional header version 1 */
778+
if (gtp_h->extension || gtp_h->sequence || gtp_h->n_pdu) {
779+
new_layer_len += sizeof(struct gtp_header_optional);
780+
781+
if (gtp_buffer_len < new_layer_len)
782+
return 0;
783+
}
784+
if (gtp_h->extension) {
785+
unsigned int length = 0;
786+
787+
while (new_layer_len < (gtp_buffer_len - 1)) {
788+
length = gtp_buffer[new_layer_len] << 2;
789+
new_layer_len += length;
790+
if (new_layer_len > gtp_buffer_len ||
791+
gtp_buffer[new_layer_len - 1] == 0 || length == 0)
792+
break;
793+
}
794+
if (new_layer_len > gtp_buffer_len ||
795+
gtp_buffer[new_layer_len - 1] != 0 ||
796+
length == 0) {
797+
return 0;
798+
}
799+
}
800+
801+
/* Trying to detect next proto. Code taken from wireshark */
802+
if (gtp_buffer_len < new_layer_len + 1)
803+
return 0;
804+
sub_proto = gtp_buffer[new_layer_len];
805+
if ((sub_proto >= 0x45) && (sub_proto <= 0x4e)) {
806+
/* This is most likely an IPv4 packet
807+
* we can exclude 0x40 - 0x44 because the minimum header size is 20 octets
808+
* 0x4f is excluded because PPP protocol type "IPv6 header compression"
809+
* with protocol field compression is more likely than a plain
810+
* IPv4 packet with 60 octet header size */
811+
*l3_proto = ETH_P_IP;
812+
} else if ((sub_proto & 0xf0) == 0x60) {
813+
/* This is most likely an IPv6 packet */
814+
*l3_proto = ETH_P_IPV6;
815+
} else {
816+
/* This seems to be a PPP packet */
817+
/* TODO: code not back-ported from wireshark yet*/
818+
return 0;
819+
}
820+
821+
return new_layer_len;
822+
}
823+
static int dissect_l4_detunneling(struct m_pkt *p)
824+
{
825+
unsigned char *data = p->raw_data + p->l5_offset;
826+
int data_len = p->header.caplen - p->l5_offset;
827+
u_int16_t next_l3_proto;
828+
int gtp_header_len, rc;
829+
830+
ddbg("L4(detunel): l4_proto %d data_len %d l5_length %d\n",
831+
p->l4_proto, data_len, p->l5_length);
832+
833+
if (data_len < 0 || p->l5_length > data_len)
834+
return -1;
835+
836+
/* TODO: try to handle tunnel over fragment */
837+
if (p->is_l3_fragment) {
838+
ddbg("Skip L4(detunnel) dissection because it is a fragment\n");
839+
return 0;
840+
}
841+
/* No reasons to detunnel if we skipped L4 dissection */
842+
if (p->skip_l4_dissection) {
843+
ddbg("Skip L4 dissection\n");
844+
return 0;
845+
}
846+
847+
/* GTP detunneling: looking only for MSG T-PDU that carries
848+
encapsulated data */
849+
gtp_header_len = is_gtp_u(data, data_len, p, &next_l3_proto);
850+
if (gtp_header_len > 0) {
851+
ddbg("Found GTP-U\n");
852+
if (p->prev_l3_proto == 0) {
853+
assert(p->prev_l3_offset == 0);
854+
p->prev_l3_proto = p->l3_proto;
855+
p->prev_l3_offset = p->l3_offset;
856+
} else {
857+
derr("Multiple tunnels. Unsupported\n");
858+
return -1;
859+
}
860+
assert(p->gtp_offset == 0);
861+
p->gtp_offset = p->l5_offset;
862+
p->l3_proto = next_l3_proto;
863+
p->l3_offset = p->l5_offset + gtp_header_len;
864+
rc = dissect_l3(p);
865+
if (rc != 0) {
866+
derr("Error dissect_l3 (after gtp)\n");
867+
return -1;
868+
}
869+
return dissect_l4(p);
870+
}
871+
872+
/* "Normal" L4 traffic */
873+
return 0;
874+
}
711875
static int dissect_do(int datalink_type, struct m_pkt *p)
712876
{
713877
int rc;
@@ -727,6 +891,12 @@ static int dissect_do(int datalink_type, struct m_pkt *p)
727891
derr("Error dissect_l4\n");
728892
return -1;
729893
}
894+
/* Some kind of detunneling over L4 (usually over UDP). Example: GTP */
895+
rc = dissect_l4_detunneling(p);
896+
if (rc != 0) {
897+
derr("Error dissect_l5\n");
898+
return -1;
899+
}
730900
return 0;
731901
}
732902

@@ -792,6 +962,7 @@ static void update_do_l7(struct m_pkt *p)
792962
{
793963
struct udphdr *udp_h;
794964
struct tcphdr *tcp_h;
965+
struct gtp_header *gtp_h;
795966
size_t new_l5_len;
796967
int l4_header_len = 0;
797968
int l5_len_diff;
@@ -857,6 +1028,16 @@ static void update_do_l7(struct m_pkt *p)
8571028
ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) + l5_len_diff);
8581029
}
8591030

1031+
/* Update GTP header */
1032+
if (p->gtp_offset) {
1033+
gtp_h = (struct gtp_header *)(p->raw_data + p->gtp_offset);
1034+
gtp_h->total_length = htons(ntohs(gtp_h->total_length) + l5_len_diff);
1035+
/* Update previous UDP header */
1036+
assert(p->gtp_offset > (int)sizeof(struct udphdr));
1037+
udp_h = (struct udphdr *)(p->raw_data + p->gtp_offset - sizeof(struct udphdr));
1038+
udp_h->len = htons(ntohs(udp_h->len) + l5_len_diff);
1039+
}
1040+
8601041
p->l5_length = new_l5_len;
8611042
ddbg("cap_len %u->%zu\n", p->header.caplen, p->l5_offset + new_l5_len);
8621043
p->header.caplen = p->l5_offset + new_l5_len;
@@ -955,6 +1136,7 @@ static struct m_pkt *__dup_pkt(struct m_pkt *p)
9551136
n->l2_offset = p->l2_offset;
9561137
n->prev_l3_offset = p->prev_l3_offset;
9571138
n->prev_l3_proto = p->prev_l3_proto;
1139+
n->gtp_offset = p->gtp_offset;
9581140
n->l4_offset = p->l4_offset;
9591141
n->l3_offset = p->l3_offset;
9601142
n->l3_proto = p->l3_proto;

tests/pcaps/gtpu-dns.pcapng

476 Bytes
Binary file not shown.

tests/pcaps/gtpu-tls.pcapng

14.1 KB
Binary file not shown.

tests/results/gtpu-dns.pcapng.fuzzed

19.2 KB
Binary file not shown.

tests/results/gtpu-tls.pcapng.fuzzed

72.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)