blob: 6b4c6a0498eaf11a14280be51e9faf48dda3ed0f [file] [log] [blame]
Paul Stewartf748a362012-03-07 12:01:20 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartdd7df792011-07-15 11:09:50 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/rtnl_message.h"
6
7#include <base/logging.h>
8
9#include <sys/socket.h>
10#include <linux/netlink.h>
11#include <linux/rtnetlink.h>
12
13namespace shill {
14
15struct RTNLHeader {
16 RTNLHeader() {
17 memset(this, 0, sizeof(*this));
18 }
19 struct nlmsghdr hdr;
20 union {
21 struct ifinfomsg ifi;
22 struct ifaddrmsg ifa;
23 struct rtmsg rtm;
24 struct rtgenmsg gen;
25 };
26};
27
28RTNLMessage::RTNLMessage()
Paul Stewart9a908082011-08-31 12:18:48 -070029 : type_(kTypeUnknown),
30 mode_(kModeUnknown),
Paul Stewartdd7df792011-07-15 11:09:50 -070031 flags_(0),
32 interface_index_(0),
Paul Stewart7355ce12011-09-02 10:47:01 -070033 family_(IPAddress::kFamilyUnknown) {}
Paul Stewartdd7df792011-07-15 11:09:50 -070034
Paul Stewart9a908082011-08-31 12:18:48 -070035RTNLMessage::RTNLMessage(Type type,
36 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -070037 unsigned int flags,
38 uint32 seq,
39 uint32 pid,
40 int interface_index,
41 IPAddress::Family family)
42 : type_(type),
43 mode_(mode),
44 flags_(flags),
45 seq_(seq),
46 pid_(pid),
47 interface_index_(interface_index),
48 family_(family) {}
49
50bool RTNLMessage::Decode(const ByteString &msg) {
51 bool ret = DecodeInternal(msg);
52 if (!ret) {
Paul Stewartf748a362012-03-07 12:01:20 -080053 Reset();
Paul Stewartdd7df792011-07-15 11:09:50 -070054 }
55 return ret;
56}
57
58bool RTNLMessage::DecodeInternal(const ByteString &msg) {
59 const RTNLHeader *hdr =
60 reinterpret_cast<const RTNLHeader *>(msg.GetConstData());
61
62 if (msg.GetLength() < sizeof(hdr->hdr) ||
63 msg.GetLength() < hdr->hdr.nlmsg_len)
64 return false;
65
Paul Stewart9a908082011-08-31 12:18:48 -070066 Mode mode = kModeUnknown;
Paul Stewartdd7df792011-07-15 11:09:50 -070067 switch (hdr->hdr.nlmsg_type) {
68 case RTM_NEWLINK:
69 case RTM_NEWADDR:
70 case RTM_NEWROUTE:
Paul Stewart9a908082011-08-31 12:18:48 -070071 mode = kModeAdd;
Paul Stewartdd7df792011-07-15 11:09:50 -070072 break;
73
74 case RTM_DELLINK:
75 case RTM_DELADDR:
76 case RTM_DELROUTE:
Paul Stewart9a908082011-08-31 12:18:48 -070077 mode = kModeDelete;
Paul Stewartdd7df792011-07-15 11:09:50 -070078 break;
79
80 default:
81 return false;
82 }
83
84 rtattr *attr_data = NULL;
85 int attr_length = 0;
86
87 switch (hdr->hdr.nlmsg_type) {
88 case RTM_NEWLINK:
89 case RTM_DELLINK:
90 if (!DecodeLink(hdr, mode, &attr_data, &attr_length))
91 return false;
92 break;
93
94 case RTM_NEWADDR:
95 case RTM_DELADDR:
96 if (!DecodeAddress(hdr, mode, &attr_data, &attr_length))
97 return false;
98 break;
99
100 case RTM_NEWROUTE:
101 case RTM_DELROUTE:
102 if (!DecodeRoute(hdr, mode, &attr_data, &attr_length))
103 return false;
104 break;
105
106 default:
107 NOTREACHED();
108 }
109
110 flags_ = hdr->hdr.nlmsg_flags;
111 seq_ = hdr->hdr.nlmsg_seq;
112 pid_ = hdr->hdr.nlmsg_pid;
113
114 while (attr_data && RTA_OK(attr_data, attr_length)) {
115 SetAttribute(
116 attr_data->rta_type,
117 ByteString(reinterpret_cast<unsigned char *>(RTA_DATA(attr_data)),
118 RTA_PAYLOAD(attr_data)));
119 attr_data = RTA_NEXT(attr_data, attr_length);
120 }
121
122 if (attr_length) {
123 // We hit a parse error while going through the attributes
124 attributes_.clear();
125 return false;
126 }
127
128 return true;
129}
130
131bool RTNLMessage::DecodeLink(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700132 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700133 rtattr **attr_data,
134 int *attr_length) {
135 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) {
136 return false;
137 }
138
139 mode_ = mode;
140 *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr));
141 *attr_length = IFLA_PAYLOAD(&hdr->hdr);
142
Paul Stewart9a908082011-08-31 12:18:48 -0700143 type_ = kTypeLink;
Paul Stewartdd7df792011-07-15 11:09:50 -0700144 family_ = hdr->ifi.ifi_family;
145 interface_index_ = hdr->ifi.ifi_index;
146 set_link_status(LinkStatus(hdr->ifi.ifi_type,
147 hdr->ifi.ifi_flags,
148 hdr->ifi.ifi_change));
149 return true;
150}
151
152bool RTNLMessage::DecodeAddress(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700153 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700154 rtattr **attr_data,
155 int *attr_length) {
156 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) {
157 return false;
158 }
159 mode_ = mode;
160 *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr));
161 *attr_length = IFA_PAYLOAD(&hdr->hdr);
162
Paul Stewart9a908082011-08-31 12:18:48 -0700163 type_ = kTypeAddress;
Paul Stewartdd7df792011-07-15 11:09:50 -0700164 family_ = hdr->ifa.ifa_family;
165 interface_index_ = hdr->ifa.ifa_index;
166 set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen,
167 hdr->ifa.ifa_flags,
168 hdr->ifa.ifa_scope));
169 return true;
170}
171
172bool RTNLMessage::DecodeRoute(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700173 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700174 rtattr **attr_data,
175 int *attr_length) {
176 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) {
177 return false;
178 }
179 mode_ = mode;
180 *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr));
181 *attr_length = RTM_PAYLOAD(&hdr->hdr);
182
Paul Stewart9a908082011-08-31 12:18:48 -0700183 type_ = kTypeRoute;
Paul Stewartdd7df792011-07-15 11:09:50 -0700184 family_ = hdr->rtm.rtm_family;
185 set_route_status(RouteStatus(hdr->rtm.rtm_dst_len,
186 hdr->rtm.rtm_src_len,
187 hdr->rtm.rtm_table,
188 hdr->rtm.rtm_protocol,
189 hdr->rtm.rtm_scope,
190 hdr->rtm.rtm_type,
191 hdr->rtm.rtm_flags));
192 return true;
193}
194
Paul Stewartf748a362012-03-07 12:01:20 -0800195ByteString RTNLMessage::Encode() const {
Paul Stewart9a908082011-08-31 12:18:48 -0700196 if (type_ != kTypeLink &&
197 type_ != kTypeAddress &&
198 type_ != kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700199 return ByteString();
200 }
201
202 RTNLHeader hdr;
203 hdr.hdr.nlmsg_flags = flags_;
204 hdr.hdr.nlmsg_seq = seq_;
205 hdr.hdr.nlmsg_pid = pid_;
Paul Stewartdd7df792011-07-15 11:09:50 -0700206
Paul Stewart9a908082011-08-31 12:18:48 -0700207 if (mode_ == kModeGet) {
208 if (type_ == kTypeLink) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700209 hdr.hdr.nlmsg_type = RTM_GETLINK;
Paul Stewart9a908082011-08-31 12:18:48 -0700210 } else if (type_ == kTypeAddress) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700211 hdr.hdr.nlmsg_type = RTM_GETADDR;
Paul Stewart9a908082011-08-31 12:18:48 -0700212 } else if (type_ == kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700213 hdr.hdr.nlmsg_type = RTM_GETROUTE;
Paul Stewartf748a362012-03-07 12:01:20 -0800214 } else {
215 NOTIMPLEMENTED();
216 return ByteString();
Paul Stewartdd7df792011-07-15 11:09:50 -0700217 }
218 hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
Paul Stewart9a908082011-08-31 12:18:48 -0700219 hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
Paul Stewartdd7df792011-07-15 11:09:50 -0700220 hdr.gen.rtgen_family = family_;
221 } else {
222 switch (type_) {
Paul Stewart9a908082011-08-31 12:18:48 -0700223 case kTypeLink:
Paul Stewartf748a362012-03-07 12:01:20 -0800224 if (!EncodeLink(&hdr)) {
225 return ByteString();
226 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700227 break;
228
Paul Stewart9a908082011-08-31 12:18:48 -0700229 case kTypeAddress:
Paul Stewartf748a362012-03-07 12:01:20 -0800230 if (!EncodeAddress(&hdr)) {
231 return ByteString();
232 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700233 break;
234
Paul Stewart9a908082011-08-31 12:18:48 -0700235 case kTypeRoute:
Paul Stewartf748a362012-03-07 12:01:20 -0800236 if (!EncodeRoute(&hdr)) {
237 return ByteString();
238 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700239 break;
240
241 default:
242 NOTREACHED();
243 }
244 }
245
246 size_t header_length = hdr.hdr.nlmsg_len;
247 ByteString attributes;
248
Paul Stewartf748a362012-03-07 12:01:20 -0800249 base::hash_map<uint16, ByteString>::const_iterator attr;
Paul Stewartdd7df792011-07-15 11:09:50 -0700250 for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
251 size_t len = RTA_LENGTH(attr->second.GetLength());
252 hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
253
254 struct rtattr rt_attr = { len, attr->first };
255 ByteString attr_header(reinterpret_cast<unsigned char *>(&rt_attr),
256 sizeof(rt_attr));
257 attr_header.Resize(RTA_ALIGN(attr_header.GetLength()));
258 attributes.Append(attr_header);
259
260 ByteString attr_data(attr->second);
261 attr_data.Resize(RTA_ALIGN(attr_data.GetLength()));
262 attributes.Append(attr_data);
263 }
264
265 ByteString packet(reinterpret_cast<unsigned char *>(&hdr), header_length);
266 packet.Append(attributes);
267
268 return packet;
269}
270
Paul Stewartf748a362012-03-07 12:01:20 -0800271bool RTNLMessage::EncodeLink(RTNLHeader *hdr) const {
272 switch (mode_) {
273 case kModeAdd:
274 hdr->hdr.nlmsg_type = RTM_NEWLINK;
275 break;
276 case kModeDelete:
277 hdr->hdr.nlmsg_type = RTM_DELLINK;
278 break;
279 case kModeQuery:
280 hdr->hdr.nlmsg_type = RTM_GETLINK;
281 break;
282 default:
283 NOTIMPLEMENTED();
284 return false;
285 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700286 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
287 hdr->ifi.ifi_family = family_;
Paul Stewartcba0f7f2012-02-29 16:33:05 -0800288 hdr->ifi.ifi_index = interface_index_;
Paul Stewartdd7df792011-07-15 11:09:50 -0700289 hdr->ifi.ifi_type = link_status_.type;
290 hdr->ifi.ifi_flags = link_status_.flags;
291 hdr->ifi.ifi_change = link_status_.change;
Paul Stewartf748a362012-03-07 12:01:20 -0800292 return true;
Paul Stewartdd7df792011-07-15 11:09:50 -0700293}
294
Paul Stewartf748a362012-03-07 12:01:20 -0800295bool RTNLMessage::EncodeAddress(RTNLHeader *hdr) const {
296 switch (mode_) {
297 case kModeAdd:
298 hdr->hdr.nlmsg_type = RTM_NEWADDR;
299 break;
300 case kModeDelete:
301 hdr->hdr.nlmsg_type = RTM_DELADDR;
302 break;
303 case kModeQuery:
304 hdr->hdr.nlmsg_type = RTM_GETADDR;
305 break;
306 default:
307 NOTIMPLEMENTED();
308 return false;
309 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700310 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
311 hdr->ifa.ifa_family = family_;
312 hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
313 hdr->ifa.ifa_flags = address_status_.flags;
314 hdr->ifa.ifa_scope = address_status_.scope;
315 hdr->ifa.ifa_index = interface_index_;
Paul Stewartf748a362012-03-07 12:01:20 -0800316 return true;
Paul Stewartdd7df792011-07-15 11:09:50 -0700317}
318
Paul Stewartf748a362012-03-07 12:01:20 -0800319bool RTNLMessage::EncodeRoute(RTNLHeader *hdr) const {
320 switch (mode_) {
321 case kModeAdd:
322 hdr->hdr.nlmsg_type = RTM_NEWROUTE;
323 break;
324 case kModeDelete:
325 hdr->hdr.nlmsg_type = RTM_DELROUTE;
326 break;
327 case kModeQuery:
328 hdr->hdr.nlmsg_type = RTM_GETROUTE;
329 break;
330 default:
331 NOTIMPLEMENTED();
332 return false;
333 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700334 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
335 hdr->rtm.rtm_family = family_;
336 hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
337 hdr->rtm.rtm_src_len = route_status_.src_prefix;
338 hdr->rtm.rtm_table = route_status_.table;
339 hdr->rtm.rtm_protocol = route_status_.protocol;
340 hdr->rtm.rtm_scope = route_status_.scope;
341 hdr->rtm.rtm_type = route_status_.type;
342 hdr->rtm.rtm_flags = route_status_.flags;
Paul Stewartf748a362012-03-07 12:01:20 -0800343 return true;
344}
345
346void RTNLMessage::Reset() {
347 mode_ = kModeUnknown;
348 type_ = kTypeUnknown;
349 flags_ = 0;
350 seq_ = 0;
351 interface_index_ = 0;
352 family_ = IPAddress::kFamilyUnknown;
353 link_status_ = LinkStatus();
354 address_status_ = AddressStatus();
355 route_status_ = RouteStatus();
356 attributes_.clear();
Paul Stewartdd7df792011-07-15 11:09:50 -0700357}
358
359} // namespace shill