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
30using testing::_;
31using testing::InSequence;
32using testing::Mock;
33using testing::Return;
34using testing::Test;
35
36namespace shill {
37
38class 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
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
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
68MATCHER_P(PayloadIs, payload, "") {
69  return arg.Equals(ByteString(std::string(payload), false));
70}
71
72TEST_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
80TEST_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
131TEST_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
157TEST_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