blob: 0404fb446c8cd3aad63178ace64fe502e41ab16e [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),
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) {
125 // We hit a parse error while going through the attributes
126 attributes_.clear();
127 return false;
128 }
129
130 return true;
131}
132
133bool RTNLMessage::DecodeLink(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700134 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700135 rtattr **attr_data,
136 int *attr_length) {
137 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) {
138 return false;
139 }
140
141 mode_ = mode;
142 *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr));
143 *attr_length = IFLA_PAYLOAD(&hdr->hdr);
144
Paul Stewart9a908082011-08-31 12:18:48 -0700145 type_ = kTypeLink;
Paul Stewartdd7df792011-07-15 11:09:50 -0700146 family_ = hdr->ifi.ifi_family;
147 interface_index_ = hdr->ifi.ifi_index;
148 set_link_status(LinkStatus(hdr->ifi.ifi_type,
149 hdr->ifi.ifi_flags,
150 hdr->ifi.ifi_change));
151 return true;
152}
153
154bool RTNLMessage::DecodeAddress(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700155 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700156 rtattr **attr_data,
157 int *attr_length) {
158 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) {
159 return false;
160 }
161 mode_ = mode;
162 *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr));
163 *attr_length = IFA_PAYLOAD(&hdr->hdr);
164
Paul Stewart9a908082011-08-31 12:18:48 -0700165 type_ = kTypeAddress;
Paul Stewartdd7df792011-07-15 11:09:50 -0700166 family_ = hdr->ifa.ifa_family;
167 interface_index_ = hdr->ifa.ifa_index;
168 set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen,
169 hdr->ifa.ifa_flags,
170 hdr->ifa.ifa_scope));
171 return true;
172}
173
174bool RTNLMessage::DecodeRoute(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700175 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700176 rtattr **attr_data,
177 int *attr_length) {
178 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) {
179 return false;
180 }
181 mode_ = mode;
182 *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr));
183 *attr_length = RTM_PAYLOAD(&hdr->hdr);
184
Paul Stewart9a908082011-08-31 12:18:48 -0700185 type_ = kTypeRoute;
Paul Stewartdd7df792011-07-15 11:09:50 -0700186 family_ = hdr->rtm.rtm_family;
187 set_route_status(RouteStatus(hdr->rtm.rtm_dst_len,
188 hdr->rtm.rtm_src_len,
189 hdr->rtm.rtm_table,
190 hdr->rtm.rtm_protocol,
191 hdr->rtm.rtm_scope,
192 hdr->rtm.rtm_type,
193 hdr->rtm.rtm_flags));
194 return true;
195}
196
Paul Stewartf748a362012-03-07 12:01:20 -0800197ByteString RTNLMessage::Encode() const {
Paul Stewart9a908082011-08-31 12:18:48 -0700198 if (type_ != kTypeLink &&
199 type_ != kTypeAddress &&
200 type_ != kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700201 return ByteString();
202 }
203
204 RTNLHeader hdr;
205 hdr.hdr.nlmsg_flags = flags_;
206 hdr.hdr.nlmsg_seq = seq_;
207 hdr.hdr.nlmsg_pid = pid_;
Paul Stewartdd7df792011-07-15 11:09:50 -0700208
Paul Stewart9a908082011-08-31 12:18:48 -0700209 if (mode_ == kModeGet) {
210 if (type_ == kTypeLink) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700211 hdr.hdr.nlmsg_type = RTM_GETLINK;
Paul Stewart9a908082011-08-31 12:18:48 -0700212 } else if (type_ == kTypeAddress) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700213 hdr.hdr.nlmsg_type = RTM_GETADDR;
Paul Stewart9a908082011-08-31 12:18:48 -0700214 } else if (type_ == kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700215 hdr.hdr.nlmsg_type = RTM_GETROUTE;
Paul Stewartf748a362012-03-07 12:01:20 -0800216 } else {
217 NOTIMPLEMENTED();
218 return ByteString();
Paul Stewartdd7df792011-07-15 11:09:50 -0700219 }
220 hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
Paul Stewart9a908082011-08-31 12:18:48 -0700221 hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
Paul Stewartdd7df792011-07-15 11:09:50 -0700222 hdr.gen.rtgen_family = family_;
223 } else {
224 switch (type_) {
Paul Stewart9a908082011-08-31 12:18:48 -0700225 case kTypeLink:
Paul Stewartf748a362012-03-07 12:01:20 -0800226 if (!EncodeLink(&hdr)) {
227 return ByteString();
228 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700229 break;
230
Paul Stewart9a908082011-08-31 12:18:48 -0700231 case kTypeAddress:
Paul Stewartf748a362012-03-07 12:01:20 -0800232 if (!EncodeAddress(&hdr)) {
233 return ByteString();
234 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700235 break;
236
Paul Stewart9a908082011-08-31 12:18:48 -0700237 case kTypeRoute:
Paul Stewartf748a362012-03-07 12:01:20 -0800238 if (!EncodeRoute(&hdr)) {
239 return ByteString();
240 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700241 break;
242
243 default:
244 NOTREACHED();
245 }
246 }
247
248 size_t header_length = hdr.hdr.nlmsg_len;
249 ByteString attributes;
250
Paul Stewartf748a362012-03-07 12:01:20 -0800251 base::hash_map<uint16, ByteString>::const_iterator attr;
Paul Stewartdd7df792011-07-15 11:09:50 -0700252 for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
253 size_t len = RTA_LENGTH(attr->second.GetLength());
254 hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
255
256 struct rtattr rt_attr = { len, attr->first };
257 ByteString attr_header(reinterpret_cast<unsigned char *>(&rt_attr),
258 sizeof(rt_attr));
259 attr_header.Resize(RTA_ALIGN(attr_header.GetLength()));
260 attributes.Append(attr_header);
261
262 ByteString attr_data(attr->second);
263 attr_data.Resize(RTA_ALIGN(attr_data.GetLength()));
264 attributes.Append(attr_data);
265 }
266
267 ByteString packet(reinterpret_cast<unsigned char *>(&hdr), header_length);
268 packet.Append(attributes);
269
270 return packet;
271}
272
Paul Stewartf748a362012-03-07 12:01:20 -0800273bool RTNLMessage::EncodeLink(RTNLHeader *hdr) const {
274 switch (mode_) {
275 case kModeAdd:
276 hdr->hdr.nlmsg_type = RTM_NEWLINK;
277 break;
278 case kModeDelete:
279 hdr->hdr.nlmsg_type = RTM_DELLINK;
280 break;
281 case kModeQuery:
282 hdr->hdr.nlmsg_type = RTM_GETLINK;
283 break;
284 default:
285 NOTIMPLEMENTED();
286 return false;
287 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700288 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
289 hdr->ifi.ifi_family = family_;
Paul Stewartcba0f7f2012-02-29 16:33:05 -0800290 hdr->ifi.ifi_index = interface_index_;
Paul Stewartdd7df792011-07-15 11:09:50 -0700291 hdr->ifi.ifi_type = link_status_.type;
292 hdr->ifi.ifi_flags = link_status_.flags;
293 hdr->ifi.ifi_change = link_status_.change;
Paul Stewartf748a362012-03-07 12:01:20 -0800294 return true;
Paul Stewartdd7df792011-07-15 11:09:50 -0700295}
296
Paul Stewartf748a362012-03-07 12:01:20 -0800297bool RTNLMessage::EncodeAddress(RTNLHeader *hdr) const {
298 switch (mode_) {
299 case kModeAdd:
300 hdr->hdr.nlmsg_type = RTM_NEWADDR;
301 break;
302 case kModeDelete:
303 hdr->hdr.nlmsg_type = RTM_DELADDR;
304 break;
305 case kModeQuery:
306 hdr->hdr.nlmsg_type = RTM_GETADDR;
307 break;
308 default:
309 NOTIMPLEMENTED();
310 return false;
311 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700312 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
313 hdr->ifa.ifa_family = family_;
314 hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
315 hdr->ifa.ifa_flags = address_status_.flags;
316 hdr->ifa.ifa_scope = address_status_.scope;
317 hdr->ifa.ifa_index = interface_index_;
Paul Stewartf748a362012-03-07 12:01:20 -0800318 return true;
Paul Stewartdd7df792011-07-15 11:09:50 -0700319}
320
Paul Stewartf748a362012-03-07 12:01:20 -0800321bool RTNLMessage::EncodeRoute(RTNLHeader *hdr) const {
322 switch (mode_) {
323 case kModeAdd:
324 hdr->hdr.nlmsg_type = RTM_NEWROUTE;
325 break;
326 case kModeDelete:
327 hdr->hdr.nlmsg_type = RTM_DELROUTE;
328 break;
329 case kModeQuery:
330 hdr->hdr.nlmsg_type = RTM_GETROUTE;
331 break;
332 default:
333 NOTIMPLEMENTED();
334 return false;
335 }
Paul Stewartdd7df792011-07-15 11:09:50 -0700336 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
337 hdr->rtm.rtm_family = family_;
338 hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
339 hdr->rtm.rtm_src_len = route_status_.src_prefix;
340 hdr->rtm.rtm_table = route_status_.table;
341 hdr->rtm.rtm_protocol = route_status_.protocol;
342 hdr->rtm.rtm_scope = route_status_.scope;
343 hdr->rtm.rtm_type = route_status_.type;
344 hdr->rtm.rtm_flags = route_status_.flags;
Paul Stewartf748a362012-03-07 12:01:20 -0800345 return true;
346}
347
348void RTNLMessage::Reset() {
349 mode_ = kModeUnknown;
350 type_ = kTypeUnknown;
351 flags_ = 0;
352 seq_ = 0;
Darin Petkove636c692012-05-31 10:22:17 +0200353 pid_ = 0;
Paul Stewartf748a362012-03-07 12:01:20 -0800354 interface_index_ = 0;
355 family_ = IPAddress::kFamilyUnknown;
356 link_status_ = LinkStatus();
357 address_status_ = AddressStatus();
358 route_status_ = RouteStatus();
359 attributes_.clear();
Paul Stewartdd7df792011-07-15 11:09:50 -0700360}
361
362} // namespace shill