Home | History | Annotate | Download | only in shill
      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, &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 
    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