| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2015 Intel Corporation
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #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 <glib.h>
- #include "lib/bluetooth.h"
- #include "lib/hci.h"
- #include "lib/hci_lib.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 */
- n = recv(sk, packet, sizeof(packet), MSG_PEEK);
- 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;
- }
|