blob: 8b36f61b98cee9fce23f959488e25598f5a9f1f6 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/cellular/cellular_pco.h"
#include <utility>
#include <base/memory/ptr_util.h>
#include <base/strings/stringprintf.h>
namespace shill {
// Protocol Configuration Options (PCO) is an information element with an
// identifier (IEI) 0x27 and contains between 3 and 253 octets. See 3GPP TS
// 24.008 for more details on PCO.
//
// NOTE: The standard uses one-based indexing, but to better correlate to the
// code, zero-based indexing is used in the description hereinafter.
//
// Octet | Value
// --------+--------------------------------------------
// 0 | PCO IEI (= 0x27)
// 1 | Length of PCO contents (= total length - 2)
// 2 | bit 7 : ext
// | bit 6 to 3 : spare (= 0b0000)
// | bit 2 to 0 : Configuration protocol
// 3 to 4 | Element 1 ID
// 5 | Length of element 1 contents
// 6 to m | Element 1 contents
// ... |
namespace {
constexpr size_t kPcoHeaderLength = 3;
constexpr size_t kElementHeaderLength = 3;
constexpr size_t kMaxPcoContentLength = 250;
constexpr size_t kMinNumOfOctets = kPcoHeaderLength;
constexpr size_t kMaxNumOfOctets = kPcoHeaderLength + kMaxPcoContentLength;
constexpr uint8_t kPcoIei = 0x27;
} // namespace
std::unique_ptr<CellularPco> CellularPco::CreateFromRawData(
const std::vector<uint8_t>& raw_data) {
if (raw_data.size() < kMinNumOfOctets || raw_data.size() > kMaxNumOfOctets)
return nullptr;
if (raw_data[0] != kPcoIei)
return nullptr;
if (raw_data[1] != raw_data.size() - 2)
return nullptr;
std::vector<Element> elements;
const uint8_t* data_ptr = raw_data.data() + kPcoHeaderLength;
size_t data_length = raw_data.size() - kPcoHeaderLength;
while (data_length > 0) {
if (data_length < kElementHeaderLength)
return nullptr;
uint16_t element_id = (data_ptr[0] << 8) | data_ptr[1];
size_t element_length = data_ptr[2];
data_ptr += kElementHeaderLength;
data_length -= kElementHeaderLength;
if (data_length < element_length)
return nullptr;
elements.emplace_back(
element_id, std::vector<uint8_t>(data_ptr, data_ptr + element_length));
data_ptr += element_length;
data_length -= element_length;
}
return base::WrapUnique(new CellularPco(std::move(elements)));
}
CellularPco::CellularPco(std::vector<Element> elements)
: elements_(std::move(elements)) {}
CellularPco::~CellularPco() = default;
const CellularPco::Element* CellularPco::FindElement(
uint16_t element_id) const {
for (const Element& element : elements_) {
if (element.id == element_id)
return &element;
}
return nullptr;
}
} // namespace shill