blob: 1f9409c1932d7d3b60d75280a05652951a6f947d [file] [log] [blame]
Wade Guthrie0d438532012-05-18 14:18:50 -07001// Copyright (c) 2012 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// This code is derived from the 'iw' source code. The copyright and license
6// of that code is as follows:
7//
8// Copyright (c) 2007, 2008 Johannes Berg
9// Copyright (c) 2007 Andy Lutomirski
10// Copyright (c) 2007 Mike Kershaw
11// Copyright (c) 2008-2009 Luis R. Rodriguez
12//
13// Permission to use, copy, modify, and/or distribute this software for any
14// purpose with or without fee is hereby granted, provided that the above
15// copyright notice and this permission notice appear in all copies.
16//
17// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25#include "shill/nl80211_socket.h"
26
27#include <ctype.h>
28#include <errno.h>
29
30#include <net/if.h>
31#include <netlink/attr.h>
32#include <netlink/genl/ctrl.h>
33#include <netlink/genl/family.h>
34#include <netlink/genl/genl.h>
35#include <netlink/msg.h>
36#include <netlink/netlink.h>
37
38#include <iomanip>
39#include <sstream>
40#include <string>
41
42#include <base/logging.h>
43
44#include "shill/kernel_bound_nlmessage.h"
45#include "shill/logging.h"
46#include "shill/netlink_socket.h"
47#include "shill/scope_logger.h"
48#include "shill/user_bound_nlmessage.h"
49
50using std::string;
51
52namespace shill {
53
54const char Nl80211Socket::kSocketFamilyName[] = "nl80211";
55
56bool Nl80211Socket::Init() {
57 if (!NetlinkSocket::Init()) {
58 LOG(ERROR) << "NetlinkSocket didn't initialize.";
59 return false;
60 }
61
62 nl80211_id_ = genl_ctrl_resolve(GetNlSock(), "nlctrl");
63 if (nl80211_id_ < 0) {
64 LOG(ERROR) << "nl80211 not found.";
65 return false;
66 }
67
Wade Guthried6153612012-08-23 11:36:14 -070068 LOG(INFO) << "Nl80211Socket initialized successfully";
Wade Guthrie0d438532012-05-18 14:18:50 -070069 return true;
70}
71
72bool Nl80211Socket::AddGroupMembership(const string &group_name) {
73 int id = GetMulticastGroupId(group_name);
74 if (id < 0) {
75 LOG(ERROR) << "No Id for group " << group_name;
76 return false;
77 } else {
78 int result = nl_socket_add_membership(GetNlSock(), id);
79 if (result != 0) {
80 LOG(ERROR) << "Failed call to 'nl_socket_add_membership': " << result;
81 return false;
82 }
83 }
Wade Guthried6153612012-08-23 11:36:14 -070084 LOG(INFO) << " Group " << group_name << " added successfully";
Wade Guthrie0d438532012-05-18 14:18:50 -070085 return true;
86}
87
88int Nl80211Socket::OnAck(struct nl_msg *unused_msg, void *arg) {
89 if (arg) {
90 int *ret = reinterpret_cast<int *>(arg);
91 *ret = 0;
92 }
93 return NL_STOP; // Stop parsing and discard remainder of buffer.
94}
95
96int Nl80211Socket::OnError(struct sockaddr_nl *unused_nla, struct nlmsgerr *err,
97 void *arg) {
98 int *ret = reinterpret_cast<int *>(arg);
99 if (err) {
100 if (ret)
101 *ret = err->error;
102 LOG(ERROR) << "Error(" << err->error << ") " << strerror(err->error);
103 } else {
104 if (ret)
105 *ret = -1;
106 LOG(ERROR) << "Error(<unknown>)";
107 }
108 return NL_STOP; // Stop parsing and discard remainder of buffer.
109}
110
111int Nl80211Socket::OnFamilyResponse(struct nl_msg *msg, void *arg) {
112 if (!msg) {
113 LOG(ERROR) << "NULL |msg| parameter";
114 return NL_SKIP; // Skip current message, continue parsing buffer.
115 }
116
117 if (!arg) {
118 LOG(ERROR) << "NULL |arg| parameter";
119 return NL_SKIP; // Skip current message, continue parsing buffer.
120 }
121
122 struct HandlerArgs *grp = reinterpret_cast<struct HandlerArgs *>(arg);
123 struct nlattr *tb[CTRL_ATTR_MAX + 1];
124 struct genlmsghdr *gnlh = reinterpret_cast<struct genlmsghdr *>(
125 nlmsg_data(nlmsg_hdr(msg)));
126 struct nlattr *mcgrp = NULL;
127 int rem_mcgrp;
128
129 nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
130 genlmsg_attrlen(gnlh, 0), NULL);
131
132 if (!tb[CTRL_ATTR_MCAST_GROUPS])
133 return NL_SKIP; // Skip current message, continue parsing buffer.
134
135 // nla_for_each_nested(...)
136 mcgrp = reinterpret_cast<nlattr *>(nla_data(tb[CTRL_ATTR_MCAST_GROUPS]));
137 rem_mcgrp = nla_len(tb[CTRL_ATTR_MCAST_GROUPS]);
138 bool found_one = false;
139 for (; nla_ok(mcgrp, rem_mcgrp); mcgrp = nla_next(mcgrp, &(rem_mcgrp))) {
140 struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
141
142 nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
143 reinterpret_cast<nlattr *>(nla_data(mcgrp)), nla_len(mcgrp), NULL);
144
145 if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]) {
146 LOG(ERROR) << "No group name in 'group' message";
147 continue;
148 } else if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) {
149 LOG(ERROR) << "No group id in 'group' message";
150 continue;
151 }
152
153 if (strncmp(reinterpret_cast<char *>(
154 nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])),
155 grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) {
156 continue;
157 }
158 found_one = true;
159 grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
160 SLOG(WiFi, 6) << "GROUP '"
161 << reinterpret_cast<char *>(
162 nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))
163 << "' has ID "
164 << grp->id;
165 break;
166 }
167 if (!found_one) {
168 LOG(ERROR) << "NO GROUP matched '"
169 << grp->group
170 << "', the one for which we were looking";
171 }
172
173 return NL_SKIP; // Skip current message, continue parsing buffer.
174}
175
176int Nl80211Socket::GetMulticastGroupId(const string &group) {
177 // Allocate and build the message.
178 KernelBoundNlMessage message;
179 if (!message.Init()) {
180 LOG(ERROR) << "Couldn't initialize message";
181 return -1;
182 }
183
184 if (!message.AddNetlinkHeader(NL_AUTO_PID, NL_AUTO_SEQ, GetFamilyId(), 0, 0,
185 CTRL_CMD_GETFAMILY, 0))
186 return -1;
187
188 int result = message.AddAttribute(CTRL_ATTR_FAMILY_NAME,
189 GetSocketFamilyName().length() + 1,
190 GetSocketFamilyName().c_str());
191 if (result < 0) {
192 LOG(ERROR) << "nla_put return error: " << result;
193 return -1;
194 }
195
196 if (!message.Send(this)) {
197 return -1;
198 }
199
200 // Wait for the response.
201
202 NetlinkSocket::Callback netlink_callback;
203 struct HandlerArgs grp(group.c_str(), -ENOENT);
204 int status = 1;
205
206 if (netlink_callback.Init()) {
207 if (!netlink_callback.ErrHandler(NL_CB_CUSTOM, OnError, &status)) {
208 return -1;
209 }
210 if (!netlink_callback.SetHandler(NL_CB_ACK, NL_CB_CUSTOM, OnAck, &status)) {
211 return -1;
212 }
213 if (!netlink_callback.SetHandler(NL_CB_VALID, NL_CB_CUSTOM,
214 OnFamilyResponse, &grp)) {
215 return -1;
216 }
217 } else {
218 LOG(ERROR) << "Couldn't initialize callback";
219 return -1;
220 }
221
222 while (status > 0) { // 'status' is set by the NL_CB_ACK handler.
223 if (!GetMessagesUsingCallback(&netlink_callback)) {
224 return -1;
225 }
226 }
227
228 if (status != 0) {
229 return -1;
230 }
231
232 return grp.id;
233}
234
235} // namespace shill