| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2011-2012 Intel Corporation |
| * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program 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 General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <getopt.h> |
| |
| #include "lib/bluetooth.h" |
| #include "lib/mgmt.h" |
| |
| #include "monitor/bt.h" |
| #include "src/shared/mainloop.h" |
| #include "src/shared/util.h" |
| #include "src/shared/mgmt.h" |
| #include "src/shared/hci.h" |
| #include "src/shared/crypto.h" |
| |
| #define PEER_ADDR_TYPE 0x00 |
| #define PEER_ADDR "\x00\x00\x00\x00\x00\x00" |
| |
| #define ADV_IRK "\x69\x30\xde\xc3\x8f\x84\x74\x14" \ |
| "\xe1\x23\x99\xc1\xca\x9a\xc3\x31" |
| #define SCAN_IRK "\xfa\x73\x09\x11\x3f\x03\x37\x0f" \ |
| "\xf4\xf9\x93\x1e\xf9\xa3\x63\xa6" |
| |
| static struct mgmt *mgmt; |
| static uint16_t index1 = MGMT_INDEX_NONE; |
| static uint16_t index2 = MGMT_INDEX_NONE; |
| |
| static struct bt_crypto *crypto; |
| static struct bt_hci *adv_dev; |
| static struct bt_hci *scan_dev; |
| |
| static void print_rpa(const uint8_t addr[6]) |
| { |
| printf(" Address: %02x:%02x:%02x:%02x:%02x:%02x\n", |
| addr[5], addr[4], addr[3], |
| addr[2], addr[1], addr[0]); |
| printf(" Random: %02x%02x%02x\n", addr[3], addr[4], addr[5]); |
| printf(" Hash: %02x%02x%02x\n", addr[0], addr[1], addr[2]); |
| } |
| |
| static void scan_le_adv_report(const void *data, uint8_t size, |
| void *user_data) |
| { |
| const struct bt_hci_evt_le_adv_report *evt = data; |
| |
| if (evt->addr_type == 0x01 && (evt->addr[5] & 0xc0) == 0x40) { |
| uint8_t hash[3], irk[16]; |
| |
| memcpy(irk, ADV_IRK, 16); |
| bt_crypto_ah(crypto, irk, evt->addr + 3, hash); |
| |
| if (!memcmp(evt->addr, hash, 3)) { |
| printf("Received advertising report\n"); |
| print_rpa(evt->addr); |
| |
| memcpy(irk, ADV_IRK, 16); |
| bt_crypto_ah(crypto, irk, evt->addr + 3, hash); |
| |
| printf(" -> Computed hash: %02x%02x%02x\n", |
| hash[0], hash[1], hash[2]); |
| |
| mainloop_quit(); |
| } |
| } |
| } |
| |
| static void scan_le_meta_event(const void *data, uint8_t size, |
| void *user_data) |
| { |
| uint8_t evt_code = ((const uint8_t *) data)[0]; |
| |
| switch (evt_code) { |
| case BT_HCI_EVT_LE_ADV_REPORT: |
| scan_le_adv_report(data + 1, size - 1, user_data); |
| break; |
| } |
| } |
| |
| static void scan_enable_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| } |
| |
| static void adv_enable_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| struct bt_hci_cmd_le_set_scan_parameters cmd4; |
| struct bt_hci_cmd_le_set_scan_enable cmd5; |
| |
| cmd4.type = 0x00; /* Passive scanning */ |
| cmd4.interval = cpu_to_le16(0x0010); |
| cmd4.window = cpu_to_le16(0x0010); |
| cmd4.own_addr_type = 0x00; /* Use public address */ |
| cmd4.filter_policy = 0x00; |
| |
| bt_hci_send(scan_dev, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, |
| &cmd4, sizeof(cmd4), NULL, NULL, NULL); |
| |
| cmd5.enable = 0x01; |
| cmd5.filter_dup = 0x01; |
| |
| bt_hci_send(scan_dev, BT_HCI_CMD_LE_SET_SCAN_ENABLE, |
| &cmd5, sizeof(cmd5), |
| scan_enable_callback, NULL, NULL); |
| } |
| |
| static void adv_le_evtmask_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| struct bt_hci_cmd_le_set_resolv_timeout cmd0; |
| struct bt_hci_cmd_le_add_to_resolv_list cmd1; |
| struct bt_hci_cmd_le_set_resolv_enable cmd2; |
| struct bt_hci_cmd_le_set_random_address cmd3; |
| struct bt_hci_cmd_le_set_adv_parameters cmd4; |
| struct bt_hci_cmd_le_set_adv_enable cmd5; |
| |
| cmd0.timeout = cpu_to_le16(0x0384); |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT, |
| &cmd0, sizeof(cmd0), NULL, NULL, NULL); |
| |
| cmd1.addr_type = PEER_ADDR_TYPE; |
| memcpy(cmd1.addr, PEER_ADDR, 6); |
| memset(cmd1.peer_irk, 0, 16); |
| memcpy(cmd1.local_irk, ADV_IRK, 16); |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST, |
| &cmd1, sizeof(cmd1), NULL, NULL, NULL); |
| |
| cmd2.enable = 0x01; |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RESOLV_ENABLE, |
| &cmd2, sizeof(cmd2), NULL, NULL, NULL); |
| |
| bt_crypto_random_bytes(crypto, cmd3.addr + 3, 3); |
| cmd3.addr[5] &= 0x3f; /* Clear two most significant bits */ |
| cmd3.addr[5] |= 0x40; /* Set second most significant bit */ |
| bt_crypto_ah(crypto, cmd1.local_irk, cmd3.addr + 3, cmd3.addr); |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, |
| &cmd3, sizeof(cmd3), NULL, NULL, NULL); |
| |
| printf("Setting advertising address\n"); |
| print_rpa(cmd3.addr); |
| |
| cmd4.min_interval = cpu_to_le16(0x0800); |
| cmd4.max_interval = cpu_to_le16(0x0800); |
| cmd4.type = 0x03; /* Non-connectable advertising */ |
| cmd4.own_addr_type = 0x03; /* Local IRK, random address fallback */ |
| cmd4.direct_addr_type = PEER_ADDR_TYPE; |
| memcpy(cmd4.direct_addr, PEER_ADDR, 6); |
| cmd4.channel_map = 0x07; |
| cmd4.filter_policy = 0x00; |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, |
| &cmd4, sizeof(cmd4), NULL, NULL, NULL); |
| |
| cmd5.enable = 0x01; |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE, |
| &cmd5, sizeof(cmd5), |
| adv_enable_callback, NULL, NULL); |
| } |
| |
| static void adv_le_features_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| const struct bt_hci_rsp_le_read_local_features *rsp = data; |
| uint8_t evtmask[] = { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| |
| if (rsp->status) { |
| fprintf(stderr, "Failed to read local LE features\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_EVENT_MASK, evtmask, 8, |
| adv_le_evtmask_callback, NULL, NULL); |
| } |
| |
| static void adv_features_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| const struct bt_hci_rsp_read_local_features *rsp = data; |
| uint8_t evtmask[] = { 0x90, 0xe8, 0x04, 0x02, 0x00, 0x80, 0x00, 0x20 }; |
| |
| if (rsp->status) { |
| fprintf(stderr, "Failed to read local features\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| if (!(rsp->features[4] & 0x40)) { |
| fprintf(stderr, "Controller without Low Energy support\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask, 8, |
| NULL, NULL, NULL); |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, NULL, 0, |
| adv_le_features_callback, NULL, NULL); |
| } |
| |
| static void scan_le_evtmask_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| bt_hci_send(adv_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL); |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, |
| adv_features_callback, NULL, NULL); |
| } |
| |
| static void scan_le_features_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| const struct bt_hci_rsp_le_read_local_features *rsp = data; |
| uint8_t evtmask[] = { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| |
| if (rsp->status) { |
| fprintf(stderr, "Failed to read local LE features\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_EVENT_MASK, evtmask, 8, |
| scan_le_evtmask_callback, NULL, NULL); |
| } |
| |
| static void scan_features_callback(const void *data, uint8_t size, |
| void *user_data) |
| { |
| const struct bt_hci_rsp_read_local_features *rsp = data; |
| uint8_t evtmask[] = { 0x90, 0xe8, 0x04, 0x02, 0x00, 0x80, 0x00, 0x20 }; |
| |
| if (rsp->status) { |
| fprintf(stderr, "Failed to read local features\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| if (!(rsp->features[4] & 0x40)) { |
| fprintf(stderr, "Controller without Low Energy support\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| bt_hci_send(scan_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask, 8, |
| NULL, NULL, NULL); |
| |
| bt_hci_send(scan_dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, NULL, 0, |
| scan_le_features_callback, NULL, NULL); |
| } |
| |
| static void read_index_list(uint8_t status, uint16_t len, const void *param, |
| void *user_data) |
| { |
| const struct mgmt_rp_read_index_list *rp = param; |
| uint16_t count; |
| int i; |
| |
| if (status) { |
| fprintf(stderr, "Reading index list failed: %s\n", |
| mgmt_errstr(status)); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| count = le16_to_cpu(rp->num_controllers); |
| |
| if (count < 2) { |
| fprintf(stderr, "At least 2 controllers are required\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| for (i = 0; i < count; i++) { |
| uint16_t index = cpu_to_le16(rp->index[i]); |
| |
| if (index < index1) |
| index1 = index; |
| } |
| |
| for (i = 0; i < count; i++) { |
| uint16_t index = cpu_to_le16(rp->index[i]); |
| |
| if (index < index2 && index > index1) |
| index2 = index; |
| } |
| |
| printf("Selecting index %u for advertiser\n", index1); |
| printf("Selecting index %u for scanner\n", index2); |
| |
| crypto = bt_crypto_new(); |
| if (!crypto) { |
| fprintf(stderr, "Failed to open crypto interface\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| adv_dev = bt_hci_new_user_channel(index1); |
| if (!adv_dev) { |
| fprintf(stderr, "Failed to open HCI for advertiser\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| scan_dev = bt_hci_new_user_channel(index2); |
| if (!scan_dev) { |
| fprintf(stderr, "Failed to open HCI for scanner\n"); |
| mainloop_exit_failure(); |
| return; |
| } |
| |
| bt_hci_register(scan_dev, BT_HCI_EVT_LE_META_EVENT, |
| scan_le_meta_event, NULL, NULL); |
| |
| bt_hci_send(scan_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL); |
| |
| bt_hci_send(scan_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, |
| scan_features_callback, NULL, NULL); |
| } |
| |
| static void signal_callback(int signum, void *user_data) |
| { |
| switch (signum) { |
| case SIGINT: |
| case SIGTERM: |
| mainloop_quit(); |
| break; |
| } |
| } |
| |
| static void usage(void) |
| { |
| printf("advtest - Advertising testing\n" |
| "Usage:\n"); |
| printf("\tadvtest [options]\n"); |
| printf("options:\n" |
| "\t-h, --help Show help options\n"); |
| } |
| |
| static const struct option main_options[] = { |
| { "version", no_argument, NULL, 'v' }, |
| { "help", no_argument, NULL, 'h' }, |
| { } |
| }; |
| |
| int main(int argc ,char *argv[]) |
| { |
| sigset_t mask; |
| int exit_status; |
| |
| for (;;) { |
| int opt; |
| |
| opt = getopt_long(argc, argv, "vh", main_options, NULL); |
| if (opt < 0) |
| break; |
| |
| switch (opt) { |
| case 'v': |
| printf("%s\n", VERSION); |
| return EXIT_SUCCESS; |
| case 'h': |
| usage(); |
| return EXIT_SUCCESS; |
| default: |
| return EXIT_FAILURE; |
| } |
| } |
| |
| if (argc - optind > 0) { |
| fprintf(stderr, "Invalid command line parameters\n"); |
| return EXIT_FAILURE; |
| } |
| |
| mainloop_init(); |
| |
| sigemptyset(&mask); |
| sigaddset(&mask, SIGINT); |
| sigaddset(&mask, SIGTERM); |
| |
| mainloop_set_signal(&mask, signal_callback, NULL, NULL); |
| |
| mgmt = mgmt_new_default(); |
| if (!mgmt) { |
| fprintf(stderr, "Failed to open management socket\n"); |
| return EXIT_FAILURE; |
| } |
| |
| if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, |
| MGMT_INDEX_NONE, 0, NULL, |
| read_index_list, NULL, NULL)) { |
| fprintf(stderr, "Failed to read index list\n"); |
| exit_status = EXIT_FAILURE; |
| goto done; |
| } |
| |
| exit_status = mainloop_run(); |
| |
| bt_hci_unref(adv_dev); |
| bt_hci_unref(scan_dev); |
| |
| bt_crypto_unref(crypto); |
| |
| done: |
| mgmt_unref(mgmt); |
| |
| return exit_status; |
| } |