| // |
| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // 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 |
| // |
| // 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. |
| // |
| |
| // This file tests some public interface methods of AttributeList. |
| #include "shill/net/attribute_list.h" |
| |
| #include <linux/netlink.h> |
| |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "shill/net/byte_string.h" |
| |
| using testing::_; |
| using testing::InSequence; |
| using testing::Mock; |
| using testing::Return; |
| using testing::Test; |
| |
| namespace shill { |
| |
| class AttributeListTest : public Test { |
| public: |
| MOCK_METHOD2(AttributeMethod, bool(int id, const ByteString& value)); |
| |
| protected: |
| static const uint16_t kHeaderLength = 4; |
| static const uint16_t kType1 = 1; |
| static const uint16_t kType2 = 2; |
| static const uint16_t kType3 = 3; |
| |
| static ByteString MakeNetlinkAttribute(uint16_t len, |
| uint16_t type, |
| const std::string& payload) { |
| nlattr attribute{ len, type }; |
| ByteString data(reinterpret_cast<const char*>(&attribute), |
| sizeof(attribute)); |
| data.Append(ByteString(payload, false)); |
| return data; |
| } |
| |
| static ByteString MakePaddedNetlinkAttribute(uint16_t len, |
| uint16_t type, |
| const std::string& payload) { |
| ByteString data(MakeNetlinkAttribute(len, type, payload)); |
| ByteString padding(NLA_ALIGN(data.GetLength()) - data.GetLength()); |
| data.Append(padding); |
| return data; |
| } |
| }; |
| |
| MATCHER_P(PayloadIs, payload, "") { |
| return arg.Equals(ByteString(std::string(payload), false)); |
| } |
| |
| TEST_F(AttributeListTest, IterateEmptyPayload) { |
| EXPECT_CALL(*this, AttributeMethod(_, _)).Times(0); |
| AttributeListRefPtr list(new AttributeList()); |
| EXPECT_TRUE(list->IterateAttributes( |
| ByteString(), 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| } |
| |
| TEST_F(AttributeListTest, IteratePayload) { |
| ByteString payload; |
| payload.Append(MakePaddedNetlinkAttribute( |
| kHeaderLength + 10, kType1, "0123456789")); |
| const uint16_t kLength1 = kHeaderLength + 10 + 2; // 2 bytes padding. |
| ASSERT_EQ(kLength1, payload.GetLength()); |
| payload.Append(MakePaddedNetlinkAttribute(kHeaderLength + 3, kType2, "123")); |
| const uint16_t kLength2 = kLength1 + kHeaderLength + 3 + 1; // 1 byte pad. |
| ASSERT_EQ(kLength2, payload.GetLength()); |
| |
| payload.Append(MakeNetlinkAttribute(kHeaderLength + 5, kType3, "12345")); |
| const uint16_t kLength3 = kLength2 + kHeaderLength + 5; |
| ASSERT_EQ(kLength3, payload.GetLength()); |
| |
| InSequence seq; |
| EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0123456789"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*this, AttributeMethod(kType2, PayloadIs("123"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*this, AttributeMethod(kType3, PayloadIs("12345"))) |
| .WillOnce(Return(true)); |
| AttributeListRefPtr list(new AttributeList()); |
| EXPECT_TRUE(list->IterateAttributes( |
| payload, 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // If a valid offset is provided only the attributes that follow should |
| // be enumerated. |
| EXPECT_CALL(*this, AttributeMethod(kType1, _)).Times(0); |
| EXPECT_CALL(*this, AttributeMethod(kType2, PayloadIs("123"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*this, AttributeMethod(kType3, PayloadIs("12345"))) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(list->IterateAttributes( |
| payload, kLength1, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // If one of the attribute methods returns false, the iteration should abort. |
| EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0123456789"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*this, AttributeMethod(kType2, PayloadIs("123"))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(*this, AttributeMethod(kType3, PayloadIs("12345"))).Times(0); |
| EXPECT_FALSE(list->IterateAttributes( |
| payload, 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| } |
| |
| TEST_F(AttributeListTest, SmallPayloads) { |
| // A payload must be at least 4 bytes long to incorporate the nlattr header. |
| EXPECT_CALL(*this, AttributeMethod(_, _)).Times(0); |
| AttributeListRefPtr list(new AttributeList()); |
| EXPECT_FALSE(list->IterateAttributes( |
| MakeNetlinkAttribute(kHeaderLength - 1, kType1, "0123"), 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // This is a minimal valid payload. |
| EXPECT_CALL(*this, AttributeMethod( |
| kType2, PayloadIs(""))).WillOnce(Return(true)); |
| EXPECT_TRUE(list->IterateAttributes( |
| MakeNetlinkAttribute(kHeaderLength, kType2, ""), 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // This is a minmal payload except there are not enough bytes to retrieve |
| // the attribute value. |
| const uint16_t kType3 = 1; |
| EXPECT_CALL(*this, AttributeMethod(_, _)).Times(0); |
| EXPECT_FALSE(list->IterateAttributes( |
| MakeNetlinkAttribute(kHeaderLength + 1, kType3, ""), 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| } |
| |
| TEST_F(AttributeListTest, TrailingGarbage) { |
| // +---------+ |
| // | Attr #1 | |
| // +-+-+-+-+-+ |
| // |LEN|TYP|0| |
| // +-+-+-+-+-+ |
| // Well formed frame. |
| ByteString payload(MakeNetlinkAttribute(kHeaderLength + 1, kType1, "0")); |
| EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0"))) |
| .WillOnce(Return(true)); |
| AttributeListRefPtr list(new AttributeList()); |
| EXPECT_TRUE(list->IterateAttributes( |
| payload, 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // +---------------+ |
| // | Attr #1 + pad | |
| // +-+-+-+-+-+-+-+-+ |
| // |LEN|TYP|0|1|2|3| |
| // +-+-+-+-+-+-+-+-+ |
| // "123" assumed to be padding for attr1. |
| payload.Append(ByteString(std::string("123"), false)); |
| EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0"))) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(list->IterateAttributes( |
| payload, 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // +---------------+-----+ |
| // | Attr #1 + pad |RUNT | |
| // +-+-+-+-+-+-+-+-+-+-+-+ |
| // |LEN|TYP|0|1|2|3|4|5|6| |
| // +-+-+-+-+-+-+-+-+-+-+-+ |
| // "456" is acceptable since it is not long enough to complete an netlink |
| // attribute header. |
| payload.Append(ByteString(std::string("456"), false)); |
| EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0"))) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(list->IterateAttributes( |
| payload, 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| |
| // +---------------+-------+ |
| // | Attr #1 + pad |Broken | |
| // +-+-+-+-+-+-+-+-+-+-+-+-+ |
| // |LEN|TYP|0|1|2|3|4|5|6|7| |
| // +-+-+-+-+-+-+-+-+-+-+-+-+ |
| // This is a broken frame, since '4567' can be interpreted as a complete |
| // nlatter header, but is malformed since there is not enough payload to |
| // satisfy the "length" parameter. |
| payload.Append(ByteString(std::string("7"), false)); |
| EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0"))) |
| .WillOnce(Return(true)); |
| EXPECT_FALSE(list->IterateAttributes( |
| payload, 0, |
| base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this)))); |
| Mock::VerifyAndClearExpectations(this); |
| } |
| |
| } // namespace shill |