mirror of
https://github.com/esphome/esphome.git
synced 2026-10-21 13:34:12 +00:00
[packet_interface] Leave no room, instead of underflowing, on a small link
As in the other layers, addressing ports and fragmentation get no room when max_packet_size is smaller than their header, and fragmentation fails its setup instead of dividing by zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
592d1820ba
commit
13ec5f27e9
@@ -67,7 +67,9 @@ AddressingPort::AddressingPort(BasicAddressing *addressing, uint16_t destination
|
||||
} else {
|
||||
this->inherit_from_(addressing->get_link(), CAPABILITY_ADDRESSING);
|
||||
}
|
||||
this->max_packet_size_ = addressing->get_link()->get_max_packet_size() - BasicAddressing::HEADER_SIZE;
|
||||
size_t link_size = addressing->get_link()->get_max_packet_size();
|
||||
// A link with no room leaves the layers above none either, so they fail their setup
|
||||
this->max_packet_size_ = link_size > BasicAddressing::HEADER_SIZE ? link_size - BasicAddressing::HEADER_SIZE : 0;
|
||||
addressing->add_port(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,16 @@ BasicFragmentation::BasicFragmentation(PacketInterface *lower, size_t max_packet
|
||||
|
||||
size_t BasicFragmentation::fragments_for_(size_t size) const {
|
||||
size_t fragment_size = this->fragment_size_();
|
||||
if (fragment_size == 0)
|
||||
return SIZE_MAX;
|
||||
return std::max<size_t>(1, (size + fragment_size - 1) / fragment_size);
|
||||
}
|
||||
|
||||
void BasicFragmentation::setup() {
|
||||
if (this->fragment_size_() == 0) {
|
||||
this->mark_failed(LOG_STR("max_packet_size too small for the configured layers"));
|
||||
return;
|
||||
}
|
||||
if (this->fragments_for_(this->max_packet_size_) > MAX_FRAGMENTS) {
|
||||
this->mark_failed(LOG_STR("max_packet_size needs more than 128 fragments"));
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,10 @@ class BasicFragmentation : public PacketInterface {
|
||||
|
||||
protected:
|
||||
void receive_(const PacketBuffer &data, const PacketMetaData &meta_data);
|
||||
size_t fragment_size_() const { return this->lower_->get_max_packet_size() - HEADER_SIZE; }
|
||||
size_t fragment_size_() const {
|
||||
size_t lower_size = this->lower_->get_max_packet_size();
|
||||
return lower_size > HEADER_SIZE ? lower_size - HEADER_SIZE : 0;
|
||||
}
|
||||
size_t fragments_for_(size_t size) const;
|
||||
|
||||
PacketInterface *lower_;
|
||||
|
||||
@@ -80,6 +80,14 @@ TEST(BasicAddressingTest, HeaderAndCapability) {
|
||||
EXPECT_EQ(a.link.sent, (std::vector<Packet>{{0x78, 0x56, 0x34, 0x12, 0xAA}, {0x42, 0x00, 0x34, 0x12, 0xBB}}));
|
||||
}
|
||||
|
||||
TEST(BasicAddressingTest, LinkTooSmallLeavesNoRoom) {
|
||||
MockLink link;
|
||||
link.set_max_packet_size(BasicAddressing::HEADER_SIZE);
|
||||
BasicAddressing addressing(&link, 0x0001);
|
||||
AddressingPort port(&addressing, 0x0002, true);
|
||||
EXPECT_EQ(port.get_max_packet_size(), 0);
|
||||
}
|
||||
|
||||
TEST(BasicAddressingTest, BroadcastReachesEveryoneButSender) {
|
||||
Device a(1), b(2), c(3);
|
||||
a.port().send({0x10});
|
||||
|
||||
@@ -191,4 +191,13 @@ TEST_F(BasicFragmentationTest, OversizedSingleFragmentDropped) {
|
||||
EXPECT_EQ(received, std::vector<Packet>{Packet({1, 2, 3, 4})});
|
||||
}
|
||||
|
||||
TEST_F(BasicFragmentationTest, LinkTooSmallFailsSetup) {
|
||||
this->link_a.set_max_packet_size(BasicFragmentation::HEADER_SIZE);
|
||||
BasicFragmentation fragmentation(&this->link_a, 100);
|
||||
fragmentation.setup();
|
||||
EXPECT_TRUE(fragmentation.is_failed());
|
||||
Packet packet{1};
|
||||
EXPECT_FALSE(fragmentation.send_to_interface(PacketBuffer(packet), {}));
|
||||
}
|
||||
|
||||
} // namespace esphome::packet_interface::fragmentation_testing
|
||||
|
||||
Reference in New Issue
Block a user