Weave port [1/n]: Introduce Weave packet implementation

PiperOrigin-RevId: 517526002
This commit is contained in:
Anay Wadhera
2023-03-17 15:44:09 -07:00
committed by Copybara-Service
parent 8f887064ec
commit 69d2098ecb
7 changed files with 507 additions and 0 deletions
+2
View File
@@ -410,6 +410,7 @@ let package = Package(
"internal/BUILD",
"internal/crypto/BUILD",
"internal/crypto/BUILD.gn",
"internal/weave/BUILD",
"internal/platform/flags/BUILD",
"internal/platform/implementation/shared/BUILD",
"internal/platform/implementation/apple/Mediums/BUILD",
@@ -543,6 +544,7 @@ let package = Package(
"internal/test/fake_timer_test.cc",
"internal/test/fake_device_info_test.cc",
"internal/test/fake_task_runner_test.cc",
"internal/weave/packet_test.cc",
// simulation
"connections/implementation/offline_simulation_user.cc",
"connections/implementation/simulation_user.cc",
+1
View File
@@ -50,6 +50,7 @@ cc_library(
"//internal:__pkg__",
"//internal/platform:__subpackages__",
"//internal/platform/implementation:__subpackages__",
"//internal/weave:__pkg__",
"//location/nearby/cpp:__subpackages__",
"//presence:__subpackages__",
],
@@ -133,6 +133,7 @@ cc_library(
"//internal/platform:__subpackages__",
"//internal/proto/analytics:__subpackages__",
"//internal/test:__subpackages__",
"//internal/weave:__subpackages__",
"//location/nearby/cpp:__subpackages__",
"//presence:__subpackages__",
],
+31
View File
@@ -0,0 +1,31 @@
cc_library(
name = "weave",
srcs = [
"packet.cc",
],
hdrs = [
"packet.h",
],
deps = [
"//internal/platform:base",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
cc_test(
name = "packet_test",
srcs = [
"packet_test.cc",
],
deps = [
":weave",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings:str_format",
"@com_google_googletest//:gtest_main",
],
)
+166
View File
@@ -0,0 +1,166 @@
// Copyright 2023 Google LLC
//
// Licensed 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
//
// https://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.
#include "internal/weave/packet.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
namespace nearby {
namespace weave {
namespace {
constexpr char kControlType = 0b10000000;
constexpr char kDataType = 0b00000000;
constexpr char kMaskPacketCounter = 0b01110000;
constexpr char kMaskControlCommandNumber = 0b00001111;
constexpr int kConnectionRequestExtraDataSize = 13;
constexpr int kConnectionConfirmExtraDataSize = 15;
constexpr int kConnectionConfirmPacketHeaderLength = 4;
constexpr int kConnectionRequestPacketHeaderLength = 6;
} // namespace
Packet Packet::CreateDataPacket(bool is_first_packet, bool is_last_packet,
ByteArray payload) {
int next_four_bits = ((is_first_packet ? kFirstPacketBit : 0) |
(is_last_packet ? kLastPacketBit : 0));
Packet packet = Packet(ByteArray(kPacketHeaderLength + payload.size()));
packet.SetHeader(/* is_control_packet = */ false, next_four_bits);
payload.AsStringView().copy(packet.bytes_.data() + kPacketHeaderLength,
payload.size());
return packet;
}
Packet Packet::CreateControlPacket(ControlPacketType command_number,
int payload_size) {
Packet packet = Packet(ByteArray(kPacketHeaderLength + payload_size));
packet.SetHeader(/* is_control_packet = */ true,
static_cast<int>(command_number));
return packet;
}
absl::StatusOr<Packet> Packet::CreateConnectionRequestPacket(
int16_t min_protocol_version, int16_t max_protocol_version,
int16_t max_packet_size, absl::string_view extra_data) {
if (extra_data.size() > kConnectionRequestExtraDataSize) {
return absl::InvalidArgumentError(
"Connection request packet may contain at most 13 bytes of extra "
"data.");
}
Packet packet = CreateControlPacket(
ControlPacketType::kControlConnectionRequest,
kConnectionRequestPacketHeaderLength + extra_data.size());
// Create a string of length |kConnectionRequestPacketHeaderLength| filled
// with char 0. This string is used to store raw binary data.
std::string header_data(kConnectionRequestPacketHeaderLength, 0);
// Put min_protocol_version into the header in big-endian order.
header_data[0] = (0xFF & (min_protocol_version >> 8));
header_data[1] = (0xFF & min_protocol_version);
// Put max_protocol_version into the header in big-endian order.
header_data[2] = (0xFF & (max_protocol_version >> 8));
header_data[3] = (0xFF & max_protocol_version);
// Put max_packet_size into the header in big-endian order.
header_data[4] = (0xFF & (max_packet_size >> 8));
header_data[5] = (0xFF & max_packet_size);
// Finally, move to packet.
header_data.copy(packet.bytes_.data() + kPacketHeaderLength,
header_data.size());
// Copy extra_data.
extra_data.copy(packet.bytes_.data() + kConnectionRequestPacketHeaderLength +
kPacketHeaderLength,
extra_data.size());
return packet;
}
absl::StatusOr<Packet> Packet::CreateConnectionConfirmPacket(
int16_t selected_protocol_version, int16_t selected_packet_size,
absl::string_view extra_data) {
if (extra_data.size() > kConnectionConfirmExtraDataSize) {
return absl::InvalidArgumentError(
"Connection confirm packet may contain at most 15 bytes of extra "
"data.");
}
Packet packet = CreateControlPacket(
ControlPacketType::kControlConnectionConfirm,
kConnectionConfirmPacketHeaderLength + extra_data.size());
// Create a string of length |kConnectionConfirmPacketHeaderLength| filled
// with char 0. This string is used to store raw binary data.
std::string header_data(kConnectionConfirmPacketHeaderLength, 0);
// Put selected_protocol_version into the header in big-endian order.
header_data[0] = (0xFF & (selected_protocol_version >> 8));
header_data[1] = (0xFF & selected_protocol_version);
// Put selected_packet_size into the header in big-endian order.
header_data[2] = (0xFF & (selected_packet_size >> 8));
header_data[3] = (0xFF & selected_packet_size);
// Finally, move to packet.
header_data.copy(packet.bytes_.data() + kPacketHeaderLength,
header_data.size());
// Copy extra_data.
extra_data.copy(packet.bytes_.data() + kConnectionConfirmPacketHeaderLength +
kPacketHeaderLength,
extra_data.size());
return packet;
}
void Packet::SetHeader(bool is_control_packet, int last_four_bits) {
bytes_[0] = (is_control_packet ? kControlType : kDataType) |
(last_four_bits & 0b00001111);
}
bool Packet::IsDataPacket() const { return !IsControlPacket(); }
Packet::ControlPacketType Packet::GetControlCommandNumber() const {
return (ControlPacketType)(bytes_[0] & kMaskControlCommandNumber);
}
int Packet::GetPacketCounter() const {
if (bytes_.empty()) return 0;
return (bytes_[0] & kMaskPacketCounter) >> 4;
}
absl::Status Packet::SetPacketCounter(int packetCounter) {
if (packetCounter < 0 || packetCounter > kMaxPacketCounter) {
return absl::InvalidArgumentError(
absl::StrFormat("Packet counter %d out of range", packetCounter));
}
bytes_[0] |= ((packetCounter << 4) & kMaskPacketCounter);
return absl::OkStatus();
}
std::string Packet::ToString() {
return absl::StrFormat("Packet[header: 0b%08d + payload: %d bytes]",
bytes_.front(), bytes_.size() - 1);
}
} // namespace weave
} // namespace nearby
+153
View File
@@ -0,0 +1,153 @@
// Copyright 2023 Google LLC
//
// Licensed 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
//
// https://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.
#ifndef THIRD_PARTY_NEARBY_INTERNAL_WEAVE_PACKET_H_
#define THIRD_PARTY_NEARBY_INTERNAL_WEAVE_PACKET_H_
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "internal/platform/byte_array.h"
namespace nearby {
namespace weave {
/*
Spec: go/weave-ble-gatt-transport
A weave packet has a 1 byte header indicating its packet counter, what type of
packet it is, and what type of control packet it may be.
Each control packet can also contain an optional payload, sized as follows:
Connection confirm packet: up to 15 bytes
Connection request packet: up to 13 bytes
Error packet: no payload
A data packet by contrast, is sized to fit the connection as so:
min(server_packet_size, client_packet_size, connection_packet_size).
The general format of the header of the Weave packet is as follows:
---------------------------
| 7 | 6 5 4 | 3 | 2 | 1 0 |
---------------------------
7) This bit is set to 0 if the packet is a data packet, and 1 if it is a control
packet.
6, 5, 4) These bits represent the packet counter, which wraps after every 7
packets.
3) If set to 1, this bit indicates that the packet is the first packet of the
message.
2) This bit indicates whether the packet is the last packet of the
message.
1, 0) These bits indicate the type of control packet where 0b00 is a
connection request packet, 0b01 is a connection confirm packet, and 0b10 is an
error packet. 0b11 is not a type of control packet.
If the packet is a control packet, and it is a connection request packet, the
first 6 bytes of the payload are as follows:
-------------------------------------
| 0x55 0x44 | 0x33 0x22 | 0x11 0x00 |
-------------------------------------
0x55, 0x44) The minimum protocol version the client supports.
0x33, 0x22) The maximum protocol version the client supports.
0x11, 0x00) The maximum packet size the client supports.
If the packet is a connection confirm packet, the first 4 bytes of the payload
are as follows:
-------------------------
| 0x44 0x33 | 0x22 0x11 |
-------------------------
0x44, 0x33) The selected protocol version for this connection based on the
server support.
0x22, 0x11) The selected packet size of this connection based on the server's
packet size.
*/
class Packet {
public:
static constexpr int kMaxPacketCounter = 0b111;
enum class ControlPacketType {
kControlConnectionRequest = 0,
kControlConnectionConfirm = 1,
kControlError = 2,
};
// NOTE: The below Packet builders will take ownership of the bytes passed
// into them.
static absl::StatusOr<Packet> FromBytes(ByteArray bytes) {
if (bytes.Empty()) {
return absl::InvalidArgumentError(
"Need at least one byte in this packet");
}
return Packet(std::move(bytes));
}
static Packet CreateDataPacket(bool is_first_packet, bool is_last_packet,
ByteArray payload);
static absl::StatusOr<Packet> CreateConnectionRequestPacket(
int16_t min_protocol_version, int16_t max_protocol_version,
int16_t max_packet_size, absl::string_view extra_data);
static absl::StatusOr<Packet> CreateConnectionConfirmPacket(
int16_t selected_protocol_version, int16_t selected_packet_size,
absl::string_view extra_data);
static Packet CreateErrorPacket() {
return CreateControlPacket(ControlPacketType::kControlError,
/*payload_size=*/0);
}
Packet(Packet&& other) = default;
Packet& operator=(Packet&& other) = default;
bool IsFirstPacket() const {
return !bytes_.empty() && (bytes_.data()[0] & kFirstPacketBit) != 0;
}
bool IsLastPacket() const {
return !bytes_.empty() && (bytes_.data()[0] & kLastPacketBit) != 0;
}
bool IsControlPacket() const {
return !bytes_.empty() && (bytes_.data()[0] & kMaskType) != 0;
}
bool IsDataPacket() const;
int GetPacketCounter() const;
ControlPacketType GetControlCommandNumber() const;
std::string GetPayload() const { return bytes_.substr(kPacketHeaderLength); }
std::string GetBytes() const { return bytes_; }
absl::Status SetPacketCounter(int packetCounter);
std::string ToString();
private:
static constexpr char kFirstPacketBit = 0b00001000;
static constexpr char kLastPacketBit = 0b00000100;
static constexpr char kMaskType = 0b10000000;
static constexpr int kPacketHeaderLength = 1;
static Packet CreateControlPacket(ControlPacketType command_number,
int payload_size);
explicit Packet(ByteArray&& bytes) : bytes_(std::move(bytes)) {}
void SetHeader(bool is_control_packet, int last_four_bits);
// Raw binary packet data.
std::string bytes_;
};
} // namespace weave
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_INTERNAL_WEAVE_PACKET_H_
+153
View File
@@ -0,0 +1,153 @@
// Copyright 2023 Google LLC
//
// Licensed 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
//
// https://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.
#include "internal/weave/packet.h"
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
namespace nearby {
namespace weave {
namespace {
constexpr absl::string_view kExtraData = "\x34\x56\x78";
constexpr absl::string_view kConnectionRequestExtraData = "thirteen char";
constexpr absl::string_view kConnectionConfirmExtraData = "fifteen char...";
TEST(PacketTest, PacketIsNotTriviallyConstructible) {
EXPECT_FALSE(std::is_trivially_constructible<Packet>());
}
TEST(PacketTest, CreateErrorPacketTest) {
Packet packet = Packet::CreateErrorPacket();
EXPECT_TRUE(packet.IsControlPacket());
EXPECT_EQ(packet.GetControlCommandNumber(),
Packet::ControlPacketType::kControlError);
EXPECT_EQ(packet.GetPayload().size(), 0);
}
TEST(PacketTest, CreateConnectionConfirmPacketGoodTest) {
Packet packet =
Packet::CreateConnectionConfirmPacket(1, 15, kConnectionConfirmExtraData)
.value();
EXPECT_TRUE(packet.IsControlPacket());
EXPECT_EQ(packet.GetControlCommandNumber(),
Packet::ControlPacketType::kControlConnectionConfirm);
ASSERT_EQ(packet.GetBytes().size(), 20);
auto expected = absl::StrFormat("%c%c%c%c%c%s", 0b10000001, 0x00, 0x01, 0x00,
0x0F, kConnectionConfirmExtraData);
EXPECT_EQ(packet.GetBytes(), expected);
}
TEST(PacketTest, CreateConnectionConfirmPacketBadTest) {
EXPECT_FALSE(Packet::CreateConnectionConfirmPacket(
/*selected_protocol_version=*/1, /*selected_packet_size=*/15,
"> than fifteen..")
.ok());
}
TEST(PacketTest, CreateConnectionRequestGoodPacketTest) {
Packet packet = Packet::CreateConnectionRequestPacket(
0x0001, 0x0001, 0x000F, kConnectionRequestExtraData)
.value();
EXPECT_TRUE(packet.IsControlPacket());
EXPECT_EQ(packet.GetControlCommandNumber(),
Packet::ControlPacketType::kControlConnectionRequest);
ASSERT_EQ(packet.GetPayload().size(), 19);
ASSERT_EQ(packet.GetBytes().size(), 20);
auto expected = absl::StrCat(
absl::StrFormat("%c%c%c%c%c%c%c%s", 0b10000000, 0x00, 0x01, 0x00, 0x01,
0x00, 0x0F, kConnectionRequestExtraData));
EXPECT_EQ(packet.GetBytes(), expected);
}
TEST(PacketTest, CreateConnectionRequestBigProtocolVersionPacketTest) {
ByteArray extraData = ByteArray(std::string(kExtraData));
Packet packet =
Packet::CreateConnectionRequestPacket(0x1001, 0x1001, 0x000F, kExtraData)
.value();
EXPECT_TRUE(packet.IsControlPacket());
EXPECT_EQ(packet.GetControlCommandNumber(),
Packet::ControlPacketType::kControlConnectionRequest);
ASSERT_EQ(packet.GetPayload().size(), 9);
ASSERT_EQ(packet.GetBytes().size(), 10);
auto expected =
absl::StrCat(absl::StrFormat("%c%c%c%c%c%c%c%s", 0b10000000, 0x10, 0x01,
0x10, 0x01, 0x00, 0x0F, kExtraData));
EXPECT_EQ(packet.GetBytes(), expected);
}
TEST(PacketTest, CreateConnectionRequestBadPacketTest) {
EXPECT_FALSE(
Packet::CreateConnectionRequestPacket(1, 1, 15, "> than 13 chr.").ok());
}
TEST(PacketTest, CreateDataPacketTest) {
Packet packet =
Packet::CreateDataPacket(false, false, ByteArray("big payload"));
ASSERT_EQ(packet.GetBytes().size(), 12);
EXPECT_TRUE(packet.IsDataPacket());
EXPECT_FALSE(packet.IsFirstPacket());
EXPECT_FALSE(packet.IsLastPacket());
EXPECT_EQ(packet.GetPayload(), "big payload");
EXPECT_EQ(packet.GetBytes().substr(1), "big payload");
EXPECT_EQ(packet.GetPacketCounter(), 0);
}
TEST(PacketTest, SetPacketCounterTest) {
Packet packet = Packet::CreateDataPacket(false, false, ByteArray("sample"));
EXPECT_OK(packet.SetPacketCounter(1));
EXPECT_EQ(packet.GetPacketCounter(), 1);
EXPECT_THAT(packet.SetPacketCounter(Packet::kMaxPacketCounter + 1),
testing::status::StatusIs(absl::StatusCode::kInvalidArgument));
EXPECT_THAT(packet.SetPacketCounter(-1),
testing::status::StatusIs(absl::StatusCode::kInvalidArgument));
EXPECT_THAT(packet.SetPacketCounter(8),
testing::status::StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(PacketTest, StringifyTest) {
Packet packet = Packet::CreateDataPacket(false, false, ByteArray("sample"));
EXPECT_EQ(packet.ToString(),
absl::StrFormat("Packet[header: 0b%08d + payload: %d bytes]",
0b00000000, 6));
}
TEST(PacketTest, CreateFirstPacketTest) {
Packet packet = Packet::CreateDataPacket(
/*is_first_packet=*/true, /*is_last_packet=*/false, ByteArray("sample"));
EXPECT_FALSE(packet.IsControlPacket());
EXPECT_TRUE(packet.IsFirstPacket());
EXPECT_FALSE(packet.IsLastPacket());
EXPECT_EQ(packet.GetPayload(), "sample");
}
TEST(PacketTest, CreateLastPacketTest) {
Packet packet = Packet::CreateDataPacket(false, true, ByteArray("sample"));
EXPECT_FALSE(packet.IsControlPacket());
EXPECT_FALSE(packet.IsFirstPacket());
EXPECT_TRUE(packet.IsLastPacket());
EXPECT_EQ(packet.GetPayload(), "sample");
}
} // namespace
} // namespace weave
} // namespace nearby