Commit 41560bed authored by gauthier's avatar gauthier

gtpv1u

parent 7f351d8e
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file 3gpp_29.281.hpp
\brief
\author Lionel Gauthier
\company Eurecom
\email: lionel.gauthier@eurecom.fr
*/
#ifndef FILE_3GPP_29_281_HPP_SEEN
#define FILE_3GPP_29_281_HPP_SEEN
#include <arpa/inet.h>
#include <cstring>
#include <exception>
//#include <fmt/format.h> conflicts with spdlog
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "3gpp_29.281.h"
#include "msg_gtpv1u.hpp"
#include "logger.hpp"
#include "serializable.hpp"
namespace oai::cn::proto::gtpv1u {
//------------------------------------------------------------------------------
class gtpv1u_tlv : public stream_serializable {
public:
static const uint16_t tlv_ie_length = 4;
uint8_t type;
uint16_t length;
gtpv1u_tlv() :
type(0),
length(0) {}
gtpv1u_tlv(uint8_t t, uint16_t l = 0) :
type(t),
length(l) {}
//~gtpv1u_tlv() {};
void set_type(const uint8_t& t) {
type = t;
}
uint8_t get_type() {
return type;
}
void set_length(const uint16_t& l) {
length = l;
}
uint16_t get_length() {
return length ;
}
void dump_to(std::ostream& os) {
os.write(reinterpret_cast<const char*>(&type), sizeof(type));
if (type & 0x80) {
auto be_length = htobe16(length);
os.write(reinterpret_cast<const char*>(&be_length), sizeof(be_length));
}
}
void load_from(std::istream& is) {
is.read(reinterpret_cast<char*>(&type), sizeof(type));
if (type & 0x80) {
is.read(reinterpret_cast<char*>(&length), sizeof(length));
length = be16toh(length);
} else {
length = 0;
}
}
};
//------------------------------------------------------------------------------
class gtpv1u_ie : public stream_serializable {
public:
gtpv1u_tlv tlv;
gtpv1u_ie() :
tlv() {}
gtpv1u_ie(const gtpv1u_tlv& t) :
tlv(t) {}
gtpv1u_ie(const uint8_t tlv_type) :
tlv() {
tlv.type = tlv_type;
}
virtual ~gtpv1u_ie() {};
virtual void to_core_type(gtpv1u_ies_container& s) {throw gtpu_msg_illegal_ie_exception(s.msg_id, tlv.type);}
virtual void dump_to(std::ostream& os) {
tlv.dump_to(os);
};
virtual void load_from(std::istream& is) {throw gtpu_ie_unimplemented_exception(tlv.type); /* should not hapen of course*/};
static gtpv1u_ie * new_gtpv1u_ie_from_stream(std::istream& is);
};
//------------------------------------------------------------------------------
class gtpv1u_msg_header : public stream_serializable {
private:
#define GTPV1U_MSG_HEADER_MIN_SIZE 8
//-----------------------
// Always present fields:
//-----------------------
union {
struct {
// N-PDU Number flag (PN): This flag indicates the presence of a meaningful value of the N-PDU Number field.
// When it is set to '0', the N-PDU Number field either is not present, or, if present, shall not be interpreted. When it
// is set to '1', the N-PDU Number field is present, and shall be interpreted
uint8_t pn:1;
// Sequence number flag (S): This flag indicates the presence of a meaningful value of the Sequence Number field.
// When it is set to '0', the Sequence Number field either is not present or, if present, shall not be interpreted. When
// it is set to '1', the Sequence Number field is present, and shall be interpreted.
// For the Echo Request, Echo Response, Error Indication and Supported Extension Headers Notification
// messages, the S flag shall be set to '1'. Since the use of Sequence Numbers is optional for G-PDUs, the PGW,
// SGW, ePDG, eNodeB and TWAN should set the flag to '0'. However, when a G-PDU (T-PDU+header) is being
// relayed by the Indirect Data Forwarding for Inter RAT HO procedure, then if the received G-PDU has the S flag
// set to '1', then the relaying entity shall set S flag to '1' and forward the G-PDU (T-PDU+header). In an End
// marker message the S flag shall be set to '0'.
uint8_t s:1;
// Extension Header flag (E): This flag indicates the presence of a meaningful value of the Next Extension Header
// field. When it is set to '0', the Next Extension Header field either is not present or, if present, shall not be
// interpreted. When it is set to '1', the Next Extension Header field is present, and shall be interpreted
uint8_t e:1;
uint8_t spare:1;
// Protocol Type (PT): This bit is used as a protocol discriminator between GTP (when PT is '1') and GTP' (when PT is '0')
uint8_t pt:1;
// Version field: This field is used to determine the version of the GTP-U protocol. The version number shall be set to '1'.
uint8_t version:3;
} bf;
uint8_t b;
} u1;
// Message Type: This field indicates the type of GTP-U message.
uint8_t message_type;
// Length: This field indicates the length in octets of the payload, i.e. the rest of the packet following the mandatory
// part of the GTP header (that is the first 8 octets). The Sequence Number, the N-PDU Number or any Extension
// headers shall be considered to be part of the payload, i.e. included in the length count.
uint16_t message_length;
// Tunnel Endpoint Identifier (TEID): This field unambiguously identifies a tunnel endpoint in the receiving
// GTP-U protocol entity. The receiving end side of a GTP tunnel locally assigns the TEID value the transmitting
// side has to use. The TEID value shall be assigned in a non-predictable manner for PGW S5/S8/S2a/S2b
// interfaces (see 3GPP TS 33.250 [32]). The TEID shall be used by the receiving entity to find the PDP context,
// except for the following cases:
// -) The Echo Request/Response and Supported Extension Headers notification messages, where the Tunnel
// Endpoint Identifier shall be set to all zeroes
// -) The Error Indication message where the Tunnel Endpoint Identifier shall be set to all zeros.
uint32_t teid;
//------------------
// Optional fields:
//------------------
// Sequence Number: If Sequence Number field is used for G-PDUs (T-PDUs+headers), an increasing sequence
// number for T-PDUs is transmitted via GTP-U tunnels, when transmission order must be preserved. For
// Supported Extension Headers Notification and Error Indication messages, the Sequence Number shall be ignored
// by the receiver, even though the S flag is set to '1'.
uint16_t sequence_number;
// N-PDU Number: This field is used at the Inter SGSN Routeing Area Update procedure and some inter-system
// handover procedures (e.g. between 2G and 3G radio access networks). This field is used to co-ordinate the data
// transmission for acknowledged mode of communication between the MS and the SGSN. The exact meaning of
// this field depends upon the scenario. (For example, for GSM/GPRS to GSM/GPRS, the SNDCP N-PDU number
// is present in this field).
uint8_t npdu_number;
// Next Extension Header Type: This field defines the type of Extension Header that follows this field in the
// GTP-PDU.
uint8_t next_extension_header_type;
public:
gtpv1u_msg_header() {
u1.b = 0;
message_type = 0;
message_length = GTPV1U_MSG_HEADER_MIN_SIZE; //
teid = 0;
sequence_number = 0;
npdu_number = 0;
next_extension_header_type = 0;
u1.bf.version = 1;
}
gtpv1u_msg_header(const gtpv1u_msg_header& h) {
u1.b = h.u1.b;
message_type = h.message_type;
message_length = h.message_length;
teid = h.teid;
sequence_number = h.sequence_number;
npdu_number = h.npdu_number;
next_extension_header_type = h.next_extension_header_type;
}
void set_teid(const uint32_t& tid) {
teid = tid;
}
uint32_t get_teid() const {
return teid;
}
void set_message_type(const uint8_t& t) {
message_type = t;
}
uint8_t get_message_type() const {
return message_type;
}
void set_message_length(const uint16_t& l) {
message_length = l;
}
uint16_t get_message_length() const {
return message_length;
}
void set_sequence_number(const uint16_t& s) {
sequence_number = s;
}
uint16_t get_sequence_number() const {
return sequence_number;
}
virtual void dump_to(std::ostream& os) {
u1.bf.spare = 0;
os.write(reinterpret_cast<const char*>(&u1.b), sizeof(u1.b));
os.write(reinterpret_cast<const char*>(&message_type), sizeof(message_type));
auto be_message_length = htobe16(message_length);
os.write(reinterpret_cast<const char*>(&be_message_length), sizeof(be_message_length));
auto be_teid = htobe32(teid);
os.write(reinterpret_cast<const char*>(&be_teid), sizeof(be_teid));
if (u1.b & 0x07) {
auto be_sequence_number = htobe16(sequence_number);
os.write(reinterpret_cast<const char*>(&be_sequence_number), sizeof(be_sequence_number));
os.write(reinterpret_cast<const char*>(&npdu_number), sizeof(npdu_number));
os.write(reinterpret_cast<const char*>(&next_extension_header_type), sizeof(next_extension_header_type));
}
}
virtual void load_from(std::istream& is) {
// ! Control Plane GTP header length shall be a multiple of 4 octets.
is.read(reinterpret_cast<char*>(&u1.b), sizeof(u1.b));
is.read(reinterpret_cast<char*>(&message_type), sizeof(message_type));
is.read(reinterpret_cast<char*>(&message_length), sizeof(message_length));
message_length = be16toh(message_length);
if (u1.b & 0x07) {
is.read(reinterpret_cast<char*>(&sequence_number), sizeof(sequence_number));
sequence_number = be16toh(sequence_number);
is.read(reinterpret_cast<char*>(&npdu_number), sizeof(npdu_number));
is.read(reinterpret_cast<char*>(&next_extension_header_type), sizeof(next_extension_header_type));
}
}
};
//------------------------------------------------------------------------------
class gtpv1u_msg : public gtpv1u_msg_header
{
public:
uint16_t remote_port;
std::vector<std::shared_ptr<gtpv1u_ie>> ies;
gtpv1u_msg() : gtpv1u_msg_header(), remote_port(0), ies() {}
gtpv1u_msg(const gtpv1u_msg& m) : gtpv1u_msg_header(m),
remote_port(m.remote_port),
ies(m.ies) {}
gtpv1u_msg(const gtpv1u_msg_header& hdr) : gtpv1u_msg_header(hdr), remote_port(0), ies() {}
gtpv1u_msg(const gtpv1u_echo_request& gtp_ies);
gtpv1u_msg(const gtpv1u_echo_response& gtp_ies);
gtpv1u_msg(const gtpv1u_error_indication& gtp_ies);
gtpv1u_msg(const gtpv1u_supported_extension_headers_notification& gtp_ies);
gtpv1u_msg(const gtpv1u_end_marker& gtp_ies);
~gtpv1u_msg() {
ies.clear();
}
void add_ie(std::shared_ptr<gtpv1u_ie> ie) {
ies.push_back(ie);
//std::cout << std::dec<< " add_ie = " << get_message_length() << " -> "<< get_message_length() + gtpv1u_tlv::tlv_ie_length + ie.get()->tlv.get_length() << std::endl;
set_message_length(get_message_length() + gtpv1u_tlv::tlv_ie_length + ie.get()->tlv.get_length());
}
void to_core_type(gtpv1u_echo_request& s) {
for (auto i : ies) {
i.get()->to_core_type(s);
}
}
void to_core_type(gtpv1u_echo_response& s) {
for (auto i : ies) {
i.get()->to_core_type(s);
}
}
void to_core_type(gtpv1u_error_indication& s) {
for (auto i : ies) {
i.get()->to_core_type(s);
}
}
void to_core_type(gtpv1u_supported_extension_headers_notification& s) {
for (auto i : ies) {
i.get()->to_core_type(s);
}
}
void to_core_type(gtpv1u_end_marker& s) {
for (auto i : ies) {
i.get()->to_core_type(s);
}
}
void dump_to(std::ostream& os) {
gtpv1u_msg_header::dump_to(os);
for (auto i : ies) {
i.get()->dump_to(os);
}
}
void load_from(std::istream& is) {
gtpv1u_msg_header::load_from(is);
uint16_t check_msg_length = get_message_length(); // total length of message - fixed part of the gtpu header (GTPV1U_MSG_HEADER_MIN_SIZE)
gtpv1u_ie * ie = nullptr;
uint16_t ies_length = 0;
//std::cout << std::dec<< " check_msg_length = " << check_msg_length << std::endl;
do {
ie = gtpv1u_ie::new_gtpv1u_ie_from_stream(is);
if (ie) {
ies_length += (gtpv1u_tlv::tlv_ie_length + ie->tlv.get_length());
ies.push_back(std::shared_ptr<gtpv1u_ie>(ie));
//std::cout << std::dec << " ies length = " << ies_length << " IE length = " << ie->tlv.get_length() << std::endl;
}
} while((ie) && (ies_length < check_msg_length));
if (ies_length != check_msg_length) {
//std::cout << " check_msg_length = " << check_msg_length << " ies_length = " << ies_length << std::endl;
throw gtpu_msg_bad_length_exception(get_message_type(), get_message_length());
}
}
};
inline void ipv6_address_dump_to(std::ostream& os, const struct in6_addr& ipv6_address) {
os.write(reinterpret_cast<const char*>(ipv6_address.s6_addr), 16);
}
inline void ipv6_address_load_from(std::istream& is, struct in6_addr& ipv6_address) {
is.read(reinterpret_cast<char*>(ipv6_address.s6_addr), 16);
}
inline void ipv4_address_dump_to(std::ostream& os, const struct in_addr& ipv4_address) {
os.write(reinterpret_cast<const char*>(&ipv4_address.s_addr), sizeof(ipv4_address.s_addr));
}
inline void ipv4_address_load_from(std::istream& is, struct in_addr& ipv4_address) {
is.read(reinterpret_cast<char*>(&ipv4_address.s_addr), sizeof(ipv4_address.s_addr));
}
//------------------------------------------------------------------------------
class gtpv1u_recovery_ie : public gtpv1u_ie {
public:
uint8_t restart_counter;
//--------
gtpv1u_recovery_ie(const core::recovery_t& i) : gtpv1u_ie(GTPU_IE_RECOVERY) {
// avoid using b[]
restart_counter = i.restart_counter;
tlv.set_length(1);
};
//--------
gtpv1u_recovery_ie() : gtpv1u_ie(GTPU_IE_RECOVERY) {
restart_counter = 0;
tlv.set_length(1);
};
//--------
gtpv1u_recovery_ie(const gtpv1u_tlv& t) : gtpv1u_ie(t) {
restart_counter = 0;
};
//--------
void to_core_type(core::recovery_t& i) {
i.restart_counter = restart_counter;
};
//--------
void dump_to(std::ostream& os) {
tlv.dump_to(os);
os.write(reinterpret_cast<const char*>(&restart_counter), sizeof(restart_counter));
}
//--------
void load_from(std::istream& is) {
//tlv.load_from(is);
is.read(reinterpret_cast<char*>(&restart_counter), sizeof(restart_counter));
}
void to_core_type(gtpv1u_ies_container& s) {
core::recovery_t v = {};
to_core_type(v);
s.set(v);
}
};
//------------------------------------------------------------------------------
class gtpv1u_tunnel_endpoint_identifier_data_i_ie : public gtpv1u_ie {
public:
uint32_t tunnel_endpoint_identifier_data_i;
//--------
gtpv1u_tunnel_endpoint_identifier_data_i_ie(const core::tunnel_endpoint_identifier_data_i_t& i) : gtpv1u_ie(GTPU_IE_TUNNEL_ENDPOINT_IDENTIFIER_DATA_I) {
tunnel_endpoint_identifier_data_i = i.tunnel_endpoint_identifier_data_i;
}
//--------
gtpv1u_tunnel_endpoint_identifier_data_i_ie() : gtpv1u_ie(GTPU_IE_TUNNEL_ENDPOINT_IDENTIFIER_DATA_I) {
tunnel_endpoint_identifier_data_i = 0;
};
//--------
gtpv1u_tunnel_endpoint_identifier_data_i_ie(const gtpv1u_tlv& t) : gtpv1u_ie(t) {
tunnel_endpoint_identifier_data_i = 0;
};
void to_core_type(core::tunnel_endpoint_identifier_data_i_t& c) {
c.tunnel_endpoint_identifier_data_i = tunnel_endpoint_identifier_data_i;
}
//--------
void dump_to(std::ostream& os) {
tlv.dump_to(os);
auto be_tunnel_endpoint_identifier_data_i = htobe32(tunnel_endpoint_identifier_data_i);
os.write(reinterpret_cast<const char*>(&be_tunnel_endpoint_identifier_data_i), sizeof(be_tunnel_endpoint_identifier_data_i));
}
//--------
void load_from(std::istream& is) {
//tlv.load_from(is);
is.read(reinterpret_cast<char*>(&tunnel_endpoint_identifier_data_i), sizeof(tunnel_endpoint_identifier_data_i));
tunnel_endpoint_identifier_data_i = be32toh(tunnel_endpoint_identifier_data_i);
}
void to_core_type(gtpv1u_ies_container& s) {
core::tunnel_endpoint_identifier_data_i_t v = {};
to_core_type(v);
s.set(v);
}
};
//------------------------------------------------------------------------------
class gtpv1u_gtp_u_peer_address_ie : public gtpv1u_ie {
public:
bool is_v4;
struct in6_addr ipv6_address;
struct in_addr ipv4_address;
//--------
gtpv1u_gtp_u_peer_address_ie(const core::gtp_u_peer_address_t& p) :
gtpv1u_ie(GTPU_IE_GTP_U_PEER_ADDRESS){
is_v4 = p.is_v4;
if (is_v4) {
ipv4_address = p.ipv4_address;
tlv.set_length(4);
} else {
ipv6_address = p.ipv6_address;
tlv.set_length(16);
}
}
//--------
gtpv1u_gtp_u_peer_address_ie() : gtpv1u_ie(GTPU_IE_GTP_U_PEER_ADDRESS){
is_v4 = true;
ipv4_address.s_addr = INADDR_ANY;
ipv6_address = in6addr_any;
tlv.set_length(4);
}
//--------
gtpv1u_gtp_u_peer_address_ie(const gtpv1u_tlv& t) : gtpv1u_ie(t) {
is_v4 = true;
ipv4_address.s_addr = INADDR_ANY;
ipv6_address = in6addr_any;
};
//--------
void to_core_type(core::gtp_u_peer_address_t& p) {
if (is_v4) {
p.ipv4_address = ipv4_address;
} else {
p.ipv6_address = ipv6_address;
}
}
//--------
void dump_to(std::ostream& os) {
if (is_v4) {
tlv.set_length(4);
} else {
tlv.set_length(16);
}
tlv.dump_to(os);
if (is_v4) {
ipv4_address_dump_to(os, ipv4_address);
} else {
ipv6_address_dump_to(os, ipv6_address);
}
}
//--------
void load_from(std::istream& is) {
//tlv.load_from(is);
if (tlv.get_length() == 4) {
ipv4_address_load_from(is, ipv4_address);
} else if (tlv.get_length() == 16) {
ipv6_address_load_from(is, ipv6_address);
} else {
throw gtpu_tlv_bad_length_exception(GTPU_IE_GTP_U_PEER_ADDRESS, tlv.length);
}
}
//--------
void to_core_type(gtpv1u_ies_container& s) {
core::gtp_u_peer_address_t v = {};
to_core_type(v);
s.set(v);
}
};
//------------------------------------------------------------------------------
class gtpv1u_private_extension_ie : public gtpv1u_ie {
public:
uint16_t extension_identifier;
std::string extension_value;
//--------
gtpv1u_private_extension_ie(const core::private_extension_t& i) : gtpv1u_ie(GTPU_IE_PRIVATE_EXTENSION),
extension_identifier(i.enterprise_id),
extension_value(i.proprietary_value)
{
tlv.set_length(sizeof(extension_identifier) + extension_value.size());
}
//--------
gtpv1u_private_extension_ie() : gtpv1u_ie(GTPU_IE_PRIVATE_EXTENSION),
extension_identifier(0),
extension_value()
{
tlv.set_length(sizeof(extension_identifier));
};
//--------
gtpv1u_private_extension_ie(const gtpv1u_tlv& t) : gtpv1u_ie(t),
extension_identifier(0),
extension_value() {}
void to_core_type(core::private_extension_t& c) {
c.enterprise_id = extension_identifier;
c.proprietary_value = extension_value;
}
//--------
void dump_to(std::ostream& os) {
tlv.set_length(sizeof(extension_identifier) + extension_value.size());
tlv.dump_to(os);
auto be_extension_identifier = htobe16(extension_identifier);
os.write(reinterpret_cast<const char*>(&be_extension_identifier), sizeof(be_extension_identifier));
os << extension_value;
}
//--------
void load_from(std::istream& is) {
//tlv.load_from(is);
is.read(reinterpret_cast<char*>(&extension_identifier), sizeof(extension_identifier));
extension_identifier = be16toh(extension_identifier);
uint16_t length = tlv.get_length();
if (length > 2) {
char apn[length - 2];
is.read(apn, length - 2);
extension_value.assign(apn, length - 2);
} else {
extension_value = {};
}
}
void to_core_type(gtpv1u_ies_container& s) {
core::private_extension_t v = {};
to_core_type(v);
s.set(v);
}
};
} // namespace gtpv1u
#endif /* FILE_3GPP_29_281_HPP_SEEN */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file gtpu.h
* \brief
* \author Lionel Gauthier
* \company Eurecom
* \email: lionel.gauthier@eurecom.fr
*/
#ifndef FILE_GTPU_SEEN
#define FILE_GTPU_SEEN
#include <endian.h>
#include <stdint.h>
struct gtpuhdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int pn:1;
unsigned int s:1;
unsigned int e:1;
unsigned int spare:1;
unsigned int pt:1;
unsigned int version:3;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:3;
unsigned int pt:1;
unsigned int spare:1;
unsigned int e:1;
unsigned int s:1;
unsigned int pn:1;
#else
# error "Please fix <bits/endian.h>"
#endif
// Message Type: This field indicates the type of GTP-U message.
uint8_t message_type;
// Length: This field indicates the length in octets of the payload, i.e. the rest of the packet following the mandatory
// part of the GTP header (that is the first 8 octets). The Sequence Number, the N-PDU Number or any Extension
// headers shall be considered to be part of the payload, i.e. included in the length count.
uint16_t message_length;
// Tunnel Endpoint Identifier (TEID): This field unambiguously identifies a tunnel endpoint in the receiving
// GTP-U protocol entity. The receiving end side of a GTP tunnel locally assigns the TEID value the transmitting
// side has to use. The TEID value shall be assigned in a non-predictable manner for PGW S5/S8/S2a/S2b
// interfaces (see 3GPP TS 33.250 [32]). The TEID shall be used by the receiving entity to find the PDP context,
// except for the following cases:
// -) The Echo Request/Response and Supported Extension Headers notification messages, where the Tunnel
// Endpoint Identifier shall be set to all zeroes
// -) The Error Indication message where the Tunnel Endpoint Identifier shall be set to all zeros.
uint32_t teid;
/*The options start here. */
};
#endif
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file gtpv1u.cpp
\brief
\author Lionel Gauthier
\company Eurecom
\email: lionel.gauthier@eurecom.fr
*/
#include "gtpu.h"
#include "gtpv1u.hpp"
#include <cstdlib>
#include <sched.h>
using namespace oai::cn::proto::gtpv1u;
using namespace oai::cn::core::itti;
using namespace std;
extern itti_mw *itti_inst;
uint64_t oai::cn::proto::gtpv1u::gtpu_l4_stack::gtpu_tx_id_generator = 1; //odd in any case.
//------------------------------------------------------------------------------
static std::string string_to_hex(const std::string& input)
{
static const char* const lut = "0123456789ABCDEF";
size_t len = input.length();
std::string output;
output.reserve(2 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
return output;
}
//------------------------------------------------------------------------------
void udp_server::udp_read_loop(int cpu)
{
socklen_t r_endpoint_addr_len = 0;
struct sockaddr_storage r_endpoint = {};
size_t bytes_received = 0;
if (cpu) {
cpu_set_t cpuset;
CPU_SET(cpu,&cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
int policy;
struct sched_param sparam;
memset(&sparam, 0, sizeof(sparam));
sparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
policy = SCHED_FIFO ;
pthread_setschedparam(pthread_self(), policy, &sparam);
while (1) {
r_endpoint_addr_len = sizeof(struct sockaddr_storage);
if ((bytes_received = recvfrom (socket_, recv_buffer_, UDP_RECV_BUFFER_SIZE, 0, (struct sockaddr *)&r_endpoint, &r_endpoint_addr_len)) > 0) {
app_->handle_receive(recv_buffer_, bytes_received, r_endpoint, r_endpoint_addr_len);
} else {
Logger::udp().error( "Recvfrom failed %s\n", strerror (errno));
}
}
}
//------------------------------------------------------------------------------
int udp_server::create_socket (const struct in_addr &address, const uint16_t port)
{
struct sockaddr_in addr = {};
int sd = 0;
/*
* Create UDP socket
*/
if ((sd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
/*
* Socket creation has failed...
*/
Logger::udp().error( "Socket creation failed (%s)\n", strerror (errno));
return errno;
}
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_addr.s_addr = address.s_addr;
std::string ipv4 = oai::cn::core::toString(address);
Logger::udp().debug("Creating new listen socket on address %s and port %" PRIu16 "\n", ipv4.c_str(), port);
if (bind (sd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) < 0) {
/*
* Bind failed
*/
Logger::udp().error("Socket bind failed (%s) for address %s and port %" PRIu16 "\n", strerror (errno), ipv4.c_str(), port);
close (sd);
return errno;
}
return sd;
}
//------------------------------------------------------------------------------
int udp_server::create_socket (const struct in6_addr &address, const uint16_t port)
{
struct sockaddr_in6 addr = {};
int sd = 0;
/*
* Create UDP socket
*/
if ((sd = socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
/*
* Socket creation has failed...
*/
Logger::udp().error( "Socket creation failed (%s)\n", strerror (errno));
return errno;
}
addr.sin6_family = AF_INET6;
addr.sin6_port = htons (port);
addr.sin6_addr = address;
std::string ipv6 = oai::cn::core::toString(address);
Logger::udp().debug("Creating new listen socket on address %s and port %" PRIu16 "\n", ipv6.c_str(), port);
if (bind (sd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in6)) < 0) {
/*
* Bind failed
*/
Logger::udp().error("Socket bind failed (%s) for address %s and port %" PRIu16 "\n", strerror (errno), ipv6.c_str(), port);
close (sd);
return errno;
}
return sd;
}
//------------------------------------------------------------------------------
int udp_server::create_socket (char * address, const uint16_t port_num)
{
unsigned char buf_in_addr[sizeof(struct in6_addr)];
if (inet_pton (AF_INET, address, buf_in_addr) == 1) {
struct in_addr addr4 = {};
memcpy (&addr4, buf_in_addr, sizeof (struct in_addr));
return create_socket(addr4, port_num);
} else if (inet_pton (AF_INET6, address, buf_in_addr) == 1) {
struct in6_addr addr6 = {};
memcpy (&addr6, buf_in_addr, sizeof (struct in6_addr));
return create_socket(addr6, port_num);
} else {
Logger::udp().error( "udp_server::create_socket(%s:%d)", address, port_num);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
throw std::system_error(socket_, std::generic_category(), "UDP socket creation failed!");
}
}
//------------------------------------------------------------------------------
void udp_server::start_receive(gtpu_l4_stack * gtp_stack, const int cpu)
{
app_ = gtp_stack;
Logger::udp().trace( "udp_server::start_receive");
thread_ = thread(&udp_server::udp_read_loop,this, cpu);
thread_.detach();
}
////------------------------------------------------------------------------------
//void udp_server::handle_receive(const int& error, std::size_t bytes_transferred)
//{
// if (!error) {
// Logger::udp().trace( "udp_server::handle_receive on %s:%d from %s:%d",
// socket_.local_endpoint().address().to_string().c_str(), socket_.local_endpoint().port(),
// remote_endpoint_.address().to_string().c_str(), remote_endpoint_.port());
// if (app_) {
// app_->handle_receive(recv_buffer_.data(), bytes_transferred, remote_endpoint_);
// } else {
// Logger::udp().error( "No upper layer configured for handling UDP packet");
// }
// start_receive(app_);
// } else {
// Logger::udp().error( "udp_server::handle_receive err=%s/%d: %s", error.category().name(), error.value(), error.message());
// }
//}
//------------------------------------------------------------------------------
gtpu_l4_stack::gtpu_l4_stack(const struct in_addr& address, const uint16_t port_num, const int cpu) :
udp_s(udp_server(address, port_num))
{
Logger::gtpv1_u().info( "gtpu_l4_stack created listening to %s:%d", oai::cn::core::toString(address).c_str(), port_num);
id = 0;
srand (time(NULL));
seq_num = rand() & 0x7FFFFFFF;
restart_counter = 0;
udp_s.start_receive(this, cpu);
}
//------------------------------------------------------------------------------
gtpu_l4_stack::gtpu_l4_stack(const struct in6_addr& address, const uint16_t port_num, const int cpu) :
udp_s(udp_server(address, port_num))
{
Logger::gtpv1_u().info( "gtpu_l4_stack created listening to %s:%d", oai::cn::core::toString(address).c_str(), port_num);
id = 0;
srand (time(NULL));
seq_num = rand() & 0x7FFFFFFF;
restart_counter = 0;
udp_s.start_receive(this, cpu);
}
//------------------------------------------------------------------------------
gtpu_l4_stack::gtpu_l4_stack(char * address, const uint16_t port_num, const int cpu) :
udp_s(udp_server(address, port_num))
{
Logger::gtpv1_u().info( "gtpu_l4_stack created listening to %s:%d", address, port_num);
id = 0;
srand (time(NULL));
seq_num = rand() & 0x7FFFFFFF;
restart_counter = 0;
udp_s.start_receive(this, cpu);
}
//------------------------------------------------------------------------------
uint32_t gtpu_l4_stack::get_next_seq_num() {
seq_num++;
if (seq_num & 0x80000000) {
seq_num = 0;
}
return seq_num;
}
//------------------------------------------------------------------------------
void gtpu_l4_stack::handle_receive(char* recv_buffer, const std::size_t bytes_transferred, const struct sockaddr_storage& r_endpoint, const socklen_t& r_endpoint_addr_len)
{
Logger::gtpv1_u().error( "TODO implement in derived class");
}
//------------------------------------------------------------------------------
bool gtpu_l4_stack::check_initial_message_type(const uint8_t initial)
{
switch (initial) {
case GTPU_ECHO_REQUEST:
case GTPU_END_MARKER:
return true;
break;
default:
return false;
}
}
//------------------------------------------------------------------------------
bool gtpu_l4_stack::check_triggered_message_type(const uint8_t initial, const uint8_t triggered)
{
Logger::gtpv1_u().info( "check_triggered_message_type GTPV1-U msg type %d/%d", (int)initial, (int)triggered);
switch (initial) {
case GTPU_ECHO_REQUEST:
if (triggered == GTPU_ECHO_RESPONSE) return true;
return false;
break;
case GTPU_ERROR_INDICATION:
case GTPU_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION:
return true;
break;
default:
return false;
}
}
//------------------------------------------------------------------------------
void gtpu_l4_stack::handle_receive_message_cb(const gtpv1u_msg& msg, const struct sockaddr_storage& r_endpoint, const socklen_t& r_endpoint_addr_len, const task_id_t& task_id, bool &error, uint64_t& gtpc_tx_id)
{
}
//------------------------------------------------------------------------------
void gtpu_l4_stack::send_g_pdu(const struct sockaddr_in& peer_addr, const teid_t teid, const char* payload, const ssize_t payload_len)
{
struct gtpuhdr *gtpuhdr = reinterpret_cast<struct gtpuhdr *>(reinterpret_cast<uintptr_t>(payload) - (uintptr_t)sizeof(struct gtpuhdr));
gtpuhdr->spare = 0;
gtpuhdr->e = 0;
gtpuhdr->s = 0;
gtpuhdr->pn = 0;
gtpuhdr->pt = 1;
gtpuhdr->version = 1;
gtpuhdr->message_type = GTPU_G_PDU;
gtpuhdr->message_length = htobe16(payload_len);
gtpuhdr->teid = htobe32(teid);
udp_s.async_send_to(reinterpret_cast<const char*>(gtpuhdr), payload_len + sizeof(struct gtpuhdr), peer_addr);
}
//------------------------------------------------------------------------------
void gtpu_l4_stack::send_g_pdu(const struct sockaddr_in6& peer_addr, const teid_t teid, const char* payload, const ssize_t payload_len)
{
struct gtpuhdr *gtpuhdr = reinterpret_cast<struct gtpuhdr *>(reinterpret_cast<uintptr_t>(payload) - (uintptr_t)sizeof(struct gtpuhdr));
gtpuhdr->spare = 0;
gtpuhdr->e = 0;
gtpuhdr->s = 0;
gtpuhdr->pn = 0;
gtpuhdr->pt = 1;
gtpuhdr->version = 1;
gtpuhdr->message_type = GTPU_G_PDU;
gtpuhdr->message_length = htobe16(payload_len);
gtpuhdr->teid = htobe32(teid);
udp_s.async_send_to(reinterpret_cast<const char*>(gtpuhdr), payload_len + sizeof(struct gtpuhdr), peer_addr);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file gtpv1u.hpp
\brief
\author Lionel Gauthier
\company Eurecom
\email: lionel.gauthier@eurecom.fr
*/
#ifndef FILE_GTPV1U_HPP_SEEN
#define FILE_GTPV1U_HPP_SEEN
#include "3gpp_29.281.hpp"
#include "itti.hpp"
#include "msg_gtpv1u.hpp"
#include <iostream>
#include <map>
#include <memory>
#include <stdint.h>
#include <string>
#include <system_error>
#include <thread>
#include <utility>
#include <vector>
namespace oai::cn::proto::gtpv1u {
class gtpu_l4_stack;
class udp_server
{
public:
udp_server(const struct in_addr& address, const uint16_t port_num)
: app_(nullptr), port_(port_num)
{
socket_ = create_socket (address, port_);
if (socket_ > 0) {
Logger::udp().debug( "udp_server::udp_server(%s:%d)", oai::cn::core::toString(address).c_str(), port_);
} else {
Logger::udp().error( "udp_server::udp_server(%s:%d)", oai::cn::core::toString(address).c_str(), port_);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
throw std::system_error(socket_, std::generic_category(), "GTPV1-U socket creation failed!");
}
}
udp_server(const struct in6_addr& address, const uint16_t port_num)
: app_(nullptr), port_(port_num)
{
socket_ = create_socket (address, port_);
if (socket_ > 0) {
Logger::udp().debug( "udp_server::udp_server(%s:%d)", oai::cn::core::toString(address).c_str(), port_);
} else {
Logger::udp().error( "udp_server::udp_server(%s:%d)", oai::cn::core::toString(address).c_str(), port_);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
throw std::system_error(socket_, std::generic_category(), "GTPV1-U socket creation failed!");
}
}
udp_server(char * address, const uint16_t port_num)
: app_(nullptr), port_(port_num)
{
socket_ = create_socket (address, port_);
if (socket_ > 0) {
Logger::udp().debug( "udp_server::udp_server(%s:%d)", address, port_);
} else {
Logger::udp().error( "udp_server::udp_server(%s:%d)", address, port_);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
throw std::system_error(socket_, std::generic_category(), "GTPV1-U socket creation failed!");
}
}
~udp_server()
{
close(socket_);
}
void udp_read_loop(int cpu_id);
void async_send_to(const char* send_buffer, const ssize_t num_bytes, const struct sockaddr_in& peer_addr)
{
//Logger::udp().trace( "udp_server::async_send_to(%s:%d) %d bytes", oai::cn::core::toString(peer_addr.sin_addr).c_str(), peer_addr.sin_port, num_bytes);
ssize_t bytes_written = sendto (socket_, send_buffer, num_bytes, 0, (struct sockaddr *)&peer_addr, sizeof (struct sockaddr_in));
if (bytes_written != num_bytes) {
Logger::udp().error( "sendto failed(%d:%s)\n", errno, strerror (errno));
}
}
void async_send_to(const char* send_buffer, const ssize_t num_bytes, const struct sockaddr_in6& peer_addr)
{
ssize_t bytes_written = sendto (socket_, send_buffer, num_bytes, 0, (struct sockaddr *)&peer_addr, sizeof (struct sockaddr_in6));
if (bytes_written != num_bytes) {
Logger::udp().error( "sendto failed(%d:%s)\n", errno, strerror (errno));
}
}
void start_receive(gtpu_l4_stack * gtp_stack, const int cpu);
protected:
int create_socket (const struct in_addr& address, const uint16_t port);
int create_socket (const struct in6_addr& address, const uint16_t port);
int create_socket (char * address, const uint16_t port_num);
// void handle_receive(const int& error, std::size_t bytes_transferred);
void handle_send(const char *, /*buffer*/
const int& /*error*/,
std::size_t /*bytes_transferred*/)
{
}
gtpu_l4_stack* app_;
std::thread thread_;
int socket_;
uint16_t port_;
#define UDP_RECV_BUFFER_SIZE 8192
char recv_buffer_[UDP_RECV_BUFFER_SIZE];
};
class gtpu_l4_stack {
#define GTPV1U_T3_RESPONSE_MS 1000
#define GTPV1U_N3_REQUESTS 5
#define GTPV1U_PROC_TIME_OUT_MS ((GTPV1U_T3_RESPONSE_MS) * (GTPV1U_N3_REQUESTS + 1))
protected:
static uint64_t gtpu_tx_id_generator;
uint32_t id;
udp_server udp_s;
// seems no need for std::atomic_uint32_t
uint32_t seq_num;
uint32_t restart_counter;
boost::array<char, 4096> send_buffer;
static bool check_initial_message_type(const uint8_t initial);
static bool check_triggered_message_type(const uint8_t initial, const uint8_t triggered);
uint32_t get_next_seq_num();
public:
static const uint8_t version = 1;
gtpu_l4_stack(const struct in_addr& address, const uint16_t port_num, const int cpu);
gtpu_l4_stack(const struct in6_addr& address, const uint16_t port_num, const int cpu);
gtpu_l4_stack(char * ip_address, const uint16_t port_num, const int cpu);
virtual void handle_receive(char* recv_buffer, const std::size_t bytes_transferred, const struct sockaddr_storage& r_endpoint, const socklen_t& r_endpoint_addr_len);
void handle_receive_message_cb(const gtpv1u_msg& msg, const struct sockaddr_storage& r_endpoint, const socklen_t& r_endpoint_addr_len, const core::itti::task_id_t& task_id, bool &error, uint64_t& gtpc_tx_id);
void send_g_pdu(const struct sockaddr_in& peer_addr, const teid_t teid, const char* payload, const ssize_t payload_len);
void send_g_pdu(const struct sockaddr_in6& peer_addr, const teid_t teid, const char* payload, const ssize_t payload_len);
//virtual uint32_t send_request(const boost::asio::ip::udp::endpoint& dest, const teid_t teid, const gtpv1u_create_session_request& gtp_ies, const core::itti::task_id_t& task_id, const uint64_t gtp_tx_id);
//virtual void send_response(const boost::asio::ip::udp::endpoint& dest, const teid_t teid, const gtpv1u_create_session_response& gtp_ies, const uint64_t gtp_tx_id);
//virtual uint32_t send_gpdu(const boost::asio::ip::udp::endpoint& dest, const teid_t teid, const gtpv1u_create_session_request& gtp_ies, const core::itti::task_id_t& task_id, const uint64_t gtp_tx_id);
};
} // namespace gtpv1u
#endif /* FILE_GTPV1U_HPP_SEEN */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file msg_gtpv1u.hpp
\brief
\author Sebastien ROUX, Lionel Gauthier
\company Eurecom
\email: lionel.gauthier@eurecom.fr
*/
#ifndef MSG_GTPV1U_HPP_INCLUDED_
#define MSG_GTPV1U_HPP_INCLUDED_
#include "3gpp_29.274.h"
#include "3gpp_29.281.h"
#include "common_defs.h"
#include <vector>
namespace oai::cn::proto::gtpv1u {
class gtpv1u_ies_container {
public:
static const uint8_t msg_id = 0;
virtual bool get(core::recovery_t& v) const {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_RECOVERY);}
virtual bool get(core::tunnel_endpoint_identifier_data_i_t& v) const {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_TUNNEL_ENDPOINT_IDENTIFIER_DATA_I);}
virtual bool get(core::gtp_u_peer_address_t& v) const {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_GTP_U_PEER_ADDRESS);}
virtual bool get(core::extension_header_type_list_t& v) const {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_EXTENSION_HEADER_TYPE_LIST);}
virtual bool get(core::private_extension_t& v) const {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_PRIVATE_EXTENSION);}
virtual void set(const core::recovery_t& v) {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_RECOVERY);}
virtual void set(const core::tunnel_endpoint_identifier_data_i_t& v) {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_TUNNEL_ENDPOINT_IDENTIFIER_DATA_I);}
virtual void set(const core::gtp_u_peer_address_t& v) {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_GTP_U_PEER_ADDRESS);}
virtual void set(const core::extension_header_type_list_t& v) {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_EXTENSION_HEADER_TYPE_LIST);}
virtual void set(const core::private_extension_t& v) {throw gtpu_msg_illegal_ie_exception(0, GTPU_IE_PRIVATE_EXTENSION);}
virtual ~gtpv1u_ies_container() {};
};
//------------------------------------------------------------------------------
class gtpv1u_echo_request : public gtpv1u_ies_container {
public:
static const uint8_t msg_id = GTPU_ECHO_REQUEST;
std::pair<bool, core::private_extension_t> private_extension;
gtpv1u_echo_request(): private_extension() {}
gtpv1u_echo_request(const gtpv1u_echo_request& i) : private_extension(i.private_extension) {}
const char* get_msg_name() const {return "GTPU_ECHO_REQUEST";};
bool get(core::private_extension_t& v) const {if (private_extension.first) {v = private_extension.second;return true;}return false;}
void set(const core::private_extension_t& v) {private_extension.first = true; private_extension.second = v;}
};
//------------------------------------------------------------------------------
class gtpv1u_echo_response : public gtpv1u_ies_container {
public:
static const uint8_t msg_id = GTPU_ECHO_RESPONSE;
std::pair<bool, core::recovery_t> recovery;
std::pair<bool, core::private_extension_t> private_extension;
gtpv1u_echo_response(): recovery(), private_extension() {}
gtpv1u_echo_response(const gtpv1u_echo_response& i): recovery(i.recovery), private_extension(i.private_extension) {}
const char* get_msg_name() const {return "GTPU_ECHO_RESPONSE";};
bool get(core::recovery_t& v) const {if (recovery.first) {v = recovery.second;return true;}return false;}
bool get(core::private_extension_t& v) const {if (private_extension.first) {v = private_extension.second;return true;}return false;}
void set(const core::recovery_t& v) {recovery.first = true; recovery.second = v;}
void set(const core::private_extension_t& v) {private_extension.first = true; private_extension.second = v;}
};
//------------------------------------------------------------------------------
class gtpv1u_error_indication : public gtpv1u_ies_container {
public:
static const uint8_t msg_id = GTPU_ERROR_INDICATION;
std::pair<bool, core::tunnel_endpoint_identifier_data_i_t> tunnel_endpoint_identifier_data_i;
std::pair<bool, core::gtp_u_peer_address_t> gtp_u_peer_address;
std::pair<bool, core::private_extension_t> private_extension;
gtpv1u_error_indication(): tunnel_endpoint_identifier_data_i(), gtp_u_peer_address(), private_extension() {}
gtpv1u_error_indication(const gtpv1u_error_indication& i) :
tunnel_endpoint_identifier_data_i(i.tunnel_endpoint_identifier_data_i),
gtp_u_peer_address(i.gtp_u_peer_address),
private_extension(i.private_extension) {}
const char* get_msg_name() const {return "GTPU_ERROR_INDICATION";};
bool get(core::tunnel_endpoint_identifier_data_i_t& v) const {if (tunnel_endpoint_identifier_data_i.first) {v = tunnel_endpoint_identifier_data_i.second;return true;}return false;}
bool get(core::gtp_u_peer_address_t& v) const {if (gtp_u_peer_address.first) {v = gtp_u_peer_address.second;return true;}return false;}
bool get(core::private_extension_t& v) const {if (private_extension.first) {v = private_extension.second;return true;}return false;}
void set(const core::tunnel_endpoint_identifier_data_i_t& v) {tunnel_endpoint_identifier_data_i.first = true; tunnel_endpoint_identifier_data_i.second = v;}
void set(const core::gtp_u_peer_address_t& v) {gtp_u_peer_address.first = true; gtp_u_peer_address.second = v;}
void set(const core::private_extension_t& v) {private_extension.first = true; private_extension.second = v;}
};
//------------------------------------------------------------------------------
class gtpv1u_supported_extension_headers_notification : public gtpv1u_ies_container {
public:
static const uint8_t msg_id = GTPU_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION;
std::pair<bool, core::extension_header_type_list_t> extension_header_type_list;
gtpv1u_supported_extension_headers_notification() :
extension_header_type_list() {}
gtpv1u_supported_extension_headers_notification(const gtpv1u_supported_extension_headers_notification& i) :
extension_header_type_list(i.extension_header_type_list) {}
const char* get_msg_name() const {return "GTPU_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION";};
bool get(core::extension_header_type_list_t& v) const {if (extension_header_type_list.first) {v = extension_header_type_list.second;return true;}return false;}
void set(const core::extension_header_type_list_t& v) {extension_header_type_list.first = true; extension_header_type_list.second = v;}
};
//------------------------------------------------------------------------------
class gtpv1u_end_marker : public gtpv1u_ies_container {
public:
static const uint8_t msg_id = GTPU_END_MARKER;
std::pair<bool, core::private_extension_t> private_extension;
gtpv1u_end_marker() :
private_extension() {}
gtpv1u_end_marker(const gtpv1u_error_indication& i) :
private_extension(i.private_extension) {}
const char* get_msg_name() const {return "GTPU_END_MARKER";};
bool get(core::private_extension_t& v) const {if (private_extension.first) {v = private_extension.second;return true;}return false;}
void set(const core::private_extension_t& v) {private_extension.first = true; private_extension.second = v;}
};
} // namespace gtpv1u
#endif /* MSG_GTPV1U_HPP_INCLUDED_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment