1 //
2 // Copyright (C) 2015 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 // This file tests some public interface methods of AttributeList.
18 #include "shill/net/attribute_list.h"
19
20 #include <linux/netlink.h>
21
22 #include <string>
23
24 #include <base/bind.h>
25 #include <gmock/gmock.h>
26 #include <gtest/gtest.h>
27
28 #include "shill/net/byte_string.h"
29
30 using testing::_;
31 using testing::InSequence;
32 using testing::Mock;
33 using testing::Return;
34 using testing::Test;
35
36 namespace shill {
37
38 class AttributeListTest : public Test {
39 public:
40 MOCK_METHOD2(AttributeMethod, bool(int id, const ByteString& value));
41
42 protected:
43 static const uint16_t kHeaderLength = 4;
44 static const uint16_t kType1 = 1;
45 static const uint16_t kType2 = 2;
46 static const uint16_t kType3 = 3;
47
MakeNetlinkAttribute(uint16_t len,uint16_t type,const std::string & payload)48 static ByteString MakeNetlinkAttribute(uint16_t len,
49 uint16_t type,
50 const std::string& payload) {
51 nlattr attribute{ len, type };
52 ByteString data(reinterpret_cast<const char*>(&attribute),
53 sizeof(attribute));
54 data.Append(ByteString(payload, false));
55 return data;
56 }
57
MakePaddedNetlinkAttribute(uint16_t len,uint16_t type,const std::string & payload)58 static ByteString MakePaddedNetlinkAttribute(uint16_t len,
59 uint16_t type,
60 const std::string& payload) {
61 ByteString data(MakeNetlinkAttribute(len, type, payload));
62 ByteString padding(NLA_ALIGN(data.GetLength()) - data.GetLength());
63 data.Append(padding);
64 return data;
65 }
66 };
67
68 MATCHER_P(PayloadIs, payload, "") {
69 return arg.Equals(ByteString(std::string(payload), false));
70 }
71
TEST_F(AttributeListTest,IterateEmptyPayload)72 TEST_F(AttributeListTest, IterateEmptyPayload) {
73 EXPECT_CALL(*this, AttributeMethod(_, _)).Times(0);
74 AttributeListRefPtr list(new AttributeList());
75 EXPECT_TRUE(list->IterateAttributes(
76 ByteString(), 0,
77 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
78 }
79
TEST_F(AttributeListTest,IteratePayload)80 TEST_F(AttributeListTest, IteratePayload) {
81 ByteString payload;
82 payload.Append(MakePaddedNetlinkAttribute(
83 kHeaderLength + 10, kType1, "0123456789"));
84 const uint16_t kLength1 = kHeaderLength + 10 + 2; // 2 bytes padding.
85 ASSERT_EQ(kLength1, payload.GetLength());
86 payload.Append(MakePaddedNetlinkAttribute(kHeaderLength + 3, kType2, "123"));
87 const uint16_t kLength2 = kLength1 + kHeaderLength + 3 + 1; // 1 byte pad.
88 ASSERT_EQ(kLength2, payload.GetLength());
89
90 payload.Append(MakeNetlinkAttribute(kHeaderLength + 5, kType3, "12345"));
91 const uint16_t kLength3 = kLength2 + kHeaderLength + 5;
92 ASSERT_EQ(kLength3, payload.GetLength());
93
94 InSequence seq;
95 EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0123456789")))
96 .WillOnce(Return(true));
97 EXPECT_CALL(*this, AttributeMethod(kType2, PayloadIs("123")))
98 .WillOnce(Return(true));
99 EXPECT_CALL(*this, AttributeMethod(kType3, PayloadIs("12345")))
100 .WillOnce(Return(true));
101 AttributeListRefPtr list(new AttributeList());
102 EXPECT_TRUE(list->IterateAttributes(
103 payload, 0,
104 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
105 Mock::VerifyAndClearExpectations(this);
106
107 // If a valid offset is provided only the attributes that follow should
108 // be enumerated.
109 EXPECT_CALL(*this, AttributeMethod(kType1, _)).Times(0);
110 EXPECT_CALL(*this, AttributeMethod(kType2, PayloadIs("123")))
111 .WillOnce(Return(true));
112 EXPECT_CALL(*this, AttributeMethod(kType3, PayloadIs("12345")))
113 .WillOnce(Return(true));
114 EXPECT_TRUE(list->IterateAttributes(
115 payload, kLength1,
116 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
117 Mock::VerifyAndClearExpectations(this);
118
119 // If one of the attribute methods returns false, the iteration should abort.
120 EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0123456789")))
121 .WillOnce(Return(true));
122 EXPECT_CALL(*this, AttributeMethod(kType2, PayloadIs("123")))
123 .WillOnce(Return(false));
124 EXPECT_CALL(*this, AttributeMethod(kType3, PayloadIs("12345"))).Times(0);
125 EXPECT_FALSE(list->IterateAttributes(
126 payload, 0,
127 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
128 Mock::VerifyAndClearExpectations(this);
129 }
130
TEST_F(AttributeListTest,SmallPayloads)131 TEST_F(AttributeListTest, SmallPayloads) {
132 // A payload must be at least 4 bytes long to incorporate the nlattr header.
133 EXPECT_CALL(*this, AttributeMethod(_, _)).Times(0);
134 AttributeListRefPtr list(new AttributeList());
135 EXPECT_FALSE(list->IterateAttributes(
136 MakeNetlinkAttribute(kHeaderLength - 1, kType1, "0123"), 0,
137 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
138 Mock::VerifyAndClearExpectations(this);
139
140 // This is a minimal valid payload.
141 EXPECT_CALL(*this, AttributeMethod(
142 kType2, PayloadIs(""))).WillOnce(Return(true));
143 EXPECT_TRUE(list->IterateAttributes(
144 MakeNetlinkAttribute(kHeaderLength, kType2, ""), 0,
145 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
146 Mock::VerifyAndClearExpectations(this);
147
148 // This is a minmal payload except there are not enough bytes to retrieve
149 // the attribute value.
150 const uint16_t kType3 = 1;
151 EXPECT_CALL(*this, AttributeMethod(_, _)).Times(0);
152 EXPECT_FALSE(list->IterateAttributes(
153 MakeNetlinkAttribute(kHeaderLength + 1, kType3, ""), 0,
154 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
155 }
156
TEST_F(AttributeListTest,TrailingGarbage)157 TEST_F(AttributeListTest, TrailingGarbage) {
158 // +---------+
159 // | Attr #1 |
160 // +-+-+-+-+-+
161 // |LEN|TYP|0|
162 // +-+-+-+-+-+
163 // Well formed frame.
164 ByteString payload(MakeNetlinkAttribute(kHeaderLength + 1, kType1, "0"));
165 EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0")))
166 .WillOnce(Return(true));
167 AttributeListRefPtr list(new AttributeList());
168 EXPECT_TRUE(list->IterateAttributes(
169 payload, 0,
170 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
171 Mock::VerifyAndClearExpectations(this);
172
173 // +---------------+
174 // | Attr #1 + pad |
175 // +-+-+-+-+-+-+-+-+
176 // |LEN|TYP|0|1|2|3|
177 // +-+-+-+-+-+-+-+-+
178 // "123" assumed to be padding for attr1.
179 payload.Append(ByteString(std::string("123"), false));
180 EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0")))
181 .WillOnce(Return(true));
182 EXPECT_TRUE(list->IterateAttributes(
183 payload, 0,
184 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
185 Mock::VerifyAndClearExpectations(this);
186
187 // +---------------+-----+
188 // | Attr #1 + pad |RUNT |
189 // +-+-+-+-+-+-+-+-+-+-+-+
190 // |LEN|TYP|0|1|2|3|4|5|6|
191 // +-+-+-+-+-+-+-+-+-+-+-+
192 // "456" is acceptable since it is not long enough to complete an netlink
193 // attribute header.
194 payload.Append(ByteString(std::string("456"), false));
195 EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0")))
196 .WillOnce(Return(true));
197 EXPECT_TRUE(list->IterateAttributes(
198 payload, 0,
199 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
200 Mock::VerifyAndClearExpectations(this);
201
202 // +---------------+-------+
203 // | Attr #1 + pad |Broken |
204 // +-+-+-+-+-+-+-+-+-+-+-+-+
205 // |LEN|TYP|0|1|2|3|4|5|6|7|
206 // +-+-+-+-+-+-+-+-+-+-+-+-+
207 // This is a broken frame, since '4567' can be interpreted as a complete
208 // nlatter header, but is malformed since there is not enough payload to
209 // satisfy the "length" parameter.
210 payload.Append(ByteString(std::string("7"), false));
211 EXPECT_CALL(*this, AttributeMethod(kType1, PayloadIs("0")))
212 .WillOnce(Return(true));
213 EXPECT_FALSE(list->IterateAttributes(
214 payload, 0,
215 base::Bind(&AttributeListTest::AttributeMethod, base::Unretained(this))));
216 Mock::VerifyAndClearExpectations(this);
217 }
218
219 } // namespace shill
220