monitor: Add initial support for parsing new control messages
diff --git a/monitor/display.h b/monitor/display.h
index 36e189a..b85f37b 100644
--- a/monitor/display.h
+++ b/monitor/display.h
@@ -38,6 +38,11 @@
#define COLOR_WHITE_BG "\x1B[0;47;30m"
#define COLOR_HIGHLIGHT "\x1B[1;39m"
+#define COLOR_RED_BOLD "\x1B[1;31m"
+#define COLOR_GREEN_BOLD "\x1B[1;32m"
+#define COLOR_BLUE_BOLD "\x1B[1;34m"
+#define COLOR_MAGENTA_BOLD "\x1B[1;35m"
+
#define COLOR_ERROR "\x1B[1;31m"
#define COLOR_WARN "\x1B[1m"
#define COLOR_INFO COLOR_OFF
diff --git a/monitor/packet.c b/monitor/packet.c
index 259c20b..0cf458b 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -57,6 +57,7 @@
#include "broadcom.h"
#include "packet.h"
+#define COLOR_CHANNEL_LABEL COLOR_WHITE
#define COLOR_INDEX_LABEL COLOR_WHITE
#define COLOR_TIMESTAMP COLOR_YELLOW
@@ -66,13 +67,12 @@
#define COLOR_CLOSE_INDEX COLOR_RED
#define COLOR_INDEX_INFO COLOR_GREEN
#define COLOR_VENDOR_DIAG COLOR_YELLOW
+#define COLOR_SYSTEM_NOTE COLOR_OFF
#define COLOR_HCI_COMMAND COLOR_BLUE
#define COLOR_HCI_COMMAND_UNKNOWN COLOR_WHITE_BG
-
#define COLOR_HCI_EVENT COLOR_MAGENTA
#define COLOR_HCI_EVENT_UNKNOWN COLOR_WHITE_BG
-
#define COLOR_HCI_ACLDATA COLOR_CYAN
#define COLOR_HCI_SCODATA COLOR_YELLOW
@@ -84,6 +84,15 @@
#define COLOR_UNKNOWN_SERVICE_CLASS COLOR_WHITE_BG
#define COLOR_UNKNOWN_PKT_TYPE_BIT COLOR_WHITE_BG
+#define COLOR_CTRL_OPEN COLOR_GREEN_BOLD
+#define COLOR_CTRL_CLOSE COLOR_RED_BOLD
+#define COLOR_CTRL_COMMAND COLOR_BLUE_BOLD
+#define COLOR_CTRL_COMMAND_UNKNOWN COLOR_WHITE_BG
+#define COLOR_CTRL_EVENT COLOR_MAGENTA_BOLD
+#define COLOR_CTRL_EVENT_UNKNOWN COLOR_WHITE_BG
+
+#define COLOR_UNKNOWN_SETTINGS_BIT COLOR_WHITE_BG
+
#define COLOR_PHY_PACKET COLOR_BLUE
static time_t time_offset = ((time_t) -1);
@@ -95,6 +104,69 @@
#define UNKNOWN_MANUFACTURER 0xffff
+#define CTRL_MGMT 0x0001
+
+#define MAX_CTRL 64
+
+struct ctrl_data {
+ bool used;
+ uint32_t cookie;
+ uint16_t format;
+ char name[20];
+};
+
+static struct ctrl_data ctrl_list[MAX_CTRL];
+
+static void assign_ctrl(uint32_t cookie, uint16_t format, const char *name)
+{
+ int i;
+
+ for (i = 0; i < MAX_CTRL; i++) {
+ if (!ctrl_list[i].used) {
+ ctrl_list[i].used = true;
+ ctrl_list[i].cookie = cookie;
+ ctrl_list[i].format = format;
+ if (name) {
+ strncpy(ctrl_list[i].name, name, 19);
+ ctrl_list[i].name[19] = '\0';
+ } else
+ strcpy(ctrl_list[i].name, "null");
+ break;
+ }
+ }
+}
+
+static void release_ctrl(uint32_t cookie, uint16_t *format, char *name)
+{
+ int i;
+
+ if (format)
+ *format = 0xffff;
+
+ for (i = 0; i < MAX_CTRL; i++) {
+ if (ctrl_list[i].used && ctrl_list[i].cookie == cookie) {
+ ctrl_list[i].used = false;
+ if (format)
+ *format = ctrl_list[i].format;
+ if (name)
+ strncpy(name, ctrl_list[i].name, 20);
+ break;
+ }
+ }
+}
+
+static uint16_t get_format(uint32_t cookie)
+{
+ int i;
+
+ for (i = 0; i < MAX_CTRL; i++) {
+ if (ctrl_list[i].used && ctrl_list[i].cookie == cookie)
+ return ctrl_list[i].format;
+ }
+
+ return 0xffff;
+}
+
#define MAX_CONN 16
struct conn_data {
@@ -181,15 +253,29 @@
#define print_space(x) printf("%*c", (x), ' ');
-static void print_packet(struct timeval *tv, struct ucred *cred,
- uint16_t index, char ident,
+static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
+ uint16_t index, const char *channel,
const char *color, const char *label,
const char *text, const char *extra)
{
int col = num_columns();
- char line[256], ts_str[64];
+ char line[256], ts_str[96];
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
+ if (channel) {
+ if (use_color()) {
+ n = sprintf(ts_str + ts_pos, "%s", COLOR_CHANNEL_LABEL);
+ if (n > 0)
+ ts_pos += n;
+ }
+
+ n = sprintf(ts_str + ts_pos, " {%s}", channel);
+ if (n > 0) {
+ ts_pos += n;
+ ts_len += n;
+ }
+ }
+
if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
index != HCI_DEV_NONE) {
if (use_color()) {
@@ -2380,8 +2466,12 @@
break;
}
- print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)", label, str, version,
+ if (sublabel)
+ print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)",
+ label, str, version,
sublabel, subversion, subversion);
+ else
+ print_field("%s: %s (0x%2.2x)", label, str, version);
}
static void print_hci_version(uint8_t version, uint16_t revision)
@@ -3802,9 +3892,21 @@
packet_user_logging(tv, cred, index, ul->priority, ident,
data + sizeof(*ul) + ul->ident_len);
break;
+ case BTSNOOP_OPCODE_CTRL_OPEN:
+ packet_ctrl_open(tv, cred, index, data, size);
+ break;
+ case BTSNOOP_OPCODE_CTRL_CLOSE:
+ packet_ctrl_close(tv, cred, index, data, size);
+ break;
+ case BTSNOOP_OPCODE_CTRL_COMMAND:
+ packet_ctrl_command(tv, cred, index, data, size);
+ break;
+ case BTSNOOP_OPCODE_CTRL_EVENT:
+ packet_ctrl_event(tv, cred, index, data, size);
+ break;
default:
sprintf(extra_str, "(code %d len %d)", opcode, size);
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Unknown packet", NULL, extra_str);
packet_hexdump(data, size);
break;
@@ -3821,7 +3923,7 @@
sprintf(str, "%u MHz", frequency);
- print_packet(tv, NULL, 0, '*', COLOR_PHY_PACKET,
+ print_packet(tv, NULL, '*', 0, NULL, COLOR_PHY_PACKET,
"Physical packet:", NULL, str);
ll_packet(frequency, data, size, false);
@@ -8663,26 +8765,26 @@
sprintf(details, "(%s,%s,%s)", hci_typetostr(type),
hci_bustostr(bus), name);
- print_packet(tv, NULL, index, '=', COLOR_NEW_INDEX, "New Index",
- label, details);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_NEW_INDEX,
+ "New Index", label, details);
}
void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
{
- print_packet(tv, NULL, index, '=', COLOR_DEL_INDEX, "Delete Index",
- label, NULL);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_DEL_INDEX,
+ "Delete Index", label, NULL);
}
void packet_open_index(struct timeval *tv, uint16_t index, const char *label)
{
- print_packet(tv, NULL, index, '=', COLOR_OPEN_INDEX, "Open Index",
- label, NULL);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_OPEN_INDEX,
+ "Open Index", label, NULL);
}
void packet_close_index(struct timeval *tv, uint16_t index, const char *label)
{
- print_packet(tv, NULL, index, '=', COLOR_CLOSE_INDEX, "Close Index",
- label, NULL);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_CLOSE_INDEX,
+ "Close Index", label, NULL);
}
void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
@@ -8692,8 +8794,8 @@
sprintf(details, "(%s)", bt_compidtostr(manufacturer));
- print_packet(tv, NULL, index, '=', COLOR_INDEX_INFO, "Index Info",
- label, details);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_INDEX_INFO,
+ "Index Info", label, details);
}
void packet_vendor_diag(struct timeval *tv, uint16_t index,
@@ -8704,7 +8806,7 @@
sprintf(extra_str, "(len %d)", size);
- print_packet(tv, NULL, index, '=', COLOR_VENDOR_DIAG,
+ print_packet(tv, NULL, '=', index, NULL, COLOR_VENDOR_DIAG,
"Vendor Diagnostic", NULL, extra_str);
switch (manufacturer) {
@@ -8720,7 +8822,8 @@
void packet_system_note(struct timeval *tv, struct ucred *cred,
uint16_t index, const void *message)
{
- print_packet(tv, cred, index, '=', COLOR_INFO, "Note", message, NULL);
+ print_packet(tv, cred, '=', index, NULL, COLOR_SYSTEM_NOTE,
+ "Note", message, NULL);
}
void packet_user_logging(struct timeval *tv, struct ucred *cred,
@@ -8780,7 +8883,7 @@
label = "Message";
}
- print_packet(tv, cred, index, '=', color, label, message, NULL);
+ print_packet(tv, cred, '=', index, NULL, color, label, message, NULL);
}
void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
@@ -8798,7 +8901,7 @@
if (size < HCI_COMMAND_HDR_SIZE) {
sprintf(extra_str, "(len %d)", size);
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed HCI Command packet", NULL, extra_str);
packet_hexdump(data, size);
return;
@@ -8856,7 +8959,7 @@
sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen);
- print_packet(tv, cred, index, '<', opcode_color, "HCI Command",
+ print_packet(tv, cred, '<', index, NULL, opcode_color, "HCI Command",
opcode_str, extra_str);
if (!opcode_data || !opcode_data->cmd_func) {
@@ -8899,7 +9002,7 @@
if (size < HCI_EVENT_HDR_SIZE) {
sprintf(extra_str, "(len %d)", size);
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed HCI Event packet", NULL, extra_str);
packet_hexdump(data, size);
return;
@@ -8928,7 +9031,7 @@
sprintf(extra_str, "(0x%2.2x) plen %d", hdr->evt, hdr->plen);
- print_packet(tv, cred, index, '>', event_color, "HCI Event",
+ print_packet(tv, cred, '>', index, NULL, event_color, "HCI Event",
event_str, extra_str);
if (!event_data || !event_data->func) {
@@ -8969,24 +9072,24 @@
uint8_t flags = acl_flags(handle);
char handle_str[16], extra_str[32];
- if (size < sizeof(*hdr)) {
+ if (size < HCI_ACL_HDR_SIZE) {
if (in)
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed ACL Data RX packet", NULL, NULL);
else
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed ACL Data TX packet", NULL, NULL);
packet_hexdump(data, size);
return;
}
- data += sizeof(*hdr);
- size -= sizeof(*hdr);
+ data += HCI_ACL_HDR_SIZE;
+ size -= HCI_ACL_HDR_SIZE;
sprintf(handle_str, "Handle %d", acl_handle(handle));
sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, dlen);
- print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_ACLDATA,
+ print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_ACLDATA,
in ? "ACL Data RX" : "ACL Data TX",
handle_str, extra_str);
@@ -9013,10 +9116,10 @@
if (size < HCI_SCO_HDR_SIZE) {
if (in)
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed SCO Data RX packet", NULL, NULL);
else
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed SCO Data TX packet", NULL, NULL);
packet_hexdump(data, size);
return;
@@ -9028,7 +9131,7 @@
sprintf(handle_str, "Handle %d", acl_handle(handle));
sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen);
- print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_SCODATA,
+ print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_SCODATA,
in ? "SCO Data RX" : "SCO Data TX",
handle_str, extra_str);
@@ -9043,6 +9146,1071 @@
packet_hexdump(data, size);
}
+void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format;
+ char channel[11];
+
+ if (size < 6) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Open packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+ format = get_le16(data + 4);
+
+ data += 6;
+ size -= 6;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ if (format == CTRL_MGMT && size >= 8) {
+ uint8_t version;
+ uint16_t revision;
+ uint32_t flags;
+ uint8_t ident_len;
+ const char *comm;
+ char details[48];
+
+ version = get_u8(data);
+ revision = get_le16(data + 1);
+ flags = get_le32(data + 3);
+ ident_len = get_u8(data + 7);
+
+ data += 8;
+ size -= 8;
+
+ comm = ident_len > 0 ? data : "unknown";
+
+ data += ident_len;
+ size -= ident_len;
+
+ assign_ctrl(cookie, format, comm);
+
+ sprintf(details, "%sversion %u.%u",
+ flags & 0x0001 ? "(privileged) " : "",
+ version, revision);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
+ "MGMT Open", comm, details);
+ } else {
+ char label[7];
+
+ assign_ctrl(cookie, format, NULL);
+
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
+ "Control Open", label, NULL);
+ }
+
+ packet_hexdump(data, size);
+}
+
+void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format;
+ char channel[11], label[22];
+
+ if (size < 4) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Close packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+
+ data += 4;
+ size -= 4;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ release_ctrl(cookie, &format, label);
+
+ if (format == CTRL_MGMT) {
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ "MGMT Close", label, NULL);
+ } else {
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ "Control Close", label, NULL);
+ }
+
+ packet_hexdump(data, size);
+}
+
+static const struct {
+ uint8_t status;
+ const char *str;
+} mgmt_status_table[] = {
+ { 0x00, "Success" },
+ { 0x01, "Unknown Command" },
+ { 0x02, "Not Connected" },
+ { 0x03, "Failed" },
+ { 0x04, "Connect Failed" },
+ { 0x05, "Authentication Failed" },
+ { 0x06, "Not Paired" },
+ { 0x07, "No Resources" },
+ { 0x08, "Timeout" },
+ { 0x09, "Already Connected" },
+ { 0x0a, "Busy" },
+ { 0x0b, "Rejected" },
+ { 0x0c, "Not Supported" },
+ { 0x0d, "Invalid Parameters" },
+ { 0x0e, "Disconnected" },
+ { 0x0f, "Not Powered" },
+ { 0x10, "Cancelled" },
+ { 0x11, "Invalid Index" },
+ { 0x12, "RFKilled" },
+ { 0x13, "Already Paired" },
+ { 0x14, "Permission Denied" },
+ { }
+};
+
+static void mgmt_print_status(uint8_t status)
+{
+ const char *str = "Unknown";
+ const char *color_on, *color_off;
+ bool unknown = true;
+ int i;
+
+ for (i = 0; mgmt_status_table[i].str; i++) {
+ if (mgmt_status_table[i].status == status) {
+ str = mgmt_status_table[i].str;
+ unknown = false;
+ break;
+ }
+ }
+
+ if (use_color()) {
+ if (status) {
+ if (unknown)
+ color_on = COLOR_UNKNOWN_ERROR;
+ else
+ color_on = COLOR_RED;
+ } else
+ color_on = COLOR_GREEN;
+ color_off = COLOR_OFF;
+ } else {
+ color_on = "";
+ color_off = "";
+ }
+
+ print_field("Status: %s%s%s (0x%2.2x)",
+ color_on, str, color_off, status);
+}
+
+static void mgmt_print_address(const uint8_t *address, uint8_t type)
+{
+ switch (type) {
+ case 0x00:
+ print_addr_resolve("BR/EDR Address", address, 0x00, false);
+ break;
+ case 0x01:
+ print_addr_resolve("LE Address", address, 0x00, false);
+ break;
+ case 0x02:
+ print_addr_resolve("LE Address", address, 0x01, false);
+ break;
+ default:
+ print_addr_resolve("Address", address, 0xff, false);
+ break;
+ }
+}
+
+static void mgmt_print_version(uint8_t version)
+{
+ packet_print_version("Version", version, NULL, 0x0000);
+}
+
+static void mgmt_print_manufacturer(uint16_t manufacturer)
+{
+ packet_print_company("Manufacturer", manufacturer);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mgmt_settings_table[] = {
+ { 0, "Powered" },
+ { 1, "Connectable" },
+ { 2, "Fast Connectable" },
+ { 3, "Discoverable" },
+ { 4, "Bondable" },
+ { 5, "Link Security" },
+ { 6, "Secure Simple Pairing" },
+ { 7, "BR/EDR" },
+ { 8, "High Speed" },
+ { 9, "Low Energy" },
+ { 10, "Advertising" },
+ { 11, "Secure Connections" },
+ { 12, "Debug Keys" },
+ { 13, "Privacy" },
+ { 14, "Controller Configuration"},
+ { 15, "Static Address" },
+ { }
+};
+
+static void mgmt_print_settings(const char *label, uint32_t settings)
+{
+ uint32_t mask = settings;
+ int i;
+
+ print_field("%s: 0x%8.8x", label, settings);
+
+ for (i = 0; mgmt_settings_table[i].str; i++) {
+ if (settings & (1 << mgmt_settings_table[i].bit)) {
+ print_field(" %s", mgmt_settings_table[i].str);
+ mask &= ~(1 << mgmt_settings_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_SETTINGS_BIT, " Unknown settings "
+ "(0x%8.8x)", mask);
+}
+
+static void mgmt_print_name(const void *data)
+{
+ print_field("Name: %s", (char *) data);
+ print_field("Short name: %s", (char *) (data + 249));
+}
+
+static void mgmt_print_enable(const char *label, uint8_t enable)
+{
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s: %s (0x%2.2x)", label, str, enable);
+}
+
+static void mgmt_null_cmd(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_read_version_info_rsp(const void *data, uint16_t size)
+{
+ uint8_t version;
+ uint16_t revision;
+
+ version = get_u8(data);
+ revision = get_le16(data + 1);
+
+ print_field("Version: %u.%u", version, revision);
+}
+
+static void mgmt_read_supported_commands_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_commands;
+ uint16_t num_events;
+
+ num_commands = get_le16(data);
+ num_events = get_le16(data + 2);
+
+ print_field("Commands: %u", num_commands);
+ print_field("Events: %u", num_events);
+
+ packet_hexdump(data + 4, size - 4);
+}
+
+static void mgmt_read_index_list_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_controllers;
+ int i;
+
+ num_controllers = get_le16(data);
+
+ print_field("Controllers: %u", num_controllers);
+
+ if (size - 2 != num_controllers * 2) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_controllers; i++) {
+ uint16_t index = get_le16(data + 2 + (i * 2));
+
+ print_field(" hci%u", index);
+ }
+}
+
+static void mgmt_read_controller_info_rsp(const void *data, uint16_t size)
+{
+ uint8_t version;
+ uint16_t manufacturer;
+ uint32_t supported_settings, current_settings;
+
+ version = get_u8(data + 6);
+ manufacturer = get_le16(data + 7);
+ supported_settings = get_le32(data + 9);
+ current_settings = get_le32(data + 13);
+
+ mgmt_print_address(data, 0x00);
+ mgmt_print_version(version);
+ mgmt_print_manufacturer(manufacturer);
+ mgmt_print_settings("Supported settings", supported_settings);
+ mgmt_print_settings("Current settings", current_settings);
+ print_dev_class(data + 17);
+ mgmt_print_name(data + 20);
+}
+
+static void mgmt_set_powered_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Powered", enable);
+}
+
+static void mgmt_set_discoverable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ uint16_t timeout = get_le16(data + 1);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "General";
+ break;
+ case 0x02:
+ str = "Limited";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Discoverable: %s (0x%2.2x)", str, enable);
+ print_field("Timeout: %u", timeout);
+}
+
+static void mgmt_set_connectable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Connectable", enable);
+}
+
+static void mgmt_set_fast_connectable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Fast Connectable", enable);
+}
+
+static void mgmt_set_bondable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Bondable", enable);
+}
+
+static void mgmt_set_link_security_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Link Security", enable);
+}
+
+static void mgmt_set_secure_simple_pairing_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Secure Simple Pairing", enable);
+}
+
+static void mgmt_set_high_speed_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("High Speed", enable);
+}
+
+static void mgmt_set_low_energy_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Low Energy", enable);
+}
+
+static void mgmt_new_settings_rsp(const void *data, uint16_t size)
+{
+ uint32_t settings = get_le32(data);
+
+ mgmt_print_settings("Settings", settings);
+}
+
+static void mgmt_set_device_class_cmd(const void *data, uint16_t size)
+{
+ uint8_t major = get_u8(data);
+ uint8_t minor = get_u8(data + 1);
+
+ print_field("Major class: 0x%2.2x", major);
+ print_field("Minor class: 0x%2.2x", minor);
+}
+
+static void mgmt_set_device_class_rsp(const void *data, uint16_t size)
+{
+ print_dev_class(data);
+}
+
+static void mgmt_set_local_name_cmd(const void *data, uint16_t size)
+{
+ mgmt_print_name(data);
+}
+
+static void mgmt_set_local_name_rsp(const void *data, uint16_t size)
+{
+ mgmt_print_name(data);
+}
+
+static void mgmt_set_advertising_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Connectable";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Advertising: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_bredr_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("BR/EDR", enable);
+}
+
+static void mgmt_set_secure_connections_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Only";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Secure Connections: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_debug_keys_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Generate";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Debug Keys: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_privacy_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Limited";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Privacy: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_read_unconf_index_list_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_controllers;
+ int i;
+
+ num_controllers = get_le16(data);
+
+ print_field("Controllers: %u", num_controllers);
+
+ if (size - 2 != num_controllers * 2) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_controllers; i++) {
+ uint16_t index = get_le16(data + 2 + (i * 2));
+
+ print_field(" hci%u", index);
+ }
+}
+
+static void mgmt_read_ext_index_list_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_controllers;
+ int i;
+
+ num_controllers = get_le16(data);
+
+ print_field("Controllers: %u", num_controllers);
+
+ if (size - 2 != num_controllers * 4) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_controllers; i++) {
+ uint16_t index = get_le16(data + 2 + (i * 4));
+ uint16_t type = get_u8(data + 4 + (i * 4));
+ uint16_t bus = get_u8(data + 5 + (i * 4));
+
+ print_field(" hci%u - type 0x%2.2x - bus 0x%2.2x",
+ index, type, bus);
+ }
+}
+
+struct mgmt_data {
+ uint16_t opcode;
+ const char *str;
+ void (*func) (const void *data, uint16_t size);
+ uint16_t size;
+ bool fixed;
+ void (*rsp_func) (const void *data, uint16_t size);
+ uint16_t rsp_size;
+ bool rsp_fixed;
+};
+
+static const struct mgmt_data mgmt_command_table[] = {
+ { 0x0001, "Read Management Version Information",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_version_info_rsp, 3, true },
+ { 0x0002, "Read Management Supported Commands",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_supported_commands_rsp, 4, false },
+ { 0x0003, "Read Controller Index List",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_index_list_rsp, 2, false },
+ { 0x0004, "Read Controller Information",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_controller_info_rsp, 280, true },
+ { 0x0005, "Set Powered",
+ mgmt_set_powered_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0006, "Set Discoverable",
+ mgmt_set_discoverable_cmd, 3, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0007, "Set Connectable",
+ mgmt_set_connectable_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0008, "Set Fast Connectable",
+ mgmt_set_fast_connectable_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0009, "Set Bondable",
+ mgmt_set_bondable_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000a, "Set Link Security",
+ mgmt_set_link_security_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000b, "Set Secure Simple Pairing",
+ mgmt_set_secure_simple_pairing_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000c, "Set High Speed",
+ mgmt_set_high_speed_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000d, "Set Low Energy",
+ mgmt_set_low_energy_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000e, "Set Device Class",
+ mgmt_set_device_class_cmd, 2, true,
+ mgmt_set_device_class_rsp, 3, true },
+ { 0x000f, "Set Local Name",
+ mgmt_set_local_name_cmd, 260, true,
+ mgmt_set_local_name_rsp, 260, true },
+ { 0x0010, "Add UUID" },
+ { 0x0011, "Remove UUID" },
+ { 0x0012, "Load Link Keys" },
+ { 0x0013, "Load Long Term Keys" },
+ { 0x0014, "Disconnect" },
+ { 0x0015, "Get Connections",
+ mgmt_null_cmd, 0, true },
+ { 0x0016, "PIN Code Reply" },
+ { 0x0017, "PIN Code Negative Reply" },
+ { 0x0018, "Set IO Capability" },
+ { 0x0019, "Pair Device" },
+ { 0x001a, "Cancel Pair Device" },
+ { 0x001b, "Unpair Device" },
+ { 0x001c, "User Confirmation Reply" },
+ { 0x001d, "User Confirmation Negative Reply" },
+ { 0x001e, "User Passkey Reply" },
+ { 0x001f, "User Passkey Negative Reply" },
+ { 0x0020, "Read Local Out Of Band Data",
+ mgmt_null_cmd, 0, true },
+ { 0x0021, "Add Remote Out Of Band Data" },
+ { 0x0022, "Remove Remote Out Of Band Data" },
+ { 0x0023, "Start Discovery" },
+ { 0x0024, "Stop Discovery" },
+ { 0x0025, "Confirm Name" },
+ { 0x0026, "Block Device" },
+ { 0x0027, "Unblock Device" },
+ { 0x0028, "Set Device ID" },
+ { 0x0029, "Set Advertising",
+ mgmt_set_advertising_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002a, "Set BR/EDR",
+ mgmt_set_bredr_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002b, "Set Static Address" },
+ { 0x002c, "Set Scan Parameters" },
+ { 0x002d, "Set Secure Connections",
+ mgmt_set_secure_connections_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002e, "Set Debug Keys",
+ mgmt_set_debug_keys_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002f, "Set Privacy",
+ mgmt_set_privacy_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0030, "Load Identity Resolving Keys" },
+ { 0x0031, "Get Connection Information" },
+ { 0x0032, "Get Clock Information" },
+ { 0x0033, "Add Device" },
+ { 0x0034, "Remove Device" },
+ { 0x0035, "Load Connection Parameters" },
+ { 0x0036, "Read Unconfigured Controller Index List",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_unconf_index_list_rsp, 2, false },
+ { 0x0037, "Read Controller Configuration Information",
+ mgmt_null_cmd, 0, true },
+ { 0x0038, "Set External Configuration" },
+ { 0x0039, "Set Public Address" },
+ { 0x003a, "Start Service Discovery" },
+ { 0x003b, "Read Local Out Of Band Extended Data" },
+ { 0x003c, "Read Extended Controller Index List",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_ext_index_list_rsp, 2, false },
+ { 0x003d, "Read Advertising Features",
+ mgmt_null_cmd, 0, true },
+ { 0x003e, "Add Advertising " },
+ { 0x003f, "Remove Advertising" },
+ { 0x0040, "Get Advertising Size Information" },
+ { 0x0041, "Start Limited Discovery" },
+ { }
+};
+
+static void mgmt_null_evt(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_command_complete_evt(const void *data, uint16_t size)
+{
+ uint16_t opcode;
+ uint8_t status;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ int i;
+
+ opcode = get_le16(data);
+ status = get_u8(data + 2);
+
+ data += 3;
+ size -= 3;
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_command_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ mgmt_color = COLOR_CTRL_COMMAND;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF,
+ " (0x%4.4x) plen %u", opcode, size);
+
+ mgmt_print_status(status);
+
+ if (status || !mgmt_data || !mgmt_data->rsp_func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (mgmt_data->rsp_fixed) {
+ if (size != mgmt_data->rsp_size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < mgmt_data->rsp_size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ mgmt_data->rsp_func(data, size);
+}
+
+static void mgmt_command_status_evt(const void *data, uint16_t size)
+{
+ uint16_t opcode;
+ uint8_t status;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ int i;
+
+ opcode = get_le16(data);
+ status = get_u8(data + 2);
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_command_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ mgmt_color = COLOR_CTRL_COMMAND;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF,
+ " (0x%4.4x)", opcode);
+
+ mgmt_print_status(status);
+}
+
+static void mgmt_controller_error_evt(const void *data, uint16_t size)
+{
+ uint8_t error = get_u8(data);
+
+ print_field("Error: 0x%2.2x", error);
+}
+
+static void mgmt_new_settings_evt(const void *data, uint16_t size)
+{
+ uint32_t settings = get_le32(data);
+
+ mgmt_print_settings("Settings", settings);
+}
+
+static void mgmt_class_of_dev_changed_evt(const void *data, uint16_t size)
+{
+ print_dev_class(data);
+}
+
+static void mgmt_local_name_changed_evt(const void *data, uint16_t size)
+{
+ mgmt_print_name(data);
+}
+
+static const struct mgmt_data mgmt_event_table[] = {
+ { 0x0001, "Command Complete",
+ mgmt_command_complete_evt, 3, false },
+ { 0x0002, "Command Status",
+ mgmt_command_status_evt, 3, true },
+ { 0x0003, "Controller Error",
+ mgmt_controller_error_evt, 1, true },
+ { 0x0004, "Index Added",
+ mgmt_null_evt, 0, true },
+ { 0x0005, "Index Removed",
+ mgmt_null_evt, 0, true },
+ { 0x0006, "New Settings",
+ mgmt_new_settings_evt, 4, true },
+ { 0x0007, "Class Of Device Changed",
+ mgmt_class_of_dev_changed_evt, 3, true },
+ { 0x0008, "Local Name Changed",
+ mgmt_local_name_changed_evt, 260, true },
+ { 0x0009, "New Link Key" },
+ { 0x000a, "New Long Term Key" },
+ { 0x000b, "Device Connected" },
+ { 0x000c, "Device Disconnected" },
+ { 0x000d, "Connect Failed" },
+ { 0x000e, "PIN Code Request" },
+ { 0x000f, "User Confirmation Request" },
+ { 0x0010, "User Passkey Request" },
+ { 0x0011, "Authentication Failed" },
+ { 0x0012, "Device Found" },
+ { 0x0013, "Discovering" },
+ { 0x0014, "Device Blocked" },
+ { 0x0015, "Device Unblocked" },
+ { 0x0016, "Device Unpaired" },
+ { 0x0017, "Passkey Notify" },
+ { 0x0018, "New Identity Resolving Key" },
+ { 0x0019, "New Signature Resolving Key" },
+ { 0x001a, "Device Added" },
+ { 0x001b, "Device Removed" },
+ { 0x001c, "New Connection Parameter" },
+ { 0x001d, "Unconfigured Index Added",
+ mgmt_null_evt, 0, true },
+ { 0x001e, "Unconfigured Index Removed",
+ mgmt_null_evt, 0, true },
+ { 0x001f, "New Configuration Options" },
+ { 0x0020, "Extended Index Added" },
+ { 0x0021, "Extended Index Removed" },
+ { 0x0022, "Local Out Of Band Extended Data Updated" },
+ { 0x0023, "Advertising Added" },
+ { 0x0024, "Advertising Removed" },
+ { }
+};
+
+void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format, opcode;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ char channel[11], extra_str[25];
+ int i;
+
+ if (size < 4) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Command packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+
+ data += 4;
+ size -= 4;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ format = get_format(cookie);
+
+ if (format != CTRL_MGMT) {
+ char label[7];
+
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ "Control Command", label, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (size < 2) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed MGMT Command packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ opcode = get_le16(data);
+
+ data += 2;
+ size -= 2;
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_command_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ if (mgmt_data->func)
+ mgmt_color = COLOR_CTRL_COMMAND;
+ else
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size);
+
+ print_packet(tv, cred, '@', index, channel, mgmt_color,
+ "MGMT Command", mgmt_str, extra_str);
+
+ if (!mgmt_data || !mgmt_data->func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (mgmt_data->fixed) {
+ if (size != mgmt_data->size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < mgmt_data->size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ mgmt_data->func(data, size);
+}
+
+void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format, opcode;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ char channel[11], extra_str[25];
+ int i;
+
+ if (size < 4) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Event packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+
+ data += 4;
+ size -= 4;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ format = get_format(cookie);
+
+ if (format != CTRL_MGMT) {
+ char label[7];
+
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ "Control Event", label, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (size < 2) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed MGMT Event packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ opcode = get_le16(data);
+
+ data += 2;
+ size -= 2;
+
+ for (i = 0; mgmt_event_table[i].str; i++) {
+ if (mgmt_event_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_event_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ if (mgmt_data->func)
+ mgmt_color = COLOR_CTRL_EVENT;
+ else
+ mgmt_color = COLOR_CTRL_EVENT_UNKNOWN;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_EVENT_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size);
+
+ print_packet(tv, cred, '@', index, channel, mgmt_color,
+ "MGMT Event", mgmt_str, extra_str);
+
+ if (!mgmt_data || !mgmt_data->func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (mgmt_data->fixed) {
+ if (size != mgmt_data->size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < mgmt_data->size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ mgmt_data->func(data, size);
+}
+
void packet_todo(void)
{
int i;
@@ -9074,4 +10242,22 @@
printf("\t%s\n", le_meta_event_table[i].str);
}
+
+ printf("MGMT commands with missing decodings:\n");
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].func)
+ continue;
+
+ printf("\t%s\n", mgmt_command_table[i].str);
+ }
+
+ printf("MGMT events with missing decodings:\n");
+
+ for (i = 0; mgmt_event_table[i].str; i++) {
+ if (mgmt_event_table[i].func)
+ continue;
+
+ printf("\t%s\n", mgmt_event_table[i].str);
+ }
}
diff --git a/monitor/packet.h b/monitor/packet.h
index 322f101..354f4fe 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -89,4 +89,13 @@
void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
bool in, const void *data, uint16_t size);
+void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+
void packet_todo(void);