/* * * Wireless daemon for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "linux/nl80211.h" #include "src/mpdu.h" #include "src/nl80211util.h" static struct l_genl *genl; static struct l_genl_family *nl80211; static int exit_status; static uint64_t wdev_id; static uint8_t wdev_addr[6]; static uint32_t freq; static const uint8_t probe_req_body[] = { /* SSID */ 0x00, 0x07, 'D', 'I', 'R', 'E', 'C', 'T', '-', /* Supported Rates */ 0x01, 0x08, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, /* DS Parameter Set */ 0x03, 0x01, 0x00, /* HT Capabilities */ 0x2d, 0x1a, 0xef, 0x11, 0x17, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* WPS */ 0xdd, 0x6c, 0x00, 0x50, 0xf2, 0x04, /* > Version */ 0x10, 0x4a, 0x00, 0x01, 0x10, /* > Request Type */ 0x10, 0x3a, 0x00, 0x01, 0x00, /* > Config Methods */ 0x10, 0x08, 0x00, 0x02, 0x13, 0x80, /* > UUID E */ 0x10, 0x47, 0x00, 0x10, 0x46, 0x92, 0x49, 0x6f, 0xce, 0x1e, 0x5f, 0xd1, 0xa5, 0x45, 0x9b, 0x1c, 0xa5, 0xde, 0xb9, 0x41, /* > Primary Device Type */ 0x10, 0x54, 0x00, 0x08, 0x00, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x01, /* > RF Bands */ 0x10, 0x3c, 0x00, 0x01, 0x01, /* > Association State */ 0x10, 0x02, 0x00, 0x02, 0x00, 0x00, /* > Configuration Error */ 0x10, 0x09, 0x00, 0x02, 0x00, 0x00, /* > Device Password ID */ 0x10, 0x12, 0x00, 0x02, 0x00, 0x00, /* > Manufacturer */ 0x10, 0x21, 0x00, 0x01, 0x20, /* > Model Name */ 0x10, 0x23, 0x00, 0x01, 0x20, /* > Model Numbers */ 0x10, 0x24, 0x00, 0x01, 0x20, /* > Device Name */ 0x10, 0x11, 0x00, 0x04, 't', 'e', 's', 't', /* > Vendor Extension > Version2 */ 0x10, 0x49, 0x00, 0x06, 0x00, 0x37, 0x2a, 0x00, 0x01, 0x20, /* P2P */ 0xdd, 0x11, 0x50, 0x6f, 0x9a, 0x09, /* > P2P Capability */ 0x02, 0x02, 0x00, 0x04, 0x00, /* > Listen Channel */ 0x06, 0x05, 0x00, 'X', 'X', 0x04, 0x51, 0x01, }; static void frame_cb(struct l_genl_msg *msg, void *user_data) { int err = l_genl_msg_get_error(msg); if (err < 0) { l_error("CMD_FRAME failed: %s (%i)", strerror(-err), -err); exit_status = EXIT_FAILURE; } else { l_info("Frame queued"); exit_status = EXIT_SUCCESS; } l_main_quit(); } static void get_interface_callback(struct l_genl_msg *msg, void *user_data) { uint32_t ifindex; uint32_t iftype; const char *ifname; const uint8_t *ifaddr; uint64_t cur_wdev_id; struct ifreq ifr; int sock; int r; /* * For now hoose the first interface with iftype station, require it * to be UP and have an ifindex. */ if (wdev_id) return; if (nl80211_parse_attrs(msg, NL80211_ATTR_IFINDEX, &ifindex, NL80211_ATTR_WDEV, &cur_wdev_id, NL80211_ATTR_IFTYPE, &iftype, NL80211_ATTR_IFNAME, &ifname, NL80211_ATTR_MAC, &ifaddr, NL80211_ATTR_UNSPEC) < 0) return; if (iftype != NL80211_IFTYPE_STATION) return; sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock == -1) return; memset(&ifr, 0, sizeof(ifr)); l_strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); r = ioctl(sock, SIOCGIFFLAGS, &ifr); close(sock); /* IFF_RUNNING not required */ if (r == -1 || !(ifr.ifr_flags & IFF_UP)) return; l_info("Selected interface %s", ifname); wdev_id = cur_wdev_id; memcpy(wdev_addr, ifaddr, 6); } static void get_interface_done(void *user_data) { struct l_genl_msg *msg; uint8_t frame_buf[256] __attribute__ ((aligned)); struct mmpdu_header *hdr = (void *) frame_buf; static const uint8_t bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; size_t frame_len; if (!wdev_id) { l_error("No suitable interface found"); exit_status = EXIT_FAILURE; l_main_quit(); return; } memset(frame_buf, 0, sizeof(*hdr)); hdr->fc.protocol_version = 0; hdr->fc.type = MPDU_TYPE_MANAGEMENT; hdr->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST; memcpy(hdr->address_1, bcast_addr, 6); /* DA */ memcpy(hdr->address_2, wdev_addr, 6); /* SA */ memcpy(hdr->address_3, bcast_addr, 6); /* BSSID */ frame_len = (uint8_t *) mmpdu_body(hdr) - (uint8_t *) hdr; memcpy((void *) mmpdu_body(hdr), probe_req_body, sizeof(probe_req_body)); frame_len += sizeof(probe_req_body); msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + frame_len); l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &wdev_id); if (freq) { l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &freq); l_genl_msg_append_attr(msg, NL80211_ATTR_OFFCHANNEL_TX_OK, 0, NULL); } l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME, frame_len, frame_buf); l_genl_msg_append_attr(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK, 0, NULL); l_genl_msg_append_attr(msg, NL80211_ATTR_TX_NO_CCK_RATE, 0, NULL); if (!l_genl_family_send(nl80211, msg, frame_cb, user_data, NULL)) { l_error("l_genl_family_send failed"); exit_status = EXIT_FAILURE; l_main_quit(); return; } } static void dump_interfaces(void) { struct l_genl_msg *msg; msg = l_genl_msg_new(NL80211_CMD_GET_INTERFACE); if (!l_genl_family_dump(nl80211, msg, get_interface_callback, NULL, get_interface_done)) { l_genl_msg_unref(msg); l_error("Getting nl80211 interface information failed"); exit_status = EXIT_FAILURE; l_main_quit(); return; } } static void family_discovered(const struct l_genl_family_info *info, void *user_data) { if (!strcmp(l_genl_family_info_get_name(info), NL80211_GENL_NAME)) nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME); } static void discovery_done(void *user_data) { if (!nl80211) { l_error("nl80211 doesn't exist.\n" "Load it manually using modprobe cfg80211"); goto quit; } dump_interfaces(); return; quit: exit_status = EXIT_FAILURE; l_main_quit(); } int main(int argc, char *argv[]) { if (argc >= 2) { char *endp; if (!strcmp(argv[1], "-h")) { fprintf(stderr, "Usage: %s []\n\n" "Send out a broadcast Probe Request frame. " "A wireless interface must be UP. If a " "frequency is not given, the frame is " "transmitted on the current channel.\n", argv[0]); return EXIT_SUCCESS; } freq = strtol(argv[1], &endp, 0); if (*endp != '\0') { fprintf(stderr, "Can't parse '%s'\n", endp); return EXIT_FAILURE; } } if (!l_main_init()) return EXIT_FAILURE; l_log_set_stderr(); exit_status = EXIT_FAILURE; genl = l_genl_new(); if (!genl) { l_error("Failed to initialize generic netlink"); goto done; } if (!l_genl_discover_families(genl, family_discovered, NULL, discovery_done)) { l_error("Unable to start family discovery"); l_genl_unref(genl); goto done; } l_main_run(); l_genl_family_free(nl80211); l_genl_unref(genl); done: l_main_exit(); return exit_status; }