blob: a2d85c1763a1ab915533af70313f2db9d6c9933c [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
Paul Stewartdd7df792011-07-15 11:09:50 -07007#include <sys/socket.h>
8#include <linux/netlink.h>
9#include <linux/rtnetlink.h>
10
Christopher Wileyb691efd2012-08-09 13:51:51 -070011#include "shill/logging.h"
12
Paul Stewartdd7df792011-07-15 11:09:50 -070013namespace 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),
Darin Petkove636c692012-05-31 10:22:17 +020032 seq_(0),
33 pid_(0),
Paul Stewartdd7df792011-07-15 11:09:50 -070034 interface_index_(0),
Paul Stewart7355ce12011-09-02 10:47:01 -070035 family_(IPAddress::kFamilyUnknown) {}
Paul Stewartdd7df792011-07-15 11:09:50 -070036
Paul Stewart9a908082011-08-31 12:18:48 -070037RTNLMessage::RTNLMessage(Type type,
38 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -070039 unsigned int flags,
40 uint32 seq,
41 uint32 pid,
42 int interface_index,
43 IPAddress::Family family)
44 : type_(type),
45 mode_(mode),
46 flags_(flags),
47 seq_(seq),
48 pid_(pid),
49 interface_index_(interface_index),
50 family_(family) {}
51
52bool RTNLMessage::Decode(const ByteString &msg) {
53 bool ret = DecodeInternal(msg);
54 if (!ret) {
Paul Stewartf748a362012-03-07 12:01:20 -080055 Reset();
Paul Stewartdd7df792011-07-15 11:09:50 -070056 }
57 return ret;
58}
59
60bool RTNLMessage::DecodeInternal(const ByteString &msg) {
61 const RTNLHeader *hdr =
62 reinterpret_cast<const RTNLHeader *>(msg.GetConstData());
63
64 if (msg.GetLength() < sizeof(hdr->hdr) ||
65 msg.GetLength() < hdr->hdr.nlmsg_len)
66 return false;
67
Paul Stewart9a908082011-08-31 12:18:48 -070068 Mode mode = kModeUnknown;
Paul Stewartdd7df792011-07-15 11:09:50 -070069 switch (hdr->hdr.nlmsg_type) {
70 case RTM_NEWLINK:
71 case RTM_NEWADDR:
72 case RTM_NEWROUTE:
Paul Stewart9a908082011-08-31 12:18:48 -070073 mode = kModeAdd;
Paul Stewartdd7df792011-07-15 11:09:50 -070074 break;
75
76 case RTM_DELLINK:
77 case RTM_DELADDR:
78 case RTM_DELROUTE:
Paul Stewart9a908082011-08-31 12:18:48 -070079 mode = kModeDelete;
Paul Stewartdd7df792011-07-15 11:09:50 -070080 break;
81
82 default:
83 return false;
84 }
85
86 rtattr *attr_data = NULL;
87 int attr_length = 0;
88
89 switch (hdr->hdr.nlmsg_type) {
90 case RTM_NEWLINK:
91 case RTM_DELLINK:
92 if (!DecodeLink(hdr, mode, &attr_data, &attr_length))
93 return false;
94 break;
95
96 case RTM_NEWADDR:
97 case RTM_DELADDR:
98 if (!DecodeAddress(hdr, mode, &attr_data, &attr_length))
99 return false;
100 break;
101
102 case RTM_NEWROUTE:
103 case RTM_DELROUTE:
104 if (!DecodeRoute(hdr, mode, &attr_data, &attr_length))
105 return false;
106 break;
107
108 default:
109 NOTREACHED();
110 }
111
112 flags_ = hdr->hdr.nlmsg_flags;
113 seq_ = hdr->hdr.nlmsg_seq;
114 pid_ = hdr->hdr.nlmsg_pid;
115
116 while (attr_data && RTA_OK(attr_data, attr_length)) {
117 SetAttribute(
118 attr_data->rta_type,
119 ByteString(reinterpret_cast<unsigned char *>(RTA_DATA(attr_data)),
120 RTA_PAYLOAD(attr_data)));
121 attr_data = RTA_NEXT(attr_data, attr_length);
122 }
123
124 if (attr_length) {
Julius Werner87fc23e2012-10-18 14:21:33 -0700125 // temporary debug output
126 // TODO(jwerner@chromium.org): Remove after gathering data (crosbug 35479)
127 if (hdr->hdr.nlmsg_type == RTM_NEWLINK)
128 LOG(INFO) << "Broken RTM_NEWLINK attributes: " << ByteString((char *)
129 IFLA_RTA(NLMSG_DATA(&hdr->hdr)), IFLA_PAYLOAD(&hdr->hdr)).HexEncode();
130
Paul Stewartdd7df792011-07-15 11:09:50 -0700131 // We hit a parse error while going through the attributes
132 attributes_.clear();
133 return false;
134 }
135
136 return true;
137}
138
139bool RTNLMessage::DecodeLink(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700140 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700141 rtattr **attr_data,
142 int *attr_length) {
143 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) {
144 return false;
145 }
146
147 mode_ = mode;
148 *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr));
149 *attr_length = IFLA_PAYLOAD(&hdr->hdr);
150
Paul Stewart9a908082011-08-31 12:18:48 -0700151 type_ = kTypeLink;
Paul Stewartdd7df792011-07-15 11:09:50 -0700152 family_ = hdr->ifi.ifi_family;
153 interface_index_ = hdr->ifi.ifi_index;
154 set_link_status(LinkStatus(hdr->ifi.ifi_type,
155 hdr->ifi.ifi_flags,
156 hdr->ifi.ifi_change));
157 return true;
158}
159
160bool RTNLMessage::DecodeAddress(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700161 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700162 rtattr **attr_data,
163 int *attr_length) {
164 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) {
165 return false;
166 }
167 mode_ = mode;
168 *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr));
169 *attr_length = IFA_PAYLOAD(&hdr->hdr);
170
Paul Stewart9a908082011-08-31 12:18:48 -0700171 type_ = kTypeAddress;
Paul Stewartdd7df792011-07-15 11:09:50 -0700172 family_ = hdr->ifa.ifa_family;
173 interface_index_ = hdr->ifa.ifa_index;
174 set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen,
175 hdr->ifa.ifa_flags,
176 hdr->ifa.ifa_scope));
177 return true;
178}
179
180bool RTNLMessage::DecodeRoute(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700181 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700182 rtattr **attr_data,
183 int *attr_length) {
184 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) {
185 return false;
186 }
187 mode_ = mode;
188 *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr));
189 *attr_length = RTM_PAYLOAD(&hdr->hdr);
190
Paul Stewart9a908082011-08-31 12:18:48 -0700191 type_ = kTypeRoute;
Paul Stewartdd7df792011-07-15 11:09:50 -0700192 family_ = hdr->rtm.rtm_family;
193 set_route_status(RouteStatus(hdr->rtm.rtm_dst_len,
194 hdr->rtm.rtm_src_len,
195 hdr->rtm.rtm_table,
196 hdr->rtm.rtm_protocol,
197 hdr->rtm.rtm_scope,
198 hdr->rtm.rtm_type,
199 hdr->rtm.rtm_flags));
200 return true;
201}
202
Paul Stewartf748a362012-03-07 12:01:20 -0800203ByteString RTNLMessage::Encode() const {
Paul Stewart9a908082011-08-31 12:18:48 -0700204 if (type_ != kTypeLink &&
205 type_ != kTypeAddress &&
206 type_ != kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700207 return ByteString();
208 }
209
210 RTNLHeader hdr;
211 hdr.hdr.nlmsg_flags = flags_;
212 hdr.hdr.nlmsg_seq = seq_;
213 hdr.hdr.nlmsg_pid = pid_;
Paul Stewartdd7df792011-07-15 11:09:50 -0700214
Paul Stewart9a908082011-08-31 12:18:48 -0700215 if (mode_ == kModeGet) {
216 if (type_ == kTypeLink) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700217 hdr.hdr.nlmsg_type = RTM_GETLINK;
Paul Stewart9a908082011-08-31 12:18:48 -0700218 } else if (type_ == kTypeAddress) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700219 hdr.hdr.nlmsg_type = RTM_GETADDR;
Paul Stewart9a908082011-08-31 12:18:48 -0700220 } else if (type_ == kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700221 hdr.hdr.nlmsg_type = RTM_GETROUTE;
Paul Stewartf748a362012-03-07 12:01:20 -0800222 } else {
223 NOTIMPLEMENTED();
224 return ByteString();
Paul Stewartdd7df792011-07-15 11:09:50 -0700225 }
226 hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
Paul Stewart9a908082011-08-31 12:18:48 -0700227 hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
Paul Stewartdd7df792011-07-15 11:09:50 -0700228 hdr.gen.rtgen_family = family_;
229 } else {
230 switch (type_) {
Paul Stewart9a908082011-08-31 12:18:48 -0700231 case kTypeLink:
Paul Stewartf748a362012-03-07 12:01:20 -0800232 if (!EncodeLink(&hdr)) {
233 return ByteString();
234 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700235 break;
236
Paul Stewart9a908082011-08-31 12:18:48 -0700237 case kTypeAddress:
Paul Stewartf748a362012-03-07 12:01:20 -0800238 if (!EncodeAddress(&hdr)) {
239 return ByteString();
240 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700241 break;
242
Paul Stewart9a908082011-08-31 12:18:48 -0700243 case kTypeRoute:
Paul Stewartf748a362012-03-07 12:01:20 -0800244 if (!EncodeRoute(&hdr)) {
245 return ByteString();
246 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700247 break;
248
249 default:
250 NOTREACHED();
251 }
252 }
253
254 size_t header_length = hdr.hdr.nlmsg_len;
255 ByteString attributes;
256
Paul Stewartf748a362012-03-07 12:01:20 -0800257 base::hash_map<uint16, ByteString>::const_iterator attr;
Paul Stewartdd7df792011-07-15 11:09:50 -0700258 for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
259 size_t len = RTA_LENGTH(attr->second.GetLength());
260 hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
261
Han Shenb970b8f2012-07-18 15:57:19 -0700262 struct rtattr rt_attr = { static_cast<unsigned short>(len), attr->first };
Paul Stewartdd7df792011-07-15 11:09:50 -0700263 ByteString attr_header(reinterpret_cast<unsigned char *>(&rt_attr),
264 sizeof(rt_attr));
265 attr_header.Resize(RTA_ALIGN(attr_header.GetLength()));
266 attributes.Append(attr_header);
267
268 ByteString attr_data(attr->second);
269 attr_data.Resize(RTA_ALIGN(attr_data.GetLength()));
270 attributes.Append(attr_data);
271 }
272
273 ByteString packet(reinterpret_cast<unsigned char *>(&hdr), header_length);
274 packet.Append(attributes);
275
276 return packet;
277}
278
Paul Stewartf748a362012-03-07 12:01:20 -0800279bool RTNLMessage::EncodeLink(RTNLHeader *hdr) const {
280 switch (mode_) {
281 case kModeAdd:
282 hdr->hdr.nlmsg_type = RTM_NEWLINK;
283 break;
284 case kModeDelete:
285 hdr->hdr.nlmsg_type = RTM_DELLINK;
286 break;
287 case kModeQuery:
288 hdr->hdr.nlmsg_type = RTM_GETLINK;
289 break;
290 default:
291 NOTIMPLEMENTED();
292 return false;
293 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700294 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
295 hdr->ifi.ifi_family = family_;
Paul Stewartcba0f7f2012-02-29 16:33:05 -0800296 hdr->ifi.ifi_index = interface_index_;
Paul Stewartdd7df792011-07-15 11:09:50 -0700297 hdr->ifi.ifi_type = link_status_.type;
298 hdr->ifi.ifi_flags = link_status_.flags;
299 hdr->ifi.ifi_change = link_status_.change;
Paul Stewartf748a362012-03-07 12:01:20 -0800300 return true;
Paul Stewartdd7df792011-07-15 11:09:50 -0700301}
302
Paul Stewartf748a362012-03-07 12:01:20 -0800303bool RTNLMessage::EncodeAddress(RTNLHeader *hdr) const {
304 switch (mode_) {
305 case kModeAdd:
306 hdr->hdr.nlmsg_type = RTM_NEWADDR;
307 break;
308 case kModeDelete:
309 hdr->hdr.nlmsg_type = RTM_DELADDR;
310 break;
311 case kModeQuery:
312 hdr->hdr.nlmsg_type = RTM_GETADDR;
313 break;
314 default:
315 NOTIMPLEMENTED();
316 return false;
317 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700318 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
319 hdr->ifa.ifa_family = family_;
320 hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
321 hdr->ifa.ifa_flags = address_status_.flags;
322 hdr->ifa.ifa_scope = address_status_.scope;
323 hdr->ifa.ifa_index = interface_index_;
Paul Stewartf748a362012-03-07 12:01:20 -0800324 return true;
Paul Stewartdd7df792011-07-15 11:09:50 -0700325}
326
Paul Stewartf748a362012-03-07 12:01:20 -0800327bool RTNLMessage::EncodeRoute(RTNLHeader *hdr) const {
328 switch (mode_) {
329 case kModeAdd:
330 hdr->hdr.nlmsg_type = RTM_NEWROUTE;
331 break;
332 case kModeDelete:
333 hdr->hdr.nlmsg_type = RTM_DELROUTE;
334 break;
335 case kModeQuery:
336 hdr->hdr.nlmsg_type = RTM_GETROUTE;
337 break;
338 default:
339 NOTIMPLEMENTED();
340 return false;
341 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700342 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
343 hdr->rtm.rtm_family = family_;
344 hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
345 hdr->rtm.rtm_src_len = route_status_.src_prefix;
346 hdr->rtm.rtm_table = route_status_.table;
347 hdr->rtm.rtm_protocol = route_status_.protocol;
348 hdr->rtm.rtm_scope = route_status_.scope;
349 hdr->rtm.rtm_type = route_status_.type;
350 hdr->rtm.rtm_flags = route_status_.flags;
Paul Stewartf748a362012-03-07 12:01:20 -0800351 return true;
352}
353
354void RTNLMessage::Reset() {
355 mode_ = kModeUnknown;
356 type_ = kTypeUnknown;
357 flags_ = 0;
358 seq_ = 0;
Darin Petkove636c692012-05-31 10:22:17 +0200359 pid_ = 0;
Paul Stewartf748a362012-03-07 12:01:20 -0800360 interface_index_ = 0;
361 family_ = IPAddress::kFamilyUnknown;
362 link_status_ = LinkStatus();
363 address_status_ = AddressStatus();
364 route_status_ = RouteStatus();
365 attributes_.clear();
Paul Stewartdd7df792011-07-15 11:09:50 -0700366}
367
368} // namespace shill