Commit f70921a9 authored by Tien Thinh NGUYEN's avatar Tien Thinh NGUYEN

Code cleanup of Payload container

parent 1a8eb820
......@@ -164,6 +164,7 @@ static const std::vector<std::string> nas_ciphering_algorithm_list_e2str = {
/********************* UL NAS TRANSPORT payload container type
* **************************/
// TODO: define enum class
#define N1_SM_INFORMATION 0x01
#define SMS_CONTAINER 0x02
#define LTE_POSITIONING_PROTOCOL 0x03
......
......@@ -83,4 +83,5 @@ constexpr uint8_t kIeiSscMode = 0x0a;
constexpr uint8_t kIeiSuggestedInterfaceIdentifier = 0x29;
constexpr uint8_t kIeiUeDsTtResidenceTime = 0x6f;
constexpr uint8_t kIeiEpsNasMessageContainer = 0x70;
constexpr uint8_t kIeiEpsNasMessageContainer = 0x70; // OK
constexpr uint8_t kIeiPayloadContainerType = 0x08; // Should be verified
......@@ -19,56 +19,53 @@
* contact@openairinterface.org
*/
/*! \file
\brief
\author Keliang DU, BUPT
\date 2020
\email: contact@openairinterface.org
*/
#include "Payload_Container.hpp"
#include "3gpp_24.501.hpp"
#include "common_defs.h"
#include "logger.hpp"
using namespace nas;
//------------------------------------------------------------------------------
Payload_Container::Payload_Container(uint8_t iei) : content() {
Payload_Container::Payload_Container(uint8_t iei) {
_iei = iei;
length = 0;
CONTENT = {};
content = std::nullopt;
CONTENT = std::nullopt;
}
//------------------------------------------------------------------------------
Payload_Container::Payload_Container(uint8_t iei, bstring b) {
_iei = iei;
content = b;
CONTENT = {};
content = std::optional<bstring>(b);
CONTENT = std::nullopt;
length = 1 + blength(b);
}
//------------------------------------------------------------------------------
Payload_Container::Payload_Container(
const uint8_t iei, std::vector<PayloadContainerEntry> content) {
_iei = iei;
const uint8_t iei, std::vector<PayloadContainerEntry> contents) {
_iei = iei;
content = std::nullopt;
if (_iei) {
length = 4 + content.size() * 2;
length = 4; // 1 for IEI, 2 for length, 1 for number of entries
} else {
length = 3 + content.size() * 2;
length = 3; // 2 for length, 1 for number of entries
}
CONTENT.assign(content.begin(), content.end());
for (int i = 0; i < content.size(); i++) {
length = length + content.at(i).optionalIE.size() * 2;
for (int j = 0; j < content.at(i).optionalIE.size(); j++) {
length += blength(content.at(i).optionalIE.at(j).ie_value);
}
// CONTENT.assign(content.begin(), content.end());
CONTENT = std::optional<std::vector<PayloadContainerEntry>>(contents);
for (int i = 0; i < contents.size(); i++) {
length = length + 1 + contents.at(i).length;
}
}
//------------------------------------------------------------------------------
Payload_Container::Payload_Container() : content() {
Payload_Container::Payload_Container() {
_iei = 0;
length = 0;
CONTENT = {};
content = std::nullopt;
CONTENT = std::nullopt;
}
//------------------------------------------------------------------------------
......@@ -77,140 +74,123 @@ Payload_Container::~Payload_Container() {}
//------------------------------------------------------------------------------
void Payload_Container::setValue(uint8_t iei, uint8_t value) {
_iei = iei;
//_value = value;
}
//------------------------------------------------------------------------------
void Payload_Container::getValue(std::vector<PayloadContainerEntry>& content) {
content.assign(CONTENT.begin(), CONTENT.end());
if (CONTENT.has_value())
content.assign(CONTENT.value().begin(), CONTENT.value().end());
}
//------------------------------------------------------------------------------
void Payload_Container::getValue(bstring& cnt) {
cnt = content;
if (content.has_value()) cnt = content.value();
}
//------------------------------------------------------------------------------
int Payload_Container::encode2Buffer(uint8_t* buf, int len) {
Logger::nas_mm().debug("Encoding Payload_Container iei(0x%x)", _iei);
if (len < length) {
// Logger::nas_mm().error("len is less than %d", length);
// return 0;
int Payload_Container::encode2Buffer(uint8_t* buf, int len, uint8_t type) {
Logger::nas_mm().debug("Encoding Payload_Container");
if ((len < kPayloadContainerMinimumLength) or (len < length + 3)) {
Logger::nas_mm().error(
"Buffer length is less than the minimum length of this IE (%d octet)",
kPayloadContainerMinimumLength);
return KEncodeDecodeError;
}
int encoded_size = 0;
if (_iei) {
*(buf + encoded_size) = _iei;
encoded_size++;
ENCODE_U8(buf + encoded_size, _iei, encoded_size);
}
*(buf + encoded_size) = (blength(content) & 0xff00) >> 8;
encoded_size++;
*(buf + encoded_size) = (blength(content) & 0x00ff);
encoded_size++;
uint8_t* buf_tmp = (uint8_t*) bdata(content);
if (buf_tmp != nullptr) memcpy(buf + encoded_size, buf_tmp, blength(content));
encoded_size += blength(content);
#if 0
if (_iei) {
*(buf + encoded_size) = _iei; encoded_size++;
*(buf + encoded_size) = (length - 2)&0x00ff; encoded_size++;
*(buf + encoded_size) = (length - 2) & 0xff00; encoded_size++;
*(buf + encoded_size) = CONTENT.size(); encoded_size++;
for (int i = 0; i < CONTENT.size(); i++) {
/*Length of Payload container entry*/
*(buf + encoded_size) = CONTENT.at(i).optionalIE.size() * 2 + 1;
for (int j = 0; j < CONTENT.at(i).optionalIE.size(); j++) {
*(buf + encoded_size) += blength(CONTENT.at(i).optionalIE.at(j).ie_value);
}
encoded_size++;
/*Length of Payload container entry*/
*(buf + encoded_size) = ((CONTENT.at(i).optionalIE.size()&0x0f)<<4)| CONTENT.at(i).payloadContainerType;
encoded_size++;
for (int j = 0; j < CONTENT.at(i).optionalIE.size(); j++) {
*(buf + encoded_size) += CONTENT.at(i).optionalIE.at(j).ie_type; encoded_size++;
*(buf + encoded_size) += CONTENT.at(i).optionalIE.at(j).ie_len; encoded_size++;
int size = encode_bstring(CONTENT.at(i).optionalIE.at(j).ie_value, (buf + encoded_size), len - encoded_size);
encoded_size += size;
}
}
}
else {
*(buf + encoded_size) = (length - 2)&0x00ff; encoded_size++;
*(buf + encoded_size) = (length - 2) & 0xff00; encoded_size++;
*(buf + encoded_size) = CONTENT.size(); encoded_size++;
for (int i = 0; i < CONTENT.size(); i++) {
/*Length of Payload container entry*/
*(buf + encoded_size) = CONTENT.at(i).optionalIE.size() * 2 + 1;
for (int j = 0; j < CONTENT.at(i).optionalIE.size(); j++) {
*(buf + encoded_size) += blength(CONTENT.at(i).optionalIE.at(j).ie_value);
}
encoded_size++;
/*Length of Payload container entry*/
*(buf + encoded_size) = ((CONTENT.at(i).optionalIE.size()&0x0f)<<4)| CONTENT.at(i).payloadContainerType;
encoded_size++;
for (int j = 0; j < CONTENT.at(i).optionalIE.size(); j++) {
*(buf + encoded_size) += CONTENT.at(i).optionalIE.at(j).ie_type; encoded_size++;
*(buf + encoded_size) += CONTENT.at(i).optionalIE.at(j).ie_len; encoded_size++;
int size = encode_bstring(CONTENT.at(i).optionalIE.at(j).ie_value, (buf + encoded_size), len - encoded_size);
encoded_size += size;
}
}
}
#endif
Logger::nas_mm().debug("Encoded Payload_Container len(%d)", encoded_size);
return encoded_size;
}
//------------------------------------------------------------------------------
ENCODE_U16(buf + encoded_size, length, encoded_size);
int Payload_Container::decodeFromBuffer(uint8_t* buf, int len, bool is_option) {
return 0;
if (content.has_value()) {
uint8_t* buf_tmp = (uint8_t*) bdata(content.value());
if (buf_tmp != nullptr)
memcpy(buf + encoded_size, buf_tmp, blength(content.value()));
encoded_size += blength(content.value());
return encoded_size;
}
if (CONTENT.has_value()) {
// Number of entries
ENCODE_U8(buf + encoded_size, CONTENT.value().size(), encoded_size);
for (int i = 0; i < CONTENT.value().size(); i++) {
// Length of Payload container entry
ENCODE_U8(buf + encoded_size, CONTENT.value().at(i).length, encoded_size);
// Number of optional IEs and Payload container type
uint8_t octet = 0;
octet = ((CONTENT.value().at(i).optionalIE.size() & 0x0f) << 4) |
CONTENT.value().at(i).payloadContainerType;
ENCODE_U8(buf + encoded_size, octet, encoded_size);
// Optional IEs
for (int j = 0; j < CONTENT.value().at(i).optionalIE.size(); j++) {
// Type
ENCODE_U8(
buf + encoded_size, CONTENT.value().at(i).optionalIE.at(j).ie_type,
encoded_size);
// Length
ENCODE_U8(
buf + encoded_size, CONTENT.value().at(i).optionalIE.at(j).ie_len,
encoded_size);
// Value
int size = encode_bstring(
CONTENT.value().at(i).optionalIE.at(j).ie_value,
(buf + encoded_size), len - encoded_size);
encoded_size += size;
}
}
}
Logger::nas_mm().debug("Encoded Payload_Container len(%d)", encoded_size);
return encoded_size;
}
//------------------------------------------------------------------------------
int Payload_Container::decodeFromBuffer(
uint8_t* buf, int len, bool is_option, uint8_t type) {
Logger::nas_mm().debug("Decoding Payload_Container iei (0x%x)", _iei);
Logger::nas_mm().debug("Decoding Payload_Container");
int decoded_size = 0;
if (is_option) {
decoded_size++;
DECODE_U8(buf + decoded_size, _iei, decoded_size);
}
if (type != 0x0f) { // not multiple payload
uint8_t octet = *(buf + decoded_size);
decoded_size++;
length = 0;
length |= (octet << 8);
octet = *(buf + decoded_size);
decoded_size++;
length |= octet;
content = blk2bstr(buf + decoded_size, length);
DECODE_U16(buf + decoded_size, length, decoded_size);
if (type != MULTIPLE_PAYLOADS) { // not multiple payloads
uint8_t octet = 0;
bstring content_tmp = {};
content_tmp = blk2bstr(buf + decoded_size, length);
content = std::optional<bstring>(content_tmp);
decoded_size += length;
return decoded_size;
}
// Multiple payloads
uint8_t num_entries;
uint8_t num_optional;
IE_t value;
PayloadContainerEntry payloadcontainerentry;
length = 0x00;
length |= *(buf + decoded_size);
decoded_size++;
length |= (*(buf + decoded_size)) << 8;
decoded_size++;
num_entries = *(buf + decoded_size);
decoded_size++;
std::vector<PayloadContainerEntry> CONTENT_tmp;
PayloadContainerEntry payloadcontainerentry = {};
DECODE_U8(buf + decoded_size, num_entries, decoded_size);
while (num_entries) {
uint8_t Length_entry = *(buf + decoded_size);
decoded_size++;
payloadcontainerentry.payloadContainerType = *(buf + decoded_size) & 0x0f;
num_optional = (*(buf + decoded_size) & 0xf0) >> 4;
decoded_size++;
uint8_t length_entry = 0;
DECODE_U8(buf + decoded_size, length_entry, decoded_size);
uint8_t octet = 0;
// Number of optional IEs and Payload container type
DECODE_U8(buf + decoded_size, octet, decoded_size);
payloadcontainerentry.payloadContainerType = octet & 0x0f;
num_optional = (octet & 0xf0) >> 4;
IE_t value;
while (num_optional) {
value.ie_type = *(buf + decoded_size);
decoded_size++;
value.ie_len = *(buf + decoded_size);
decoded_size++;
DECODE_U8(buf + decoded_size, value.ie_type, decoded_size);
DECODE_U8(buf + decoded_size, value.ie_len, decoded_size);
decode_bstring(
&value.ie_value, value.ie_len, (buf + decoded_size),
len - decoded_size);
......@@ -219,9 +199,10 @@ int Payload_Container::decodeFromBuffer(
payloadcontainerentry.optionalIE.end(), value);
num_optional--;
}
CONTENT.insert(CONTENT.end(), payloadcontainerentry);
CONTENT_tmp.insert(CONTENT_tmp.end(), payloadcontainerentry);
num_entries--;
}
Logger::nas_mm().debug("Decoded Payload_Container len (%d)", decoded_size);
CONTENT = std::optional<std::vector<PayloadContainerEntry>>(CONTENT);
Logger::nas_mm().debug("Decoded Payload_Container (len %d)", decoded_size);
return decoded_size;
}
......@@ -19,13 +19,6 @@
* contact@openairinterface.org
*/
/*! \file
\brief
\author Keliang DU, BUPT
\date 2020
\email: contact@openairinterface.org
*/
#ifndef __Payload_Container_H_
#define __Payload_Container_H_
#include <stdint.h>
......@@ -39,6 +32,10 @@ extern "C" {
#include "TLVEncoder.h"
#include "bstrlib.h"
}
constexpr uint8_t kPayloadContainerMinimumLength = 4;
constexpr uint32_t kPayloadContainerMaximumLength = 65538;
namespace nas {
class Payload_Container {
public:
......@@ -49,17 +46,16 @@ class Payload_Container {
const uint8_t iei, std::vector<PayloadContainerEntry> content);
~Payload_Container();
void setValue(uint8_t iei, uint8_t value);
int encode2Buffer(uint8_t* buf, int len);
int encode2Buffer(uint8_t* buf, int len, uint8_t type);
int decodeFromBuffer(uint8_t* buf, int len, bool is_option, uint8_t type);
int decodeFromBuffer(uint8_t* buf, int len, bool is_option);
void getValue(std::vector<PayloadContainerEntry>& content);
void getValue(bstring& cnt);
private:
uint8_t _iei;
uint16_t length;
bstring content;
std::vector<PayloadContainerEntry> CONTENT;
std::optional<bstring> content;
std::optional<std::vector<PayloadContainerEntry>> CONTENT;
};
} // namespace nas
......
......@@ -19,15 +19,10 @@
* contact@openairinterface.org
*/
/*! \file
\brief
\author Keliang DU, BUPT
\date 2020
\email: contact@openairinterface.org
*/
#include "Payload_Container_Type.hpp"
#include "3gpp_24.501.hpp"
#include "common_defs.h"
#include "logger.hpp"
using namespace nas;
......@@ -35,7 +30,7 @@ using namespace nas;
Payload_Container_Type::Payload_Container_Type(
const uint8_t iei, uint8_t value) {
_iei = iei;
_value = value;
_value = value & 0x0f;
}
//------------------------------------------------------------------------------
......@@ -46,7 +41,7 @@ Payload_Container_Type::~Payload_Container_Type(){};
//------------------------------------------------------------------------------
void Payload_Container_Type::setValue(const uint8_t value) {
_value = value;
_value = value & 0x0f;
}
//------------------------------------------------------------------------------
......@@ -56,44 +51,50 @@ uint8_t Payload_Container_Type::getValue() {
//------------------------------------------------------------------------------
int Payload_Container_Type::encode2Buffer(uint8_t* buf, int len) {
Logger::nas_mm().debug("encoding Payload_Container_Type IE iei(0x%x)", _iei);
if (len < 1) {
Logger::nas_mm().error("len is less than one");
return -1;
Logger::nas_mm().debug("Encoding Payload_Container_Type IE");
if (len < kPayloadContainerTypeLength) {
Logger::nas_mm().error(
"Buffer length is less than the minimum length of this IE (%d octet)",
kPayloadContainerTypeLength);
return KEncodeDecodeError;
}
int encoded_size = 0;
uint8_t octet = 0;
if (_iei) {
octet = (_iei << 4) | (_value & 0x0f);
} else {
uint8_t octet = 0;
if (!(_iei & 0x0f)) {
octet = (_value & 0x0f);
*buf = octet;
Logger::nas_mm().debug(
"encoded Payload_Container_Type IE(len(1/2 octet))");
return 1;
} else {
octet = (_iei << 4) | (_value & 0x0f);
*buf = octet;
Logger::nas_mm().debug("encoded Payload_Container_Type IE(len(1 octet))");
return 1;
}
octet = (_value & 0x0f);
}
ENCODE_U8(buf + encoded_size, octet, encoded_size);
Logger::nas_mm().debug("Encoded Payload_Container_Type IE");
return encoded_size;
}
//------------------------------------------------------------------------------
int Payload_Container_Type::decodeFromBuffer(
uint8_t* buf, int len, bool is_option) {
Logger::nas_mm().debug("decoding Payload_Container_Type IE");
if (len < 1) {
Logger::nas_mm().error("len is less than one");
return 0;
} else {
uint8_t octet = (*buf);
if (is_option) {
_iei = (octet & 0xf0) >> 4;
} else {
_iei = 0;
}
_value = octet & 0x0f;
Logger::nas_mm().debug(
"decoded Payload_Container_Type iei(0x%x) value(0x%x)", _iei, _value);
return 1;
if (len < kPayloadContainerTypeLength) {
Logger::nas_mm().error(
"Buffer length is less than the minimum length of this IE (%d octet)",
kPayloadContainerTypeLength);
return KEncodeDecodeError;
}
int decoded_size = 0;
uint8_t octet = 0;
DECODE_U8(buf + decoded_size, octet, decoded_size);
if (is_option) {
_iei = (octet & 0xf0) >> 4;
}
_value = octet & 0x0f;
Logger::nas_mm().debug(
"Decoded Payload_Container_Type (IEI 0x%x, value 0x%x)", _iei, _value);
return decoded_size;
}
......@@ -24,6 +24,8 @@
#include <stdint.h>
constexpr uint8_t kPayloadContainerTypeLength = 1;
namespace nas {
class Payload_Container_Type {
......
......@@ -94,6 +94,7 @@ typedef struct {
} IE_t_E;
typedef struct {
uint8_t length;
uint8_t payloadContainerType : 4;
std::vector<IE_t> optionalIE;
} PayloadContainerEntry;
......
......@@ -113,11 +113,12 @@ int DLNASTransport::encode2Buffer(uint8_t* buf, int len) {
return 0;
}
}
if (!ie_payload_container) {
if (!ie_payload_container or !ie_payload_container_type) {
Logger::nas_mm().warn("IE ie_payload_container is not available");
} else {
if (int size = ie_payload_container->encode2Buffer(
buf + encoded_size, len - encoded_size)) {
buf + encoded_size, len - encoded_size,
ie_payload_container_type->getValue())) {
encoded_size += size;
} else {
Logger::nas_mm().error("Encoding ie_payload_container error");
......@@ -183,7 +184,8 @@ int DLNASTransport::decodeFromBuffer(
buf + decoded_size, len - decoded_size, false);
ie_payload_container = new Payload_Container();
decoded_size += ie_payload_container->decodeFromBuffer(
buf + decoded_size, len - decoded_size, false);
buf + decoded_size, len - decoded_size, false,
N1_SM_INFORMATION); // TODO: verified Typeb of Payload Container
Logger::nas_mm().debug("Decoded_size (%d)", decoded_size);
uint8_t octet = *(buf + decoded_size);
Logger::nas_mm().debug("First option IEI (0x%x)", octet);
......
......@@ -46,7 +46,7 @@ RegistrationRequest::RegistrationRequest()
ie_5gs_drx_parameters = std::nullopt;
ie_eps_nas_message_container = std::nullopt;
ie_ladn_indication = std::nullopt;
ie_payload_container_type = nullptr;
ie_payload_container_type = std::nullopt;
ie_payload_container = nullptr;
ie_network_slicing_indication = nullptr;
ie_5gs_update_type = nullptr;
......@@ -428,13 +428,14 @@ bool RegistrationRequest::getLadnIndication(std::vector<bstring>& ladnValue) {
//------------------------------------------------------------------------------
void RegistrationRequest::setPayload_Container_Type(uint8_t value) {
ie_payload_container_type = new Payload_Container_Type(0x08, value);
ie_payload_container_type = std::make_optional<Payload_Container_Type>(
kIeiPayloadContainerType, value);
}
//------------------------------------------------------------------------------
uint8_t RegistrationRequest::getPayloadContainerType() {
if (ie_payload_container_type) {
return ie_payload_container_type->getValue();
if (ie_payload_container_type.has_value()) {
return ie_payload_container_type.value().getValue();
} else {
return 0;
}
......@@ -758,10 +759,10 @@ int RegistrationRequest::encode2Buffer(uint8_t* buf, int len) {
return 0;
}
}
if (!ie_payload_container_type) {
if (!ie_payload_container_type.has_value()) {
Logger::nas_mm().warn("IE ie_payload_container_type is not available");
} else {
if (int size = ie_payload_container_type->encode2Buffer(
if (int size = ie_payload_container_type.value().encode2Buffer(
buf + encoded_size, len - encoded_size)) {
encoded_size += size;
} else {
......@@ -769,11 +770,12 @@ int RegistrationRequest::encode2Buffer(uint8_t* buf, int len) {
return 0;
}
}
if (!ie_payload_container) {
if (!ie_payload_container or !ie_payload_container_type) {
Logger::nas_mm().warn("IE ie_payload_container is not available");
} else {
if (int size = ie_payload_container->encode2Buffer(
buf + encoded_size, len - encoded_size)) {
buf + encoded_size, len - encoded_size,
ie_payload_container_type.value().getValue())) {
encoded_size += size;
} else {
Logger::nas_mm().error("encoding ie_payload_container error");
......@@ -873,11 +875,13 @@ int RegistrationRequest::decodeFromBuffer(uint8_t* buf, int len) {
octet = *(buf + decoded_size);
Logger::nas_mm().debug("Next IEI 0x%x", octet);
} break;
case 0x08: {
Logger::nas_mm().debug("Decoding IEI (0x8)");
ie_payload_container_type = new Payload_Container_Type();
decoded_size += ie_payload_container_type->decodeFromBuffer(
case kIeiPayloadContainerType: {
Logger::nas_mm().debug("Decoding IEI 0x8: Payload Container Type");
Payload_Container_Type ie_payload_container_type_tmp = {};
decoded_size += ie_payload_container_type_tmp.decodeFromBuffer(
buf + decoded_size, len - decoded_size, true);
ie_payload_container_type = std::optional<Payload_Container_Type>(
ie_payload_container_type_tmp);
octet = *(buf + decoded_size);
Logger::nas_mm().debug("Next IEI 0x%x", octet);
} break;
......@@ -1046,7 +1050,8 @@ int RegistrationRequest::decodeFromBuffer(uint8_t* buf, int len) {
Logger::nas_mm().debug("Decoding IEI(0x7B)");
ie_payload_container = new Payload_Container();
decoded_size += ie_payload_container->decodeFromBuffer(
buf + decoded_size, len - decoded_size, true);
buf + decoded_size, len - decoded_size, true,
N1_SM_INFORMATION); // TODO: verified type of Payload container
octet = *(buf + decoded_size);
Logger::nas_mm().debug("Next IEI 0x%x", octet);
} break;
......
......@@ -170,10 +170,10 @@ class RegistrationRequest : public NasMmPlainHeader {
std::optional<UEUsageSetting> ie_ues_usage_setting; // Optional
std::optional<_5GS_DRX_Parameters> ie_5gs_drx_parameters; // Optional
std::optional<EPS_NAS_Message_Container>
ie_eps_nas_message_container; // Optional
std::optional<LADN_Indication> ie_ladn_indication; // Optional
ie_eps_nas_message_container; // Optional
std::optional<LADN_Indication> ie_ladn_indication; // Optional
std::optional<Payload_Container_Type> ie_payload_container_type; // Optional
Payload_Container_Type* ie_payload_container_type; // Optional
Payload_Container* ie_payload_container; // Optional
Network_Slicing_Indication* ie_network_slicing_indication; // Optional
_5GS_Update_Type* ie_5gs_update_type; // Optional
......
......@@ -195,11 +195,12 @@ int ULNASTransport::encode2Buffer(uint8_t* buf, int len) {
return 0;
}
}
if (!ie_payload_container) {
if (!ie_payload_container or !ie_payload_container_type) {
Logger::nas_mm().warn("IE ie_payload_container is not available");
} else {
if (int size = ie_payload_container->encode2Buffer(
buf + encoded_size, len - encoded_size)) {
buf + encoded_size, len - encoded_size,
ie_payload_container_type->getValue())) {
encoded_size += size;
} else {
Logger::nas_mm().error("encoding ie_payload_container error");
......
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