177 lines
5.5 KiB
C
177 lines
5.5 KiB
C
|
/*
|
||
|
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
|
||
|
*
|
||
|
* This file is part of Open5GS.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Affero General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "ogs-gtp.h"
|
||
|
|
||
|
int ogs_gtpu_parse_header(
|
||
|
ogs_gtp2_header_desc_t *header_desc, ogs_pkbuf_t *pkbuf)
|
||
|
{
|
||
|
ogs_gtp2_header_t *gtp_h = NULL;
|
||
|
ogs_gtp2_extension_header_t ext_hdesc;
|
||
|
uint8_t *ext_h = NULL;
|
||
|
uint16_t len = 0;
|
||
|
int i;
|
||
|
|
||
|
ogs_assert(pkbuf);
|
||
|
ogs_assert(pkbuf->data);
|
||
|
|
||
|
gtp_h = (ogs_gtp2_header_t *)pkbuf->data;
|
||
|
|
||
|
if (header_desc) {
|
||
|
memset(header_desc, 0, sizeof(*header_desc));
|
||
|
|
||
|
header_desc->flags = gtp_h->flags;
|
||
|
header_desc->type = gtp_h->type;
|
||
|
header_desc->teid = be32toh(gtp_h->teid);
|
||
|
}
|
||
|
|
||
|
len = OGS_GTPV1U_HEADER_LEN;
|
||
|
if (pkbuf->len < len) {
|
||
|
ogs_error("the length of the packet is insufficient[%d:%d]",
|
||
|
pkbuf->len, len);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (gtp_h->flags & OGS_GTPU_FLAGS_E) {
|
||
|
|
||
|
len += OGS_GTPV1U_EXTENSION_HEADER_LEN;
|
||
|
if (pkbuf->len < len) {
|
||
|
ogs_error("the length of the packet is insufficient[%d:%d]",
|
||
|
pkbuf->len, len);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* TS29.281
|
||
|
* 5.2.1 General format of the GTP-U Extension Header
|
||
|
*
|
||
|
* If no such Header follows,
|
||
|
* then the value of the Next Extension Header Type shall be 0. */
|
||
|
|
||
|
i = 0;
|
||
|
while (*(ext_h = (((uint8_t *)gtp_h) + len - 1))) {
|
||
|
/*
|
||
|
* The length of the Extension header shall be defined
|
||
|
* in a variable length of 4 octets, i.e. m+1 = n*4 octets,
|
||
|
* where n is a positive integer.
|
||
|
*/
|
||
|
len += (*(++ext_h)) * 4;
|
||
|
if (*ext_h == 0) {
|
||
|
ogs_error("No length in the Extension header");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (((*ext_h) * 4) > OGS_GTP2_MAX_EXTENSION_HEADER_LEN) {
|
||
|
ogs_error("Overflow length : %d", (*ext_h));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (pkbuf->len < len) {
|
||
|
ogs_error("the length of the packet is insufficient[%d:%d]",
|
||
|
pkbuf->len, len);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!header_desc) /* Skip to extract header content */
|
||
|
continue;
|
||
|
|
||
|
/* Copy Header Content */
|
||
|
memcpy(&ext_hdesc.array[i], ext_h-1, (*ext_h) * 4);
|
||
|
|
||
|
switch (ext_hdesc.array[i].type) {
|
||
|
case OGS_GTP2_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER:
|
||
|
header_desc->pdu_type = ext_hdesc.array[i].pdu_type;
|
||
|
if (ext_hdesc.array[i].pdu_type ==
|
||
|
OGS_GTP2_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION) {
|
||
|
header_desc->qos_flow_identifier =
|
||
|
ext_hdesc.array[i].qos_flow_identifier;
|
||
|
ogs_trace(" QFI [0x%x]",
|
||
|
header_desc->qos_flow_identifier);
|
||
|
}
|
||
|
break;
|
||
|
case OGS_GTP2_EXTENSION_HEADER_TYPE_UDP_PORT:
|
||
|
header_desc->udp.presence = true;
|
||
|
header_desc->udp.port = be16toh(ext_hdesc.array[i].udp_port);
|
||
|
|
||
|
ogs_trace(" UDP Port [%d]", header_desc->udp.port);
|
||
|
break;
|
||
|
case OGS_GTP2_EXTENSION_HEADER_TYPE_PDCP_NUMBER:
|
||
|
header_desc->pdcp_number_presence = true;
|
||
|
header_desc->pdcp_number =
|
||
|
be16toh(ext_hdesc.array[i].pdcp_number);
|
||
|
|
||
|
ogs_trace(" PDCP Number [%d]", header_desc->pdcp_number);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
} else if (gtp_h->flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN)) {
|
||
|
/*
|
||
|
* If and only if one or more of these three flags are set,
|
||
|
* the fields Sequence Number, N-PDU and Extension Header
|
||
|
* shall be present. The sender shall set all the bits of
|
||
|
* the unused fields to zero. The receiver shall not evaluate
|
||
|
* the unused fields.
|
||
|
* For example, if only the E flag is set to 1, then
|
||
|
* the N-PDU Number and Sequence Number fields shall also be present,
|
||
|
* but will not have meaningful values and shall not be evaluated.
|
||
|
*/
|
||
|
len += 4;
|
||
|
}
|
||
|
|
||
|
if (pkbuf->len < len) {
|
||
|
ogs_error("the length of the packet is insufficient[%d:%d]",
|
||
|
pkbuf->len, len);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
uint16_t ogs_in_cksum(uint16_t *addr, int len)
|
||
|
{
|
||
|
int nleft = len;
|
||
|
uint32_t sum = 0;
|
||
|
uint16_t *w = addr;
|
||
|
uint16_t answer = 0;
|
||
|
|
||
|
// Adding 16 bits sequentially in sum
|
||
|
while (nleft > 1) {
|
||
|
sum += *w;
|
||
|
nleft -= 2;
|
||
|
w++;
|
||
|
}
|
||
|
|
||
|
// If an odd byte is left
|
||
|
if (nleft == 1) {
|
||
|
*(uint8_t *) (&answer) = *(uint8_t *) w;
|
||
|
sum += answer;
|
||
|
}
|
||
|
|
||
|
sum = (sum >> 16) + (sum & 0xffff);
|
||
|
sum += (sum >> 16);
|
||
|
answer = ~sum;
|
||
|
|
||
|
return answer;
|
||
|
}
|