blob: 96e3764fc5a5f450eda5e02149c6fb19dc46ccd2 [file] [log] [blame]
Paul Stewartdd7df792011-07-15 11:09:50 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2// 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 Stewart9a908082011-08-31 12:18:48 -070053 mode_ = kModeUnknown;
54 type_ = kTypeUnknown;
Paul Stewartdd7df792011-07-15 11:09:50 -070055 }
56 return ret;
57}
58
59bool RTNLMessage::DecodeInternal(const ByteString &msg) {
60 const RTNLHeader *hdr =
61 reinterpret_cast<const RTNLHeader *>(msg.GetConstData());
62
63 if (msg.GetLength() < sizeof(hdr->hdr) ||
64 msg.GetLength() < hdr->hdr.nlmsg_len)
65 return false;
66
Paul Stewart9a908082011-08-31 12:18:48 -070067 Mode mode = kModeUnknown;
Paul Stewartdd7df792011-07-15 11:09:50 -070068 switch (hdr->hdr.nlmsg_type) {
69 case RTM_NEWLINK:
70 case RTM_NEWADDR:
71 case RTM_NEWROUTE:
Paul Stewart9a908082011-08-31 12:18:48 -070072 mode = kModeAdd;
Paul Stewartdd7df792011-07-15 11:09:50 -070073 break;
74
75 case RTM_DELLINK:
76 case RTM_DELADDR:
77 case RTM_DELROUTE:
Paul Stewart9a908082011-08-31 12:18:48 -070078 mode = kModeDelete;
Paul Stewartdd7df792011-07-15 11:09:50 -070079 break;
80
81 default:
82 return false;
83 }
84
85 rtattr *attr_data = NULL;
86 int attr_length = 0;
87
88 switch (hdr->hdr.nlmsg_type) {
89 case RTM_NEWLINK:
90 case RTM_DELLINK:
91 if (!DecodeLink(hdr, mode, &attr_data, &attr_length))
92 return false;
93 break;
94
95 case RTM_NEWADDR:
96 case RTM_DELADDR:
97 if (!DecodeAddress(hdr, mode, &attr_data, &attr_length))
98 return false;
99 break;
100
101 case RTM_NEWROUTE:
102 case RTM_DELROUTE:
103 if (!DecodeRoute(hdr, mode, &attr_data, &attr_length))
104 return false;
105 break;
106
107 default:
108 NOTREACHED();
109 }
110
111 flags_ = hdr->hdr.nlmsg_flags;
112 seq_ = hdr->hdr.nlmsg_seq;
113 pid_ = hdr->hdr.nlmsg_pid;
114
115 while (attr_data && RTA_OK(attr_data, attr_length)) {
116 SetAttribute(
117 attr_data->rta_type,
118 ByteString(reinterpret_cast<unsigned char *>(RTA_DATA(attr_data)),
119 RTA_PAYLOAD(attr_data)));
120 attr_data = RTA_NEXT(attr_data, attr_length);
121 }
122
123 if (attr_length) {
124 // We hit a parse error while going through the attributes
125 attributes_.clear();
126 return false;
127 }
128
129 return true;
130}
131
132bool RTNLMessage::DecodeLink(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700133 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700134 rtattr **attr_data,
135 int *attr_length) {
136 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifi))) {
137 return false;
138 }
139
140 mode_ = mode;
141 *attr_data = IFLA_RTA(NLMSG_DATA(&hdr->hdr));
142 *attr_length = IFLA_PAYLOAD(&hdr->hdr);
143
Paul Stewart9a908082011-08-31 12:18:48 -0700144 type_ = kTypeLink;
Paul Stewartdd7df792011-07-15 11:09:50 -0700145 family_ = hdr->ifi.ifi_family;
146 interface_index_ = hdr->ifi.ifi_index;
147 set_link_status(LinkStatus(hdr->ifi.ifi_type,
148 hdr->ifi.ifi_flags,
149 hdr->ifi.ifi_change));
150 return true;
151}
152
153bool RTNLMessage::DecodeAddress(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700154 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700155 rtattr **attr_data,
156 int *attr_length) {
157 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->ifa))) {
158 return false;
159 }
160 mode_ = mode;
161 *attr_data = IFA_RTA(NLMSG_DATA(&hdr->hdr));
162 *attr_length = IFA_PAYLOAD(&hdr->hdr);
163
Paul Stewart9a908082011-08-31 12:18:48 -0700164 type_ = kTypeAddress;
Paul Stewartdd7df792011-07-15 11:09:50 -0700165 family_ = hdr->ifa.ifa_family;
166 interface_index_ = hdr->ifa.ifa_index;
167 set_address_status(AddressStatus(hdr->ifa.ifa_prefixlen,
168 hdr->ifa.ifa_flags,
169 hdr->ifa.ifa_scope));
170 return true;
171}
172
173bool RTNLMessage::DecodeRoute(const RTNLHeader *hdr,
Paul Stewart9a908082011-08-31 12:18:48 -0700174 Mode mode,
Paul Stewartdd7df792011-07-15 11:09:50 -0700175 rtattr **attr_data,
176 int *attr_length) {
177 if (hdr->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(hdr->rtm))) {
178 return false;
179 }
180 mode_ = mode;
181 *attr_data = RTM_RTA(NLMSG_DATA(&hdr->hdr));
182 *attr_length = RTM_PAYLOAD(&hdr->hdr);
183
Paul Stewart9a908082011-08-31 12:18:48 -0700184 type_ = kTypeRoute;
Paul Stewartdd7df792011-07-15 11:09:50 -0700185 family_ = hdr->rtm.rtm_family;
186 set_route_status(RouteStatus(hdr->rtm.rtm_dst_len,
187 hdr->rtm.rtm_src_len,
188 hdr->rtm.rtm_table,
189 hdr->rtm.rtm_protocol,
190 hdr->rtm.rtm_scope,
191 hdr->rtm.rtm_type,
192 hdr->rtm.rtm_flags));
193 return true;
194}
195
196ByteString RTNLMessage::Encode() {
Paul Stewart9a908082011-08-31 12:18:48 -0700197 if (type_ != kTypeLink &&
198 type_ != kTypeAddress &&
199 type_ != kTypeRoute) {
Paul Stewartdd7df792011-07-15 11:09:50 -0700200 return ByteString();
201 }
202
203 RTNLHeader hdr;
204 hdr.hdr.nlmsg_flags = flags_;
205 hdr.hdr.nlmsg_seq = seq_;
206 hdr.hdr.nlmsg_pid = pid_;
207 hdr.hdr.nlmsg_seq = 0;
208
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;
216 }
217 hdr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr.gen));
Paul Stewart9a908082011-08-31 12:18:48 -0700218 hdr.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
Paul Stewartdd7df792011-07-15 11:09:50 -0700219 hdr.gen.rtgen_family = family_;
220 } else {
221 switch (type_) {
Paul Stewart9a908082011-08-31 12:18:48 -0700222 case kTypeLink:
Paul Stewartdd7df792011-07-15 11:09:50 -0700223 EncodeLink(&hdr);
224 break;
225
Paul Stewart9a908082011-08-31 12:18:48 -0700226 case kTypeAddress:
Paul Stewartdd7df792011-07-15 11:09:50 -0700227 EncodeAddress(&hdr);
228 break;
229
Paul Stewart9a908082011-08-31 12:18:48 -0700230 case kTypeRoute:
Paul Stewartdd7df792011-07-15 11:09:50 -0700231 EncodeRoute(&hdr);
232 break;
233
234 default:
235 NOTREACHED();
236 }
237 }
238
239 size_t header_length = hdr.hdr.nlmsg_len;
240 ByteString attributes;
241
242 base::hash_map<uint16, ByteString>::iterator attr;
243 for (attr = attributes_.begin(); attr != attributes_.end(); ++attr) {
244 size_t len = RTA_LENGTH(attr->second.GetLength());
245 hdr.hdr.nlmsg_len = NLMSG_ALIGN(hdr.hdr.nlmsg_len) + RTA_ALIGN(len);
246
247 struct rtattr rt_attr = { len, attr->first };
248 ByteString attr_header(reinterpret_cast<unsigned char *>(&rt_attr),
249 sizeof(rt_attr));
250 attr_header.Resize(RTA_ALIGN(attr_header.GetLength()));
251 attributes.Append(attr_header);
252
253 ByteString attr_data(attr->second);
254 attr_data.Resize(RTA_ALIGN(attr_data.GetLength()));
255 attributes.Append(attr_data);
256 }
257
258 ByteString packet(reinterpret_cast<unsigned char *>(&hdr), header_length);
259 packet.Append(attributes);
260
261 return packet;
262}
263
264void RTNLMessage::EncodeLink(RTNLHeader *hdr) {
Paul Stewart9a908082011-08-31 12:18:48 -0700265 hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWLINK : RTM_DELLINK;
Paul Stewartdd7df792011-07-15 11:09:50 -0700266 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifi));
267 hdr->ifi.ifi_family = family_;
268 hdr->ifi.ifi_type = link_status_.type;
269 hdr->ifi.ifi_flags = link_status_.flags;
270 hdr->ifi.ifi_change = link_status_.change;
271}
272
273void RTNLMessage::EncodeAddress(RTNLHeader *hdr) {
Paul Stewart9a908082011-08-31 12:18:48 -0700274 hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWADDR : RTM_DELADDR;
Paul Stewartdd7df792011-07-15 11:09:50 -0700275 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->ifa));
276 hdr->ifa.ifa_family = family_;
277 hdr->ifa.ifa_prefixlen = address_status_.prefix_len;
278 hdr->ifa.ifa_flags = address_status_.flags;
279 hdr->ifa.ifa_scope = address_status_.scope;
280 hdr->ifa.ifa_index = interface_index_;
281}
282
283void RTNLMessage::EncodeRoute(RTNLHeader *hdr) {
Paul Stewart9a908082011-08-31 12:18:48 -0700284 hdr->hdr.nlmsg_type = (mode_ == kModeAdd) ? RTM_NEWROUTE : RTM_DELROUTE;
Paul Stewartdd7df792011-07-15 11:09:50 -0700285 hdr->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(hdr->rtm));
286 hdr->rtm.rtm_family = family_;
287 hdr->rtm.rtm_dst_len = route_status_.dst_prefix;
288 hdr->rtm.rtm_src_len = route_status_.src_prefix;
289 hdr->rtm.rtm_table = route_status_.table;
290 hdr->rtm.rtm_protocol = route_status_.protocol;
291 hdr->rtm.rtm_scope = route_status_.scope;
292 hdr->rtm.rtm_type = route_status_.type;
293 hdr->rtm.rtm_flags = route_status_.flags;
294}
295
296} // namespace shill