Wade Guthrie | f9f6666 | 2013-10-31 17:20:48 -0700 | [diff] [blame^] | 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 | 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 | |
| 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 | |