blob: c4d8723ffed4f5bdd1aea98ca816fd782888a329 [file] [log] [blame]
Wade Guthrief9f66662013-10-31 17:20:48 -07001Netlink message handling in Shill.
2
3
41.0 INTRODUCTION.
5
6Shill uses netlink sockets (described in RFC 3549) to communicate with
7software in kernel space. Messages that are passed across netlink sockets take
8a specific format that includes a netlink header, a sub-domain-specific header,
9and attributes.
10
11Shill defines a NetlinkManager class for dealing with the netlink sockets and
12NetlinkMessage (and its children such as ControlNetlinkMessage and
13Nl80211Message) and NetlinkAttribute (and its children) classes for dealing
14with the messages passed across the netlink sockets.
15
16
172.0 SENDING A MESSAGE.
18
19This section describes how to send a netlink message in Shill. The steps,
20described below, are:
21
22 o Create a message.
23 o Make Response and Error Handlers.
24 o Send the Message.
25
26
272.1 Create the message.
28
29Start by declaring a message object. This will be a message-specific child
30class of the NetlinkMessage type. For example:
31
32 TriggerScanMessage trigger_scan;
33
342.1.1 Add attributes to the message.
35
36You'll want to set values for all the message's attributes. The message
37object should have all of its legal attributes pre-instantiated so all
38you should have to do is set their values (if an attribute is optional,
39don't set the value -- only the attibutes that have been explicitly
40set will be sent in the message).
41
42A message's attributes are accessed through the message's |attributes|
43or |const_attributes| methods.
44
45
462.1.1.1 Regular attributes.
47
48Netlink attributes are typed (e.g., String, U32, etc.). In order to
49set the value of an attribute you use the SetXxxAttributeValue method
50(where Xxx is the type of the attribute. For example, you may want
51to set the value of the NL80211_ATTR_IFINDEX attribute:
52
53 if (trigger_scan.attributes()->SetU32AttributeValue(
54 NL80211_ATTR_IFINDEX, wifi_interface_index_)) {
55 // ...
56
57If the message hasn't pre-instantiated the attribute you want to use, the
58'SetXxxAttributeValue' call will return false. This can be for one of
59three reasons:
60
61 a) a message of this type may not be expecting this kind of attribute,
62 b) the data type of the attribute may not agree with the setter you
63 used, or
64 c) the definition of the specific message class is incomplete (that
65 is, the attribute hasn't been, but should be, added to the message
66 type).
67
68You can check the kernel code to determine the attributes each message is
69expecting and the type of those attributes.
70
71 a) Find the command (NL80211_CMD_TRIGGER_SCAN, in the case of
72 TriggerScanMessage) in the |nl80211_ops| array in the kernel sources
73 (.../src/third_party/kernel/files/net/wireless/nl80211.c in the ChromeOS
74 sources).
75 b) Find the name of the command handler (in the |.doit| structure member)
76 in that structure. Find that handler. In the case of
77 NL80211_CMD_TRIGGER_SCAN, the handler is |nl80211_trigtger_scan|.
78 c) Look for handling of the attribute in question. It will be an offset
79 into the |info->attrs[]| array. You can also see the data type expected
80 for the attribute.
81
82If the kernel expects the attribute, modify the message's constructor
83(probably in one of the message handling source files, like
84nl80211_message.cc) to create the attribute:
85
86 attributes()->CreateAttribute(
87 NL80211_ATTR_IFINDEX, Bind(&NetlinkAttribute::NewNl80211AttributeFromId));
88
89
902.1.1.2 Nested attributes.
91
92So, this is all fun and games until someone needs a nested attribute.
93A nested attribute contains a number of other attributes (like a structure)
94or a list of identically-typed attributes (like an array). To set a nested
95attribute, declare an AttributeListRefPtr, and fill it with the attribute
96in question:
97
98 AttributeListRefPtr nested;
99 if (!trigger_scan.attributes()->GetNestedAttributeList(
100 NL80211_ATTR_SCAN_FREQUENCIES, &nested) || !nested) {
101 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES.";
102 }
103
104Set the 'has a value' trait of the nested attribute:
105
106 trigger_scan.attributes()->SetNestedAttributeHasAValue(
107 NL80211_ATTR_SCAN_FREQUENCIES);
108
109Now, create and set the nested attributes within AttributeList. You can
110create an array:
111
112 int i = 0;
113 for (const auto freq : scan_frequencies) {
114 nested->CreateU32Attribute(i, StringPrintf("Frequency-%d", i).c_str());
115 nested->SetU32AttributeValue(i, freq);
116 ++i;
117 }
118
119Or you can just create and add ordinary named attributes:
120
121 nested->CreateStringAttribute(type, kSsidString);
122 nested->SetStringAttributeValue(type, "Foo");
123
124You can even nest nested attributes inside nested attributes:
125
126 nested->CreateNestedAttribute(type, kRatesString);
127 AttributeListRefPtr nested_nested;
128 if (!nested->GetNestedAttributeList(type, &nested_nested) ||
129 !nested_nested) {
130 LOG(ERROR) << "Couldn't get attribute " << attribute_name
131 << " which we just created.";
132 return;
133 }
134 for (size_t i = 0; i < payload_bytes; ++i) {
135 string rate_name = StringPrintf("Rate-%zu", i);
136 nested_nested->CreateU8Attribute(i, rate_name.c_str());
137 nested_nested->SetU8AttributeValue(i, payload[i]);
138 }
139 nested->SetNestedAttributeHasAValue(type);
140
141
1422.2 Make Response and Error Handlers.
143
144Make some sort of handler for the response message.
145
146 class Foo {
147 // ...
148 private:
149 // More on this, later.
150 void OnTriggerScanResponse(const Nl80211Message &response) {
151 // Do whatever you want with the response.
152 return;
153 }
154
155 void OnTriggerScanErrorResponse(
156 NetlinkManager::AuxilliaryMessageType type,
157 const NetlinkMessage *netlink_message) {
158 switch (type) {
159 case NetlinkManager::kErrorFromKernel: {
160 if (!netlink_message) {
161 LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error.";
162 break;
163 }
164 if (netlink_message->message_type() !=
165 ErrorAckMessage::GetMessageType()) {
166 LOG(ERROR) << __func__ << ": Message failed: Not an error.";
167 break;
168 }
169 const ErrorAckMessage *error_ack_message =
170 dynamic_cast<const ErrorAckMessage *>(netlink_message);
171 if (error_ack_message->error()) {
172 LOG(ERROR) << __func__ << ": Message failed: "
173 << error_ack_message->ToString();
174 } else {
175 SLOG(WiFi, 6) << __func__ << ": Message ACKed";
176 }
177 }
178 break;
179
180 case NetlinkManager::kUnexpectedResponseType:
181 LOG(ERROR) << "Message not handled by regular message handler:";
182 if (netlink_message) {
183 netlink_message->Print(0, 0);
184 }
185 found_error_ = true;
186 on_scan_failed_.Run();
187 break;
188
189 case NetlinkManager::kTimeoutWaitingForResponse:
190 // Handle this one.
191 break;
192
193 default:
194 LOG(ERROR) << "Unexpected auxiliary message type: " << type;
195 found_error_ = true;
196 on_scan_failed_.Run();
197 break;
198 }
199 }
200 }
201
202
2032.3 Send the Message.
204
205Send the message with the handlers for the various cases.
206
207 NetlinkManager::GetInstance()->SendNl80211Message(
208 &trigger_scan,
209 Bind(&Foo::OnTriggerScanResponse,
210 weak_ptr_factory_.GetWeakPtr()),
211 Bind(&Foo::OnTriggerScanErrorResponse,
212 weak_ptr_factory_.GetWeakPtr()));
213
214
2153.0 RECEIVING A NETLINK MESSAGE.
216
2173.1 Build a Message Handler (to which I've alluded, above).
218
219The message handler should take a single parameter of the type of message you
220want to handle. For example:
221
222 void NetlinkManager::OnNewFamilyMessage(const ControlNetlinkMessage &message) {
223
224You'll probably want to look for some attributes:
225
226 uint16_t family_id;
227 if (!message.const_attributes()->GetU16AttributeValue(CTRL_ATTR_FAMILY_ID,
228 &family_id)) {
229 LOG(ERROR) << __func__ << ": Couldn't get family_id attribute";
230 return;
231 }
232
233 string family_name;
234 if (!message.const_attributes()->GetStringAttributeValue(
235 CTRL_ATTR_FAMILY_NAME, &family_name)) {
236 LOG(ERROR) << __func__ << ": Couldn't get family_name attribute";
237 return;
238 }
239
240And, some of these attributes may be nested. In this example, we've got an
241array of structures that looks sort-of like (this isn't the way the data is
242stored, it just _logically_ looks like this):
243
244 struct {
245 u32 ignored; // CTRL_ATTR_MCAST_GRP_UNSPEC;
246 string group_name; // CTRL_ATTR_MCAST_GRP_NAME;
247 u32 group_id; // CTRL_ATTR_MCAST_GRP_ID;
248 } multicast_groups[];
249
250But the actual code for reading this array is as follows:
251
252 AttributeListConstRefPtr multicast_groups;
253 if (message.const_attributes()->ConstGetNestedAttributeList(
254 CTRL_ATTR_MCAST_GROUPS, &multicast_groups)) {
255 AttributeListConstRefPtr current_group;
256
257 for (int i = 1;
258 multicast_groups->ConstGetNestedAttributeList(i, &current_group);
259 ++i) {
260
261 string group_name;
262 if (!current_group->GetStringAttributeValue(CTRL_ATTR_MCAST_GRP_NAME,
263 &group_name)) {
264 LOG(WARNING) << "Expected CTRL_ATTR_MCAST_GRP_NAME, found none";
265 continue;
266 }
267
268 uint32_t group_id;
269 if (!current_group->GetU32AttributeValue(CTRL_ATTR_MCAST_GRP_ID,
270 &group_id)) {
271 LOG(WARNING) << "Expected CTRL_ATTR_MCAST_GRP_ID, found none";
272 continue;
273 }
274
275 SLOG(WiFi, 3) << " Adding group '" << group_name << "' = " << group_id;
276 message_types_[family_name].groups[group_name] = group_id;
277 }
278 }
279
280
2813.2 Install the Message Handler.
282
283The message you're handling can either be a broadcast message or a response
284(I've not seen a case where the kernel sends a message directly to us but, I'd
285imagine it's possible. This case could be handled as a broadcast message
286followed by a hasty name change fot that method).
287
2883.2.1 Install a Broadcast Message Handler.
289
290Broadcast handlers are installed to the NetlinkManager as follows:
291
292 NetlinkManager::GetInstance()->AddBroadcastHandler(handler);
293
294Where 'handler' is the handler described, above. Broadcast messaes just
295handle generic NetlinkMessages rather than a specific kind.
296
2973.2.2 Install a Unicast (i.e., a Response) Message Handler.
298
299Otherwise, the handler is installed as the response handler for a message.
300For example:
301
302 ControlNetlinkMessage message;
303 // Build the message.
304
305 NetlinkManager::GetInstance()->SendControlMessage(&message,
306 &message_handler,
307 &error_handler);
308