1 Netlink message handling in Shill. 2 3 4 1.0 INTRODUCTION. 5 6 Shill uses netlink sockets (described in RFC 3549) to communicate with 7 software in kernel space. Messages that are passed across netlink sockets take 8 a specific format that includes a netlink header, a sub-domain-specific header, 9 and attributes. 10 11 Shill defines a NetlinkManager class for dealing with the netlink sockets and 12 NetlinkMessage (and its children such as ControlNetlinkMessage and 13 Nl80211Message) and NetlinkAttribute (and its children) classes for dealing 14 with the messages passed across the netlink sockets. 15 16 17 2.0 SENDING A MESSAGE. 18 19 This section describes how to send a netlink message in Shill. The steps, 20 described below, are: 21 22 o Create a message. 23 o Make Response and Error Handlers. 24 o Send the Message. 25 26 27 2.1 Create the message. 28 29 Start by declaring a message object. This will be a message-specific child 30 class of the NetlinkMessage type. For example: 31 32 TriggerScanMessage trigger_scan; 33 34 2.1.1 Add attributes to the message. 35 36 You'll want to set values for all the message's attributes. The message 37 object should have all of its legal attributes pre-instantiated so all 38 you should have to do is set their values (if an attribute is optional, 39 don't set the value -- only the attibutes that have been explicitly 40 set will be sent in the message). 41 42 A message's attributes are accessed through the message's |attributes| 43 or |const_attributes| methods. 44 45 46 2.1.1.1 Regular attributes. 47 48 Netlink attributes are typed (e.g., String, U32, etc.). In order to 49 set the value of an attribute you use the SetXxxAttributeValue method 50 (where Xxx is the type of the attribute. For example, you may want 51 to 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 57 If 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 59 three 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 68 You can check the kernel code to determine the attributes each message is 69 expecting 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 82 If the kernel expects the attribute, modify the message's constructor 83 (probably in one of the message handling source files, like 84 nl80211_message.cc) to create the attribute: 85 86 attributes()->CreateAttribute( 87 NL80211_ATTR_IFINDEX, Bind(&NetlinkAttribute::NewNl80211AttributeFromId)); 88 89 90 2.1.1.2 Nested attributes. 91 92 So, this is all fun and games until someone needs a nested attribute. 93 A nested attribute contains a number of other attributes (like a structure) 94 or a list of identically-typed attributes (like an array). To set a nested 95 attribute, declare an AttributeListRefPtr, and fill it with the attribute 96 in 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 104 Set the 'has a value' trait of the nested attribute: 105 106 trigger_scan.attributes()->SetNestedAttributeHasAValue( 107 NL80211_ATTR_SCAN_FREQUENCIES); 108 109 Now, create and set the nested attributes within AttributeList. You can 110 create 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 119 Or you can just create and add ordinary named attributes: 120 121 nested->CreateStringAttribute(type, kSsidString); 122 nested->SetStringAttributeValue(type, "Foo"); 123 124 You 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 142 2.2 Make Response and Error Handlers. 143 144 Make 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 static_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 203 2.3 Send the Message. 204 205 Send 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 215 3.0 RECEIVING A NETLINK MESSAGE. 216 217 3.1 Build a Message Handler (to which I've alluded, above). 218 219 The message handler should take a single parameter of the type of message you 220 want to handle. For example: 221 222 void NetlinkManager::OnNewFamilyMessage(const ControlNetlinkMessage& message) { 223 224 You'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 240 And, some of these attributes may be nested. In this example, we've got an 241 array of structures that looks sort-of like (this isn't the way the data is 242 stored, 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 250 But 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, ¤t_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 281 3.2 Install the Message Handler. 282 283 The 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 285 imagine it's possible. This case could be handled as a broadcast message 286 followed by a hasty name change fot that method). 287 288 3.2.1 Install a Broadcast Message Handler. 289 290 Broadcast handlers are installed to the NetlinkManager as follows: 291 292 NetlinkManager::GetInstance()->AddBroadcastHandler(handler); 293 294 Where 'handler' is the handler described, above. Broadcast messaes just 295 handle generic NetlinkMessages rather than a specific kind. 296 297 3.2.2 Install a Unicast (i.e., a Response) Message Handler. 298 299 Otherwise, the handler is installed as the response handler for a message. 300 For example: 301 302 ControlNetlinkMessage message; 303 // Build the message. 304 305 NetlinkManager::GetInstance()->SendControlMessage(&message, 306 &message_handler, 307 &error_handler); 308 309