diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/bneptest.c | 710 | ||||
-rw-r--r-- | tools/btgatt-client.c | 218 | ||||
-rw-r--r-- | tools/btgatt-server.c | 23 | ||||
-rw-r--r-- | tools/btmgmt.c | 999 | ||||
-rw-r--r-- | tools/btsnoop.c | 100 | ||||
-rw-r--r-- | tools/mgmt-tester.c | 1123 | ||||
-rw-r--r-- | tools/mpris-proxy.c | 2 | ||||
-rw-r--r-- | tools/obexctl.c | 37 | ||||
-rw-r--r-- | tools/oobtest.c | 451 |
9 files changed, 3459 insertions, 204 deletions
diff --git a/tools/bneptest.c b/tools/bneptest.c new file mode 100644 index 00000000..84319b9e --- /dev/null +++ b/tools/bneptest.c @@ -0,0 +1,710 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2015 Intel Corporation + * + * + * 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 <stdio.h> +#include <signal.h> +#include <stdlib.h> +#include <getopt.h> +#include <stdbool.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/sockios.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> + +#include <glib.h> + +#include "src/log.h" +#include "src/shared/util.h" +#include "btio/btio.h" +#include "lib/bnep.h" +#include "profiles/network/bnep.h" + +enum { + MODE_LISTEN, + MODE_CONNECT, +}; + +static GMainLoop *mloop; +static GIOChannel *bnep_io; +static struct bnep *session; + +static int mode; +static bool no_close_after_disconn; +static int send_frame_timeout; + +static bdaddr_t src_addr, dst_addr; +static char iface[16]; +static char bridge[16]; +static bool send_ctrl_msg_type_set = false; +static uint8_t ctrl_msg_type = 0x00; +static bool send_bnep_msg_type_set = false; +static uint8_t bnep_msg_type = 0x00; +static int ctrl_msg_retransmition_nb = 0; +static int bnep_msg_retransmission_nb = 0; +static uint16_t local_role = BNEP_SVC_PANU; +static uint16_t remote_role = BNEP_SVC_NAP; +static uint16_t ntw_proto_down_range = 0x0000; +static uint16_t ntw_proto_up_range = 0xdc05; +static uint16_t ntw_proto_type = 0x0000; +static uint8_t mcast_addr_down_range[6]; +static uint8_t mcast_addr_up_range[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +static uint8_t src_hw_addr[6]; +static uint8_t dst_hw_addr[6]; +static uint8_t general_frame_payload[] = "abcdef0123456789_bnep_test_data"; + +static int set_forward_delay(int sk) +{ + unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0, 0, 0 }; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); + ifr.ifr_data = (char *) args; + + if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) { + error("setting forward delay failed: %d (%s)", + errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int nap_create_bridge(void) +{ + int sk, err; + + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -EOPNOTSUPP; + + if (ioctl(sk, SIOCBRADDBR, bridge) < 0) { + if (errno != EEXIST) { + close(sk); + return -EOPNOTSUPP; + } + } + + err = set_forward_delay(sk); + if (err < 0) { + printf("failed to set forward delay\n"); + ioctl(sk, SIOCBRDELBR, bridge); + } + + close(sk); + + return err; +} + +static int cleanup(void) +{ + bnep_cleanup(); + + if (mode == MODE_LISTEN) + bnep_server_delete(bridge, iface, &dst_addr); + + if (bnep_io) { + g_io_channel_shutdown(bnep_io, TRUE, NULL); + g_io_channel_unref(bnep_io); + bnep_io = NULL; + } + + return 0; +} + +static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + printf("%s\n", __func__); + + if (no_close_after_disconn) + return FALSE; + + /* Cleanup since it's called when disconnected l2cap */ + if (cleanup() < 0) { + printf("cleanup went wrong...\n"); + return FALSE; + } + + g_main_loop_quit(mloop); + return FALSE; +} + +static ssize_t send_compressed_frame(int sk, uint8_t type) +{ + uint8_t frame[100]; + + printf("%s\n", __func__); + + if (send_frame_timeout > 0) { + printf("waiting %d seconds before sending msg\n", + send_frame_timeout); + sleep(send_frame_timeout); + } + + frame[0] = type; + memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr)); + memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr)); + frame[13] = ntw_proto_type & 0xff; + frame[14] = (ntw_proto_type >> 8); + memcpy(&frame[15], general_frame_payload, + sizeof(general_frame_payload)); + + /* TODO - set frame payload by user */ + return send(sk, frame, 15 + sizeof(general_frame_payload), 0); +} + +static ssize_t send_general_frame(int sk) +{ + uint8_t frame[100]; + + printf("%s\n", __func__); + + if (send_frame_timeout > 0) { + printf("waiting %d seconds before sending msg\n", + send_frame_timeout); + sleep(send_frame_timeout); + } + + frame[0] = BNEP_GENERAL; + memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr)); + memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr)); + frame[13] = ntw_proto_type & 0xff; + frame[14] = (ntw_proto_type >> 8); + memcpy(&frame[15], general_frame_payload, + sizeof(general_frame_payload)); + + /* TODO - set frame payload by user */ + return send(sk, frame, 15 + sizeof(general_frame_payload), 0); +} + +static ssize_t send_ctrl_frame(int sk) +{ + /* + * Max buff size = type(1byte) + ctrl(1byte) + len(2byte) + + * mcast_addr_down(6byte) + mcast_addr_up(6byte) + */ + uint8_t buff[16]; + struct bnep_set_filter_req *frame = (void *) buff; + int err; + + printf("%s\n", __func__); + + if (send_frame_timeout > 0) { + printf("waiting %d seconds before sending msg\n", + send_frame_timeout); + sleep(send_frame_timeout); + } + + switch (ctrl_msg_type) { + case BNEP_FILTER_NET_TYPE_SET: + frame->type = BNEP_CONTROL; + frame->ctrl = ctrl_msg_type; + frame->len = htons(sizeof(ntw_proto_down_range) + + sizeof(ntw_proto_up_range)); + memcpy(frame->list, &ntw_proto_down_range, + sizeof(ntw_proto_down_range)); + memcpy(frame->list + sizeof(ntw_proto_down_range), + &ntw_proto_up_range, sizeof(ntw_proto_up_range)); + + err = send(sk, frame, sizeof(*frame) + + sizeof(ntw_proto_down_range) + + sizeof(ntw_proto_up_range), 0); + break; + case BNEP_FILTER_MULT_ADDR_SET: + frame->type = BNEP_CONTROL; + frame->ctrl = ctrl_msg_type; + frame->len = htons(sizeof(mcast_addr_down_range) + + sizeof(mcast_addr_up_range)); + memcpy(frame->list, mcast_addr_down_range, + sizeof(mcast_addr_down_range)); + memcpy(frame->list + sizeof(mcast_addr_down_range), + mcast_addr_up_range, sizeof(mcast_addr_up_range)); + + err = send(sk, frame, sizeof(*frame) + + sizeof(mcast_addr_down_range) + + sizeof(mcast_addr_up_range), 0); + break; + default: + err = -1; + break; + } + + return err; +} + +static int send_bnep_frame(int sk) +{ + int err; + + switch (bnep_msg_type) { + case BNEP_GENERAL: + err = send_general_frame(sk); + break; + case BNEP_COMPRESSED: + err = send_compressed_frame(sk, BNEP_COMPRESSED); + break; + case BNEP_COMPRESSED_SRC_ONLY: + err = send_compressed_frame(sk, + BNEP_COMPRESSED_SRC_ONLY); + break; + case BNEP_COMPRESSED_DST_ONLY: + err = send_compressed_frame(sk, + BNEP_COMPRESSED_DST_ONLY); + break; + default: + printf("wrong bnep_msg_type 0x%02x\n", bnep_msg_type); + err = -EINVAL; + break; + } + + return err; +} + +static void handle_bnep_msg_send(int sk) +{ + if (send_ctrl_msg_type_set) { + do { + if (send_ctrl_frame(sk) < 0) + printf("sending ctrl frame error: %s (%d)\n", + strerror(errno), errno); + } while (ctrl_msg_retransmition_nb--); + } + + if (send_bnep_msg_type_set) { + do { + if (send_bnep_frame(sk) < 0) + printf("sending bnep frame error: %s (%d)\n", + strerror(errno), errno); + } while (bnep_msg_retransmission_nb--); + } +} + +static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + uint8_t packet[BNEP_MTU]; + int sk, n, err; + + printf("%s\n", __func__); + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + error("hangup or error or inval on BNEP socket"); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(chan); + + /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */ +#ifdef __TIZEN_PATCH__ + n = recv(sk, packet, sizeof(packet), MSG_PEEK); +#else + n = read(sk, packet, sizeof(packet)); +#endif + if (n < 0) { + error("read(): %s(%d)", strerror(errno), errno); + return FALSE; + } + + err = nap_create_bridge(); + if (err < 0) { + error("failed to create bridge: %s (%d)", strerror(-err), err); + return FALSE; + } + + if (bnep_server_add(sk, (err < 0) ? NULL : bridge, iface, &dst_addr, + packet, n) < 0) { + printf("server_connadd failed\n"); + cleanup(); + return FALSE; + } + + g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, bnep_watchdog_cb, + NULL); + + handle_bnep_msg_send(sk); + + g_io_channel_unref(bnep_io); + bnep_io = NULL; + + return FALSE; +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + printf("%s\n", __func__); + + if (err) { + error("%s", err->message); + return; + } + + g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + setup_bnep_cb, NULL); +} + +static void connected_client_cb(char *iface, int err, void *data) +{ + int sk = PTR_TO_INT(data); + + printf("%s\n", __func__); + + handle_bnep_msg_send(sk); +} + +static void disconnected_client_cb(void *data) +{ + printf("%s\n", __func__); + + if (no_close_after_disconn) + return; + + /* Cleanup since it's called when disconnected l2cap */ + if (cleanup() < 0) { + printf("cleanup went wrong...\n"); + return; + } + + g_main_loop_quit(mloop); +} + +static void connect_client_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + int perr; + int sk; + + sk = g_io_channel_unix_get_fd(bnep_io); + + session = bnep_new(sk, local_role, remote_role, bridge); + if (!session) { + printf("cannot create bnep session\n"); + return; + } + + perr = bnep_connect(session, connected_client_cb, + disconnected_client_cb, INT_TO_PTR(sk), NULL); + if (perr < 0) + printf("cannot initiate bnep connection\n"); +} + +static void confirm_cb(GIOChannel *chan, gpointer data) +{ + GError *err = NULL; + char address[18]; + + printf("%s\n", __func__); + + bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst_addr, BT_IO_OPT_DEST, + address, BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + return; + } + + printf("incoming connection from: %s\n", address); + + bnep_io = g_io_channel_ref(chan); + g_io_channel_set_close_on_unref(bnep_io, TRUE); + + if (!bt_io_accept(bnep_io, connect_cb, NULL, NULL, &err)) { + error("bt_io_accept: %s", err->message); + g_error_free(err); + g_io_channel_unref(bnep_io); + } +} + +static int bnep_server_listen(void) +{ + GError *gerr = NULL; + + printf("%s\n", __func__); + + bnep_io = bt_io_listen(NULL, confirm_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &src_addr, + BT_IO_OPT_PSM, BNEP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_OMTU, BNEP_MTU, + BT_IO_OPT_IMTU, BNEP_MTU, + BT_IO_OPT_INVALID); + if (!bnep_io) { + printf("can't start server listening: err %s\n", gerr->message); + g_error_free(gerr); + return -1; + } + + return 0; +} + +static int bnep_client_connect(void) +{ + GError *gerr = NULL; + char bdastr[18]; + + printf("%s\n", __func__); + + ba2str(&dst_addr, bdastr); + printf("connecting %s\n", bdastr); + + bnep_io = bt_io_connect(connect_client_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &src_addr, + BT_IO_OPT_DEST_BDADDR, &dst_addr, + BT_IO_OPT_PSM, BNEP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_OMTU, BNEP_MTU, + BT_IO_OPT_IMTU, BNEP_MTU, + BT_IO_OPT_INVALID); + if (!bnep_io) { + printf("cannot connect: err %s\n", gerr->message); + g_error_free(gerr); + return -1; + } + + return 0; +} + +static void exit_handler(int sig) +{ + printf("got sig = %d, cleaning up...\n", sig); + + if (cleanup() < 0) + printf("cleanup failure...\n"); + else + printf("cleanup successful - exit\n"); + + exit(0); +} + +static void usage(void) +{ + printf("bneptest - BNEP testing ver %s\n", VERSION); + printf("Usage:\n" + "\tbneptest [-i] -b <bridge name> -n <iface name>" + " <connection mode> [send_ctrl_cmd] [options]\n" + "\t-i hci dev number <hci number>, def. 0\n" + "\t-b bridge name <string>\n" + "\t-n interface name <string>\n"); + printf("Connect Mode:\n" + "\t-c connect <dst_addr>\n" + "\t-r remote role <16 bit svc value>\n" + "\t-l local role <16 bit svc valu>\n"); + printf("Listen Mode:\n" + "\t-s start server listening\n"); + printf("Send control command:\n" + "\t-t send message type <control msg type>, def. 0\n" + "\t-e start network protocol type range <16 bit val>, def. 0\n" + "\t-d end network protocol type range <16 bit val>, def. 1500\n" + "\t-g start multicast addr range <xx:xx:xx:xx:xx:xx>, def. 0\n" + "\t-j end multicast addr range <xx:xx:xx:xx:xx:xx>, def. f\n" + "\t-y number of ctrl frame retransmission <integer>, def. 0\n" + "\t-u number of bnep frame retransmission <integer>, def. 0\n"); + printf("Send bnep generic frame:\n" + "\t-w send bnep generic frame <bnep generic type>, def. 0\n" + "\t-k set src mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n" + "\t-f set dst mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n"); + printf("Options:\n" + "\t-T send message timeout after setup <seconds>\n" + "\t-N don't close bneptest after disconnect\n"); +} + +static struct option main_options[] = { + { "device", 1, 0, 'i' }, + { "listen", 0, 0, 's' }, + { "connect", 1, 0, 'c' }, + { "snd_ctrl_msg_type", 1, 0, 't' }, + { "snd_bnep_msg_type", 1, 0, 'w' }, + { "src_hw_addr", 1, 0, 'k' }, + { "dst_hw_addr", 1, 0, 'f' }, + { "send_timeout", 1, 0, 'T' }, + { "ntw_proto_down_range", 1, 0, 'd' }, + { "ntw_proto_up_range", 1, 0, 'e' }, + { "mcast_addr_down_range", 1, 0, 'g' }, + { "mcast_addr_up_range", 1, 0, 'j' }, + { "local_role", 1, 0, 'l' }, + { "remote_role", 1, 0, 'r' }, + { "bridge name", 1, 0, 'b' }, + { "iface name", 1, 0, 'n' }, + { "no_close", 0, 0, 'N' }, + { "retrans_ctrl_nb", 0, 0, 'y' }, + { "retrans_bnep_nb", 0, 0, 'u' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + int opt, i; + int err; + bool is_set_b_name = false, is_set_i_name = false; + + DBG(""); + + signal(SIGINT, exit_handler); + + hci_devba(0, &src_addr); + bacpy(&src_addr, BDADDR_ANY); + + mloop = g_main_loop_new(NULL, FALSE); + if (!mloop) { + printf("cannot create main loop\n"); + + exit(1); + } + + while ((opt = getopt_long(argc, argv, + "+i:c:b:n:t:T:d:e:g:j:k:f:w:l:r:y:u:Nsh", + main_options, NULL)) != EOF) { + switch (opt) { + case 'i': + if (!strncmp(optarg, "hci", 3)) + hci_devba(atoi(optarg + 3), &src_addr); + else + str2ba(optarg, &src_addr); + break; + case 's': + mode = MODE_LISTEN; + break; + case 'c': + str2ba(optarg, &dst_addr); + mode = MODE_CONNECT; + break; + case 't': + send_ctrl_msg_type_set = true; + ctrl_msg_type = atoi(optarg); + break; + case 'w': + send_bnep_msg_type_set = true; + bnep_msg_type = atoi(optarg); + break; + case 'k': + for (i = 0; i <= 5; i++, optarg += 3) + src_hw_addr[i] = strtol(optarg, NULL, 16); + break; + case 'f': + for (i = 0; i <= 5; i++, optarg += 3) + dst_hw_addr[i] = strtol(optarg, NULL, 16); + break; + case 'T': + send_frame_timeout = atoi(optarg); + break; + case 'd': + ntw_proto_down_range = htons(atoi(optarg)); + break; + case 'e': + ntw_proto_up_range = htons(atoi(optarg)); + break; + case 'g': + for (i = 5; i >= 0; i--, optarg += 3) + mcast_addr_down_range[i] = + strtol(optarg, NULL, 16); + break; + case 'j': + for (i = 5; i >= 0; i--, optarg += 3) + mcast_addr_up_range[i] = + strtol(optarg, NULL, 16); + break; + case 'l': + local_role = atoi(optarg); + break; + case 'r': + remote_role = atoi(optarg); + break; + case 'b': + strncpy(bridge, optarg, 16); + bridge[15] = '\0'; + is_set_b_name = true; + break; + case 'n': + strncpy(iface, optarg, 14); + strcat(iface, "\%d"); + iface[15] = '\0'; + is_set_i_name = true; + break; + case 'N': + no_close_after_disconn = true; + break; + case 'y': + ctrl_msg_retransmition_nb = atoi(optarg); + break; + case 'u': + bnep_msg_retransmission_nb = atoi(optarg); + break; + case 'h': + default: + usage(); + exit(0); + } + } + + if (!is_set_b_name || !is_set_i_name) { + printf("bridge, interface name must be set!\n"); + exit(1); + } + + switch (mode) { + case MODE_CONNECT: + err = bnep_init(); + if (err < 0) { + printf("cannot initialize bnep\n"); + exit(1); + } + err = bnep_client_connect(); + if (err < 0) + exit(1); + + break; + case MODE_LISTEN: + err = bnep_init(); + if (err < 0) { + printf("cannot initialize bnep\n"); + exit(1); + } + err = bnep_server_listen(); + if (err < 0) + exit(1); + + break; + default: + printf("connect/listen mode not set, exit...\n"); + exit(1); + } + + g_main_loop_run(mloop); + + printf("Done\n"); + + g_main_loop_unref(mloop); + + return 0; +} diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 02d9b57d..ee5315d4 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -67,6 +67,8 @@ struct client { struct bt_att *att; struct gatt_db *db; struct bt_gatt_client *gatt; + + unsigned int reliable_session_id; }; static void print_prompt(void) @@ -112,12 +114,6 @@ static const char *ecode_to_string(uint8_t ecode) return "Group type Not Supported"; case BT_ATT_ERROR_INSUFFICIENT_RESOURCES: return "Insufficient Resources"; - case BT_ERROR_CCC_IMPROPERLY_CONFIGURED: - return "CCC Improperly Configured"; - case BT_ERROR_ALREADY_IN_PROGRESS: - return "Procedure Already in Progress"; - case BT_ERROR_OUT_OF_RANGE: - return "Out of Range"; default: return "Unknown error type"; } @@ -890,6 +886,208 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) free(value); } +static void write_prepare_usage(void) +{ + printf("Usage: write-prepare [options] <value_handle> <offset> " + "<value>\n" + "Options:\n" + "\t-s, --session-id\tSession id\n" + "e.g.:\n" + "\twrite-prepare -s 1 0x0001 00 01 00\n"); +} + +static struct option write_prepare_options[] = { + { "session-id", 1, 0, 's' }, + { } +}; + +static void cmd_write_prepare(struct client *cli, char *cmd_str) +{ + int opt, i; + char *argvbuf[516]; + char **argv = argvbuf; + int argc = 0; + unsigned int id = 0; + uint16_t handle; + uint16_t offset; + char *endptr = NULL; + unsigned int length; + uint8_t *value = NULL; + + if (!bt_gatt_client_is_ready(cli->gatt)) { + printf("GATT client not initialized\n"); + return; + } + + if (!parse_args(cmd_str, 514, argv + 1, &argc)) { + printf("Too many arguments\n"); + write_value_usage(); + return; + } + + /* Add command name for getopt_long */ + argc++; + argv[0] = "write-prepare"; + + optind = 0; + while ((opt = getopt_long(argc, argv , "s:", write_prepare_options, + NULL)) != -1) { + switch (opt) { + case 's': + if (!optarg) { + write_prepare_usage(); + return; + } + + id = atoi(optarg); + + break; + default: + write_prepare_usage(); + return; + } + } + + argc -= optind; + argv += optind; + + if (argc < 3) { + write_prepare_usage(); + return; + } + + if (cli->reliable_session_id != id) { + printf("Session id != Ongoing session id (%u!=%u)\n", id, + cli->reliable_session_id); + return; + } + + handle = strtol(argv[0], &endptr, 0); + if (!endptr || *endptr != '\0' || !handle) { + printf("Invalid handle: %s\n", argv[0]); + return; + } + + endptr = NULL; + offset = strtol(argv[1], &endptr, 0); + if (!endptr || *endptr != '\0' || errno == ERANGE) { + printf("Invalid offset: %s\n", argv[1]); + return; + } + + /* + * First two arguments are handle and offset. What remains is the value + * length + */ + length = argc - 2; + + if (length == 0) + goto done; + + if (length > UINT16_MAX) { + printf("Write value too long\n"); + return; + } + + value = malloc(length); + if (!value) { + printf("Failed to allocate memory for value\n"); + return; + } + + for (i = 2; i < argc; i++) { + if (strlen(argv[i]) != 2) { + printf("Invalid value byte: %s\n", argv[i]); + free(value); + return; + } + + value[i-2] = strtol(argv[i], &endptr, 0); + if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) { + printf("Invalid value byte: %s\n", argv[i]); + free(value); + return; + } + } + +done: + cli->reliable_session_id = + bt_gatt_client_prepare_write(cli->gatt, id, + handle, offset, + value, length, + write_long_cb, NULL, + NULL); + if (!cli->reliable_session_id) + printf("Failed to proceed prepare write\n"); + else + printf("Prepare write success.\n" + "Session id: %d to be used on next write\n", + cli->reliable_session_id); + + free(value); +} + +static void write_execute_usage(void) +{ + printf("Usage: write-execute <session_id> <execute>\n" + "e.g.:\n" + "\twrite-execute 1 0\n"); +} + +static void cmd_write_execute(struct client *cli, char *cmd_str) +{ + char *argvbuf[516]; + char **argv = argvbuf; + int argc = 0; + char *endptr = NULL; + unsigned int session_id; + bool execute; + + if (!bt_gatt_client_is_ready(cli->gatt)) { + printf("GATT client not initialized\n"); + return; + } + + if (!parse_args(cmd_str, 514, argv, &argc)) { + printf("Too many arguments\n"); + write_value_usage(); + return; + } + + if (argc < 2) { + write_execute_usage(); + return; + } + + session_id = strtol(argv[0], &endptr, 0); + if (!endptr || *endptr != '\0') { + printf("Invalid session id: %s\n", argv[0]); + return; + } + + if (session_id != cli->reliable_session_id) { + printf("Invalid session id: %u != %u\n", session_id, + cli->reliable_session_id); + return; + } + + execute = !!strtol(argv[1], &endptr, 0); + if (!endptr || *endptr != '\0') { + printf("Invalid execute: %s\n", argv[1]); + return; + } + + if (execute) { + if (!bt_gatt_client_write_execute(cli->gatt, session_id, + write_cb, NULL, NULL)) + printf("Failed to proceed write execute\n"); + } else { + bt_gatt_client_cancel(cli->gatt, session_id); + } + + cli->reliable_session_id = 0; +} + static void register_notify_usage(void) { printf("Usage: register-notify <chrc value handle>\n"); @@ -1136,14 +1334,18 @@ static struct { "\tWrite a characteristic or descriptor value" }, { "write-long-value", cmd_write_long_value, "Write long characteristic or descriptor value" }, + { "write-prepare", cmd_write_prepare, + "\tWrite prepare characteristic or descriptor value" }, + { "write-execute", cmd_write_execute, + "\tExecute already prepared write" }, { "register-notify", cmd_register_notify, "\tSubscribe to not/ind from a characteristic" }, { "unregister-notify", cmd_unregister_notify, "Unregister a not/ind session"}, { "set-sec-level", cmd_set_sec_level, - "Set security level on le connection"}, + "\tSet security level on le connection"}, { "get-sec-level", cmd_get_sec_level, - "Get security level on le connection"}, + "\tGet security level on le connection"}, { "set-sign-key", cmd_set_sign_key, "\tSet signing key for signed write command"}, { } diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c index b4fbe607..b30a958f 100644 --- a/tools/btgatt-server.c +++ b/tools/btgatt-server.c @@ -630,6 +630,7 @@ static void usage(void) "\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n" "\t-s, --security-level <sec>\tSet security level (low|" "medium|high)\n" + "\t-t, --type [random|public] \t The source address type\n" "\t-v, --verbose\t\t\tEnable extra logging\n" "\t-r, --heart-rate\t\tEnable Heart Rate service\n" "\t-h, --help\t\t\tDisplay help\n"); @@ -639,13 +640,15 @@ static struct option main_options[] = { { "index", 1, 0, 'i' }, { "mtu", 1, 0, 'm' }, { "security-level", 1, 0, 's' }, + { "type", 1, 0, 't' }, { "verbose", 0, 0, 'v' }, { "heart-rate", 0, 0, 'r' }, { "help", 0, 0, 'h' }, { } }; -static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec) +static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec, + uint8_t src_type) { int sk, nsk; struct sockaddr_l2 srcaddr, addr; @@ -663,7 +666,7 @@ static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec) memset(&srcaddr, 0, sizeof(srcaddr)); srcaddr.l2_family = AF_BLUETOOTH; srcaddr.l2_cid = htobs(ATT_CID); - srcaddr.l2_bdaddr_type = 0; + srcaddr.l2_bdaddr_type = src_type; bacpy(&srcaddr.l2_bdaddr, src); if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) { @@ -1131,12 +1134,13 @@ int main(int argc, char *argv[]) int dev_id = -1; int fd; int sec = BT_SECURITY_LOW; + uint8_t src_type = BDADDR_LE_PUBLIC; uint16_t mtu = 0; sigset_t mask; bool hr_visible = false; struct server *server; - while ((opt = getopt_long(argc, argv, "+hvrs:m:i:", + while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:", main_options, NULL)) != -1) { switch (opt) { case 'h': @@ -1160,6 +1164,17 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } break; + case 't': + if (strcmp(optarg, "random") == 0) + src_type = BDADDR_LE_RANDOM; + else if (strcmp(optarg, "public") == 0) + src_type = BDADDR_LE_PUBLIC; + else { + fprintf(stderr, + "Allowed types: random, public\n"); + return EXIT_FAILURE; + } + break; case 'm': { int arg; @@ -1207,7 +1222,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - fd = l2cap_le_att_listen_and_accept(&src_addr, sec); + fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type); if (fd < 0) { fprintf(stderr, "Failed to accept L2CAP ATT connection\n"); return EXIT_FAILURE; diff --git a/tools/btmgmt.c b/tools/btmgmt.c index 8eff56b8..d7af7f08 100644 --- a/tools/btmgmt.c +++ b/tools/btmgmt.c @@ -58,6 +58,10 @@ #include "src/shared/util.h" #include "src/shared/mgmt.h" +#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR) +#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM)) +#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE) + static struct mgmt *mgmt = NULL; static uint16_t mgmt_index = MGMT_INDEX_NONE; @@ -153,6 +157,56 @@ static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str, return i; } +static void print_eir(const uint8_t *eir, uint16_t eir_len) +{ + uint16_t parsed = 0; + char str[33]; + + while (parsed < eir_len - 1) { + uint8_t field_len = eir[0]; + + if (field_len == 0) + break; + + parsed += field_len + 1; + + if (parsed > eir_len) + break; + + switch (eir[1]) { + case 0x01: + print("Flags: 0x%02x", eir[2]); + break; + case 0x0d: + print("Class of Device: 0x%02x%02x%02x", + eir[4], eir[3], eir[2]); + break; + case 0x1b: + ba2str((bdaddr_t *) (eir + 2), str); + print("LE Device Address: %s (%s)", str, + eir[8] ? "random" : "public"); + break; + case 0x1c: + print("LE Role: 0x%02x", eir[2]); + break; + case 0x22: + bin2hex(eir + 2, 16, str, sizeof(str)); + print("LE SC Confirmation Value: %s", str); + break; + case 0x23: + bin2hex(eir + 2, 16, str, sizeof(str)); + print("LE SC Random Value: %s", str); + break; + default: + print("Type %u: %u byte%s", eir[1], field_len - 1, + (field_len - 1) == 1 ? "" : "s"); + break; + } + + eir += field_len + 1; + } +} + static bool load_identity(uint16_t index, struct mgmt_irk_info *irk) { char identity_path[PATH_MAX]; @@ -235,6 +289,22 @@ static void unconf_index_removed(uint16_t index, uint16_t len, print("hci%u removed (unconfigured)", index); } +static void ext_index_added(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_ext_index_added *ev = param; + + print("hci%u added (type %u bus %u)", index, ev->type, ev->bus); +} + +static void ext_index_removed(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_ext_index_removed *ev = param; + + print("hci%u removed (type %u bus %u)", index, ev->type, ev->bus); +} + static const char *options_str[] = { "external", "public-address", @@ -472,6 +542,20 @@ static void auth_failed(uint16_t index, uint16_t len, const void *param, index, addr, ev->status, mgmt_errstr(ev->status)); } +static void class_of_dev_changed(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_class_of_dev_changed *ev = param; + + if (len != sizeof(*ev)) { + error("Invalid class_of_dev_changed length (%u bytes)", len); + return; + } + + print("hci%u class of device changed: 0x%02x%02x%02x", index, + ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]); +} + static void local_name_changed(uint16_t index, uint16_t len, const void *param, void *user_data) { @@ -962,6 +1046,54 @@ static void passkey_notify(uint16_t index, uint16_t len, const void *param, ev->entered); } +static void local_oob_data_updated(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_local_oob_data_updated *ev = param; + uint16_t eir_len; + + if (len < sizeof(*ev)) { + error("Too small (%u bytes) local_oob_updated event", len); + return; + } + + eir_len = le16_to_cpu(ev->eir_len); + if (len != sizeof(*ev) + eir_len) { + error("local_oob_updated: expected %zu bytes, got %u bytes", + sizeof(*ev) + eir_len, len); + return; + } + + print("hci%u oob data updated: type %u len %u", index, + ev->type, eir_len); +} + +static void advertising_added(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_advertising_added *ev = param; + + if (len < sizeof(*ev)) { + error("Too small (%u bytes) advertising_added event", len); + return; + } + + print("hci%u advertising_added: instance %u", index, ev->instance); +} + +static void advertising_removed(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_advertising_removed *ev = param; + + if (len < sizeof(*ev)) { + error("Too small (%u bytes) advertising_removed event", len); + return; + } + + print("hci%u advertising_removed: instance %u", index, ev->instance); +} + static void version_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { @@ -1055,6 +1187,43 @@ static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc, } } +static void config_info_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_config_info *rp = param; + uint16_t index = PTR_TO_UINT(user_data); + uint32_t supported_options, missing_options; + + if (status != 0) { + error("Reading hci%u config failed with status 0x%02x (%s)", + index, status, mgmt_errstr(status)); + goto done; + } + + if (len < sizeof(*rp)) { + error("Too small info reply (%u bytes)", len); + goto done; + } + + print("hci%u:\tUnconfigured controller", index); + + print("\tmanufacturer %u", le16_to_cpu(rp->manufacturer)); + + supported_options = le32_to_cpu(rp->supported_options); + print("\tsupported options: %s", options2str(supported_options)); + + missing_options = le32_to_cpu(rp->missing_options); + print("\tmissing options: %s", options2str(missing_options)); + +done: + pending_index--; + + if (pending_index > 0) + return; + + noninteractive_quit(EXIT_SUCCESS); +} + static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { @@ -1065,43 +1234,67 @@ static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param, if (status != 0) { error("Reading index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { error("Too small index list reply (%u bytes)", len); - goto done; + return noninteractive_quit(EXIT_FAILURE); } - count = get_le16(&rp->num_controllers); + count = le16_to_cpu(rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { error("Index count (%u) doesn't match reply length (%u)", count, len); - goto done; + return noninteractive_quit(EXIT_FAILURE); } print("Unconfigured index list with %u item%s", - count, count != 1 ? "s" : ""); + count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { - uint16_t index; + uint16_t index = le16_to_cpu(rp->index[i]); - index = get_le16(&rp->index[i]); + if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, + config_info_rsp, UINT_TO_PTR(index), NULL)) { + error("Unable to send read_config_info cmd"); + return noninteractive_quit(EXIT_FAILURE); + } + + pending_index++; + } + + if (!count) + noninteractive_quit(EXIT_SUCCESS); +} - print("\thci%u", index); +static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv) +{ + if (index == MGMT_INDEX_NONE) { + if (!mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST, + MGMT_INDEX_NONE, 0, NULL, + unconf_index_rsp, mgmt, NULL)) { + error("Unable to send unconf_index_list cmd"); + return noninteractive_quit(EXIT_FAILURE); + } + return; } -done: - noninteractive_quit(EXIT_SUCCESS); + if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, + config_info_rsp, UINT_TO_PTR(index), NULL)) { + error("Unable to send read_config_info cmd"); + return noninteractive_quit(EXIT_FAILURE); + } } -static void config_info_rsp(uint8_t status, uint16_t len, const void *param, +static void config_options_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_config_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); + uint32_t supported_options, missing_options; if (status != 0) { error("Reading hci%u config failed with status 0x%02x (%s)", @@ -1114,39 +1307,21 @@ static void config_info_rsp(uint8_t status, uint16_t len, const void *param, goto done; } - print("hci%u:\tmanufacturer %u", index, get_le16(&rp->manufacturer)); - - print("\tsupported options: %s", - options2str(get_le32(&rp->supported_options))); - print("\tmissing options: %s", - options2str(get_le32(&rp->missing_options))); + print("hci%u:\tConfiguration options", index); -done: - noninteractive_quit(EXIT_SUCCESS); -} + supported_options = le32_to_cpu(rp->supported_options); + print("\tsupported options: %s", options2str(supported_options)); -static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv) -{ - void *data; + missing_options = le32_to_cpu(rp->missing_options); + print("\tmissing options: %s", options2str(missing_options)); - if (index == MGMT_INDEX_NONE) { - if (mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST, - MGMT_INDEX_NONE, 0, NULL, - unconf_index_rsp, mgmt, NULL) == 0) { - error("Unable to send unconf_index_list cmd"); - return noninteractive_quit(EXIT_FAILURE); - } +done: + pending_index--; + if (pending_index > 0) return; - } - data = UINT_TO_PTR(index); - - if (mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, - config_info_rsp, data, NULL) == 0) { - error("Unable to send read_config_info cmd"); - return noninteractive_quit(EXIT_FAILURE); - } + noninteractive_quit(EXIT_SUCCESS); } static void info_rsp(uint8_t status, uint16_t len, const void *param, @@ -1154,10 +1329,9 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, { const struct mgmt_rp_read_info *rp = param; uint16_t index = PTR_TO_UINT(user_data); + uint32_t supported_settings, current_settings; char addr[18]; - pending_index--; - if (status != 0) { error("Reading hci%u info failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); @@ -1169,25 +1343,38 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, goto done; } + print("hci%u:\tPrimary controller", index); + ba2str(&rp->bdaddr, addr); - print("hci%u:\taddr %s version %u manufacturer %u" - " class 0x%02x%02x%02x", index, - addr, rp->version, get_le16(&rp->manufacturer), + print("\taddr %s version %u manufacturer %u class 0x%02x%02x%02x", + addr, rp->version, le16_to_cpu(rp->manufacturer), rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); - print("\tsupported settings: %s", - settings2str(get_le32(&rp->supported_settings))); + supported_settings = le32_to_cpu(rp->supported_settings); + print("\tsupported settings: %s", settings2str(supported_settings)); - print("\tcurrent settings: %s", - settings2str(get_le32(&rp->current_settings))); + current_settings = le32_to_cpu(rp->current_settings); + print("\tcurrent settings: %s", settings2str(current_settings)); print("\tname %s", rp->name); print("\tshort name %s", rp->short_name); - if (pending_index > 0) + if (supported_settings & MGMT_SETTING_CONFIGURATION) { + if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, + index, 0, NULL, config_options_rsp, + UINT_TO_PTR(index), NULL)) { + error("Unable to send read_config cmd"); + goto done; + } return; + } done: + pending_index--; + + if (pending_index > 0) + return; + noninteractive_quit(EXIT_SUCCESS); } @@ -1210,7 +1397,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, return noninteractive_quit(EXIT_FAILURE); } - count = get_le16(&rp->num_controllers); + count = le16_to_cpu(rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { error("Index count (%u) doesn't match reply length (%u)", @@ -1221,15 +1408,10 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, print("Index list with %u item%s", count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { - uint16_t index; - void *data; - - index = get_le16(&rp->index[i]); + uint16_t index = le16_to_cpu(rp->index[i]); - data = UINT_TO_PTR(index); - - if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, - info_rsp, data, NULL) == 0) { + if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, + info_rsp, UINT_TO_PTR(index), NULL)) { error("Unable to send read_info cmd"); return noninteractive_quit(EXIT_FAILURE); } @@ -1243,12 +1425,10 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) { - void *data; - if (index == MGMT_INDEX_NONE) { - if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, + if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, - index_rsp, mgmt, NULL) == 0) { + index_rsp, mgmt, NULL)) { error("Unable to send index_list cmd"); return noninteractive_quit(EXIT_FAILURE); } @@ -1256,15 +1436,93 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) return; } - data = UINT_TO_PTR(index); - - if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, - data, NULL) == 0) { + if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, + UINT_TO_PTR(index), NULL)) { error("Unable to send read_info cmd"); return noninteractive_quit(EXIT_FAILURE); } } +static void ext_index_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_ext_index_list *rp = param; + uint16_t count, index_filter = PTR_TO_UINT(user_data); + unsigned int i; + + if (status != 0) { + error("Reading ext index list failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); + return noninteractive_quit(EXIT_FAILURE); + } + + if (len < sizeof(*rp)) { + error("Too small ext index list reply (%u bytes)", len); + return noninteractive_quit(EXIT_FAILURE); + } + + count = get_le16(&rp->num_controllers); + + if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) { + error("Index count (%u) doesn't match reply length (%u)", + count, len); + return noninteractive_quit(EXIT_FAILURE); + } + + print("Extended index list with %u item%s", + count, count != 1 ? "s" : ""); + + for (i = 0; i < count; i++) { + uint16_t index = le16_to_cpu(rp->entry[i].index); + + if (index_filter != MGMT_INDEX_NONE && index_filter != index) + continue; + + switch (rp->entry[i].type) { + case 0x00: + if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, + index, 0, NULL, info_rsp, + UINT_TO_PTR(index), NULL)) { + error("Unable to send read_info cmd"); + return noninteractive_quit(EXIT_FAILURE); + } + pending_index++; + break; + case 0x01: + if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, + index, 0, NULL, config_info_rsp, + UINT_TO_PTR(index), NULL)) { + error("Unable to send read_config cmd"); + return noninteractive_quit(EXIT_FAILURE); + } + pending_index++; + break; + case 0x02: + print("hci%u:\tAMP controller (%u)", index, + rp->entry[i].bus); + break; + default: + print("hci%u:\tType %u controller (%u)", index, + rp->entry[i].type, rp->entry[i].bus); + break; + } + } + + if (!count) + noninteractive_quit(EXIT_SUCCESS); +} + +static void cmd_extinfo(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST, + MGMT_INDEX_NONE, 0, NULL, + ext_index_rsp, UINT_TO_PTR(index), NULL)) { + error("Unable to send ext_index_list cmd"); + return noninteractive_quit(EXIT_FAILURE); + } +} + /* Wrapper to get the index and opcode to the response callback */ struct command_data { uint16_t id; @@ -1540,7 +1798,7 @@ static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, } print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op), - rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]); + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); noninteractive_quit(EXIT_SUCCESS); } @@ -1621,9 +1879,11 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc, break; case 'h': disconnect_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: disconnect_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -1739,7 +1999,7 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, uuid_t uuid; uint128_t uint128; uuid_t uuid128; - uint8_t type; + uint8_t type = SCAN_TYPE_DUAL; int8_t rssi; uint16_t count; int opt; @@ -1747,10 +2007,6 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, if (index == MGMT_INDEX_NONE) index = 0; - type = 0; - type |= (1 << BDADDR_BREDR); - type |= (1 << BDADDR_LE_PUBLIC); - type |= (1 << BDADDR_LE_RANDOM); rssi = 127; count = 0; @@ -1763,23 +2019,23 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, find_service_options, NULL)) != -1) { switch (opt) { case 'l': - type &= ~(1 << BDADDR_BREDR); - type |= (1 << BDADDR_LE_PUBLIC); - type |= (1 << BDADDR_LE_RANDOM); + type &= ~SCAN_TYPE_BREDR; + type |= SCAN_TYPE_LE; break; case 'b': - type |= (1 << BDADDR_BREDR); - type &= ~(1 << BDADDR_LE_PUBLIC); - type &= ~(1 << BDADDR_LE_RANDOM); + type |= SCAN_TYPE_BREDR; + type &= ~SCAN_TYPE_LE; break; case 'u': if (count == MAX_UUIDS) { print("Max %u UUIDs supported", MAX_UUIDS); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } if (bt_string2uuid(&uuid, optarg) < 0) { print("Invalid UUID: %s", optarg); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } cp = (void *) buf; @@ -1793,9 +2049,11 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, break; case 'h': find_service_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: find_service_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -1850,35 +2108,30 @@ static struct option find_options[] = { static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv) { struct mgmt_cp_start_discovery cp; - uint8_t type; + uint8_t type = SCAN_TYPE_DUAL; int opt; if (index == MGMT_INDEX_NONE) index = 0; - type = 0; - type |= (1 << BDADDR_BREDR); - type |= (1 << BDADDR_LE_PUBLIC); - type |= (1 << BDADDR_LE_RANDOM); - while ((opt = getopt_long(argc, argv, "+lbh", find_options, NULL)) != -1) { switch (opt) { case 'l': - type &= ~(1 << BDADDR_BREDR); - type |= (1 << BDADDR_LE_PUBLIC); - type |= (1 << BDADDR_LE_RANDOM); + type &= ~SCAN_TYPE_BREDR; + type |= SCAN_TYPE_LE; break; case 'b': - type |= (1 << BDADDR_BREDR); - type &= ~(1 << BDADDR_LE_PUBLIC); - type &= ~(1 << BDADDR_LE_RANDOM); + type |= SCAN_TYPE_BREDR; + type &= ~SCAN_TYPE_LE; break; case 'h': find_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: find_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -1897,6 +2150,78 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv) } } +static void stop_find_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + if (status != 0) { + fprintf(stderr, + "Stop Discovery failed: status 0x%02x (%s)\n", + status, mgmt_errstr(status)); + mainloop_quit(); + return; + } + + printf("Discovery stopped\n"); + discovery = false; + + noninteractive_quit(EXIT_SUCCESS); +} + +static void stop_find_usage(void) +{ + printf("Usage: btmgmt stop-find [-l|-b]>\n"); +} + +static struct option stop_find_options[] = { + { "help", 0, 0, 'h' }, + { "le-only", 1, 0, 'l' }, + { "bredr-only", 1, 0, 'b' }, + { 0, 0, 0, 0 } +}; + +static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc, + char **argv) +{ + struct mgmt_cp_stop_discovery cp; + uint8_t type = SCAN_TYPE_DUAL; + int opt; + + if (index == MGMT_INDEX_NONE) + index = 0; + + while ((opt = getopt_long(argc, argv, "+lbh", stop_find_options, + NULL)) != -1) { + switch (opt) { + case 'l': + type &= ~SCAN_TYPE_BREDR; + type |= SCAN_TYPE_LE; + break; + case 'b': + type |= SCAN_TYPE_BREDR; + type &= ~SCAN_TYPE_LE; + break; + case 'h': + default: + stop_find_usage(); + optind = 0; + exit(EXIT_SUCCESS); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + memset(&cp, 0, sizeof(cp)); + cp.type = type; + + if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp, + stop_find_rsp, NULL, NULL) == 0) { + fprintf(stderr, "Unable to send stop_discovery cmd\n"); + exit(EXIT_FAILURE); + } +} + static void name_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { @@ -1995,9 +2320,11 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) break; case 'h': pair_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: pair_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -2084,9 +2411,11 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc, break; case 'h': cancel_pair_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: cancel_pair_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -2168,9 +2497,11 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc, break; case 'h': unpair_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: unpair_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -2299,6 +2630,7 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) case 'l': if (count >= MAX_IRKS) { error("Number of IRKs exceeded"); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } if (strlen(optarg) > 3 && @@ -2308,15 +2640,18 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) local_index = atoi(optarg); if (!load_identity(local_index, &cp->irks[count])) { error("Unable to load identity"); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } count++; break; case 'h': irks_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: irks_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -2394,9 +2729,11 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv) break; case 'h': block_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: block_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -2444,9 +2781,11 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc, break; case 'h': unblock_usage(); + optind = 0; return noninteractive_quit(EXIT_SUCCESS); default: unblock_usage(); + optind = 0; return noninteractive_quit(EXIT_FAILURE); } } @@ -2555,7 +2894,6 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_local_oob_data *rp = param; - const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param; char str[33]; if (status != 0) { @@ -2569,19 +2907,19 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param, return noninteractive_quit(EXIT_FAILURE); } - bin2hex(rp->hash, 16, str, sizeof(str)); + bin2hex(rp->hash192, 16, str, sizeof(str)); print("Hash C from P-192: %s", str); - bin2hex(rp->randomizer, 16, str, sizeof(str)); + bin2hex(rp->rand192, 16, str, sizeof(str)); print("Randomizer R with P-192: %s", str); - if (len < sizeof(*rp_ext)) + if (len < sizeof(*rp)) return noninteractive_quit(EXIT_SUCCESS); - bin2hex(rp_ext->hash256, 16, str, sizeof(str)); + bin2hex(rp->hash256, 16, str, sizeof(str)); print("Hash C from P-256: %s", str); - bin2hex(rp_ext->randomizer256, 16, str, sizeof(str)); + bin2hex(rp->rand256, 16, str, sizeof(str)); print("Randomizer R with P-256: %s", str); noninteractive_quit(EXIT_SUCCESS); @@ -3236,6 +3574,456 @@ static void cmd_clr_devices(struct mgmt *mgmt, uint16_t index, cmd_del_device(mgmt, index, 2, rm_argv); } +static void local_oob_ext_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_local_oob_ext_data *rp = param; + uint16_t eir_len; + + if (status != 0) { + error("Read Local OOB Ext Data failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); + return noninteractive_quit(EXIT_FAILURE); + } + + if (len < sizeof(*rp)) { + error("Too small (%u bytes) read_local_oob_ext rsp", len); + return noninteractive_quit(EXIT_FAILURE); + } + + eir_len = le16_to_cpu(rp->eir_len); + if (len != sizeof(*rp) + eir_len) { + error("local_oob_ext: expected %zu bytes, got %u bytes", + sizeof(*rp) + eir_len, len); + return noninteractive_quit(EXIT_FAILURE); + } + + print_eir(rp->eir, eir_len); + + noninteractive_quit(EXIT_SUCCESS); +} + +static void cmd_bredr_oob(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + struct mgmt_cp_read_local_oob_ext_data cp; + + if (index == MGMT_INDEX_NONE) + index = 0; + + cp.type = SCAN_TYPE_BREDR; + + if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + index, sizeof(cp), &cp, + local_oob_ext_rsp, NULL, NULL)) { + error("Unable to send read_local_oob_ext cmd"); + return noninteractive_quit(EXIT_FAILURE); + } +} + +static void cmd_le_oob(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + struct mgmt_cp_read_local_oob_ext_data cp; + + if (index == MGMT_INDEX_NONE) + index = 0; + + cp.type = SCAN_TYPE_LE; + + if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, + index, sizeof(cp), &cp, + local_oob_ext_rsp, NULL, NULL)) { + error("Unable to send read_local_oob_ext cmd"); + return noninteractive_quit(EXIT_FAILURE); + } +} + +static const char *adv_flags_str[] = { + "connectable", + "general-discoverable", + "limited-discoverable", + "managed-flags", + "tx-power", + "scan-rsp-appearance", + "scan-rsp-local-name", +}; + +static const char *adv_flags2str(uint32_t flags) +{ + static char str[256]; + unsigned i; + int off; + + off = 0; + str[0] = '\0'; + + for (i = 0; i < NELEM(adv_flags_str); i++) { + if ((flags & (1 << i)) != 0) + off += snprintf(str + off, sizeof(str) - off, "%s ", + adv_flags_str[i]); + } + + return str; +} + +static void adv_features_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_adv_features *rp = param; + uint32_t supported_flags; + + if (status != 0) { + error("Reading adv features failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); + return noninteractive_quit(EXIT_FAILURE); + } + + if (len < sizeof(*rp)) { + error("Too small adv features reply (%u bytes)", len); + return noninteractive_quit(EXIT_FAILURE); + } + + if (len < sizeof(*rp) + rp->num_instances * sizeof(uint8_t)) { + error("Instances count (%u) doesn't match reply length (%u)", + rp->num_instances, len); + return noninteractive_quit(EXIT_FAILURE); + } + + supported_flags = le32_to_cpu(rp->supported_flags); + print("Supported flags: %s", adv_flags2str(supported_flags)); + print("Max advertising data len: %u", rp->max_adv_data_len); + print("Max scan response data len: %u", rp->max_scan_rsp_len); + print("Max instances: %u", rp->max_instances); + + print("Instances list with %u item%s", rp->num_instances, + rp->num_instances != 1 ? "s" : ""); + + return noninteractive_quit(EXIT_SUCCESS); +} + +static void cmd_advinfo(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + if (index == MGMT_INDEX_NONE) + index = 0; + + if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL, + adv_features_rsp, NULL, NULL)) { + error("Unable to send advertising features command"); + return noninteractive_quit(EXIT_FAILURE); + } +} + +static void add_adv_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_add_advertising *rp = param; + + if (status != 0) { + error("Add Advertising failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); + return noninteractive_quit(EXIT_FAILURE); + } + + if (len != sizeof(*rp)) { + error("Invalid Add Advertising response length (%u)", len); + return noninteractive_quit(EXIT_FAILURE); + } + + print("Instance added: %u", rp->instance); + + return noninteractive_quit(EXIT_SUCCESS); +} + +static void add_adv_usage(void) +{ + print("Usage: add-adv [options] <instance_id>\nOptions:\n" + "\t -u, --uuid <uuid> Service UUID\n" + "\t -d, --adv-data <data> Advertising Data bytes\n" + "\t -s, --scan-rsp <data> Scan Response Data bytes\n" + "\t -t, --timeout <timeout> Timeout in seconds\n" + "\t -c, --connectable \"connectable\" flag\n" + "\t -g, --general-discov \"general-discoverable\" flag\n" + "\t -l, --limited-discov \"limited-discoverable\" flag\n" + "\t -m, --managed-flags \"managed-flags\" flag\n" + "\t -p, --tx-power \"tx-power\" flag\n" + "e.g.:\n" + "\tadd-adv -u 180d -u 180f -d 080954657374204C45 1"); +} + +static struct option add_adv_options[] = { + { "help", 0, 0, 'h' }, + { "uuid", 1, 0, 'u' }, + { "adv-data", 1, 0, 'd' }, + { "scan-rsp", 1, 0, 's' }, + { "timeout", 1, 0, 't' }, + { "connectable", 0, 0, 'c' }, + { "general-discov", 0, 0, 'g' }, + { "limited-discov", 0, 0, 'l' }, + { "managed-flags", 0, 0, 'm' }, + { "tx-power", 0, 0, 'p' }, + { 0, 0, 0, 0} +}; + +static bool parse_bytes(char *optarg, uint8_t **bytes, size_t *len) +{ + unsigned i; + + if (!optarg) { + add_adv_usage(); + return false; + } + + *len = strlen(optarg); + + if (*len % 2) { + error("Malformed data"); + return false; + } + + *len /= 2; + if (*len > UINT8_MAX) { + error("Data too long"); + return false; + } + + *bytes = malloc(*len); + if (!*bytes) { + error("Failed to allocate memory"); + return false; + } + + for (i = 0; i < *len; i++) { + if (sscanf(optarg + (i * 2), "%2hhx", *bytes + i) != 1) { + error("Invalid data"); + free(*bytes); + *bytes = NULL; + return false; + } + } + + return true; +} + +#define MAX_AD_UUID_BYTES 32 + +static void cmd_add_adv(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + struct mgmt_cp_add_advertising *cp = NULL; + int opt; + uint8_t *adv_data = NULL, *scan_rsp = NULL; + size_t adv_len = 0, scan_rsp_len = 0; + size_t cp_len; + uint8_t uuids[MAX_AD_UUID_BYTES]; + size_t uuid_bytes = 0; + uint8_t uuid_type = 0; + uint16_t timeout = 0; + uint8_t instance; + uuid_t uuid; + bool success = false; + bool quit = true; + uint32_t flags = 0; + + while ((opt = getopt_long(argc, argv, "+u:d:s:t:cglmph", + add_adv_options, NULL)) != -1) { + switch (opt) { + case 'u': + if (bt_string2uuid(&uuid, optarg) < 0) { + print("Invalid UUID: %s", optarg); + goto done; + } + + if (uuid_type && uuid_type != uuid.type) { + print("UUID types must be consistent"); + goto done; + } + + if (uuid.type == SDP_UUID16) { + if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) { + print("Too many UUIDs"); + goto done; + } + + put_le16(uuid.value.uuid16, uuids + uuid_bytes); + uuid_bytes += 2; + } else if (uuid.type == SDP_UUID128) { + if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) { + print("Too many UUIDs"); + goto done; + } + + bswap_128(uuid.value.uuid128.data, + uuids + uuid_bytes); + uuid_bytes += 16; + } else { + printf("Unsupported UUID type"); + goto done; + } + + if (!uuid_type) + uuid_type = uuid.type; + + break; + case 'd': + if (adv_len) { + print("Only one adv-data option allowed"); + goto done; + } + + if (!parse_bytes(optarg, &adv_data, &adv_len)) + goto done; + break; + case 's': + if (scan_rsp_len) { + print("Only one scan-rsp option allowed"); + goto done; + } + + if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len)) + goto done; + break; + case 't': + timeout = strtol(optarg, NULL, 0); + break; + case 'c': + flags |= MGMT_ADV_FLAG_CONNECTABLE; + break; + case 'g': + flags |= MGMT_ADV_FLAG_DISCOV; + break; + case 'l': + flags |= MGMT_ADV_FLAG_LIMITED_DISCOV; + break; + case 'm': + flags |= MGMT_ADV_FLAG_MANAGED_FLAGS; + break; + case 'p': + flags |= MGMT_ADV_FLAG_TX_POWER; + break; + case 'h': + success = true; + default: + add_adv_usage(); + goto done; + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc != 1) { + add_adv_usage(); + goto done; + } + + if (uuid_bytes) + uuid_bytes += 2; + + instance = strtol(argv[0], NULL, 0); + + if (index == MGMT_INDEX_NONE) + index = 0; + + cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len; + cp = malloc0(cp_len); + if (!cp) + goto done; + + cp->instance = instance; + put_le32(flags, &cp->flags); + put_le16(timeout, &cp->timeout); + cp->adv_data_len = adv_len + uuid_bytes; + cp->scan_rsp_len = scan_rsp_len; + + if (uuid_bytes) { + cp->data[0] = uuid_bytes - 1; + cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07; + memcpy(cp->data + 2, uuids, uuid_bytes - 2); + } + + memcpy(cp->data + uuid_bytes, adv_data, adv_len); + memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len); + + if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp, + add_adv_rsp, NULL, NULL)) { + error("Unable to send \"Add Advertising\" command"); + goto done; + } + + quit = false; + +done: + free(adv_data); + free(scan_rsp); + free(cp); + + if (quit) + noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_remove_advertising *rp = param; + + if (status != 0) { + error("Remove Advertising failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); + return noninteractive_quit(EXIT_FAILURE); + } + + if (len != sizeof(*rp)) { + error("Invalid Remove Advertising response length (%u)", len); + return noninteractive_quit(EXIT_FAILURE); + } + + print("Instance removed: %u", rp->instance); + + return noninteractive_quit(EXIT_SUCCESS); +} + +static void rm_adv_usage(void) +{ + print("Usage: rm-adv <instance_id>"); +} + +static void cmd_rm_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv) +{ + struct mgmt_cp_remove_advertising cp; + uint8_t instance; + + if (argc != 2) { + rm_adv_usage(); + return noninteractive_quit(EXIT_FAILURE); + } + + instance = strtol(argv[1], NULL, 0); + + if (index == MGMT_INDEX_NONE) + index = 0; + + memset(&cp, 0, sizeof(cp)); + + cp.instance = instance; + + if (!mgmt_send(mgmt, MGMT_OP_REMOVE_ADVERTISING, index, sizeof(cp), &cp, + rm_adv_rsp, NULL, NULL)) { + error("Unable to send \"Remove Advertising\" command"); + return noninteractive_quit(EXIT_FAILURE); + } +} + +static void cmd_clr_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv) +{ + char *all_instances = "0"; + char *rm_argv[] = { "rm-adv", all_instances, NULL }; + + cmd_rm_adv(mgmt, index, 2, rm_argv); +} + struct cmd_info { char *cmd; void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv); @@ -3249,6 +4037,7 @@ static struct cmd_info all_cmd[] = { { "commands", cmd_commands, "List supported commands" }, { "config", cmd_config, "Show configuration info" }, { "info", cmd_info, "Show controller info" }, + { "extinfo", cmd_extinfo, "Show extended controller info" }, { "power", cmd_power, "Toggle powered state" }, { "discov", cmd_discov, "Toggle discoverable state" }, { "connectable",cmd_connectable,"Toggle connectable state" }, @@ -3268,6 +4057,7 @@ static struct cmd_info all_cmd[] = { { "con", cmd_con, "List connections" }, { "find", cmd_find, "Discover nearby devices" }, { "find-service", cmd_find_service, "Discover nearby service" }, + { "stop-find", cmd_stop_find, "Stop discovery" }, { "name", cmd_name, "Set local name" }, { "pair", cmd_pair, "Pair with a remote device" }, { "cancelpair", cmd_cancel_pair,"Cancel pairing" }, @@ -3294,6 +4084,12 @@ static struct cmd_info all_cmd[] = { { "add-device", cmd_add_device, "Add Device" }, { "del-device", cmd_del_device, "Remove Device" }, { "clr-devices",cmd_clr_devices,"Clear Devices" }, + { "bredr-oob", cmd_bredr_oob, "Local OOB data (BR/EDR)" }, + { "le-oob", cmd_le_oob, "Local OOB data (LE)" }, + { "advinfo", cmd_advinfo, "Show advertising features" }, + { "add-adv", cmd_add_adv, "Add advertising instance" }, + { "rm-adv", cmd_rm_adv, "Remove advertising instance" }, + { "clr-adv", cmd_clr_adv, "Clear advertising instances" }, }; static void cmd_quit(struct mgmt *mgmt, uint16_t index, @@ -3324,6 +4120,8 @@ static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index) NULL, NULL); mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, index, + class_of_dev_changed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index, local_name_changed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found, @@ -3342,7 +4140,16 @@ static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index) unconf_index_removed, NULL, NULL); mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index, new_config_options, NULL, NULL); - + mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, index, + ext_index_added, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, index, + ext_index_removed, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_LOCAL_OOB_DATA_UPDATED, index, + local_oob_data_updated, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index, + advertising_added, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index, + advertising_removed, NULL, NULL); } static void cmd_select(struct mgmt *mgmt, uint16_t index, diff --git a/tools/btsnoop.c b/tools/btsnoop.c index 3eb8082d..278e18ce 100644 --- a/tools/btsnoop.c +++ b/tools/btsnoop.c @@ -34,6 +34,8 @@ #include <stdint.h> #include <stdbool.h> #include <string.h> +#include <time.h> +#include <sys/time.h> #include <getopt.h> #include <endian.h> #include <arpa/inet.h> @@ -272,6 +274,86 @@ close_input: close(input_fd[i]); } +#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci" + +static void command_split(const char *input) +{ + unsigned char buf[BTSNOOP_MAX_PACKET_SIZE]; + uint16_t pktlen,opcode; + uint32_t type; + struct timeval tv; + uint16_t index, max_index = 0; + char write_file_name[255]; + struct btsnoop *btsnoop_read_file = NULL; + struct btsnoop *btsnoop_write_file[16]; + time_t t; + struct tm tm; + unsigned long num_packets = 0; + + btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT); + if (!btsnoop_read_file) + return; + + type = btsnoop_get_type(btsnoop_read_file); + if (type != BTSNOOP_TYPE_MONITOR) { + fprintf(stderr, "unsupported link data type %u\n", type); + btsnoop_unref(btsnoop_read_file); + return; + } + +next_packet: + if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf, + &pktlen)) + goto close_files; + + if (opcode == 0xffff) + goto next_packet; + + switch (opcode) { + case BTSNOOP_OPCODE_NEW_INDEX: + t = tv.tv_sec; + localtime_r(&t, &tm); + + if (max_index < index) + max_index = index; + + sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log", + BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min, + tm.tm_sec, tv.tv_usec); + + printf("New Index %d would be saved in %s\n", index, + write_file_name); + + btsnoop_write_file[index] = btsnoop_create(write_file_name, + BTSNOOP_TYPE_HCI, -1, -1); + if (!btsnoop_write_file[index]) + goto close_files; + + break; + case BTSNOOP_OPCODE_DEL_INDEX: + printf("Del Index %d\n", index); + + btsnoop_unref(btsnoop_write_file[index]); + btsnoop_write_file[index] = NULL; + break; + default: + btsnoop_write_hci(btsnoop_write_file[index], &tv, index, + opcode, buf, pktlen); + } + num_packets++; + + goto next_packet; + +close_files: + for (index = 0; index < max_index; index++) + btsnoop_unref(btsnoop_write_file[index]); + + btsnoop_unref(btsnoop_read_file); + + printf("BT Snoop data link transfer is completed for %lu packets\n", + num_packets); +} + static void command_extract_eir(const char *input) { struct btsnoop_pkt pkt; @@ -518,6 +600,7 @@ static void usage(void) printf("commands:\n" "\t-m, --merge <output> Merge multiple btsnoop files\n" "\t-e, --extract <input> Extract data from btsnoop file\n" + "\t-s, --split <input> Split btmon file into legacy btsnoop file(s)\n" "\t-h, --help Show help options\n"); } @@ -525,12 +608,13 @@ static const struct option main_options[] = { { "merge", required_argument, NULL, 'm' }, { "extract", required_argument, NULL, 'e' }, { "type", required_argument, NULL, 't' }, + { "split", required_argument, NULL, 's' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; -enum { INVALID, MERGE, EXTRACT }; +enum { INVALID, MERGE, EXTRACT, SPLIT }; int main(int argc, char *argv[]) { @@ -542,7 +626,7 @@ int main(int argc, char *argv[]) for (;;) { int opt; - opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL); + opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL); if (opt < 0) break; @@ -555,6 +639,9 @@ int main(int argc, char *argv[]) command = EXTRACT; input_path = optarg; break; + case 's': + command = SPLIT; + input_path = optarg; case 't': type = optarg; break; @@ -600,6 +687,15 @@ int main(int argc, char *argv[]) fprintf(stderr, "extract type not supported\n"); break; + case SPLIT: + if (argc - optind > 0) { + fprintf(stderr, "extra arguments not allowed\n"); + return EXIT_FAILURE; + } + + command_split(input_path); + break; + default: usage(); return EXIT_FAILURE; diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c index e5fb8894..d9970103 100644 --- a/tools/mgmt-tester.c +++ b/tools/mgmt-tester.c @@ -319,6 +319,25 @@ static void test_condition_complete(struct test_data *data) test_post_teardown, 2, user, free); \ } while (0) +#define test_bredr20(name, data, setup, func) \ + do { \ + struct test_data *user; \ + user = malloc(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_LEGACY; \ + user->test_setup = setup; \ + user->test_data = data; \ + user->expected_version = 0x04; \ + user->expected_manufacturer = 0x003f; \ + user->expected_supported_settings = 0x000010bf; \ + user->initial_settings = 0x00000080; \ + user->unmet_conditions = 0; \ + tester_add_full(name, data, \ + test_pre_setup, test_setup, func, NULL, \ + test_post_teardown, 2, user, free); \ + } while (0) + #define test_bredr(name, data, setup, func) \ do { \ struct test_data *user; \ @@ -378,6 +397,7 @@ struct generic_data { uint16_t send_len; const void * (*send_func)(uint16_t *len); uint8_t expect_status; + bool expect_ignore_param; const void *expect_param; uint16_t expect_len; const void * (*expect_func)(uint16_t *len); @@ -477,6 +497,45 @@ static const struct generic_data read_info_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static const struct generic_data read_unconf_index_list_invalid_param_test = { + .send_index_none = true, + .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST, + .send_param = dummy_data, + .send_len = sizeof(dummy_data), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data read_unconf_index_list_invalid_index_test = { + .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST, + .expect_status = MGMT_STATUS_INVALID_INDEX, +}; + +static const struct generic_data read_config_info_invalid_param_test = { + .send_opcode = MGMT_OP_READ_CONFIG_INFO, + .send_param = dummy_data, + .send_len = sizeof(dummy_data), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data read_config_info_invalid_index_test = { + .send_index_none = true, + .send_opcode = MGMT_OP_READ_CONFIG_INFO, + .expect_status = MGMT_STATUS_INVALID_INDEX, +}; + +static const struct generic_data read_ext_index_list_invalid_param_test = { + .send_index_none = true, + .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST, + .send_param = dummy_data, + .send_len = sizeof(dummy_data), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data read_ext_index_list_invalid_index_test = { + .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST, + .expect_status = MGMT_STATUS_INVALID_INDEX, +}; + static const char set_powered_on_param[] = { 0x01 }; static const char set_powered_invalid_param[] = { 0x02 }; static const char set_powered_garbage_param[] = { 0x01, 0x00 }; @@ -797,6 +856,12 @@ static const struct generic_data set_connectable_off_le_test_2 = { .expect_hci_len = sizeof(set_connectable_off_adv_param), }; +static uint16_t settings_powered_le_discoverable[] = { + MGMT_OP_SET_LE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_POWERED, + MGMT_OP_SET_DISCOVERABLE, 0 }; + static uint16_t settings_powered_le_discoverable_advertising[] = { MGMT_OP_SET_LE, MGMT_OP_SET_CONNECTABLE, @@ -835,6 +900,8 @@ static const struct generic_data set_connectable_off_le_test_4 = { static const char set_fast_conn_on_param[] = { 0x01 }; static const char set_fast_conn_on_settings_1[] = { 0x87, 0x00, 0x00, 0x00 }; +static const char set_fast_conn_on_settings_2[] = { 0x85, 0x00, 0x00, 0x00 }; +static const char set_fast_conn_on_settings_3[] = { 0x84, 0x00, 0x00, 0x00 }; static const struct generic_data set_fast_conn_on_success_test_1 = { .setup_settings = settings_powered_connectable, @@ -847,6 +914,27 @@ static const struct generic_data set_fast_conn_on_success_test_1 = { .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE, }; +static const struct generic_data set_fast_conn_on_success_test_2 = { + .setup_settings = settings_powered, + .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE, + .send_param = set_fast_conn_on_param, + .send_len = sizeof(set_fast_conn_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_fast_conn_on_settings_2, + .expect_len = sizeof(set_fast_conn_on_settings_2), + .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE, +}; + +static const struct generic_data set_fast_conn_on_success_test_3 = { + .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE, + .send_param = set_fast_conn_on_param, + .send_len = sizeof(set_fast_conn_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_fast_conn_on_settings_3, + .expect_len = sizeof(set_fast_conn_on_settings_3), + .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE, +}; + static const struct generic_data set_fast_conn_on_not_supported_test_1 = { .setup_settings = settings_powered_connectable, .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE, @@ -855,6 +943,15 @@ static const struct generic_data set_fast_conn_on_not_supported_test_1 = { .expect_status = MGMT_STATUS_NOT_SUPPORTED, }; +static const char set_fast_conn_nval_param[] = { 0xff }; + +static const struct generic_data set_fast_conn_nval_param_test_1 = { + .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE, + .send_param = set_fast_conn_nval_param, + .send_len = sizeof(set_fast_conn_nval_param), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + static const char set_bondable_on_param[] = { 0x01 }; static const char set_bondable_invalid_param[] = { 0x02 }; static const char set_bondable_garbage_param[] = { 0x01, 0x00 }; @@ -1286,6 +1383,10 @@ static const struct generic_data set_ssp_on_invalid_index_test = { static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP, MGMT_OP_SET_POWERED, 0 }; +static uint16_t settings_powered_sc[] = { MGMT_OP_SET_SSP, + MGMT_OP_SET_SECURE_CONN, + MGMT_OP_SET_POWERED, 0 }; + static const char set_sc_on_param[] = { 0x01 }; static const char set_sc_only_on_param[] = { 0x02 }; static const char set_sc_invalid_param[] = { 0x03 }; @@ -3748,6 +3849,650 @@ static const struct generic_data remove_device_success_5 = { .expect_hci_len = sizeof(set_le_scan_off), }; +static const struct generic_data read_adv_features_invalid_param_test = { + .send_opcode = MGMT_OP_READ_ADV_FEATURES, + .send_param = dummy_data, + .send_len = sizeof(dummy_data), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data read_adv_features_invalid_index_test = { + .send_index_none = true, + .send_opcode = MGMT_OP_READ_ADV_FEATURES, + .expect_status = MGMT_STATUS_INVALID_INDEX, +}; + +static const uint8_t add_advertising_param_1[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_param_2[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0a, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, + 0x03, 0x19, 0x40, 0x03, + 0x05, 0x03, 0x0d, 0x18, 0x0f, 0x18, +}; + +static const uint8_t add_advertising_param_3[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_param_4[] = { + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_param_5[] = { + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_param_6[] = { + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_param_7[] = { + 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_param_8[] = { + 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x02, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t advertising_instance_param[] = { + 0x01, +}; + +static const uint8_t set_adv_data_1[] = { + 0x09, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_2[] = { + 0x0c, 0x02, 0x01, 0x04, 0x03, 0x03, 0x0d, 0x18, 0x04, 0xff, + 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_3[] = { + 0x06, 0x05, 0x08, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_4[] = { + 0x03, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_5[] = { + 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, + 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_6[] = { + 0x0c, 0x02, 0x01, 0x01, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, + 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_7[] = { + 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, + 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_adv_data_8[] = { + 0x0c, 0x02, 0x0a, 0x00, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, + 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t set_scan_rsp_1[] = { + 0x0a, 0x03, 0x19, 0x40, 0x03, 0x05, 0x03, 0x0d, 0x18, 0x0f, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +static const uint8_t add_advertising_invalid_param_1[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x03, 0x03, 0x0d, 0x18, + 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, +}; + +static const uint8_t add_advertising_invalid_param_2[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x04, 0x03, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_invalid_param_3[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x03, 0x0d, 0x18, + 0x02, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_invalid_param_4[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x03, 0x03, 0x0d, 0x18, + 0x05, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_invalid_param_5[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, + 0x03, 0x03, 0x0d, 0x18, + 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, +}; + +static const uint8_t add_advertising_invalid_param_6[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x03, 0x03, 0x0d, 0x18, + 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, +}; + +static const uint8_t add_advertising_invalid_param_7[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x04, 0x03, 0x0d, 0x18, + 0x04, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_invalid_param_8[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x03, 0x03, 0x0d, 0x18, + 0x02, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_invalid_param_9[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x03, 0x03, 0x0d, 0x18, + 0x05, 0xff, 0x01, 0x02, 0x03, +}; + +static const uint8_t add_advertising_invalid_param_10[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, + 0x03, 0x03, 0x0d, 0x18, + 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, +}; + +static const struct generic_data add_advertising_fail_1 = { + .setup_settings = settings_powered, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_1, + .send_len = sizeof(add_advertising_param_1), + .expect_status = MGMT_STATUS_REJECTED, +}; + +static const struct generic_data add_advertising_fail_2 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_1, + .send_len = sizeof(add_advertising_invalid_param_1), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_3 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_2, + .send_len = sizeof(add_advertising_invalid_param_2), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_4 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_3, + .send_len = sizeof(add_advertising_invalid_param_3), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_5 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_4, + .send_len = sizeof(add_advertising_invalid_param_4), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_6 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_5, + .send_len = sizeof(add_advertising_invalid_param_5), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_7 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_6, + .send_len = sizeof(add_advertising_invalid_param_6), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_8 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_7, + .send_len = sizeof(add_advertising_invalid_param_7), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_9 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_8, + .send_len = sizeof(add_advertising_invalid_param_8), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_10 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_9, + .send_len = sizeof(add_advertising_invalid_param_9), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_11 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_invalid_param_10, + .send_len = sizeof(add_advertising_invalid_param_10), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data add_advertising_fail_12 = { + .setup_settings = settings_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_3, + .send_len = sizeof(add_advertising_param_3), + .expect_status = MGMT_STATUS_REJECTED, +}; + +static const struct generic_data add_advertising_success_1 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_1, + .send_len = sizeof(add_advertising_param_1), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED, + .expect_alt_ev_param = advertising_instance_param, + .expect_alt_ev_len = sizeof(advertising_instance_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_1, + .expect_hci_len = sizeof(set_adv_data_1), +}; + +static const char set_powered_adv_instance_settings_param[] = { + 0x81, 0x02, 0x00, 0x00, +}; + +static const struct generic_data add_advertising_success_2 = { + .send_opcode = MGMT_OP_SET_POWERED, + .send_param = set_powered_on_param, + .send_len = sizeof(set_powered_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_powered_adv_instance_settings_param, + .expect_len = sizeof(set_powered_adv_instance_settings_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_3, + .expect_hci_len = sizeof(set_adv_data_3), +}; + +static const struct generic_data add_advertising_success_3 = { + .send_opcode = MGMT_OP_SET_POWERED, + .send_param = set_powered_on_param, + .send_len = sizeof(set_powered_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_powered_adv_instance_settings_param, + .expect_len = sizeof(set_powered_adv_instance_settings_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE, + .expect_hci_param = set_adv_on_set_adv_enable_param, + .expect_hci_len = sizeof(set_adv_on_set_adv_enable_param), +}; + +static const struct generic_data add_advertising_success_4 = { + .send_opcode = MGMT_OP_SET_ADVERTISING, + .send_param = set_adv_on_param, + .send_len = sizeof(set_adv_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_adv_settings_param_2, + .expect_len = sizeof(set_adv_settings_param_2), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_4, + .expect_hci_len = sizeof(set_adv_data_4), +}; + +static const char set_adv_off_param[] = { 0x00 }; + +static const struct generic_data add_advertising_success_5 = { + .send_opcode = MGMT_OP_SET_ADVERTISING, + .send_param = set_adv_off_param, + .send_len = sizeof(set_adv_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_powered_adv_instance_settings_param, + .expect_len = sizeof(set_powered_adv_instance_settings_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_3, + .expect_hci_len = sizeof(set_adv_data_3), +}; + +static const struct generic_data add_advertising_success_6 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_2, + .send_len = sizeof(add_advertising_param_2), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED, + .expect_alt_ev_param = advertising_instance_param, + .expect_alt_ev_len = sizeof(advertising_instance_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_1, + .expect_hci_len = sizeof(set_adv_data_1), +}; + +static const struct generic_data add_advertising_success_7 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_2, + .send_len = sizeof(add_advertising_param_2), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED, + .expect_alt_ev_param = advertising_instance_param, + .expect_alt_ev_len = sizeof(advertising_instance_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, + .expect_hci_param = set_scan_rsp_1, + .expect_hci_len = sizeof(set_scan_rsp_1), +}; + +static const struct generic_data add_advertising_success_8 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_4, + .send_len = sizeof(add_advertising_param_4), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_on_adv_param, + .expect_hci_len = sizeof(set_connectable_on_adv_param), +}; + +static const struct generic_data add_advertising_success_9 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_5, + .send_len = sizeof(add_advertising_param_5), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_5, + .expect_hci_len = sizeof(set_adv_data_5), +}; + +static const struct generic_data add_advertising_success_10 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_6, + .send_len = sizeof(add_advertising_param_6), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_6, + .expect_hci_len = sizeof(set_adv_data_6), +}; + +static const struct generic_data add_advertising_success_11 = { + .setup_settings = settings_powered_le_discoverable, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_7, + .send_len = sizeof(add_advertising_param_7), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_7, + .expect_hci_len = sizeof(set_adv_data_7), +}; + +static const struct generic_data add_advertising_success_12 = { + .setup_settings = settings_powered_le_discoverable, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_8, + .send_len = sizeof(add_advertising_param_8), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_adv_data_8, + .expect_hci_len = sizeof(set_adv_data_8), +}; + +static uint16_t settings_powered_le_connectable[] = { + MGMT_OP_SET_POWERED, + MGMT_OP_SET_LE, + MGMT_OP_SET_CONNECTABLE, 0 }; + +static uint8_t set_connectable_off_scan_adv_param[] = { + 0x00, 0x08, /* min_interval */ + 0x00, 0x08, /* max_interval */ + 0x02, /* type */ + 0x01, /* own_addr_type */ + 0x00, /* direct_addr_type */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */ + 0x07, /* channel_map */ + 0x00, /* filter_policy */ +}; + +static const struct generic_data add_advertising_success_13 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_2, + .send_len = sizeof(add_advertising_param_2), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_off_scan_adv_param, + .expect_hci_len = sizeof(set_connectable_off_scan_adv_param), +}; + +static const struct generic_data add_advertising_success_14 = { + .setup_settings = settings_powered_le, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_1, + .send_len = sizeof(add_advertising_param_1), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_off_adv_param, + .expect_hci_len = sizeof(set_connectable_off_adv_param), +}; + +static const struct generic_data add_advertising_success_15 = { + .setup_settings = settings_powered_le_connectable, + .send_opcode = MGMT_OP_ADD_ADVERTISING, + .send_param = add_advertising_param_1, + .send_len = sizeof(add_advertising_param_1), + .expect_param = advertising_instance_param, + .expect_len = sizeof(advertising_instance_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_on_adv_param, + .expect_hci_len = sizeof(set_connectable_on_adv_param), +}; + +static const char set_connectable_settings_param_3[] = { + 0x83, 0x02, 0x00, 0x00 }; + +static const struct generic_data add_advertising_success_16 = { + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_on_param, + .send_len = sizeof(set_connectable_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_settings_param_3, + .expect_len = sizeof(set_connectable_settings_param_3), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_on_adv_param, + .expect_hci_len = sizeof(set_connectable_on_adv_param), +}; + +static const struct generic_data add_advertising_success_17 = { + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_off_param, + .send_len = sizeof(set_connectable_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_le_settings_param_2, + .expect_len = sizeof(set_le_settings_param_2), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_off_adv_param, + .expect_hci_len = sizeof(set_connectable_off_adv_param), +}; + +static const char set_powered_off_le_settings_param[] = { + 0x80, 0x02, 0x00, 0x00 +}; + +static const struct generic_data add_advertising_timeout_power_off = { + .send_opcode = MGMT_OP_SET_POWERED, + .send_param = set_powered_off_param, + .send_len = sizeof(set_powered_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_powered_off_le_settings_param, + .expect_len = sizeof(set_powered_off_le_settings_param), + .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED, + .expect_alt_ev_param = advertising_instance_param, + .expect_alt_ev_len = sizeof(advertising_instance_param), +}; + +static const uint8_t remove_advertising_param_1[] = { + 0x01, +}; + +static const uint8_t remove_advertising_param_2[] = { + 0x00, +}; + +static const struct generic_data remove_advertising_fail_1 = { + .send_opcode = MGMT_OP_REMOVE_ADVERTISING, + .send_param = remove_advertising_param_1, + .send_len = sizeof(remove_advertising_param_1), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data remove_advertising_success_1 = { + .send_opcode = MGMT_OP_REMOVE_ADVERTISING, + .send_param = remove_advertising_param_1, + .send_len = sizeof(remove_advertising_param_1), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = remove_advertising_param_1, + .expect_len = sizeof(remove_advertising_param_1), + .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED, + .expect_alt_ev_param = advertising_instance_param, + .expect_alt_ev_len = sizeof(advertising_instance_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE, + .expect_hci_param = set_adv_off_param, + .expect_hci_len = sizeof(set_adv_off_param), +}; + +static const struct generic_data remove_advertising_success_2 = { + .send_opcode = MGMT_OP_REMOVE_ADVERTISING, + .send_param = remove_advertising_param_2, + .send_len = sizeof(remove_advertising_param_2), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = remove_advertising_param_1, + .expect_len = sizeof(remove_advertising_param_1), + .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED, + .expect_alt_ev_param = advertising_instance_param, + .expect_alt_ev_len = sizeof(advertising_instance_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE, + .expect_hci_param = set_adv_off_param, + .expect_hci_len = sizeof(set_adv_off_param), +}; + +static const struct generic_data read_local_oob_not_powered_test = { + .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA, + .expect_status = MGMT_STATUS_NOT_POWERED, +}; + +static const struct generic_data read_local_oob_invalid_param_test = { + .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA, + .send_param = dummy_data, + .send_len = sizeof(dummy_data), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data read_local_oob_invalid_index_test = { + .send_index_none = true, + .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA, + .expect_status = MGMT_STATUS_INVALID_INDEX, +}; + +static const struct generic_data read_local_oob_legacy_pairing_test = { + .setup_settings = settings_powered, + .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA, + .expect_status = MGMT_STATUS_NOT_SUPPORTED, +}; + +static const struct generic_data read_local_oob_success_ssp_test = { + .setup_settings = settings_powered_ssp, + .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA, + .expect_status = MGMT_STATUS_SUCCESS, + .expect_ignore_param = true, + .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_DATA, +}; + +static const struct generic_data read_local_oob_success_sc_test = { + .setup_settings = settings_powered_sc, + .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA, + .expect_status = MGMT_STATUS_SUCCESS, + .expect_ignore_param = true, + .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA, +}; + static void client_cmd_complete(uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data) @@ -4098,6 +4843,199 @@ static void setup_add_device(const void *test_data) setup_powered_callback, NULL, NULL); } +static void setup_add_advertising_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + tester_setup_failed(); + return; + } + + tester_print("Add Advertising setup complete"); + + setup_bthost(); +} + +static void setup_add_advertising_not_powered(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct mgmt_cp_add_advertising *cp; + unsigned char adv_param[sizeof(*cp) + 6]; + unsigned char param[] = { 0x01 }; + + tester_print("Adding advertising instance while unpowered"); + + cp = (struct mgmt_cp_add_advertising *) adv_param; + memset(cp, 0, sizeof(*cp)); + + cp->instance = 1; + cp->adv_data_len = 6; + cp->data[0] = 0x05; + cp->data[1] = 0x08; + cp->data[2] = 't'; + cp->data[3] = 'e'; + cp->data[4] = 's'; + cp->data[5] = 't'; + + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index, + sizeof(adv_param), adv_param, + setup_add_advertising_callback, + NULL, NULL); +} + +static void setup_add_advertising(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct mgmt_cp_add_advertising *cp; + unsigned char adv_param[sizeof(*cp) + 6]; + unsigned char param[] = { 0x01 }; + + tester_print("Adding advertising instance while unpowered"); + + cp = (struct mgmt_cp_add_advertising *) adv_param; + memset(cp, 0, sizeof(*cp)); + + cp->instance = 1; + cp->adv_data_len = 6; + cp->data[0] = 0x05; + cp->data[1] = 0x08; + cp->data[2] = 't'; + cp->data[3] = 'e'; + cp->data[4] = 's'; + cp->data[5] = 't'; + + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index, + sizeof(adv_param), adv_param, + setup_add_advertising_callback, + NULL, NULL); +} + +static void setup_add_advertising_connectable(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct mgmt_cp_add_advertising *cp; + unsigned char adv_param[sizeof(*cp) + 6]; + unsigned char param[] = { 0x01 }; + + tester_print("Adding advertising instance while unpowered"); + + cp = (struct mgmt_cp_add_advertising *) adv_param; + memset(cp, 0, sizeof(*cp)); + + cp->instance = 1; + cp->adv_data_len = 6; + cp->data[0] = 0x05; + cp->data[1] = 0x08; + cp->data[2] = 't'; + cp->data[3] = 'e'; + cp->data[4] = 's'; + cp->data[5] = 't'; + + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index, + sizeof(adv_param), adv_param, + setup_add_advertising_callback, + NULL, NULL); +} + +static void setup_add_advertising_timeout(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct mgmt_cp_add_advertising *cp; + unsigned char adv_param[sizeof(*cp) + 6]; + unsigned char param[] = { 0x01 }; + + tester_print("Adding advertising instance while unpowered"); + + cp = (struct mgmt_cp_add_advertising *) adv_param; + memset(cp, 0, sizeof(*cp)); + + cp->instance = 1; + cp->timeout = 5; + cp->adv_data_len = 6; + cp->data[0] = 0x05; + cp->data[1] = 0x08; + cp->data[2] = 't'; + cp->data[3] = 'e'; + cp->data[4] = 's'; + cp->data[5] = 't'; + + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index, + sizeof(adv_param), adv_param, + setup_add_advertising_callback, + NULL, NULL); +} + +static void setup_set_and_add_advertising(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct mgmt_cp_add_advertising *cp; + unsigned char adv_param[sizeof(*cp) + 6]; + unsigned char param[] = { 0x01 }; + + tester_print("Adding advertising instance while unpowered"); + + cp = (struct mgmt_cp_add_advertising *) adv_param; + memset(cp, 0, sizeof(*cp)); + + cp->instance = 1; + cp->adv_data_len = 6; + cp->data[0] = 0x05; + cp->data[1] = 0x08; + cp->data[2] = 't'; + cp->data[3] = 'e'; + cp->data[4] = 's'; + cp->data[5] = 't'; + + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index, + sizeof(param), ¶m, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index, + sizeof(adv_param), adv_param, + setup_add_advertising_callback, + NULL, NULL); +} + static void setup_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -4402,18 +5340,20 @@ static void command_generic_callback(uint8_t status, uint16_t length, return; } - if (test->expect_func) - expect_param = test->expect_func(&expect_len); + if (!test->expect_ignore_param) { + if (test->expect_func) + expect_param = test->expect_func(&expect_len); - if (length != expect_len) { - tester_test_failed(); - return; - } + if (length != expect_len) { + tester_test_failed(); + return; + } - if (expect_param && expect_len > 0 && - memcmp(param, expect_param, length)) { - tester_test_failed(); - return; + if (expect_param && expect_len > 0 && + memcmp(param, expect_param, length)) { + tester_test_failed(); + return; + } } test_condition_complete(data); @@ -4679,6 +5619,24 @@ int main(int argc, char *argv[]) test_bredrle("Read info - Invalid index", &read_info_invalid_index_test, NULL, test_command_generic); + test_bredrle("Read unconfigured index list - Invalid parameters", + &read_unconf_index_list_invalid_param_test, + NULL, test_command_generic); + test_bredrle("Read unconfigured index list - Invalid index", + &read_unconf_index_list_invalid_index_test, + NULL, test_command_generic); + test_bredrle("Read configuration info - Invalid parameters", + &read_config_info_invalid_param_test, + NULL, test_command_generic); + test_bredrle("Read configuration info - Invalid index", + &read_config_info_invalid_index_test, + NULL, test_command_generic); + test_bredrle("Read extended index list - Invalid parameters", + &read_ext_index_list_invalid_param_test, + NULL, test_command_generic); + test_bredrle("Read extended index list - Invalid index", + &read_ext_index_list_invalid_index_test, + NULL, test_command_generic); test_bredrle("Set powered on - Success", &set_powered_on_success_test, @@ -4770,6 +5728,15 @@ int main(int argc, char *argv[]) test_bredrle("Set fast connectable on - Success 1", &set_fast_conn_on_success_test_1, NULL, test_command_generic); + test_bredrle("Set fast connectable on - Success 2", + &set_fast_conn_on_success_test_2, + NULL, test_command_generic); + test_bredrle("Set fast connectable on - Success 3", + &set_fast_conn_on_success_test_3, + NULL, test_command_generic); + test_bredrle("Set fast connectable on - Invalid Params 1", + &set_fast_conn_nval_param_test_1, + NULL, test_command_generic); test_le("Set fast connectable on - Not Supported 1", &set_fast_conn_on_not_supported_test_1, NULL, test_command_generic); @@ -5411,5 +6378,141 @@ int main(int argc, char *argv[]) &remove_device_success_5, setup_add_device, test_command_generic); + test_bredrle("Read Advertising Features - Invalid parameters", + &read_adv_features_invalid_param_test, + NULL, test_command_generic); + test_bredrle("Read Advertising Features - Invalid index", + &read_adv_features_invalid_index_test, + NULL, test_command_generic); + + test_bredrle("Add Advertising - Failure: LE off", + &add_advertising_fail_1, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 1 (AD too long)", + &add_advertising_fail_2, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 2 (Malformed len)", + &add_advertising_fail_3, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 3 (Malformed len)", + &add_advertising_fail_4, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 4 (Malformed len)", + &add_advertising_fail_5, + NULL, test_command_generic); + test_le("Add Advertising - Invalid Params 5 (AD too long)", + &add_advertising_fail_6, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 6 (ScRsp too long)", + &add_advertising_fail_7, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 7 (Malformed len)", + &add_advertising_fail_8, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 8 (Malformed len)", + &add_advertising_fail_9, + NULL, test_command_generic); + test_bredrle("Add Advertising - Invalid Params 9 (Malformed len)", + &add_advertising_fail_10, + NULL, test_command_generic); + test_le("Add Advertising - Invalid Params 10 (ScRsp too long)", + &add_advertising_fail_11, + NULL, test_command_generic); + test_bredrle("Add Advertising - Timeout Not Powered", + &add_advertising_fail_12, + NULL, test_command_generic); + test_bredrle("Add Advertising - Timeout Power off", + &add_advertising_timeout_power_off, + setup_add_advertising_timeout, + test_command_generic); + test_bredrle("Add Advertising - Success 1", + &add_advertising_success_1, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 2", + &add_advertising_success_2, + setup_add_advertising_not_powered, + test_command_generic); + test_bredrle("Add Advertising - Success 3", + &add_advertising_success_3, + setup_add_advertising_not_powered, + test_command_generic); + test_bredrle("Add Advertising - Set Advertising on override 1", + &add_advertising_success_4, + setup_add_advertising, + test_command_generic); + test_bredrle("Add Advertising - Set Advertising off override 2", + &add_advertising_success_5, + setup_set_and_add_advertising, + test_command_generic); + test_bredrle("Add Advertising - Success 4", + &add_advertising_success_6, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 5", + &add_advertising_success_7, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 6 - Flag 0", + &add_advertising_success_8, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 7 - Flag 1", + &add_advertising_success_9, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 8 - Flag 2", + &add_advertising_success_10, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 8 - Flag 3", + &add_advertising_success_11, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 9 - Flag 4", + &add_advertising_success_12, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 10 - ADV_SCAN_IND", + &add_advertising_success_13, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 11 - ADV_NONCONN_IND", + &add_advertising_success_14, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 12 - ADV_IND", + &add_advertising_success_15, + NULL, test_command_generic); + test_bredrle("Add Advertising - Success 13 - connectable -> on", + &add_advertising_success_16, + setup_add_advertising, + test_command_generic); + test_bredrle("Add Advertising - Success 14 - connectable -> off", + &add_advertising_success_17, + setup_add_advertising_connectable, + test_command_generic); + + test_bredrle("Remove Advertising - Invalid Params 1", + &remove_advertising_fail_1, + NULL, test_command_generic); + test_bredrle("Remove Advertising - Success 1", + &remove_advertising_success_1, + setup_add_advertising, + test_command_generic); + test_bredrle("Remove Advertising - Success 2", + &remove_advertising_success_2, + setup_add_advertising, + test_command_generic); + + test_bredrle("Read Local OOB Data - Not powered", + &read_local_oob_not_powered_test, + NULL, test_command_generic); + test_bredrle("Read Local OOB Data - Invalid parameters", + &read_local_oob_invalid_param_test, + NULL, test_command_generic); + test_bredrle("Read Local OOB Data - Invalid index", + &read_local_oob_invalid_index_test, + NULL, test_command_generic); + test_bredr20("Read Local OOB Data - Legacy pairing", + &read_local_oob_legacy_pairing_test, + NULL, test_command_generic); + test_bredrle("Read Local OOB Data - Success SSP", + &read_local_oob_success_ssp_test, + NULL, test_command_generic); + test_bredrle("Read Local OOB Data - Success SC", + &read_local_oob_success_sc_test, + NULL, test_command_generic); + return tester_run(); } diff --git a/tools/mpris-proxy.c b/tools/mpris-proxy.c index bf8148fe..693055ed 100644 --- a/tools/mpris-proxy.c +++ b/tools/mpris-proxy.c @@ -406,7 +406,7 @@ static DBusHandlerResult player_message(DBusConnection *conn, done: dbus_message_unref(copy); - return DBUS_HANDLER_RESULT_HANDLED; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static struct player *find_player_by_bus_name(const char *name) diff --git a/tools/obexctl.c b/tools/obexctl.c index 86c81d57..4faff6b6 100644 --- a/tools/obexctl.c +++ b/tools/obexctl.c @@ -758,7 +758,7 @@ static void send_reply(DBusMessage *message, void *user_data) dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == TRUE) { - rl_printf("Failed to send/pull: %s\n", error.name); + rl_printf("Failed to send: %s\n", error.name); dbus_error_free(&error); return; } @@ -792,23 +792,6 @@ static void opp_send(GDBusProxy *proxy, int argc, char *argv[]) g_dbus_proxy_get_path(proxy)); } -static void opp_pull(GDBusProxy *proxy, int argc, char *argv[]) -{ - if (argc < 2) { - rl_printf("Missing file argument\n"); - return; - } - - if (g_dbus_proxy_method_call(proxy, "PullBusinessCard", send_setup, - send_reply, g_strdup(argv[1]), g_free) == FALSE) { - rl_printf("Failed to pull\n"); - return; - } - - rl_printf("Attempting to pull %s from %s\n", argv[1], - g_dbus_proxy_get_path(proxy)); -} - static void push_reply(DBusMessage *message, void *user_data) { DBusMessageIter iter; @@ -886,22 +869,6 @@ static void cmd_send(int argc, char *argv[]) rl_printf("Command not supported\n"); } -static void cmd_pull(int argc, char *argv[]) -{ - GDBusProxy *proxy; - - if (!check_default_session()) - return; - - proxy = find_opp(g_dbus_proxy_get_path(default_session)); - if (proxy) { - opp_pull(proxy, argc, argv); - return; - } - - rl_printf("Command not supported\n"); -} - static void change_folder_reply(DBusMessage *message, void *user_data) { DBusError error; @@ -2012,8 +1979,6 @@ static const struct { { "suspend", "<transfer>", cmd_suspend, "Suspend transfer" }, { "resume", "<transfer>", cmd_resume, "Resume transfer" }, { "send", "<file>", cmd_send, "Send file" }, - { "pull", "<file>", cmd_pull, - "Pull Vobject & stores in file" }, { "cd", "<path>", cmd_cd, "Change current folder" }, { "ls", "<options>", cmd_ls, "List current folder" }, { "cp", "<source file> <destination file>", cmd_cp, diff --git a/tools/oobtest.c b/tools/oobtest.c index 9cc6c162..e77320ba 100644 --- a/tools/oobtest.c +++ b/tools/oobtest.c @@ -34,6 +34,10 @@ #include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" +#include "src/shared/crypto.h" + +#define REMOTE_IRK "\x69\x30\xde\xc3\x8f\x84\x74\x14" \ + "\xe1\x23\x99\xc1\xca\x9a\xc3\x31" static bool use_bredr = false; static bool use_le = false; @@ -41,16 +45,21 @@ static bool use_sc = false; static bool use_sconly = false; static bool use_legacy = false; static bool use_random = false; +static bool use_privacy = false; static bool use_debug = false; static bool use_cross = false; +static bool provide_tk = false; static bool provide_p192 = false; static bool provide_p256 = false; +static bool provide_initiator = false; +static bool provide_acceptor = false; static struct mgmt *mgmt; static uint16_t index1 = MGMT_INDEX_NONE; static uint16_t index2 = MGMT_INDEX_NONE; static bdaddr_t bdaddr1; static bdaddr_t bdaddr2; +static uint8_t oob_tk[16]; static void pin_code_request_event(uint16_t index, uint16_t len, const void *param, void *user_data) @@ -254,9 +263,12 @@ static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr, cp.addr.type = BDADDR_LE_RANDOM; else cp.addr.type = BDADDR_LE_PUBLIC; - if (hash192 && rand192) { + if (hash192) { memcpy(cp.hash192, hash192, 16); - memcpy(cp.rand192, rand192, 16); + if (rand192) + memcpy(cp.rand192, rand192, 16); + else + memset(cp.rand192, 0, 16); } else { memset(cp.hash192, 0, 16); memset(cp.rand192, 0, 16); @@ -277,7 +289,7 @@ static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr, static void read_oob_data_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { - const struct mgmt_rp_read_local_oob_ext_data *rp = param; + const struct mgmt_rp_read_local_oob_data *rp = param; uint16_t index = PTR_TO_UINT(user_data); const uint8_t *hash192, *rand192, *hash256, *rand256; int i; @@ -291,12 +303,22 @@ static void read_oob_data_complete(uint8_t status, uint16_t len, printf("[Index %u]\n", index); + hash192 = NULL; + rand192 = NULL; + hash256 = NULL; + rand256 = NULL; + + if (index == index1 && !provide_initiator) { + printf(" Skipping initiator OOB data\n"); + goto done; + } else if (index == index2 && !provide_acceptor) { + printf(" Skipping acceptor OOB data\n"); + goto done; + } + if (provide_p192) { hash192 = rp->hash192; - rand192 = rp->randomizer192; - } else { - hash192 = NULL; - rand192 = NULL; + rand192 = rp->rand192; } printf(" Hash C from P-192: "); @@ -306,21 +328,15 @@ static void read_oob_data_complete(uint8_t status, uint16_t len, printf(" Randomizer R with P-192: "); for (i = 0; i < 16; i++) - printf("%02x", rp->randomizer192[i]); + printf("%02x", rp->rand192[i]); printf("\n"); - if (len < sizeof(*rp)) { - hash256 = NULL; - rand256 = NULL; + if (len < sizeof(*rp)) goto done; - } if (provide_p256) { hash256 = rp->hash256; - rand256 = rp->randomizer256; - } else { - hash256 = NULL; - rand256 = NULL; + rand256 = rp->rand256; } printf(" Hash C from P-256: "); @@ -330,7 +346,7 @@ static void read_oob_data_complete(uint8_t status, uint16_t len, printf(" Randomizer R with P-256: "); for (i = 0; i < 16; i++) - printf("%02x", rp->randomizer256[i]); + printf("%02x", rp->rand256[i]); printf("\n"); done: @@ -342,6 +358,105 @@ done: hash192, rand192, hash256, rand256); } +static void read_oob_ext_data_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_local_oob_ext_data *rp = param; + uint16_t index = PTR_TO_UINT(user_data); + uint16_t eir_len, parsed; + const uint8_t *eir, *tk, *hash256, *rand256; + int i; + + if (status) { + fprintf(stderr, "Reading OOB data for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } + + printf("[Index %u]\n", index); + + eir_len = le16_to_cpu(rp->eir_len); + printf(" OOB data len: %u\n", eir_len); + + if (provide_tk) + tk = oob_tk; + else + tk = NULL; + + hash256 = NULL; + rand256 = NULL; + + if (index == index1 && !provide_initiator) { + printf(" Skipping initiator OOB data\n"); + goto done; + } else if (index == index2 && !provide_acceptor) { + printf(" Skipping acceptor OOB data\n"); + goto done; + } + + if (eir_len < 2) + goto done; + + eir = rp->eir; + parsed = 0; + + while (parsed < eir_len - 1) { + uint8_t field_len = eir[0]; + + if (field_len == 0) + break; + + parsed += field_len + 1; + + if (parsed > eir_len) + break; + + /* LE Bluetooth Device Address */ + if (eir[1] == 0x1b) { + char str[18]; + + ba2str((bdaddr_t *) (eir + 2), str); + printf(" Device address: %s (%s)\n", str, + eir[8] ? "random" : "public"); + } + + /* LE Role */ + if (eir[1] == 0x1c) + printf(" Role: 0x%02x\n", eir[2]); + + /* LE Secure Connections Confirmation Value */ + if (eir[1] == 0x22) { + hash256 = eir + 2; + + printf(" Hash C from P-256: "); + for (i = 0; i < 16; i++) + printf("%02x", hash256[i]); + printf("\n"); + } + + /* LE Secure Connections Random Value */ + if (eir[1] == 0x23) { + rand256 = eir + 2; + + printf(" Randomizer R with P-256: "); + for (i = 0; i < 16; i++) + printf("%02x", rand256[i]); + printf("\n"); + } + + eir += field_len + 1; + } + +done: + if (index == index1) + add_remote_oob_data(index2, &bdaddr1, + tk, NULL, hash256, rand256); + else if (index == index2) + add_remote_oob_data(index1, &bdaddr2, + tk, NULL, hash256, rand256); +} + static void set_powered_complete(uint8_t status, uint16_t len, const void *param, void *user_data) { @@ -380,6 +495,23 @@ static void set_powered_complete(uint8_t status, uint16_t len, mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL, read_oob_data_complete, UINT_TO_PTR(index), NULL); + } else if (use_le && provide_p256) { + uint8_t type = (1 << BDADDR_LE_PUBLIC) | + (1 << BDADDR_LE_RANDOM); + + mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index, + sizeof(type), &type, + read_oob_ext_data_complete, + UINT_TO_PTR(index), NULL); + } else if (use_le && provide_tk) { + const uint8_t *tk = oob_tk; + + if (index == index1) + add_remote_oob_data(index2, &bdaddr1, + tk, NULL, NULL, NULL); + else if (index == index2) + add_remote_oob_data(index1, &bdaddr2, + tk, NULL, NULL, NULL); } else { if (index == index1) add_remote_oob_data(index2, &bdaddr1, @@ -413,6 +545,17 @@ static void clear_long_term_keys(uint16_t index) sizeof(cp), &cp, NULL, NULL, NULL); } +static void clear_identity_resolving_keys(uint16_t index) +{ + struct mgmt_cp_load_irks cp; + + memset(&cp, 0, sizeof(cp)); + cp.irk_count = cpu_to_le16(0); + + mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + static void clear_remote_oob_data(uint16_t index) { struct mgmt_cp_remove_remote_oob_data cp; @@ -425,6 +568,123 @@ static void clear_remote_oob_data(uint16_t index) sizeof(cp), &cp, NULL, NULL, NULL); } +static void set_powered_down_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Power down for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_bredr_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Setting BR/EDR for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_le_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Setting LE for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_ssp_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Simple Pairing for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_static_address_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Static address for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_secure_conn_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Secure connections for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_privacy_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Setting privacy for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_debug_keys_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Setting debug keys for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + +static void set_bondable_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Setting bondable for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } +} + static void read_info(uint8_t status, uint16_t len, const void *param, void *user_data) { @@ -483,6 +743,12 @@ static void read_info(uint8_t status, uint16_t len, const void *param, return; } + if (use_privacy && !(supported_settings & MGMT_SETTING_PRIVACY)) { + fprintf(stderr, "Privacy support missing\n"); + mainloop_quit(); + return; + } + if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) { fprintf(stderr, "Debug keys support missing\n"); mainloop_quit(); @@ -496,6 +762,16 @@ static void read_info(uint8_t status, uint16_t len, const void *param, return; } + if (provide_tk) { + const uint8_t *tk = oob_tk; + int i; + + printf(" TK Value: "); + for (i = 0; i < 16; i++) + printf("%02x", tk[i]); + printf("\n"); + } + mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, pin_code_request_event, UINT_TO_PTR(index), NULL); @@ -510,32 +786,46 @@ static void read_info(uint8_t status, uint16_t len, const void *param, val = 0x00; mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val, - NULL, NULL, NULL); + set_powered_down_complete, + UINT_TO_PTR(index), NULL); clear_link_keys(index); clear_long_term_keys(index); + clear_identity_resolving_keys(index); clear_remote_oob_data(index); if (use_bredr) { val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val, - NULL, NULL, NULL); + set_bredr_complete, + UINT_TO_PTR(index), NULL); val = use_cross ? 0x01 : 0x00; mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val, - NULL, NULL, NULL); + set_le_complete, + UINT_TO_PTR(index), NULL); val = use_legacy ? 0x00 : 0x01; mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val, - NULL, NULL, NULL); + set_ssp_complete, + UINT_TO_PTR(index), NULL); } else if (use_le) { val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val, - NULL, NULL, NULL); + set_le_complete, + UINT_TO_PTR(index), NULL); val = use_cross ? 0x01 : 0x00; mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val, - NULL, NULL, NULL); + set_bredr_complete, + UINT_TO_PTR(index), NULL); + + if (use_cross) { + val = use_legacy ? 0x00 : 0x01; + mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val, + set_ssp_complete, + UINT_TO_PTR(index), NULL); + } } else { fprintf(stderr, "Invalid transport for pairing\n"); mainloop_quit(); @@ -548,8 +838,9 @@ static void read_info(uint8_t status, uint16_t len, const void *param, str2ba("c0:00:aa:bb:00:00", &bdaddr); bdaddr.b[0] = index; - mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, - 6, &bdaddr, NULL, NULL, NULL); + mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr, + set_static_address_complete, + UINT_TO_PTR(index), NULL); if (index == index1) bacpy(&bdaddr1, &bdaddr); @@ -560,31 +851,62 @@ static void read_info(uint8_t status, uint16_t len, const void *param, bacpy(&bdaddr, BDADDR_ANY); - mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, - 6, &bdaddr, NULL, NULL, NULL); + mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr, + set_static_address_complete, + UINT_TO_PTR(index), NULL); } if (use_sc) { val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, - NULL, NULL, NULL); + set_secure_conn_complete, + UINT_TO_PTR(index), NULL); } else if (use_sconly) { val = 0x02; mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, - NULL, NULL, NULL); + set_secure_conn_complete, + UINT_TO_PTR(index), NULL); } else { val = 0x00; mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, - NULL, NULL, NULL); + set_secure_conn_complete, + UINT_TO_PTR(index), NULL); + } + + if (use_privacy) { + struct mgmt_cp_set_privacy cp; + + if (index == index2) { + cp.privacy = 0x01; + memcpy(cp.irk, REMOTE_IRK, sizeof(cp.irk)); + } else { + cp.privacy = 0x00; + memset(cp.irk, 0, sizeof(cp.irk)); + } + + mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp, + set_privacy_complete, + UINT_TO_PTR(index), NULL); + } else { + struct mgmt_cp_set_privacy cp; + + cp.privacy = 0x00; + memset(cp.irk, 0, sizeof(cp.irk)); + + mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp, + set_privacy_complete, + UINT_TO_PTR(index), NULL); } val = 0x00; mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val, - NULL, NULL, NULL); + set_debug_keys_complete, + UINT_TO_PTR(index), NULL); val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val, - NULL, NULL, NULL); + set_bondable_complete, + UINT_TO_PTR(index), NULL); val = 0x01; mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val, @@ -631,6 +953,16 @@ static void read_index_list(uint8_t status, uint16_t len, const void *param, printf("Selecting index %u for initiator\n", index1); printf("Selecting index %u for acceptor\n", index2); + if (provide_tk) { + struct bt_crypto *crypto; + + printf("Generating Security Manager TK Value\n"); + + crypto = bt_crypto_new(); + bt_crypto_random_bytes(crypto, oob_tk, 16); + bt_crypto_unref(crypto); + } + mgmt_send(mgmt, MGMT_OP_READ_INFO, index1, 0, NULL, read_info, UINT_TO_PTR(index1), NULL); mgmt_send(mgmt, MGMT_OP_READ_INFO, index2, 0, NULL, @@ -659,28 +991,36 @@ static void usage(void) "\t-O, --sconly Use Secure Connections Only\n" "\t-P, --legacy Use Legacy Pairing\n" "\t-R, --random Use Static random address\n" + "\t-Y, --privacy Use LE privacy feature\n" "\t-D, --debug Use Pairing debug keys\n" "\t-C, --cross Use cross-transport pairing\n" + "\t-0, --tk Provide LE legacy OOB data\n" "\t-1, --p192 Provide P-192 OOB data\n" "\t-2, --p256 Provide P-256 OOB data\n" + "\t-I, --initiator Initiator provides OOB data\n" + "\t-A, --acceptor Acceptor provides OOB data\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { - { "bredr", no_argument, NULL, 'B' }, - { "le", no_argument, NULL, 'L' }, - { "sc", no_argument, NULL, 'S' }, - { "sconly", no_argument, NULL, 'O' }, - { "legacy", no_argument, NULL, 'P' }, - { "random", no_argument, NULL, 'R' }, - { "static", no_argument, NULL, 'R' }, - { "debug", no_argument, NULL, 'D' }, - { "cross", no_argument, NULL, 'C' }, - { "dual", no_argument, NULL, 'C' }, - { "p192", no_argument, NULL, '1' }, - { "p256", no_argument, NULL, '2' }, - { "version", no_argument, NULL, 'v' }, - { "help", no_argument, NULL, 'h' }, + { "bredr", no_argument, NULL, 'B' }, + { "le", no_argument, NULL, 'L' }, + { "sc", no_argument, NULL, 'S' }, + { "sconly", no_argument, NULL, 'O' }, + { "legacy", no_argument, NULL, 'P' }, + { "random", no_argument, NULL, 'R' }, + { "static", no_argument, NULL, 'R' }, + { "privacy", no_argument, NULL, 'Y' }, + { "debug", no_argument, NULL, 'D' }, + { "cross", no_argument, NULL, 'C' }, + { "dual", no_argument, NULL, 'C' }, + { "tk", no_argument, NULL, '0' }, + { "p192", no_argument, NULL, '1' }, + { "p256", no_argument, NULL, '2' }, + { "initiator", no_argument, NULL, 'I' }, + { "acceptor", no_argument, NULL, 'A' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, { } }; @@ -692,7 +1032,7 @@ int main(int argc ,char *argv[]) for (;;) { int opt; - opt = getopt_long(argc, argv, "BLSOPRDC12vh", + opt = getopt_long(argc, argv, "BLSOPRYDC012IAvh", main_options, NULL); if (opt < 0) break; @@ -716,18 +1056,30 @@ int main(int argc ,char *argv[]) case 'R': use_random = true; break; + case 'Y': + use_privacy = true; + break; case 'D': use_debug = true; break; case 'C': use_cross = true; break; + case '0': + provide_tk = true; + break; case '1': provide_p192 = true; break; case '2': provide_p256 = true; break; + case 'I': + provide_initiator = true; + break; + case 'A': + provide_acceptor = true; + break; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; @@ -754,6 +1106,11 @@ int main(int argc ,char *argv[]) return EXIT_FAILURE; } + if (use_privacy && !use_le && !use_cross ) { + fprintf(stderr, "Specify --privacy with --le or --cross\n"); + return EXIT_FAILURE; + } + if (use_random && !use_le) { fprintf(stderr, "Specify --random with --le\n"); return EXIT_FAILURE; |