0

I want to get the station information using Netlink by libnl. I issue the command NL80211_CMD_GET_STATION. Before sending the message, I add attributes NL80211_ATTR_MAC and NL80211_ATTR_IFINDEX with their respective values per instructions in nl80211.h file. I have written a simple code with error checking:

int main(int argc, char **argv)
{
    struct nl_sock *sk;                 /* Socket */
    struct nl_msg *msg;                 /* Netlink message struct */
    int err;                            /* Message flags */
    unsigned char mac_adr[ETH_ALEN];    /* MAC address */
    char path[50]="", if_name[] = "wlp4s0"; /* Interface name */
    strcat(path, "/sys/class/net/");
    strcat(path, if_name);
    strcat(path, "/address");
    /* allocate socket */
    if (!(sk = nl_socket_alloc()))
    {
        printf("Error in creating socket!\n");
        return -1;
    }
    /* Connect to generic netlink */
    if (genl_connect(sk) < 0)
    {
        printf("Error in connecting socket to Generic Netlink!\n");
        return -1;
    }
    /* Find the nl80211 driver ID */
    NL_family = genl_ctrl_resolve(sk, "nl80211");
    if (NL_family < 0)
    {
        printf("Error in getting nl80211 Netlink message ID!\n");
        return -1;
    }
    /* Attach a callback */
    err = nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM,
                nlCallback, NULL);
    if (err < 0)
    {
        printf("Error in a Callback to Netlink socket!\n");
        return -1;
    }
    /* Create a new message */
    if (!(msg = nlmsg_alloc()))
    {
        printf("Error in allocating Netlink message!\n");
        return -1;
    }
    /* Setup the message */
    if (!genlmsg_put(msg, 0, NL_AUTO_SEQ, NL_family, 0, 0,
            NL80211_CMD_GET_STATION, 0))
    {
        printf("Error in creating Netlink message!\n");
        return -1;
    }
    /* Add message attributes */
    /* Putting MAC address */
    if (get_mac(mac_adr, path) < 0)
    {
        printf ("Error in getting MAC address!\n");
        return -1;
    }
    NLA_PUT(msg, NL80211_ATTR_MAC, sizeof(mac_adr), mac_adr);
    /* Interface attribute */
    NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(if_name));
    /* Send the messge (this frees it) */
    err = nl_send_auto(sk, msg);
    if (err < 0)
    {
        printf ("Error in sending the messages!\n");
        return -1;
    }
    printf("Sent %d Bytes\n", err);
    /* Block execution to receive the answer from the kernel */
    err = nl_recvmsgs_default(sk);
    if (err < 0)
    {
        printf("Error in receiving Netlink message! Error: %s\n", nl_geterror(err));
        return -1;
    }
    return 0;

nla_put_failure:
    nlmsg_free(msg);
    printf("Error in Attribute Put\n");
    return -1;
}

When I run the program, it generates the error object not found while it tries to receive the reply from kernel using function nl_recvmsgs_default(sk) at the end of the code.

I have used the same code to get the type of the same interface with command NL80211_CMD_GET_INTERFACE. I send the message for this command with attribute NL80211_ATTR_IFINDEX. The program works for NL80211_CMD_GET_INTERFACE and it prints the interface type! Therefore, I suspect there is something wrong with the way I get the MAC address. Here is the fuction I write for getting MAC address from a file:

int get_mac(unsigned char *mac_adr, char *path)
{
    int c, i = 0, j = 0, d1, d2;
    char mac_str[20];

    FILE *file;
    file = fopen(path, "r");
    if (file) {
        while ((c = getc(file)) != EOF)
            mac_str[i++] = c;

        mac_str[i] = 0;
        fclose(file);
    }
    else return -1;

    for (i=0; i<=(ETH_ALEN-1)*3; i+=3)
    {
        if (mac_str[i]>='0' && mac_str[i]<='9')
            d1 = mac_str[i] - '0';
        else if (mac_str[i]>='a' && mac_str[i]<='f')
            d1 = mac_str[i] - 'a' + 10;
        if (mac_str[i+1]>='0' && mac_str[i+1]<='9')
            d2 = mac_str[i+1] - '0';
        else if (mac_str[i+1]>='a' && mac_str[i+1]<='f')
            d2 = mac_str[i+1] - 'a' + 10;

        mac_adr[j++] = d1*16+d2;
    }
    return 1;
}

If the name of interface is wlp4s0 the file containing the MAC address would be in path /sys/class/net/wlp4s0/address. What can cause the problem with performing NL80211_CMD_GET_STATION?

Is it possible that this error shows up because the command NL80211_CMD_GET_STATION needs root user and NL80211_CMD_GET_INTERFACE does not? But I of course use NL80211_CMD_GET_STATION with root user.

Masoud
  • 305
  • 1
  • 12

1 Answers1

0

The wireless client should be connected to AP and You need use MAC address of AP not of client.

You can see example of using NL80211_CMD_GET_STATION here (source of iw tool)

Dmitry
  • 784
  • 1
  • 5
  • 13
  • How about setting the flag `NLM_F_DUMP`? What does it bring? I want to get the `RSSI` of the link on a specific interface. By setting that flag the program works. I think because it does not need MAC address. – Masoud May 08 '18 at 20:27
  • BTW, I just tested with MAC address of the AP (which is the wireless router in my home) and it still gives me that error. – Masoud May 08 '18 at 20:45
  • @Masoud, my client is connected to AP. I get your code and callback from iw tool(print_sta_handle) and program works without any changing. – Dmitry May 10 '18 at 07:24
  • @Masoud, Are You start the program as root? – Dmitry May 10 '18 at 07:30
  • Yes I do. I use the code in the same function as you mentioned `print_sta_handle`. – Masoud May 13 '18 at 15:12
  • I get these warnings when I run the program. I am not sure if it has anything to do with the error: `/lib/x86_64-linux-gnu/libnl-3.so.200: no version information available` and `/lib/x86_64-linux-gnu/libnl-genl-3.so.200: no version information available`. – Masoud May 13 '18 at 15:35