| Netlink message handling in Shill. |
| |
| |
| 1.0 INTRODUCTION. |
| |
| Shill uses netlink sockets (described in RFC 3549) to communicate with |
| software in kernel space. Messages that are passed across netlink sockets take |
| a specific format that includes a netlink header, a sub-domain-specific header, |
| and attributes. |
| |
| Shill defines a NetlinkManager class for dealing with the netlink sockets and |
| NetlinkMessage (and its children such as ControlNetlinkMessage and |
| Nl80211Message) and NetlinkAttribute (and its children) classes for dealing |
| with the messages passed across the netlink sockets. |
| |
| |
| 2.0 SENDING A MESSAGE. |
| |
| This section describes how to send a netlink message in Shill. The steps, |
| described below, are: |
| |
| o Create a message. |
| o Make Response and Error Handlers. |
| o Send the Message. |
| |
| |
| 2.1 Create the message. |
| |
| Start by declaring a message object. This will be a message-specific child |
| class of the NetlinkMessage type. For example: |
| |
| TriggerScanMessage trigger_scan; |
| |
| 2.1.1 Add attributes to the message. |
| |
| You'll want to set values for all the message's attributes. The message |
| object should have all of its legal attributes pre-instantiated so all |
| you should have to do is set their values (if an attribute is optional, |
| don't set the value -- only the attibutes that have been explicitly |
| set will be sent in the message). |
| |
| A message's attributes are accessed through the message's |attributes| |
| or |const_attributes| methods. |
| |
| |
| 2.1.1.1 Regular attributes. |
| |
| Netlink attributes are typed (e.g., String, U32, etc.). In order to |
| set the value of an attribute you use the SetXxxAttributeValue method |
| (where Xxx is the type of the attribute. For example, you may want |
| to set the value of the NL80211_ATTR_IFINDEX attribute: |
| |
| if (trigger_scan.attributes()->SetU32AttributeValue( |
| NL80211_ATTR_IFINDEX, wifi_interface_index_)) { |
| // ... |
| |
| If the message hasn't pre-instantiated the attribute you want to use, the |
| 'SetXxxAttributeValue' call will return false. This can be for one of |
| three reasons: |
| |
| a) a message of this type may not be expecting this kind of attribute, |
| b) the data type of the attribute may not agree with the setter you |
| used, or |
| c) the definition of the specific message class is incomplete (that |
| is, the attribute hasn't been, but should be, added to the message |
| type). |
| |
| You can check the kernel code to determine the attributes each message is |
| expecting and the type of those attributes. |
| |
| a) Find the command (NL80211_CMD_TRIGGER_SCAN, in the case of |
| TriggerScanMessage) in the |nl80211_ops| array in the kernel sources |
| (.../src/third_party/kernel/files/net/wireless/nl80211.c in the ChromeOS |
| sources). |
| b) Find the name of the command handler (in the |.doit| structure member) |
| in that structure. Find that handler. In the case of |
| NL80211_CMD_TRIGGER_SCAN, the handler is |nl80211_trigtger_scan|. |
| c) Look for handling of the attribute in question. It will be an offset |
| into the |info->attrs[]| array. You can also see the data type expected |
| for the attribute. |
| |
| If the kernel expects the attribute, modify the message's constructor |
| (probably in one of the message handling source files, like |
| nl80211_message.cc) to create the attribute: |
| |
| attributes()->CreateAttribute( |
| NL80211_ATTR_IFINDEX, Bind(&NetlinkAttribute::NewNl80211AttributeFromId)); |
| |
| |
| 2.1.1.2 Nested attributes. |
| |
| So, this is all fun and games until someone needs a nested attribute. |
| A nested attribute contains a number of other attributes (like a structure) |
| or a list of identically-typed attributes (like an array). To set a nested |
| attribute, declare an AttributeListRefPtr, and fill it with the attribute |
| in question: |
| |
| AttributeListRefPtr nested; |
| if (!trigger_scan.attributes()->GetNestedAttributeList( |
| NL80211_ATTR_SCAN_FREQUENCIES, &nested) || !nested) { |
| LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES."; |
| } |
| |
| Set the 'has a value' trait of the nested attribute: |
| |
| trigger_scan.attributes()->SetNestedAttributeHasAValue( |
| NL80211_ATTR_SCAN_FREQUENCIES); |
| |
| Now, create and set the nested attributes within AttributeList. You can |
| create an array: |
| |
| int i = 0; |
| for (const auto freq : scan_frequencies) { |
| nested->CreateU32Attribute(i, StringPrintf("Frequency-%d", i).c_str()); |
| nested->SetU32AttributeValue(i, freq); |
| ++i; |
| } |
| |
| Or you can just create and add ordinary named attributes: |
| |
| nested->CreateStringAttribute(type, kSsidString); |
| nested->SetStringAttributeValue(type, "Foo"); |
| |
| You can even nest nested attributes inside nested attributes: |
| |
| nested->CreateNestedAttribute(type, kRatesString); |
| AttributeListRefPtr nested_nested; |
| if (!nested->GetNestedAttributeList(type, &nested_nested) || |
| !nested_nested) { |
| LOG(ERROR) << "Couldn't get attribute " << attribute_name |
| << " which we just created."; |
| return; |
| } |
| for (size_t i = 0; i < payload_bytes; ++i) { |
| string rate_name = StringPrintf("Rate-%zu", i); |
| nested_nested->CreateU8Attribute(i, rate_name.c_str()); |
| nested_nested->SetU8AttributeValue(i, payload[i]); |
| } |
| nested->SetNestedAttributeHasAValue(type); |
| |
| |
| 2.2 Make Response and Error Handlers. |
| |
| Make some sort of handler for the response message. |
| |
| class Foo { |
| // ... |
| private: |
| // More on this, later. |
| void OnTriggerScanResponse(const Nl80211Message& response) { |
| // Do whatever you want with the response. |
| return; |
| } |
| |
| void OnTriggerScanErrorResponse( |
| NetlinkManager::AuxilliaryMessageType type, |
| const NetlinkMessage* netlink_message) { |
| switch (type) { |
| case NetlinkManager::kErrorFromKernel: { |
| if (!netlink_message) { |
| LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error."; |
| break; |
| } |
| if (netlink_message->message_type() != |
| ErrorAckMessage::GetMessageType()) { |
| LOG(ERROR) << __func__ << ": Message failed: Not an error."; |
| break; |
| } |
| const ErrorAckMessage* error_ack_message = |
| dynamic_cast<const ErrorAckMessage*>(netlink_message); |
| if (error_ack_message->error()) { |
| LOG(ERROR) << __func__ << ": Message failed: " |
| << error_ack_message->ToString(); |
| } else { |
| SLOG(WiFi, 6) << __func__ << ": Message ACKed"; |
| } |
| } |
| break; |
| |
| case NetlinkManager::kUnexpectedResponseType: |
| LOG(ERROR) << "Message not handled by regular message handler:"; |
| if (netlink_message) { |
| netlink_message->Print(0, 0); |
| } |
| found_error_ = true; |
| on_scan_failed_.Run(); |
| break; |
| |
| case NetlinkManager::kTimeoutWaitingForResponse: |
| // Handle this one. |
| break; |
| |
| default: |
| LOG(ERROR) << "Unexpected auxiliary message type: " << type; |
| found_error_ = true; |
| on_scan_failed_.Run(); |
| break; |
| } |
| } |
| } |
| |
| |
| 2.3 Send the Message. |
| |
| Send the message with the handlers for the various cases. |
| |
| NetlinkManager::GetInstance()->SendNl80211Message( |
| &trigger_scan, |
| Bind(&Foo::OnTriggerScanResponse, |
| weak_ptr_factory_.GetWeakPtr()), |
| Bind(&Foo::OnTriggerScanErrorResponse, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| |
| 3.0 RECEIVING A NETLINK MESSAGE. |
| |
| 3.1 Build a Message Handler (to which I've alluded, above). |
| |
| The message handler should take a single parameter of the type of message you |
| want to handle. For example: |
| |
| void NetlinkManager::OnNewFamilyMessage(const ControlNetlinkMessage& message) { |
| |
| You'll probably want to look for some attributes: |
| |
| uint16_t family_id; |
| if (!message.const_attributes()->GetU16AttributeValue(CTRL_ATTR_FAMILY_ID, |
| &family_id)) { |
| LOG(ERROR) << __func__ << ": Couldn't get family_id attribute"; |
| return; |
| } |
| |
| string family_name; |
| if (!message.const_attributes()->GetStringAttributeValue( |
| CTRL_ATTR_FAMILY_NAME, &family_name)) { |
| LOG(ERROR) << __func__ << ": Couldn't get family_name attribute"; |
| return; |
| } |
| |
| And, some of these attributes may be nested. In this example, we've got an |
| array of structures that looks sort-of like (this isn't the way the data is |
| stored, it just _logically_ looks like this): |
| |
| struct { |
| u32 ignored; // CTRL_ATTR_MCAST_GRP_UNSPEC; |
| string group_name; // CTRL_ATTR_MCAST_GRP_NAME; |
| u32 group_id; // CTRL_ATTR_MCAST_GRP_ID; |
| } multicast_groups[]; |
| |
| But the actual code for reading this array is as follows: |
| |
| AttributeListConstRefPtr multicast_groups; |
| if (message.const_attributes()->ConstGetNestedAttributeList( |
| CTRL_ATTR_MCAST_GROUPS, &multicast_groups)) { |
| AttributeListConstRefPtr current_group; |
| |
| for (int i = 1; |
| multicast_groups->ConstGetNestedAttributeList(i, ¤t_group); |
| ++i) { |
| |
| string group_name; |
| if (!current_group->GetStringAttributeValue(CTRL_ATTR_MCAST_GRP_NAME, |
| &group_name)) { |
| LOG(WARNING) << "Expected CTRL_ATTR_MCAST_GRP_NAME, found none"; |
| continue; |
| } |
| |
| uint32_t group_id; |
| if (!current_group->GetU32AttributeValue(CTRL_ATTR_MCAST_GRP_ID, |
| &group_id)) { |
| LOG(WARNING) << "Expected CTRL_ATTR_MCAST_GRP_ID, found none"; |
| continue; |
| } |
| |
| SLOG(WiFi, 3) << " Adding group '" << group_name << "' = " << group_id; |
| message_types_[family_name].groups[group_name] = group_id; |
| } |
| } |
| |
| |
| 3.2 Install the Message Handler. |
| |
| The message you're handling can either be a broadcast message or a response |
| (I've not seen a case where the kernel sends a message directly to us but, I'd |
| imagine it's possible. This case could be handled as a broadcast message |
| followed by a hasty name change fot that method). |
| |
| 3.2.1 Install a Broadcast Message Handler. |
| |
| Broadcast handlers are installed to the NetlinkManager as follows: |
| |
| NetlinkManager::GetInstance()->AddBroadcastHandler(handler); |
| |
| Where 'handler' is the handler described, above. Broadcast messaes just |
| handle generic NetlinkMessages rather than a specific kind. |
| |
| 3.2.2 Install a Unicast (i.e., a Response) Message Handler. |
| |
| Otherwise, the handler is installed as the response handler for a message. |
| For example: |
| |
| ControlNetlinkMessage message; |
| // Build the message. |
| |
| NetlinkManager::GetInstance()->SendControlMessage(&message, |
| &message_handler, |
| &error_handler); |
| |