diff --git a/share/man/man4/bluetooth.4 b/share/man/man4/bluetooth.4 index 4c19dc5f490a..ae6e29ab53d0 100644 --- a/share/man/man4/bluetooth.4 +++ b/share/man/man4/bluetooth.4 @@ -1,4 +1,4 @@ -.\" $NetBSD: bluetooth.4,v 1.4 2006/09/02 23:52:45 wiz Exp $ +.\" $NetBSD: bluetooth.4,v 1.5 2007/04/21 06:15:22 plunky Exp $ .\" .\" Copyright (c) 2006 Itronix Inc. .\" All rights reserved. @@ -30,7 +30,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .\" -.Dd August 27, 2006 +.Dd April 10, 2007 .Dt BLUETOOTH 4 .Os .Sh NAME @@ -142,6 +142,30 @@ L2CAP socket options: Incoming MTU .It Dv SO_L2CAP_OMTU Op Ar uint16_t Outgoing MTU (read-only) +.It Dv SO_L2CAP_LM Op Ar int +Link Mode. +The following bits may be set: +.Pp +.Bl -tag -compact -width ".Dv L2CAP_LM_ENCRYPT" +.It Dv L2CAP_LM_AUTH +Request authentication +.Pq pairing . +.It Dv L2CAP_LM_ENCRYPT +Request encryption +.Pq includes auth . +.It Dv L2CAP_LM_SECURE +Request secured link +.Pq encryption, plus change link key . +.El +.Pp +Link mode settings will be applied to the baseband link during L2CAP +connection establishment. +If the L2CAP connection is already established, +.Dv EINPROGRESS +may be returned, and it is not possible to guarantee that data already queued +.Pq from either end +will not be delivered. +If the mode change fails, the L2CAP connection will be aborted. .El .Pp L2CAP @@ -174,6 +198,30 @@ RFCOMM socket options: .Bl -tag -width XXX .It Dv SO_RFCOMM_MTU Op Ar uint16_t Maximum Frame Size to use for this link. +.It Dv SO_RFCOMM_LM Op Ar int +Link Mode. +The following bits may be set at any time: +.Pp +.Bl -tag -compact -width ".Dv RFCOMM_LM_ENCRYPT" +.It Dv RFCOMM_LM_AUTH +Request authentication +.Pq pairing . +.It Dv RFCOMM_LM_ENCRYPT +Request encryption +.Pq includes auth . +.It Dv RFCOMM_LM_SECURE +Request secured link +.Pq encryption, plus change link key . +.El +.Pp +Link mode settings will be applied to the baseband link during RFCOMM +connection establishment. +If the RFCOMM connection is already established, +.Dv EINPROGRESS +may be returned, and it is not possible to guarantee that data already queued +.Pq from either end +will not be delivered. +If the mode change fails, the RFCOMM connection will be aborted. .El .Pp RFCOMM diff --git a/share/man/man4/bthidev.4 b/share/man/man4/bthidev.4 index 92dacfed0ec4..1434500f3e16 100644 --- a/share/man/man4/bthidev.4 +++ b/share/man/man4/bthidev.4 @@ -1,4 +1,4 @@ -.\" $NetBSD: bthidev.4,v 1.7 2006/10/04 19:29:25 wiz Exp $ +.\" $NetBSD: bthidev.4,v 1.8 2007/04/21 06:15:22 plunky Exp $ .\" .\" Copyright (c) 2006 Itronix Inc. .\" All rights reserved. @@ -29,7 +29,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd October 4, 2006 +.Dd April 10, 2007 .Dt BTHIDEV 4 .Os .Sh NAME @@ -88,6 +88,13 @@ If this boolean value is set, and is true, then the .Nm driver will initiate reconnections to the remote device when no connection is present. +.It link-mode +This optional string represents the link mode of the baseband link, and +may be one of +.Sq auth , +.Sq encrypt +or +.Sq secure . .El .Pp When the diff --git a/share/man/man9/bluetooth.9 b/share/man/man9/bluetooth.9 index cc1b71e9836a..e98eae7652d7 100644 --- a/share/man/man9/bluetooth.9 +++ b/share/man/man9/bluetooth.9 @@ -1,4 +1,4 @@ -.\" $NetBSD: bluetooth.9,v 1.1 2006/06/19 15:44:44 gdamore Exp $ +.\" $NetBSD: bluetooth.9,v 1.2 2007/04/21 06:15:22 plunky Exp $ .\" .\" Copyright (c) 2006 Itronix Inc. .\" All rights reserved. @@ -30,7 +30,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .\" -.Dd March 4, 2006 +.Dd April 10, 2007 .Dt BLUETOOTH 9 .Os .Sh NAME @@ -143,6 +143,7 @@ struct btproto { void (*disconnected)(void *, int); void *(*newconn)(void *, struct sockaddr_bt *, struct sockaddr_bt *); void (*complete)(void *, int); + void (*linkmode)(void *, int); void (*input)(void *, struct mbuf *); }; .Ed @@ -347,6 +348,11 @@ amount of data can be removed from the socket buffer. The units of the .Ar count value will be dependent on the protocol being used (eg RFCOMM is bytes, but L2CAP is packets) +.It Fn "(*linkmode)" "ref" "mode" +This function will be called for established connections, when the link mode +of the baseband link has changed. +.Ar mode +is the new mode. .It Fn "(*input)" "ref" "mbuf" This function is called to supply new data on the connection described by .Ar ref . diff --git a/sys/dev/bluetooth/btdev.h b/sys/dev/bluetooth/btdev.h index c0b6bdac4f4d..bd9e0551db20 100644 --- a/sys/dev/bluetooth/btdev.h +++ b/sys/dev/bluetooth/btdev.h @@ -1,4 +1,4 @@ -/* $NetBSD: btdev.h,v 1.5 2006/10/04 19:23:59 plunky Exp $ */ +/* $NetBSD: btdev.h,v 1.6 2007/04/21 06:15:22 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -43,6 +43,10 @@ #define BTDEVladdr "local-bdaddr" #define BTDEVraddr "remote-bdaddr" #define BTDEVservice "service-name" +#define BTDEVmode "link-mode" +#define BTDEVauth "auth" +#define BTDEVencrypt "encrypt" +#define BTDEVsecure "secure" #ifdef _KERNEL struct btdev { diff --git a/sys/dev/bluetooth/bthidev.c b/sys/dev/bluetooth/bthidev.c index 2d1f6358026f..85fdef68e68e 100644 --- a/sys/dev/bluetooth/bthidev.c +++ b/sys/dev/bluetooth/bthidev.c @@ -1,4 +1,4 @@ -/* $NetBSD: bthidev.c,v 1.7 2006/11/16 01:32:48 christos Exp $ */ +/* $NetBSD: bthidev.c,v 1.8 2007/04/21 06:15:22 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: bthidev.c,v 1.7 2006/11/16 01:32:48 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bthidev.c,v 1.8 2007/04/21 06:15:22 plunky Exp $"); #include #include @@ -72,6 +72,7 @@ struct bthidev_softc { bdaddr_t sc_laddr; /* local address */ bdaddr_t sc_raddr; /* remote address */ + int sc_mode; /* link mode */ uint16_t sc_ctlpsm; /* control PSM */ struct l2cap_channel *sc_ctl; /* control channel */ @@ -125,6 +126,7 @@ static void bthidev_int_disconnected(void *, int); static void *bthidev_ctl_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void *bthidev_int_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void bthidev_complete(void *, int); +static void bthidev_linkmode(void *, int); static void bthidev_input(void *, struct mbuf *); static const struct btproto bthidev_ctl_proto = { @@ -133,6 +135,7 @@ static const struct btproto bthidev_ctl_proto = { bthidev_ctl_disconnected, bthidev_ctl_newconn, bthidev_complete, + bthidev_linkmode, bthidev_input, }; @@ -142,6 +145,7 @@ static const struct btproto bthidev_int_proto = { bthidev_int_disconnected, bthidev_int_newconn, bthidev_complete, + bthidev_linkmode, bthidev_input, }; @@ -198,6 +202,23 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) obj = prop_dictionary_get(dict, BTDEVraddr); bdaddr_copy(&sc->sc_raddr, prop_data_data_nocopy(obj)); + obj = prop_dictionary_get(dict, BTDEVmode); + if (prop_object_type(obj) == PROP_TYPE_STRING) { + if (prop_string_equals_cstring(obj, BTDEVauth)) + sc->sc_mode = L2CAP_LM_AUTH; + else if (prop_string_equals_cstring(obj, BTDEVencrypt)) + sc->sc_mode = L2CAP_LM_ENCRYPT; + else if (prop_string_equals_cstring(obj, BTDEVsecure)) + sc->sc_mode = L2CAP_LM_SECURE; + else { + aprint_error(" unknown %s\n", BTDEVmode); + return; + } + + aprint_verbose(" %s %s", BTDEVmode, + prop_string_cstring_nocopy(obj)); + } + obj = prop_dictionary_get(dict, BTHIDEVcontrolpsm); if (prop_object_type(obj) == PROP_TYPE_NUMBER) { sc->sc_ctlpsm = prop_number_integer_value(obj); @@ -435,6 +456,10 @@ bthidev_listen(struct bthidev_softc *sc) if (err) return err; + err = l2cap_setopt(sc->sc_ctl_l, SO_L2CAP_LM, &sc->sc_mode); + if (err) + return err; + sa.bt_psm = sc->sc_ctlpsm; err = l2cap_bind(sc->sc_ctl_l, &sa); if (err) @@ -451,6 +476,10 @@ bthidev_listen(struct bthidev_softc *sc) if (err) return err; + err = l2cap_setopt(sc->sc_int_l, SO_L2CAP_LM, &sc->sc_mode); + if (err) + return err; + sa.bt_psm = sc->sc_intpsm; err = l2cap_bind(sc->sc_int_l, &sa); if (err) @@ -488,6 +517,10 @@ bthidev_connect(struct bthidev_softc *sc) return err; } + err = l2cap_setopt(sc->sc_ctl, SO_L2CAP_LM, &sc->sc_mode); + if (err) + return err; + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); err = l2cap_bind(sc->sc_ctl, &sa); if (err) { @@ -543,6 +576,10 @@ bthidev_ctl_connected(void *arg) if (err) goto fail; + err = l2cap_setopt(sc->sc_int, SO_L2CAP_LM, &sc->sc_mode); + if (err) + goto fail; + memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; @@ -707,6 +744,27 @@ bthidev_complete(void *arg, int count) /* dont care */ } +static void +bthidev_linkmode(void *arg, int new) +{ + struct bthidev_softc *sc = arg; + + if ((sc->sc_mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH)) + printf("%s: auth failed\n", device_xname((struct device *)sc)); + else if ((sc->sc_mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT)) + printf("%s: encrypt off\n", device_xname((struct device *)sc)); + else if ((sc->sc_mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE)) + printf("%s: insecure\n", device_xname((struct device *)sc)); + else + return; + + if (sc->sc_int != NULL) + l2cap_disconnect(sc->sc_int, 0); + + if (sc->sc_ctl != NULL) + l2cap_disconnect(sc->sc_ctl, 0); +} + /* * Receive reports from the protocol stack. */ diff --git a/sys/dev/bluetooth/btsco.c b/sys/dev/bluetooth/btsco.c index d82173197c29..95d63cfee0fd 100644 --- a/sys/dev/bluetooth/btsco.c +++ b/sys/dev/bluetooth/btsco.c @@ -1,4 +1,4 @@ -/* $NetBSD: btsco.c,v 1.13 2007/03/13 19:26:06 plunky Exp $ */ +/* $NetBSD: btsco.c,v 1.14 2007/04/21 06:15:22 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: btsco.c,v 1.13 2007/03/13 19:26:06 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: btsco.c,v 1.14 2007/04/21 06:15:22 plunky Exp $"); #include #include @@ -215,6 +215,7 @@ static void btsco_sco_connected(void *); static void btsco_sco_disconnected(void *, int); static void *btsco_sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void btsco_sco_complete(void *, int); +static void btsco_sco_linkmode(void *, int); static void btsco_sco_input(void *, struct mbuf *); static const struct btproto btsco_sco_proto = { @@ -223,6 +224,7 @@ static const struct btproto btsco_sco_proto = { btsco_sco_disconnected, btsco_sco_newconn, btsco_sco_complete, + btsco_sco_linkmode, btsco_sco_input, }; @@ -501,6 +503,14 @@ btsco_sco_complete(void *arg, int count) splx(s); } +static void +btsco_sco_linkmode(void *arg, int new) +{ +/* struct btsco_softc *sc = arg; */ + + /* dont care */ +} + static void btsco_sco_input(void *arg, struct mbuf *m) { diff --git a/sys/netbt/bluetooth.h b/sys/netbt/bluetooth.h index 28533531a667..5026ac2fdb5e 100644 --- a/sys/netbt/bluetooth.h +++ b/sys/netbt/bluetooth.h @@ -1,4 +1,4 @@ -/* $NetBSD: bluetooth.h,v 1.4 2006/11/16 01:33:44 christos Exp $ */ +/* $NetBSD: bluetooth.h,v 1.5 2007/04/21 06:15:22 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -115,6 +115,7 @@ struct btproto { void (*disconnected)(void *, int); void *(*newconn)(void *, struct sockaddr_bt *, struct sockaddr_bt *); void (*complete)(void *, int); + void (*linkmode)(void *, int); void (*input)(void *, struct mbuf *); }; diff --git a/sys/netbt/hci.h b/sys/netbt/hci.h index b94bd1b6d127..693985ac4bdc 100644 --- a/sys/netbt/hci.h +++ b/sys/netbt/hci.h @@ -1,4 +1,4 @@ -/* $NetBSD: hci.h,v 1.9 2007/02/20 16:53:21 kiyohara Exp $ */ +/* $NetBSD: hci.h,v 1.10 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -54,7 +54,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: hci.h,v 1.9 2007/02/20 16:53:21 kiyohara Exp $ + * $Id: hci.h,v 1.10 2007/04/21 06:15:23 plunky Exp $ * $FreeBSD: src/sys/netgraph/bluetooth/include/ng_hci.h,v 1.6 2005/01/07 01:45:43 imp Exp $ */ @@ -2044,6 +2044,7 @@ struct hci_link { /* common info */ uint16_t hl_state; /* connection state */ + uint16_t hl_flags; /* link flags */ bdaddr_t hl_bdaddr; /* dest address */ uint16_t hl_handle; /* connection handle */ uint8_t hl_type; /* link type */ @@ -2066,11 +2067,22 @@ struct hci_link { MBUFQ_HEAD() hl_data; /* SCO outgoing data */ }; -/* hci_link state bits */ +/* hci_link state */ #define HCI_LINK_CLOSED 0 /* closed */ #define HCI_LINK_WAIT_CONNECT 1 /* waiting to connect */ -#define HCI_LINK_OPEN 2 /* ready and willing */ -#define HCI_LINK_BLOCK 3 /* open but blocking (see hci_acl_start) */ +#define HCI_LINK_WAIT_AUTH 2 /* waiting for auth */ +#define HCI_LINK_WAIT_ENCRYPT 3 /* waiting for encrypt */ +#define HCI_LINK_WAIT_SECURE 4 /* waiting for secure */ +#define HCI_LINK_OPEN 5 /* ready and willing */ +#define HCI_LINK_BLOCK 6 /* open but blocking (see hci_acl_start) */ + +/* hci_link flags */ +#define HCI_LINK_AUTH_REQ (1<<0) /* authentication requested */ +#define HCI_LINK_ENCRYPT_REQ (1<<1) /* encryption requested */ +#define HCI_LINK_SECURE_REQ (1<<2) /* secure link requested */ +#define HCI_LINK_AUTH (1<<3) /* link is authenticated */ +#define HCI_LINK_ENCRYPT (1<<4) /* link is encrypted */ +#define HCI_LINK_SECURE (1<<5) /* link is secured */ /* * Bluetooth Memo @@ -2166,6 +2178,8 @@ struct hci_link *hci_acl_open(struct hci_unit *, bdaddr_t *); struct hci_link *hci_acl_newconn(struct hci_unit *, bdaddr_t *); void hci_acl_close(struct hci_link *, int); void hci_acl_timeout(void *); +int hci_acl_setmode(struct hci_link *); +void hci_acl_linkmode(struct hci_link *); void hci_acl_recv(struct mbuf *, struct hci_unit *); int hci_acl_send(struct mbuf *, struct hci_link *, struct l2cap_channel *); void hci_acl_start(struct hci_link *); diff --git a/sys/netbt/hci_event.c b/sys/netbt/hci_event.c index c40774a18aff..8ad670e0d0a4 100644 --- a/sys/netbt/hci_event.c +++ b/sys/netbt/hci_event.c @@ -1,4 +1,4 @@ -/* $NetBSD: hci_event.c,v 1.5 2007/04/05 19:22:18 plunky Exp $ */ +/* $NetBSD: hci_event.c,v 1.6 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: hci_event.c,v 1.5 2007/04/05 19:22:18 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: hci_event.c,v 1.6 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -51,6 +51,9 @@ static void hci_event_con_compl(struct hci_unit *, struct mbuf *); static void hci_event_discon_compl(struct hci_unit *, struct mbuf *); static void hci_event_con_req(struct hci_unit *, struct mbuf *); static void hci_event_num_compl_pkts(struct hci_unit *, struct mbuf *); +static void hci_event_auth_compl(struct hci_unit *, struct mbuf *); +static void hci_event_encryption_change(struct hci_unit *, struct mbuf *); +static void hci_event_change_con_link_key_compl(struct hci_unit *, struct mbuf *); static void hci_cmd_read_bdaddr(struct hci_unit *, struct mbuf *); static void hci_cmd_read_buffer_size(struct hci_unit *, struct mbuf *); static void hci_cmd_read_local_features(struct hci_unit *, struct mbuf *); @@ -174,12 +177,21 @@ hci_event(struct mbuf *m, struct hci_unit *unit) hci_event_con_req(unit, m); break; + case HCI_EVENT_AUTH_COMPL: + hci_event_auth_compl(unit, m); + break; + + case HCI_EVENT_ENCRYPTION_CHANGE: + hci_event_encryption_change(unit, m); + break; + + case HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL: + hci_event_change_con_link_key_compl(unit, m); + break; + case HCI_EVENT_SCO_CON_COMPL: case HCI_EVENT_INQUIRY_COMPL: - case HCI_EVENT_AUTH_COMPL: case HCI_EVENT_REMOTE_NAME_REQ_COMPL: - case HCI_EVENT_ENCRYPTION_CHANGE: - case HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL: case HCI_EVENT_MASTER_LINK_KEY_COMPL: case HCI_EVENT_READ_REMOTE_FEATURES_COMPL: case HCI_EVENT_READ_REMOTE_VER_INFO_COMPL: @@ -231,9 +243,10 @@ hci_event_command_status(struct hci_unit *unit, struct mbuf *m) m_copydata(m, 0, sizeof(ep), &ep); m_adj(m, sizeof(ep)); - DPRINTFN(1, "(%s) opcode (%03x|%04x) num_cmd_pkts = %d\n", + DPRINTFN(1, "(%s) opcode (%03x|%04x) status = 0x%x num_cmd_pkts = %d\n", unit->hci_devname, HCI_OGF(le16toh(ep.opcode)), HCI_OCF(le16toh(ep.opcode)), + ep.status, ep.num_cmd_pkts); unit->hci_num_cmd_pkts = ep.num_cmd_pkts; @@ -493,6 +506,11 @@ hci_event_con_compl(struct hci_unit *unit, struct mbuf *m) return; } + /* XXX could check auth_enable here */ + + if (ep.encryption_mode) + link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT); + link->hl_state = HCI_LINK_OPEN; link->hl_handle = HCI_CON_HANDLE(le16toh(ep.con_handle)); @@ -505,7 +523,11 @@ hci_event_con_compl(struct hci_unit *unit, struct mbuf *m) printf("%s: Warning, could not write link policy\n", unit->hci_devname); - hci_acl_start(link); + err = hci_acl_setmode(link); + if (err == EINPROGRESS) + return; + + hci_acl_linkmode(link); } else { (*link->hl_sco->sp_proto->connected)(link->hl_sco->sp_upper); } @@ -584,6 +606,133 @@ hci_event_con_req(struct hci_unit *unit, struct mbuf *m) } } +/* + * Auth Complete + * + * Authentication has been completed on an ACL link. We can notify the + * upper layer protocols unless further mode changes are pending. + */ +static void +hci_event_auth_compl(struct hci_unit *unit, struct mbuf *m) +{ + hci_auth_compl_ep ep; + struct hci_link *link; + int err; + + KASSERT(m->m_pkthdr.len >= sizeof(ep)); + m_copydata(m, 0, sizeof(ep), &ep); + m_adj(m, sizeof(ep)); + + ep.con_handle = HCI_CON_HANDLE(le16toh(ep.con_handle)); + + DPRINTFN(1, "handle #%d, status=0x%x\n", ep.con_handle, ep.status); + + link = hci_link_lookup_handle(unit, ep.con_handle); + if (link == NULL || link->hl_type != HCI_LINK_ACL) + return; + + if (ep.status == 0) { + link->hl_flags |= HCI_LINK_AUTH; + + if (link->hl_state == HCI_LINK_WAIT_AUTH) + link->hl_state = HCI_LINK_OPEN; + + err = hci_acl_setmode(link); + if (err == EINPROGRESS) + return; + } + + hci_acl_linkmode(link); +} + +/* + * Encryption Change + * + * The encryption status has changed. Basically, we note the change + * then notify the upper layer protocol unless further mode changes + * are pending. + * Note that if encryption gets disabled when it has been requested, + * we will attempt to enable it again.. (its a feature not a bug :) + */ +static void +hci_event_encryption_change(struct hci_unit *unit, struct mbuf *m) +{ + hci_encryption_change_ep ep; + struct hci_link *link; + int err; + + KASSERT(m->m_pkthdr.len >= sizeof(ep)); + m_copydata(m, 0, sizeof(ep), &ep); + m_adj(m, sizeof(ep)); + + ep.con_handle = HCI_CON_HANDLE(le16toh(ep.con_handle)); + + DPRINTFN(1, "handle #%d, status=0x%x, encryption_enable=0x%x\n", + ep.con_handle, ep.status, ep.encryption_enable); + + link = hci_link_lookup_handle(unit, ep.con_handle); + if (link == NULL || link->hl_type != HCI_LINK_ACL) + return; + + if (ep.status == 0) { + if (ep.encryption_enable == 0) + link->hl_flags &= ~HCI_LINK_ENCRYPT; + else + link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_ENCRYPT); + + if (link->hl_state == HCI_LINK_WAIT_ENCRYPT) + link->hl_state = HCI_LINK_OPEN; + + err = hci_acl_setmode(link); + if (err == EINPROGRESS) + return; + } + + hci_acl_linkmode(link); +} + +/* + * Change Connection Link Key Complete + * + * Link keys are handled in userland but if we are waiting to secure + * this link, we should notify the upper protocols. A SECURE request + * only needs a single key change, so we can cancel the request. + */ +static void +hci_event_change_con_link_key_compl(struct hci_unit *unit, struct mbuf *m) +{ + hci_change_con_link_key_compl_ep ep; + struct hci_link *link; + int err; + + KASSERT(m->m_pkthdr.len >= sizeof(ep)); + m_copydata(m, 0, sizeof(ep), &ep); + m_adj(m, sizeof(ep)); + + ep.con_handle = HCI_CON_HANDLE(le16toh(ep.con_handle)); + + DPRINTFN(1, "handle #%d, status=0x%x\n", ep.con_handle, ep.status); + + link = hci_link_lookup_handle(unit, ep.con_handle); + if (link == NULL || link->hl_type != HCI_LINK_ACL) + return; + + link->hl_flags &= ~HCI_LINK_SECURE_REQ; + + if (ep.status == 0) { + link->hl_flags |= (HCI_LINK_AUTH | HCI_LINK_SECURE); + + if (link->hl_state == HCI_LINK_WAIT_SECURE) + link->hl_state = HCI_LINK_OPEN; + + err = hci_acl_setmode(link); + if (err == EINPROGRESS) + return; + } + + hci_acl_linkmode(link); +} + /* * process results of read_bdaddr command_complete event */ diff --git a/sys/netbt/hci_link.c b/sys/netbt/hci_link.c index 385e8e6de413..4aacef4453a5 100644 --- a/sys/netbt/hci_link.c +++ b/sys/netbt/hci_link.c @@ -1,4 +1,4 @@ -/* $NetBSD: hci_link.c,v 1.10 2007/03/30 20:47:02 plunky Exp $ */ +/* $NetBSD: hci_link.c,v 1.11 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.10 2007/03/30 20:47:02 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: hci_link.c,v 1.11 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -114,6 +114,9 @@ hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr) break; case HCI_LINK_WAIT_CONNECT: + case HCI_LINK_WAIT_AUTH: + case HCI_LINK_WAIT_ENCRYPT: + case HCI_LINK_WAIT_SECURE: /* * somebody else already trying to connect, we just * sit on the bench with them.. @@ -211,6 +214,9 @@ hci_acl_timeout(void *arg) hci_link_free(link, ECONNRESET); break; + case HCI_LINK_WAIT_AUTH: + case HCI_LINK_WAIT_ENCRYPT: + case HCI_LINK_WAIT_SECURE: case HCI_LINK_OPEN: cp.con_handle = htole16(link->hl_handle); cp.reason = 0x13; /* "Remote User Terminated Connection" */ @@ -234,6 +240,168 @@ out: splx(s); } +/* + * Initiate any Link Mode change requests. + */ +int +hci_acl_setmode(struct hci_link *link) +{ + int err; + + KASSERT(link != NULL); + KASSERT(link->hl_unit != NULL); + + if (link->hl_state != HCI_LINK_OPEN) + return EINPROGRESS; + + if ((link->hl_flags & HCI_LINK_AUTH_REQ) + && !(link->hl_flags & HCI_LINK_AUTH)) { + hci_auth_req_cp cp; + + DPRINTF("requesting auth for handle #%d\n", + link->hl_handle); + + link->hl_state = HCI_LINK_WAIT_AUTH; + cp.con_handle = htole16(link->hl_handle); + err = hci_send_cmd(link->hl_unit, HCI_CMD_AUTH_REQ, + &cp, sizeof(cp)); + + return (err == 0 ? EINPROGRESS : err); + } + + if ((link->hl_flags & HCI_LINK_ENCRYPT_REQ) + && !(link->hl_flags & HCI_LINK_ENCRYPT)) { + hci_set_con_encryption_cp cp; + + /* XXX we should check features for encryption capability */ + + DPRINTF("requesting encryption for handle #%d\n", + link->hl_handle); + + link->hl_state = HCI_LINK_WAIT_ENCRYPT; + cp.con_handle = htole16(link->hl_handle); + cp.encryption_enable = 0x01; + + err = hci_send_cmd(link->hl_unit, HCI_CMD_SET_CON_ENCRYPTION, + &cp, sizeof(cp)); + + return (err == 0 ? EINPROGRESS : err); + } + + if ((link->hl_flags & HCI_LINK_SECURE_REQ)) { + hci_change_con_link_key_cp cp; + + /* always change link key for SECURE requests */ + link->hl_flags &= ~HCI_LINK_SECURE; + + DPRINTF("changing link key for handle #%d\n", + link->hl_handle); + + link->hl_state = HCI_LINK_WAIT_SECURE; + cp.con_handle = htole16(link->hl_handle); + + err = hci_send_cmd(link->hl_unit, HCI_CMD_CHANGE_CON_LINK_KEY, + &cp, sizeof(cp)); + + return (err == 0 ? EINPROGRESS : err); + } + + return 0; +} + +/* + * Link Mode changed. + * + * This is called from event handlers when the mode change + * is complete. We notify upstream and restart the link. + */ +void +hci_acl_linkmode(struct hci_link *link) +{ + struct l2cap_channel *chan, *next; + int err, mode = 0; + + DPRINTF("handle #%d, auth %s, encrypt %s, secure %s\n", + link->hl_handle, + (link->hl_flags & HCI_LINK_AUTH ? "on" : "off"), + (link->hl_flags & HCI_LINK_ENCRYPT ? "on" : "off"), + (link->hl_flags & HCI_LINK_SECURE ? "on" : "off")); + + if (link->hl_flags & HCI_LINK_AUTH) + mode |= L2CAP_LM_AUTH; + + if (link->hl_flags & HCI_LINK_ENCRYPT) + mode |= L2CAP_LM_ENCRYPT; + + if (link->hl_flags & HCI_LINK_SECURE) + mode |= L2CAP_LM_SECURE; + + /* + * The link state will only be OPEN here if the mode change + * was successful. So, we can proceed with L2CAP connections, + * or notify already establshed channels, to allow any that + * are dissatisfied to disconnect before we restart. + */ + next = LIST_FIRST(&l2cap_active_list); + while ((chan = next) != NULL) { + next = LIST_NEXT(chan, lc_ncid); + + if (chan->lc_link != link) + continue; + + switch(chan->lc_state) { + case L2CAP_WAIT_SEND_CONNECT_REQ: /* we are connecting */ + if ((mode & chan->lc_mode) != chan->lc_mode) { + l2cap_close(chan, ECONNABORTED); + break; + } + + chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; + err = l2cap_send_connect_req(chan); + if (err) { + l2cap_close(chan, err); + break; + } + break; + + case L2CAP_WAIT_SEND_CONNECT_RSP: /* they are connecting */ + if ((mode & chan->lc_mode) != chan->lc_mode) { + l2cap_send_connect_rsp(link, chan->lc_ident, + 0, chan->lc_rcid, + L2CAP_SECURITY_BLOCK); + + l2cap_close(chan, ECONNABORTED); + break; + } + + l2cap_send_connect_rsp(link, chan->lc_ident, + chan->lc_lcid, chan->lc_rcid, + L2CAP_SUCCESS); + + chan->lc_state = L2CAP_WAIT_CONFIG; + chan->lc_flags |= (L2CAP_WAIT_CONFIG_RSP | L2CAP_WAIT_CONFIG_REQ); + err = l2cap_send_config_req(chan); + if (err) { + l2cap_close(chan, err); + break; + } + break; + + case L2CAP_WAIT_RECV_CONNECT_RSP: + case L2CAP_WAIT_CONFIG: + case L2CAP_OPEN: /* already established */ + (*chan->lc_proto->linkmode)(chan->lc_upper, mode); + break; + + default: + break; + } + } + + link->hl_state = HCI_LINK_OPEN; + hci_acl_start(link); +} + /* * Receive ACL Data * @@ -425,12 +593,13 @@ nomem: /* * Start sending ACL data on link. * + * This is called when the queue may need restarting: as new data + * is queued, after link mode changes have completed, or when device + * buffers have cleared. + * * We may use all the available packet slots. The reason that we add * the ACL encapsulation here rather than in hci_acl_send() is that L2CAP * signal packets may be queued before the handle is given to us.. - * - * this is called from hci_acl_send() above, and the event processing - * code (for CON_COMPL and NUM_COMPL_PKTS) */ void hci_acl_start(struct hci_link *link) diff --git a/sys/netbt/l2cap.h b/sys/netbt/l2cap.h index 1cf61e90df0c..a4dba5eac65f 100644 --- a/sys/netbt/l2cap.h +++ b/sys/netbt/l2cap.h @@ -1,4 +1,4 @@ -/* $NetBSD: l2cap.h,v 1.4 2007/03/12 20:34:46 plunky Exp $ */ +/* $NetBSD: l2cap.h,v 1.5 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -54,7 +54,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: l2cap.h,v 1.4 2007/03/12 20:34:46 plunky Exp $ + * $Id: l2cap.h,v 1.5 2007/04/21 06:15:23 plunky Exp $ * $FreeBSD: src/sys/netgraph/bluetooth/include/l2cap.h,v 1.4 2005/08/31 18:13:23 emax Exp $ */ @@ -338,11 +338,18 @@ typedef union { ************************************************************************** **************************************************************************/ -#define SO_L2CAP_IMTU 1 -#define SO_L2CAP_OMTU 2 -#define SO_L2CAP_IQOS 3 -#define SO_L2CAP_OQOS 4 -#define SO_L2CAP_FLUSH 5 +/* Socket options */ +#define SO_L2CAP_IMTU 1 /* incoming MTU */ +#define SO_L2CAP_OMTU 2 /* outgoing MTU */ +#define SO_L2CAP_IQOS 3 /* incoming QoS */ +#define SO_L2CAP_OQOS 4 /* outgoing QoS */ +#define SO_L2CAP_FLUSH 5 /* flush timeout */ +#define SO_L2CAP_LM 6 /* link mode */ + +/* L2CAP link mode flags */ +#define L2CAP_LM_AUTH (1<<0) /* want authentication */ +#define L2CAP_LM_ENCRYPT (1<<1) /* want encryption */ +#define L2CAP_LM_SECURE (1<<2) /* want secured link */ #ifdef _KERNEL @@ -367,6 +374,7 @@ struct l2cap_channel { struct hci_link *lc_link; /* ACL connection (down) */ uint16_t lc_state; /* channel state */ uint16_t lc_flags; /* channel flags */ + uint8_t lc_ident; /* cached request id */ uint16_t lc_lcid; /* local channel ID */ struct sockaddr_bt lc_laddr; /* local address */ @@ -374,6 +382,7 @@ struct l2cap_channel { uint16_t lc_rcid; /* remote channel ID */ struct sockaddr_bt lc_raddr; /* remote address */ + int lc_mode; /* link mode */ uint16_t lc_imtu; /* incoming mtu */ uint16_t lc_omtu; /* outgoing mtu */ uint16_t lc_flush; /* flush timeout */ @@ -390,11 +399,13 @@ struct l2cap_channel { }; /* l2cap_channel state */ -#define L2CAP_CLOSED 0 /* closed */ -#define L2CAP_WAIT_CONNECT_RSP 1 /* have sent connect request */ -#define L2CAP_WAIT_CONFIG 2 /* waiting for configuration */ -#define L2CAP_OPEN 3 /* user data transfer state */ -#define L2CAP_WAIT_DISCONNECT 4 /* have sent disconnect request */ +#define L2CAP_CLOSED 0 /* closed */ +#define L2CAP_WAIT_SEND_CONNECT_REQ 1 /* waiting to send connect request */ +#define L2CAP_WAIT_RECV_CONNECT_RSP 2 /* waiting to recv connect response */ +#define L2CAP_WAIT_SEND_CONNECT_RSP 3 /* waiting to send connect response */ +#define L2CAP_WAIT_CONFIG 4 /* waiting for configuration */ +#define L2CAP_OPEN 5 /* user data transfer state */ +#define L2CAP_WAIT_DISCONNECT 6 /* have sent disconnect request */ /* l2cap_channel flags */ #define L2CAP_SHUTDOWN (1<<0) /* channel is closing */ @@ -436,6 +447,7 @@ void l2cap_recv_frame(struct mbuf *, struct hci_link *); int l2cap_start(struct l2cap_channel *); /* l2cap_misc.c */ +int l2cap_setmode(struct l2cap_channel *); int l2cap_cid_alloc(struct l2cap_channel *); struct l2cap_channel *l2cap_cid_lookup(uint16_t); int l2cap_request_alloc(struct l2cap_channel *, uint8_t); @@ -448,6 +460,7 @@ void l2cap_recv_signal(struct mbuf *, struct hci_link *); int l2cap_send_connect_req(struct l2cap_channel *); int l2cap_send_config_req(struct l2cap_channel *); int l2cap_send_disconnect_req(struct l2cap_channel *); +int l2cap_send_connect_rsp(struct hci_link *, uint8_t, uint16_t, uint16_t, uint16_t); /* l2cap_socket.c */ int l2cap_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *, struct lwp *); diff --git a/sys/netbt/l2cap_lower.c b/sys/netbt/l2cap_lower.c index 99de31d1c425..8c5de584364a 100644 --- a/sys/netbt/l2cap_lower.c +++ b/sys/netbt/l2cap_lower.c @@ -1,4 +1,4 @@ -/* $NetBSD: l2cap_lower.c,v 1.5 2007/03/30 20:47:03 plunky Exp $ */ +/* $NetBSD: l2cap_lower.c,v 1.6 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: l2cap_lower.c,v 1.5 2007/03/30 20:47:03 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: l2cap_lower.c,v 1.6 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -58,6 +58,7 @@ __KERNEL_RCSID(0, "$NetBSD: l2cap_lower.c,v 1.5 2007/03/30 20:47:03 plunky Exp $ * Config failed * Other end reported invalid CID * Normal disconnection + * Change link mode failed */ void l2cap_close(struct l2cap_channel *chan, int err) diff --git a/sys/netbt/l2cap_misc.c b/sys/netbt/l2cap_misc.c index be46a3ff4585..2d375b699cb1 100644 --- a/sys/netbt/l2cap_misc.c +++ b/sys/netbt/l2cap_misc.c @@ -1,4 +1,4 @@ -/* $NetBSD: l2cap_misc.c,v 1.2 2007/03/12 18:18:35 ad Exp $ */ +/* $NetBSD: l2cap_misc.c,v 1.3 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.2 2007/03/12 18:18:35 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.3 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -70,6 +70,33 @@ const l2cap_qos_t l2cap_default_qos = { int l2cap_response_timeout = 30; /* seconds */ int l2cap_response_extended_timeout = 180; /* seconds */ +/* + * Set Link Mode on channel + */ +int +l2cap_setmode(struct l2cap_channel *chan) +{ + + KASSERT(chan != NULL); + KASSERT(chan->lc_link != NULL); + + DPRINTF("CID #%d, auth %s, encrypt %s, secure %s\n", chan->lc_lcid, + (chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"), + (chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"), + (chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no")); + + if (chan->lc_mode & L2CAP_LM_AUTH) + chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ; + + if (chan->lc_mode & L2CAP_LM_ENCRYPT) + chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ; + + if (chan->lc_mode & L2CAP_LM_SECURE) + chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ; + + return hci_acl_setmode(chan->lc_link); +} + /* * Allocate a new Request structure & ID and set the timer going */ diff --git a/sys/netbt/l2cap_signal.c b/sys/netbt/l2cap_signal.c index 47f418e195c3..3e677dfd041d 100644 --- a/sys/netbt/l2cap_signal.c +++ b/sys/netbt/l2cap_signal.c @@ -1,4 +1,4 @@ -/* $NetBSD: l2cap_signal.c,v 1.6 2007/03/15 19:47:51 plunky Exp $ */ +/* $NetBSD: l2cap_signal.c,v 1.7 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: l2cap_signal.c,v 1.6 2007/03/15 19:47:51 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: l2cap_signal.c,v 1.7 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -240,7 +240,6 @@ l2cap_recv_connect_req(struct mbuf *m, struct hci_link *link) struct l2cap_channel *chan, *new; l2cap_cmd_hdr_t cmd; l2cap_con_req_cp cp; - l2cap_con_rsp_cp rp; int err; /* extract cmd */ @@ -251,20 +250,19 @@ l2cap_recv_connect_req(struct mbuf *m, struct hci_link *link) m_copydata(m, 0, sizeof(cp), &cp); m_adj(m, sizeof(cp)); - /* init response */ - memset(&rp, 0, sizeof(rp)); - rp.scid = cp.scid; + cp.scid = le16toh(cp.scid); + cp.psm = le16toh(cp.psm); memset(&laddr, 0, sizeof(struct sockaddr_bt)); laddr.bt_len = sizeof(struct sockaddr_bt); laddr.bt_family = AF_BLUETOOTH; - laddr.bt_psm = le16toh(cp.psm); + laddr.bt_psm = cp.psm; bdaddr_copy(&laddr.bt_bdaddr, &link->hl_unit->hci_bdaddr); memset(&raddr, 0, sizeof(struct sockaddr_bt)); raddr.bt_len = sizeof(struct sockaddr_bt); raddr.bt_family = AF_BLUETOOTH; - raddr.bt_psm = le16toh(cp.psm); + raddr.bt_psm = cp.psm; bdaddr_copy(&raddr.bt_bdaddr, &link->hl_bdaddr); LIST_FOREACH(chan, &l2cap_listen_list, lc_ncid) { @@ -282,9 +280,10 @@ l2cap_recv_connect_req(struct mbuf *m, struct hci_link *link) err = l2cap_cid_alloc(new); if (err) { - rp.result = htole16(L2CAP_NO_RESOURCES); - l2cap_send_signal(link, L2CAP_CONNECT_RSP, - cmd.ident, sizeof(rp), &rp); + l2cap_send_connect_rsp(link, cmd.ident, + 0, cp.scid, + L2CAP_NO_RESOURCES); + (*new->lc_proto->disconnected)(new->lc_upper, err); return; } @@ -292,24 +291,53 @@ l2cap_recv_connect_req(struct mbuf *m, struct hci_link *link) new->lc_link = hci_acl_open(link->hl_unit, &link->hl_bdaddr); KASSERT(new->lc_link == link); - new->lc_rcid = le16toh(cp.scid); + new->lc_rcid = cp.scid; + new->lc_ident = cmd.ident; memcpy(&new->lc_laddr, &laddr, sizeof(struct sockaddr_bt)); memcpy(&new->lc_raddr, &raddr, sizeof(struct sockaddr_bt)); - rp.dcid = htole16(new->lc_lcid); - rp.result = htole16(L2CAP_SUCCESS); - l2cap_send_signal(link, L2CAP_CONNECT_RSP, cmd.ident, - sizeof(rp), &rp); + new->lc_mode = chan->lc_mode; + + err = l2cap_setmode(new); + if (err == EINPROGRESS) { + new->lc_state = L2CAP_WAIT_SEND_CONNECT_RSP; + (*new->lc_proto->connecting)(new->lc_upper); + return; + } + if (err) { + new->lc_state = L2CAP_CLOSED; + hci_acl_close(link, err); + new->lc_link = NULL; + + l2cap_send_connect_rsp(link, cmd.ident, + 0, cp.scid, + L2CAP_NO_RESOURCES); + + (*new->lc_proto->disconnected)(new->lc_upper, err); + return; + } + + err = l2cap_send_connect_rsp(link, cmd.ident, + new->lc_lcid, new->lc_rcid, + L2CAP_SUCCESS); + if (err) { + l2cap_close(new, err); + return; + } new->lc_state = L2CAP_WAIT_CONFIG; new->lc_flags |= (L2CAP_WAIT_CONFIG_REQ | L2CAP_WAIT_CONFIG_RSP); - l2cap_send_config_req(new); + err = l2cap_send_config_req(new); + if (err) + l2cap_close(new, err); + return; } - rp.result = htole16(L2CAP_PSM_NOT_SUPPORTED); - l2cap_send_signal(link, L2CAP_CONNECT_RSP, cmd.ident, sizeof(rp), &rp); + l2cap_send_connect_rsp(link, cmd.ident, + 0, cp.scid, + L2CAP_PSM_NOT_SUPPORTED); } /* @@ -341,7 +369,7 @@ l2cap_recv_connect_rsp(struct mbuf *m, struct hci_link *link) if (chan != NULL && chan->lc_lcid != cp.scid) return; - if (chan == NULL || chan->lc_state != L2CAP_WAIT_CONNECT_RSP) { + if (chan == NULL || chan->lc_state != L2CAP_WAIT_RECV_CONNECT_RSP) { l2cap_request_free(req); return; } @@ -1063,3 +1091,19 @@ l2cap_send_disconnect_req(struct l2cap_channel *chan) return l2cap_send_signal(chan->lc_link, L2CAP_DISCONNECT_REQ, chan->lc_link->hl_lastid, sizeof(cp), &cp); } + +/* + * Send Connect Response + */ +int +l2cap_send_connect_rsp(struct hci_link *link, uint8_t ident, uint16_t dcid, uint16_t scid, uint16_t result) +{ + l2cap_con_rsp_cp cp; + + memset(&cp, 0, sizeof(cp)); + cp.dcid = htole16(dcid); + cp.scid = htole16(scid); + cp.result = htole16(result); + + return l2cap_send_signal(link, L2CAP_CONNECT_RSP, ident, sizeof(cp), &cp); +} diff --git a/sys/netbt/l2cap_socket.c b/sys/netbt/l2cap_socket.c index f69ca2dd0a55..7d54d10a17d4 100644 --- a/sys/netbt/l2cap_socket.c +++ b/sys/netbt/l2cap_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: l2cap_socket.c,v 1.6 2007/03/31 18:17:13 plunky Exp $ */ +/* $NetBSD: l2cap_socket.c,v 1.7 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: l2cap_socket.c,v 1.6 2007/03/31 18:17:13 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: l2cap_socket.c,v 1.7 2007/04/21 06:15:23 plunky Exp $"); /* load symbolic names */ #ifdef BLUETOOTH_DEBUG @@ -65,6 +65,7 @@ static void l2cap_connected(void *); static void l2cap_disconnected(void *, int); static void *l2cap_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void l2cap_complete(void *, int); +static void l2cap_linkmode(void *, int); static void l2cap_input(void *, struct mbuf *); static const struct btproto l2cap_proto = { @@ -73,7 +74,8 @@ static const struct btproto l2cap_proto = { l2cap_disconnected, l2cap_newconn, l2cap_complete, - l2cap_input + l2cap_linkmode, + l2cap_input, }; /* sysctl variables */ @@ -361,6 +363,24 @@ l2cap_complete(void *arg, int count) sowwakeup(so); } +static void +l2cap_linkmode(void *arg, int new) +{ + struct socket *so = arg; + int mode; + + DPRINTF("auth %s, encrypt %s, secure %s\n", + (new & L2CAP_LM_AUTH ? "on" : "off"), + (new & L2CAP_LM_ENCRYPT ? "on" : "off"), + (new & L2CAP_LM_SECURE ? "on" : "off")); + + (void)l2cap_getopt(so->so_pcb, SO_L2CAP_LM, &mode); + if (((mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH)) + || ((mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT)) + || ((mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE))) + l2cap_disconnect(so->so_pcb, 0); +} + static void l2cap_input(void *arg, struct mbuf *m) { diff --git a/sys/netbt/l2cap_upper.c b/sys/netbt/l2cap_upper.c index dfd9e67b9773..f3e8c4749f82 100644 --- a/sys/netbt/l2cap_upper.c +++ b/sys/netbt/l2cap_upper.c @@ -1,4 +1,4 @@ -/* $NetBSD: l2cap_upper.c,v 1.6 2007/04/06 17:09:00 plunky Exp $ */ +/* $NetBSD: l2cap_upper.c,v 1.7 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2005 Iain Hibbert. @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: l2cap_upper.c,v 1.6 2007/04/06 17:09:00 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: l2cap_upper.c,v 1.7 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -176,21 +176,33 @@ l2cap_connect(struct l2cap_channel *chan, struct sockaddr_bt *dest) if (chan->lc_link == NULL) return EHOSTUNREACH; - /* - * We queue a connect request right away even though the link - * may not yet be open; the queue will be started automatically - * at the right time. - */ - chan->lc_state = L2CAP_WAIT_CONNECT_RSP; - err = l2cap_send_connect_req(chan); - if (err) { - chan->lc_state = L2CAP_CLOSED; - hci_acl_close(chan->lc_link, err); - chan->lc_link = NULL; - return err; + /* set the link mode */ + err = l2cap_setmode(chan); + if (err == EINPROGRESS) { + chan->lc_state = L2CAP_WAIT_SEND_CONNECT_REQ; + (*chan->lc_proto->connecting)(chan->lc_upper); + return 0; } + if (err) + goto fail; + + /* + * We can queue a connect request now even though the link may + * not yet be open; Our mode setting is assured, and the queue + * will be started automatically at the right time. + */ + chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; + err = l2cap_send_connect_req(chan); + if (err) + goto fail; return 0; + +fail: + chan->lc_state = L2CAP_CLOSED; + hci_acl_close(chan->lc_link, err); + chan->lc_link = NULL; + return err; } /* @@ -395,15 +407,22 @@ l2cap_send(struct l2cap_channel *chan, struct mbuf *m) } /* - * l2cap_setopt(channel, opt, addr) + * l2cap_setopt(l2cap_channel, opt, addr) * * Apply configuration options to channel. This corresponds to * "Configure Channel Request" in the L2CAP specification. + * + * for SO_L2CAP_LM, the settings will take effect when the + * channel is established. If the channel is already open, + * a call to + * proto->linkmode(upper, new) + * + * will be made when the change is complete. */ int l2cap_setopt(struct l2cap_channel *chan, int opt, void *addr) { - int err = 0; + int mode, err = 0; uint16_t mtu; switch (opt) { @@ -418,6 +437,23 @@ l2cap_setopt(struct l2cap_channel *chan, int opt, void *addr) break; + case SO_L2CAP_LM: /* set link mode */ + mode = *(int *)addr; + mode &= (L2CAP_LM_SECURE | L2CAP_LM_ENCRYPT | L2CAP_LM_AUTH); + + if (mode & L2CAP_LM_SECURE) + mode |= L2CAP_LM_ENCRYPT; + + if (mode & L2CAP_LM_ENCRYPT) + mode |= L2CAP_LM_AUTH; + + chan->lc_mode = mode; + + if (chan->lc_state == L2CAP_OPEN) + err = l2cap_setmode(chan); + + break; + case SO_L2CAP_OQOS: /* set Outgoing QoS flow spec */ case SO_L2CAP_FLUSH: /* set Outgoing Flush Timeout */ default: @@ -428,6 +464,11 @@ l2cap_setopt(struct l2cap_channel *chan, int opt, void *addr) return err; } +/* + * l2cap_getopt(l2cap_channel, opt, addr) + * + * Return configuration parameters. + */ int l2cap_getopt(struct l2cap_channel *chan, int opt, void *addr) { @@ -453,6 +494,10 @@ l2cap_getopt(struct l2cap_channel *chan, int opt, void *addr) *(uint16_t *)addr = chan->lc_flush; return sizeof(uint16_t); + case SO_L2CAP_LM: /* get link mode */ + *(int *)addr = chan->lc_mode; + return sizeof(int); + default: break; } diff --git a/sys/netbt/rfcomm.h b/sys/netbt/rfcomm.h index 2e798ced3740..aa5649da82a1 100644 --- a/sys/netbt/rfcomm.h +++ b/sys/netbt/rfcomm.h @@ -1,4 +1,4 @@ -/* $NetBSD: rfcomm.h,v 1.2 2006/10/01 06:08:08 plunky Exp $ */ +/* $NetBSD: rfcomm.h,v 1.3 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -55,7 +55,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: rfcomm.h,v 1.2 2006/10/01 06:08:08 plunky Exp $ + * $Id: rfcomm.h,v 1.3 2007/04/21 06:15:23 plunky Exp $ * $FreeBSD: src/sys/netgraph/bluetooth/include/ng_btsocket_rfcomm.h,v 1.4 2005/01/11 01:39:53 emax Exp $ */ @@ -236,8 +236,10 @@ struct rfcomm_mcc_pn ************************************************************************* *************************************************************************/ +/* Socket options */ #define SO_RFCOMM_MTU 1 /* mtu */ #define SO_RFCOMM_FC_INFO 2 /* flow control info (below) */ +#define SO_RFCOMM_LM 3 /* link mode */ /* Flow control information */ struct rfcomm_fc_info { @@ -249,6 +251,11 @@ struct rfcomm_fc_info { uint8_t reserved; }; +/* RFCOMM link mode flags */ +#define RFCOMM_LM_AUTH (1<<0) /* want authentication */ +#define RFCOMM_LM_ENCRYPT (1<<1) /* want encryption */ +#define RFCOMM_LM_SECURE (1<<2) /* want secured link */ + #ifdef _KERNEL /* sysctl variables */ @@ -313,6 +320,7 @@ struct rfcomm_dlc { uint16_t rd_flags; /* DLC flags */ uint16_t rd_state; /* DLC state */ uint16_t rd_mtu; /* MTU */ + int rd_mode; /* link mode */ struct sockaddr_bt rd_laddr; /* local address */ struct sockaddr_bt rd_raddr; /* remote address */ @@ -361,9 +369,12 @@ struct rfcomm_dlc { #define RFCOMM_DLC_CLOSED 0 /* no session */ #define RFCOMM_DLC_WAIT_SESSION 1 /* waiting for session */ #define RFCOMM_DLC_WAIT_CONNECT 2 /* waiting for connect */ -#define RFCOMM_DLC_OPEN 3 /* can send/receive */ -#define RFCOMM_DLC_WAIT_DISCONNECT 4 /* waiting for disconnect */ -#define RFCOMM_DLC_LISTEN 5 /* listening DLC */ +#define RFCOMM_DLC_WAIT_SEND_SABM 3 /* waiting to send SABM */ +#define RFCOMM_DLC_WAIT_SEND_UA 4 /* waiting to send UA */ +#define RFCOMM_DLC_WAIT_RECV_UA 5 /* waiting to receive UA */ +#define RFCOMM_DLC_OPEN 6 /* can send/receive */ +#define RFCOMM_DLC_WAIT_DISCONNECT 7 /* waiting for disconnect */ +#define RFCOMM_DLC_LISTEN 8 /* listening DLC */ /* * Bluetooth RFCOMM socket kernel prototypes @@ -376,7 +387,9 @@ struct rfcomm_dlc *rfcomm_dlc_lookup(struct rfcomm_session *, int); struct rfcomm_dlc *rfcomm_dlc_newconn(struct rfcomm_session *, int); void rfcomm_dlc_close(struct rfcomm_dlc *, int); void rfcomm_dlc_timeout(void *); +int rfcomm_dlc_setmode(struct rfcomm_dlc *); int rfcomm_dlc_connect(struct rfcomm_dlc *); +int rfcomm_dlc_open(struct rfcomm_dlc *); void rfcomm_dlc_start(struct rfcomm_dlc *); /* rfcomm_session.c */ diff --git a/sys/netbt/rfcomm_dlc.c b/sys/netbt/rfcomm_dlc.c index 353585dd0f64..efcf19359234 100644 --- a/sys/netbt/rfcomm_dlc.c +++ b/sys/netbt/rfcomm_dlc.c @@ -1,4 +1,4 @@ -/* $NetBSD: rfcomm_dlc.c,v 1.2 2007/03/15 19:47:51 plunky Exp $ */ +/* $NetBSD: rfcomm_dlc.c,v 1.3 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: rfcomm_dlc.c,v 1.2 2007/03/15 19:47:51 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rfcomm_dlc.c,v 1.3 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -72,7 +72,7 @@ struct rfcomm_dlc * rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci) { struct rfcomm_session *ls; - struct rfcomm_dlc *dlc, *any, *best; + struct rfcomm_dlc *new, *dlc, *any, *best; struct sockaddr_bt laddr, raddr, addr; int chan; @@ -86,6 +86,7 @@ rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci) l2cap_sockaddr(rs->rs_l2cap, &laddr); l2cap_peeraddr(rs->rs_l2cap, &raddr); chan = RFCOMM_CHANNEL(dlci); + new = NULL; any = best = NULL; LIST_FOREACH(ls, &rfcomm_session_listen, rs_next) { @@ -117,27 +118,28 @@ rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci) * listening DLC's so all can be checked in turn.. */ if (dlc != NULL) - dlc = (*dlc->rd_proto->newconn)(dlc->rd_upper, &laddr, &raddr); + new = (*dlc->rd_proto->newconn)(dlc->rd_upper, &laddr, &raddr); - if (dlc == NULL) { + if (new == NULL) { rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci); return NULL; } - dlc->rd_dlci = dlci; - dlc->rd_mtu = rfcomm_mtu_default; + new->rd_dlci = dlci; + new->rd_mtu = rfcomm_mtu_default; + new->rd_mode = dlc->rd_mode; - memcpy(&dlc->rd_laddr, &laddr, sizeof(struct sockaddr_bt)); - dlc->rd_laddr.bt_channel = chan; + memcpy(&new->rd_laddr, &laddr, sizeof(struct sockaddr_bt)); + new->rd_laddr.bt_channel = chan; - memcpy(&dlc->rd_raddr, &raddr, sizeof(struct sockaddr_bt)); - dlc->rd_raddr.bt_channel = chan; + memcpy(&new->rd_raddr, &raddr, sizeof(struct sockaddr_bt)); + new->rd_raddr.bt_channel = chan; - dlc->rd_session = rs; - dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT; - LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next); + new->rd_session = rs; + new->rd_state = RFCOMM_DLC_WAIT_CONNECT; + LIST_INSERT_HEAD(&rs->rs_dlcs, new, rd_next); - return dlc; + return new; } /* @@ -206,6 +208,38 @@ rfcomm_dlc_timeout(void *arg) splx(s); } +/* + * rfcomm_dlc_setmode(rfcomm_dlc) + * + * Set link mode for DLC. This is only called when the session is + * already open, so we don't need to worry about any previous mode + * settings. + */ +int +rfcomm_dlc_setmode(struct rfcomm_dlc *dlc) +{ + int mode = 0; + + KASSERT(dlc->rd_session != NULL); + KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN); + + DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci, + (dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"), + (dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"), + (dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no")); + + if (dlc->rd_mode & RFCOMM_LM_AUTH) + mode |= L2CAP_LM_AUTH; + + if (dlc->rd_mode & RFCOMM_LM_ENCRYPT) + mode |= L2CAP_LM_ENCRYPT; + + if (dlc->rd_mode & RFCOMM_LM_SECURE) + mode |= L2CAP_LM_SECURE; + + return l2cap_setopt(dlc->rd_session->rs_l2cap, SO_L2CAP_LM, &mode); +} + /* * rfcomm_dlc_connect(rfcomm_dlc) * @@ -225,7 +259,7 @@ rfcomm_dlc_connect(struct rfcomm_dlc *dlc) * If we have not already sent a PN on the session, we must send * a PN to negotiate Credit Flow Control, and this setting will * apply to all future connections for this session. We ask for - * this every time. + * this every time, in order to establish initial credits. */ memset(&pn, 0, sizeof(pn)); pn.dlci = dlc->rd_dlci; @@ -248,6 +282,38 @@ rfcomm_dlc_connect(struct rfcomm_dlc *dlc) return 0; } +/* + * rfcomm_dlc_open(rfcomm_dlc) + * + * send "Modem Status Command" and mark DLC as open. + */ +int +rfcomm_dlc_open(struct rfcomm_dlc *dlc) +{ + struct rfcomm_mcc_msc msc; + int err; + + KASSERT(dlc->rd_session != NULL); + KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN); + + memset(&msc, 0, sizeof(msc)); + msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci); + msc.modem = dlc->rd_lmodem & 0xfe; /* EA = 0 */ + msc.brk = 0x00 | 0x01; /* EA = 1 */ + + err = rfcomm_session_send_mcc(dlc->rd_session, 1, + RFCOMM_MCC_MSC, &msc, sizeof(msc)); + if (err) + return err; + + callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz); + + dlc->rd_state = RFCOMM_DLC_OPEN; + (*dlc->rd_proto->connected)(dlc->rd_upper); + + return 0; +} + /* * rfcomm_dlc_start(rfcomm_dlc) * diff --git a/sys/netbt/rfcomm_session.c b/sys/netbt/rfcomm_session.c index b38c0953d477..1b6152662d05 100644 --- a/sys/netbt/rfcomm_session.c +++ b/sys/netbt/rfcomm_session.c @@ -1,4 +1,4 @@ -/* $NetBSD: rfcomm_session.c,v 1.8 2007/04/06 16:27:52 plunky Exp $ */ +/* $NetBSD: rfcomm_session.c,v 1.9 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: rfcomm_session.c,v 1.8 2007/04/06 16:27:52 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rfcomm_session.c,v 1.9 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -75,6 +75,7 @@ static void rfcomm_session_connected(void *); static void rfcomm_session_disconnected(void *, int); static void *rfcomm_session_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void rfcomm_session_complete(void *, int); +static void rfcomm_session_linkmode(void *, int); static void rfcomm_session_input(void *, struct mbuf *); static const struct btproto rfcomm_session_proto = { @@ -83,7 +84,8 @@ static const struct btproto rfcomm_session_proto = { rfcomm_session_disconnected, rfcomm_session_newconn, rfcomm_session_complete, - rfcomm_session_input + rfcomm_session_linkmode, + rfcomm_session_input, }; struct rfcomm_session_list @@ -479,6 +481,104 @@ rfcomm_session_complete(void *arg, int count) } } +/* + * Link Mode changed + * + * This is called when a mode change is complete. Proceed with connections + * where appropriate, or pass the new mode to any active DLCs. + */ +static void +rfcomm_session_linkmode(void *arg, int new) +{ + struct rfcomm_session *rs = arg; + struct rfcomm_dlc *dlc, *next; + int err, mode = 0; + + DPRINTF("auth %s, encrypt %s, secure %s\n", + (new & L2CAP_LM_AUTH ? "on" : "off"), + (new & L2CAP_LM_ENCRYPT ? "on" : "off"), + (new & L2CAP_LM_SECURE ? "on" : "off")); + + if (new & L2CAP_LM_AUTH) + mode |= RFCOMM_LM_AUTH; + + if (new & L2CAP_LM_ENCRYPT) + mode |= RFCOMM_LM_ENCRYPT; + + if (new & L2CAP_LM_SECURE) + mode |= RFCOMM_LM_SECURE; + + next = LIST_FIRST(&rs->rs_dlcs); + while ((dlc = next) != NULL) { + next = LIST_NEXT(dlc, rd_next); + + switch (dlc->rd_state) { + case RFCOMM_DLC_WAIT_SEND_SABM: /* we are connecting */ + if ((mode & dlc->rd_mode) != dlc->rd_mode) { + rfcomm_dlc_close(dlc, ECONNABORTED); + } else { + err = rfcomm_session_send_frame(rs, + RFCOMM_FRAME_SABM, dlc->rd_dlci); + if (err) { + rfcomm_dlc_close(dlc, err); + } else { + dlc->rd_state = RFCOMM_DLC_WAIT_RECV_UA; + callout_schedule(&dlc->rd_timeout, + rfcomm_ack_timeout * hz); + break; + } + } + + /* + * If we aborted the connection and there are no more DLCs + * on the session, it is our responsibility to disconnect. + */ + if (!LIST_EMPTY(&rs->rs_dlcs)) + break; + + rs->rs_state = RFCOMM_SESSION_WAIT_DISCONNECT; + rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC, 0); + callout_schedule(&rs->rs_timeout, rfcomm_ack_timeout * hz); + break; + + case RFCOMM_DLC_WAIT_SEND_UA: /* they are connecting */ + if ((mode & dlc->rd_mode) != dlc->rd_mode) { + rfcomm_session_send_frame(rs, + RFCOMM_FRAME_DM, dlc->rd_dlci); + rfcomm_dlc_close(dlc, ECONNABORTED); + break; + } + + err = rfcomm_session_send_frame(rs, + RFCOMM_FRAME_UA, dlc->rd_dlci); + if (err) { + rfcomm_session_send_frame(rs, + RFCOMM_FRAME_DM, dlc->rd_dlci); + rfcomm_dlc_close(dlc, err); + break; + } + + err = rfcomm_dlc_open(dlc); + if (err) { + rfcomm_session_send_frame(rs, + RFCOMM_FRAME_DM, dlc->rd_dlci); + rfcomm_dlc_close(dlc, err); + break; + } + + break; + + case RFCOMM_DLC_WAIT_RECV_UA: + case RFCOMM_DLC_OPEN: /* already established */ + (*dlc->rd_proto->linkmode)(dlc->rd_upper, mode); + break; + + default: + break; + } + } +} + /* * Receive data from L2CAP layer for session. There is always exactly one * RFCOMM frame contained in each L2CAP frame. @@ -598,7 +698,6 @@ done: static void rfcomm_session_recv_sabm(struct rfcomm_session *rs, int dlci) { - struct rfcomm_mcc_msc msc; struct rfcomm_dlc *dlc; int err; @@ -637,29 +736,36 @@ rfcomm_session_recv_sabm(struct rfcomm_session *rs, int dlci) return; /* (DM is sent) */ } - DPRINTFN(2, "send UA(%d) state = %d\n", dlci, dlc->rd_state); - - err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_UA, dlci); - if (err) { - rfcomm_dlc_close(dlc, err); - return; - } - /* - * If this was some kind of spurious SABM then lets - * not do anything, heh. + * ..but if this DLC is not waiting to connect, they did + * something wrong, ignore it. */ if (dlc->rd_state != RFCOMM_DLC_WAIT_CONNECT) return; - msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci); - msc.modem = dlc->rd_lmodem & 0xfe; /* EA = 0 */ - msc.brk = 0x00 | 0x01; /* EA = 1 */ - rfcomm_session_send_mcc(rs, 1, RFCOMM_MCC_MSC, &msc, sizeof(msc)); - callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz); + /* set link mode */ + err = rfcomm_dlc_setmode(dlc); + if (err == EINPROGRESS) { + dlc->rd_state = RFCOMM_DLC_WAIT_SEND_UA; + (*dlc->rd_proto->connecting)(dlc->rd_upper); + return; + } + if (err) + goto close; - dlc->rd_state = RFCOMM_DLC_OPEN; - (*dlc->rd_proto->connected)(dlc->rd_upper); + err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_UA, dlci); + if (err) + goto close; + + /* and mark it open */ + err = rfcomm_dlc_open(dlc); + if (err) + goto close; + + return; + +close: + rfcomm_dlc_close(dlc, err); } /* @@ -707,7 +813,6 @@ rfcomm_session_recv_disc(struct rfcomm_session *rs, int dlci) static void rfcomm_session_recv_ua(struct rfcomm_session *rs, int dlci) { - struct rfcomm_mcc_msc msc; struct rfcomm_dlc *dlc; DPRINTFN(5, "UA(%d)\n", dlci); @@ -747,16 +852,8 @@ rfcomm_session_recv_ua(struct rfcomm_session *rs, int dlci) goto check; switch (dlc->rd_state) { - case RFCOMM_DLC_WAIT_CONNECT: /* We sent SABM */ - msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci); - msc.modem = dlc->rd_lmodem & 0xfe; /* EA = 0 */ - msc.brk = 0x00 | 0x01; /* EA = 1 */ - rfcomm_session_send_mcc(rs, 1, RFCOMM_MCC_MSC, - &msc, sizeof(msc)); - callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz); - - dlc->rd_state = RFCOMM_DLC_OPEN; - (*dlc->rd_proto->connected)(dlc->rd_upper); + case RFCOMM_DLC_WAIT_RECV_UA: /* We sent SABM */ + rfcomm_dlc_open(dlc); return; case RFCOMM_DLC_WAIT_DISCONNECT: /* We sent DISC */ @@ -904,7 +1001,7 @@ rfcomm_session_recv_mcc(struct rfcomm_session *rs, struct mbuf *m) m_adj(m, sizeof(b)); if (RFCOMM_EA(b) == 0) { /* verify no extensions */ - DPRINTF("MCC type EA = 1, discarded\n"); + DPRINTF("MCC type EA = 0, discarded\n"); goto release; } @@ -1272,19 +1369,34 @@ rfcomm_session_recv_mcc_pn(struct rfcomm_session *rs, int cr, struct mbuf *m) } dlc->rd_mtu = pn.mtu; - /* initial credits can only be set before DLC is open */ - if (dlc->rd_state == RFCOMM_DLC_WAIT_CONNECT - && (pn.flow_control & 0xf0) == 0xe0) { + /* if DLC is not waiting to connect, we are done */ + if (dlc->rd_state != RFCOMM_DLC_WAIT_CONNECT) + return; + + /* set initial credits according to RFCOMM spec */ + if ((pn.flow_control & 0xf0) == 0xe0) { rs->rs_flags |= RFCOMM_SESSION_CFC; dlc->rd_txcred = (pn.credits & 0x07); } - /* Ok, lets go with it */ + callout_schedule(&dlc->rd_timeout, rfcomm_ack_timeout * hz); + + /* set link mode */ + err = rfcomm_dlc_setmode(dlc); + if (err == EINPROGRESS) { + dlc->rd_state = RFCOMM_DLC_WAIT_SEND_SABM; + (*dlc->rd_proto->connecting)(dlc->rd_upper); + return; + } + if (err) + goto close; + + /* we can proceed now */ err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_SABM, pn.dlci); if (err) goto close; - callout_schedule(&dlc->rd_timeout, rfcomm_ack_timeout * hz); + dlc->rd_state = RFCOMM_DLC_WAIT_RECV_UA; } return; diff --git a/sys/netbt/rfcomm_socket.c b/sys/netbt/rfcomm_socket.c index 9628432f6380..6fe7a1f73121 100644 --- a/sys/netbt/rfcomm_socket.c +++ b/sys/netbt/rfcomm_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: rfcomm_socket.c,v 1.6 2007/03/31 18:17:13 plunky Exp $ */ +/* $NetBSD: rfcomm_socket.c,v 1.7 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: rfcomm_socket.c,v 1.6 2007/03/31 18:17:13 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rfcomm_socket.c,v 1.7 2007/04/21 06:15:23 plunky Exp $"); /* load symbolic names */ #ifdef BLUETOOTH_DEBUG @@ -64,6 +64,7 @@ static void rfcomm_connected(void *); static void rfcomm_disconnected(void *, int); static void *rfcomm_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void rfcomm_complete(void *, int); +static void rfcomm_linkmode(void *, int); static void rfcomm_input(void *, struct mbuf *); static const struct btproto rfcomm_proto = { @@ -72,6 +73,7 @@ static const struct btproto rfcomm_proto = { rfcomm_disconnected, rfcomm_newconn, rfcomm_complete, + rfcomm_linkmode, rfcomm_input, }; @@ -364,6 +366,29 @@ rfcomm_complete(void *arg, int length) sowwakeup(so); } +/* + * rfcomm_linkmode(rfcomm_dlc, new) + * + * link mode change notification. + */ +static void +rfcomm_linkmode(void *arg, int new) +{ + struct socket *so = arg; + int mode; + + DPRINTF("auth %s, encrypt %s, secure %s\n", + (new & RFCOMM_LM_AUTH ? "on" : "off"), + (new & RFCOMM_LM_ENCRYPT ? "on" : "off"), + (new & RFCOMM_LM_SECURE ? "on" : "off")); + + (void)rfcomm_getopt(so->so_pcb, SO_RFCOMM_LM, &mode); + if (((mode & RFCOMM_LM_AUTH) && !(new & RFCOMM_LM_AUTH)) + || ((mode & RFCOMM_LM_ENCRYPT) && !(new & RFCOMM_LM_ENCRYPT)) + || ((mode & RFCOMM_LM_SECURE) && !(new & RFCOMM_LM_SECURE))) + rfcomm_disconnect(so->so_pcb, 0); +} + /* * rfcomm_input(rfcomm_dlc, mbuf) */ diff --git a/sys/netbt/rfcomm_upper.c b/sys/netbt/rfcomm_upper.c index 650c57d72181..bcb3c9afe353 100644 --- a/sys/netbt/rfcomm_upper.c +++ b/sys/netbt/rfcomm_upper.c @@ -1,4 +1,4 @@ -/* $NetBSD: rfcomm_upper.c,v 1.5 2007/04/06 17:09:00 plunky Exp $ */ +/* $NetBSD: rfcomm_upper.c,v 1.6 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: rfcomm_upper.c,v 1.5 2007/04/06 17:09:00 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rfcomm_upper.c,v 1.6 2007/04/21 06:15:23 plunky Exp $"); #include #include @@ -228,7 +228,14 @@ rfcomm_disconnect(struct rfcomm_dlc *dlc, int linger) case RFCOMM_DLC_LISTEN: return EINVAL; + case RFCOMM_DLC_WAIT_SEND_UA: + err = rfcomm_session_send_frame(rs, + RFCOMM_FRAME_DM, dlc->rd_dlci); + + /* fall through */ case RFCOMM_DLC_WAIT_SESSION: + case RFCOMM_DLC_WAIT_CONNECT: + case RFCOMM_DLC_WAIT_SEND_SABM: rfcomm_dlc_close(dlc, 0); break; @@ -239,7 +246,7 @@ rfcomm_disconnect(struct rfcomm_dlc *dlc, int linger) } /* else fall through */ - case RFCOMM_DLC_WAIT_CONNECT: + case RFCOMM_DLC_WAIT_RECV_UA: dlc->rd_state = RFCOMM_DLC_WAIT_DISCONNECT; err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC, dlc->rd_dlci); @@ -416,7 +423,7 @@ rfcomm_rcvd(struct rfcomm_dlc *dlc, size_t space) int rfcomm_setopt(struct rfcomm_dlc *dlc, int opt, void *addr) { - int err = 0; + int mode, err = 0; uint16_t mtu; switch (opt) { @@ -431,6 +438,23 @@ rfcomm_setopt(struct rfcomm_dlc *dlc, int opt, void *addr) break; + case SO_RFCOMM_LM: + mode = *(int *)addr; + mode &= (RFCOMM_LM_SECURE | RFCOMM_LM_ENCRYPT | RFCOMM_LM_AUTH); + + if (mode & RFCOMM_LM_SECURE) + mode |= RFCOMM_LM_ENCRYPT; + + if (mode & RFCOMM_LM_ENCRYPT) + mode |= RFCOMM_LM_AUTH; + + dlc->rd_mode = mode; + + if (dlc->rd_state == RFCOMM_DLC_OPEN) + err = rfcomm_dlc_setmode(dlc); + + break; + default: err = ENOPROTOOPT; break; @@ -466,6 +490,10 @@ rfcomm_getopt(struct rfcomm_dlc *dlc, int opt, void *addr) return sizeof(*fc); + case SO_RFCOMM_LM: + *(int *)addr = dlc->rd_mode; + return sizeof(int); + default: break; } diff --git a/sys/netbt/sco_socket.c b/sys/netbt/sco_socket.c index 21e18a6d3e4a..ebc276340f60 100644 --- a/sys/netbt/sco_socket.c +++ b/sys/netbt/sco_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: sco_socket.c,v 1.8 2007/03/31 18:17:13 plunky Exp $ */ +/* $NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -30,7 +30,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: sco_socket.c,v 1.8 2007/03/31 18:17:13 plunky Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $"); /* load symbolic names */ #ifdef BLUETOOTH_DEBUG @@ -62,6 +62,7 @@ static void sco_connected(void *); static void sco_disconnected(void *, int); static void *sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); static void sco_complete(void *, int); +static void sco_linkmode(void *, int); static void sco_input(void *, struct mbuf *); static const struct btproto sco_proto = { @@ -70,6 +71,7 @@ static const struct btproto sco_proto = { sco_disconnected, sco_newconn, sco_complete, + sco_linkmode, sco_input, }; @@ -346,6 +348,11 @@ sco_complete(void *arg, int num) sowwakeup(so); } +static void +sco_linkmode(void *arg, int mode) +{ +} + static void sco_input(void *arg, struct mbuf *m) { diff --git a/usr.bin/rfcomm_sppd/rfcomm_sppd.1 b/usr.bin/rfcomm_sppd/rfcomm_sppd.1 index cfac78579489..8348d29fc6e3 100644 --- a/usr.bin/rfcomm_sppd/rfcomm_sppd.1 +++ b/usr.bin/rfcomm_sppd/rfcomm_sppd.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: rfcomm_sppd.1,v 1.4 2007/03/01 21:44:30 plunky Exp $ +.\" $NetBSD: rfcomm_sppd.1,v 1.5 2007/04/21 06:15:23 plunky Exp $ .\" .\" Copyright (c) 2006 Itronix Inc. .\" All rights reserved. @@ -52,7 +52,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 28, 2007 +.Dd April 10, 2007 .Dt RFCOMM_SPPD 1 .Os .Sh NAME @@ -61,9 +61,10 @@ .Sh SYNOPSIS .Nm .Op Fl d Ar device +.Op Fl m Ar mode .Op Fl s Ar service .Op Fl t Ar tty -.Fl a Ar address | Fl c Ar channel +.Brq Fl a Ar address | Fl c Ar channel .Sh DESCRIPTION The .Nm @@ -116,6 +117,18 @@ for a list of available devices. If no .Ar device is specified, the connection will be set up on a system determined device. +.It Fl m Ar mode +Set connection link mode. +Supported modes are: +.Pp +.Bl -tag -compact -offset indent +.It auth +require devices be paired. +.It encrypt +auth, plus enable encryption. +.It secure +encryption, plus change of link key. +.El .It Fl s Ar service This is the service class that will be searched for on the remote device. If no @@ -173,10 +186,10 @@ server, e.g. with the use of .Pp In order to use .Nm -to automatically create a link for +to automatically create a secured link for .Xr pppd 8 , use -.Dl pty Qo rfcomm_sppd -a 00:01:02:03:04:05 -s DUN Qc +.Dl pty Qo rfcomm_sppd -a 00:01:02:03:04:05 -s DUN -m secure Qc .Pp in your .Xr pppd 8 diff --git a/usr.bin/rfcomm_sppd/rfcomm_sppd.c b/usr.bin/rfcomm_sppd/rfcomm_sppd.c index f4a76f8e672a..e0d2f61e6690 100644 --- a/usr.bin/rfcomm_sppd/rfcomm_sppd.c +++ b/usr.bin/rfcomm_sppd/rfcomm_sppd.c @@ -1,4 +1,4 @@ -/* $NetBSD: rfcomm_sppd.c,v 1.6 2007/03/31 07:14:44 plunky Exp $ */ +/* $NetBSD: rfcomm_sppd.c,v 1.7 2007/04/21 06:15:24 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -62,7 +62,7 @@ __COPYRIGHT("@(#) Copyright (c) 2007 Iain Hibbert\n" "@(#) Copyright (c) 2006 Itronix, Inc.\n" "@(#) Copyright (c) 2003 Maksim Yevmenkin \n" "All rights reserved.\n"); -__RCSID("$NetBSD: rfcomm_sppd.c,v 1.6 2007/03/31 07:14:44 plunky Exp $"); +__RCSID("$NetBSD: rfcomm_sppd.c,v 1.7 2007/04/21 06:15:24 plunky Exp $"); #include #include @@ -82,13 +82,15 @@ __RCSID("$NetBSD: rfcomm_sppd.c,v 1.6 2007/03/31 07:14:44 plunky Exp $"); #include #include +#include + #include "rfcomm_sdp.h" #define max(a, b) ((a) > (b) ? (a) : (b)) int open_tty(const char *); -int open_client(bdaddr_t *, bdaddr_t *, const char *); -int open_server(bdaddr_t *, uint8_t, const char *); +int open_client(bdaddr_t *, bdaddr_t *, int, const char *); +int open_server(bdaddr_t *, uint8_t, int, const char *); void copy_data(int, int); void sighandler(int); void usage(void); @@ -129,7 +131,7 @@ main(int argc, char *argv[]) fd_set rdset; const char *service; char *ep, *tty; - int n, rfcomm, tty_in, tty_out; + int lm, n, rfcomm, tty_in, tty_out; uint8_t channel; bdaddr_copy(&laddr, BDADDR_ANY); @@ -137,9 +139,10 @@ main(int argc, char *argv[]) service = "SP"; tty = NULL; channel = 0; + lm = 0; /* Parse command line options */ - while ((n = getopt(argc, argv, "a:c:d:hs:t:")) != -1) { + while ((n = getopt(argc, argv, "a:c:d:hm:s:t:")) != -1) { switch (n) { case 'a': /* remote device address */ if (!bt_aton(optarg, &raddr)) { @@ -166,6 +169,18 @@ main(int argc, char *argv[]) break; + case 'm': /* Link Mode */ + if (strcasecmp(optarg, "auth") == 0) + lm = RFCOMM_LM_AUTH; + else if (strcasecmp(optarg, "encrypt") == 0) + lm = RFCOMM_LM_ENCRYPT; + else if (strcasecmp(optarg, "secure") == 0) + lm = RFCOMM_LM_SECURE; + else + errx(EXIT_FAILURE, "%s: unknown mode", optarg); + + break; + case 's': /* service class */ service = optarg; break; @@ -206,9 +221,9 @@ main(int argc, char *argv[]) /* open RFCOMM */ if (channel == 0) - rfcomm = open_client(&laddr, &raddr, service); + rfcomm = open_client(&laddr, &raddr, lm, service); else - rfcomm = open_server(&laddr, channel, service); + rfcomm = open_server(&laddr, channel, lm, service); /* * now we are ready to go, so either detach or maybe turn @@ -314,7 +329,7 @@ open_tty(const char *tty) } int -open_client(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) +open_client(bdaddr_t *laddr, bdaddr_t *raddr, int lm, const char *service) { struct sockaddr_bt sa; struct service *s; @@ -358,6 +373,9 @@ open_client(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) err(EXIT_FAILURE, "linger()"); + if (setsockopt(fd, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0) + err(EXIT_FAILURE, "link mode"); + sa.bt_channel = channel; bdaddr_copy(&sa.bt_bdaddr, raddr); @@ -377,7 +395,7 @@ open_client(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) #define pdu_len sizeof(struct sdp_lan_profile) int -open_server(bdaddr_t *laddr, uint8_t channel, const char *service) +open_server(bdaddr_t *laddr, uint8_t channel, int lm, const char *service) { struct sockaddr_bt sa; struct linger l; @@ -400,6 +418,9 @@ open_server(bdaddr_t *laddr, uint8_t channel, const char *service) err(EXIT_FAILURE, "bind(%s, %d)", bt_ntoa(laddr, NULL), channel); + if (setsockopt(sv, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0) + err(EXIT_FAILURE, "link mode"); + if (listen(sv, 1) < 0) err(EXIT_FAILURE, "listen()"); @@ -481,17 +502,20 @@ reset_tio(void) void usage(void) { + const char *cmd = getprogname(); struct service *s; - fprintf(stderr, "Usage: %s [-d device] [-s service] [-t tty] -a bdaddr | -c channel\n" + fprintf(stderr, "Usage: %s [-d device] [-m mode] [-s service] [-t tty]\n" + " %*s {-a bdaddr | -c channel}\n" "\n" "Where:\n" "\t-a bdaddr remote device address\n" "\t-c channel local RFCOMM channel\n" "\t-d device local device address\n" + "\t-m mode link mode\n" "\t-s service service class\n" "\t-t tty run in background using pty\n" - "\n", getprogname()); + "\n", cmd, strlen(cmd), ""); fprintf(stderr, "Known service classes:\n"); for (s = services ; s->name != NULL ; s++) diff --git a/usr.sbin/btdevctl/btdevctl.8 b/usr.sbin/btdevctl/btdevctl.8 index 2af72f769fd0..d1dbd1797ae7 100644 --- a/usr.sbin/btdevctl/btdevctl.8 +++ b/usr.sbin/btdevctl/btdevctl.8 @@ -1,4 +1,4 @@ -.\" $NetBSD: btdevctl.8,v 1.3 2006/09/10 15:45:56 plunky Exp $ +.\" $NetBSD: btdevctl.8,v 1.4 2007/04/21 06:15:24 plunky Exp $ .\" .\" Copyright (c) 2006 Itronix Inc. .\" All rights reserved. @@ -51,10 +51,10 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $Id: btdevctl.8,v 1.3 2006/09/10 15:45:56 plunky Exp $ +.\" $Id: btdevctl.8,v 1.4 2007/04/21 06:15:24 plunky Exp $ .\" $FreeBSD: src/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8,v 1.3 2005/01/18 20:02:30 ru Exp $ .\" -.Dd September 9, 2006 +.Dd April 10, 2007 .Dt BTDEVCTL 8 .Os .Sh NAME @@ -64,6 +64,7 @@ .Nm .Op Fl A | Fl D .Op Fl qv +.Op Fl m Ar mode .Fl a Ar address .Fl d Ar device .Fl s Ar service @@ -99,6 +100,29 @@ Detach device .It Fl d Ar device Local device address. May be given as BDADDR or device name. +.It Fl m Ar mode +Connection link mode. +The following modes are supported: +.Pp +.Bl -tag -compact +.It none +clear previously set mode. +.It auth +require devices be paired, see +.Xr btpin 1 . +.It encrypt +auth, plus enable encryption. +.It secure +encryption, plus change of link key. +.El +.Pp +When configuring the HID service, +.Nm +will set +.Sq auth +by default, or +.Sq encrypt +for keyboard devices. .It Fl q Ignore any cached data and perform a SDP query for the given .Ar service . @@ -136,6 +160,7 @@ to YES. .It Pa /var/db/btdevctl.plist .El .Sh SEE ALSO +.Xr btpin 1 , .Xr bthidev 4 , .Xr bthub 4 , .Xr btsco 4 , diff --git a/usr.sbin/btdevctl/btdevctl.c b/usr.sbin/btdevctl/btdevctl.c index d6a6ccd75576..8c28bb5528c8 100644 --- a/usr.sbin/btdevctl/btdevctl.c +++ b/usr.sbin/btdevctl/btdevctl.c @@ -1,4 +1,4 @@ -/* $NetBSD: btdevctl.c,v 1.3 2006/09/11 18:30:27 plunky Exp $ */ +/* $NetBSD: btdevctl.c,v 1.4 2007/04/21 06:15:24 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -34,7 +34,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2006 Itronix, Inc.\n" "All rights reserved.\n"); -__RCSID("$NetBSD: btdevctl.c,v 1.3 2006/09/11 18:30:27 plunky Exp $"); +__RCSID("$NetBSD: btdevctl.c,v 1.4 2007/04/21 06:15:24 plunky Exp $"); #include #include @@ -64,18 +64,21 @@ main(int argc, char *argv[]) prop_dictionary_t dev; prop_object_t obj; bdaddr_t laddr, raddr; - const char *service; - int ch, query, verbose, attach, detach; + const char *service, *mode; + int ch, query, verbose, attach, detach, set, none; bdaddr_copy(&laddr, BDADDR_ANY); bdaddr_copy(&raddr, BDADDR_ANY); service = NULL; + mode = NULL; query = FALSE; verbose = FALSE; attach = FALSE; detach = FALSE; + set = FALSE; + none = FALSE; - while ((ch = getopt(argc, argv, "Aa:Dd:hqs:v")) != -1) { + while ((ch = getopt(argc, argv, "Aa:Dd:hm:qs:v")) != -1) { switch (ch) { case 'A': /* Attach device */ attach = TRUE; @@ -103,6 +106,20 @@ main(int argc, char *argv[]) break; + case 'm': /* link mode */ + if (strcasecmp(optarg, "none") == 0) + none = TRUE; + else if (strcasecmp(optarg, BTDEVauth) == 0) + mode = BTDEVauth; + else if (strcasecmp(optarg, BTDEVencrypt) == 0) + mode = BTDEVencrypt; + else if (strcasecmp(optarg, BTDEVsecure) == 0) + mode = BTDEVsecure; + else + errx(EXIT_FAILURE, "%s: unknown mode", mode); + + break; + case 'q': query = TRUE; break; @@ -143,10 +160,26 @@ main(int argc, char *argv[]) if (dev == NULL) errx(EXIT_FAILURE, "%s/%s not found", bt_ntoa(&raddr, NULL), service); - if (!db_set(dev, &laddr, &raddr, service)) - errx(EXIT_FAILURE, "service store failed"); + set = TRUE; } + if (mode != NULL) { + obj = prop_string_create_cstring_nocopy(mode); + if (obj == NULL || !prop_dictionary_set(dev, BTDEVmode, obj)) + errx(EXIT_FAILURE, "proplib failure (%s)", BTDEVmode); + + prop_object_release(obj); + set = TRUE; + } + + if (none == TRUE) { + prop_dictionary_remove(dev, BTDEVmode); + set = TRUE; + } + + if (set == TRUE && !db_set(dev, &laddr, &raddr, service)) + errx(EXIT_FAILURE, "service store failed"); + /* add binary local-bdaddr */ obj = prop_data_create_data(&laddr, sizeof(laddr)); if (obj == NULL || !prop_dictionary_set(dev, BTDEVladdr, obj)) @@ -185,12 +218,13 @@ usage(void) { fprintf(stderr, - "usage: %s [-A | -D] [-qv] -a address -d device -s service\n" + "usage: %s [-A | -D] [-qv] [-m mode] -a address -d device -s service\n" "Where:\n" "\t-A attach device\n" "\t-a address remote device address\n" "\t-D detach device\n" "\t-d device local device address\n" + "\t-m mode link mode\n" "\t-q force SDP query\n" "\t-s service remote service\n" "\t-v verbose\n" diff --git a/usr.sbin/btdevctl/btdevctl.h b/usr.sbin/btdevctl/btdevctl.h index e5f207f03e6d..4fb7205ce824 100644 --- a/usr.sbin/btdevctl/btdevctl.h +++ b/usr.sbin/btdevctl/btdevctl.h @@ -1,4 +1,4 @@ -/* $NetBSD: btdevctl.h,v 1.2 2006/09/10 15:45:56 plunky Exp $ */ +/* $NetBSD: btdevctl.h,v 1.3 2007/04/21 06:15:24 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -43,5 +43,6 @@ void cfg_print(prop_dictionary_t); /* sdp.c */ prop_dictionary_t cfg_query(bdaddr_t *, bdaddr_t *, const char *); +const char *hid_mode(prop_data_t); #endif /* __BTDEVCTL_H__ */ diff --git a/usr.sbin/btdevctl/db.c b/usr.sbin/btdevctl/db.c index 0eb52c3ef430..5608b6df89b3 100644 --- a/usr.sbin/btdevctl/db.c +++ b/usr.sbin/btdevctl/db.c @@ -1,4 +1,4 @@ -/* $NetBSD: db.c,v 1.2 2007/04/11 19:59:02 plunky Exp $ */ +/* $NetBSD: db.c,v 1.3 2007/04/21 06:15:24 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -32,7 +32,7 @@ */ #include -__RCSID("$NetBSD: db.c,v 1.2 2007/04/11 19:59:02 plunky Exp $"); +__RCSID("$NetBSD: db.c,v 1.3 2007/04/21 06:15:24 plunky Exp $"); #include #include @@ -41,17 +41,19 @@ __RCSID("$NetBSD: db.c,v 1.2 2007/04/11 19:59:02 plunky Exp $"); #include #include +#include #include #include "btdevctl.h" #define BTDEVCTL_PLIST "/var/db/btdevctl.plist" -#define BTDEVCTL_VERSION 1 +#define BTDEVCTL_VERSION 2 static prop_dictionary_t db = NULL; static int db_flush = TRUE; /* write db on set */ static void db_update0(void); +static void db_update1(void); /* * lookup laddr/raddr/service in database and return dictionary @@ -73,10 +75,8 @@ db_get(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) } else { obj = prop_dictionary_get(db, "btdevctl-version"); switch(prop_number_integer_value(obj)) { - case 0: - db_update0(); - break; - + case 0: db_update0(); + case 1: db_update1(); case BTDEVCTL_VERSION: break; @@ -222,3 +222,58 @@ db_update0(void) db_flush = TRUE; /* write on set */ } + +/* + * update database from version 1. Link Mode capability was added. + * By default, we request authentication for HIDs, and encryption + * is enabled for keyboards. + */ +static void +db_update1(void) +{ + prop_dictionary_t ldev, rdev, srv; + prop_object_iterator_t iter0, iter1; + prop_dictionary_keysym_t key; + prop_object_t obj; + bdaddr_t bdaddr; + + iter0 = prop_dictionary_iterator(db); + if (iter0 == NULL) + err(EXIT_FAILURE, "prop_dictionary_iterator"); + + while ((key = prop_object_iterator_next(iter0)) != NULL) { + ldev = prop_dictionary_get_keysym(db, key); + if (prop_object_type(ldev) != PROP_TYPE_DICTIONARY + || !bt_aton(prop_dictionary_keysym_cstring_nocopy(key), &bdaddr)) + continue; + + iter1 = prop_dictionary_iterator(ldev); + if (iter1 == NULL) + err(EXIT_FAILURE, "prop_dictionary_iterator"); + + while ((key = prop_object_iterator_next(iter1)) != NULL) { + rdev = prop_dictionary_get_keysym(ldev, key); + if (prop_object_type(rdev) != PROP_TYPE_DICTIONARY + || !bt_aton(prop_dictionary_keysym_cstring_nocopy(key), &bdaddr)) + continue; + + srv = prop_dictionary_get(rdev, "HID"); + if (prop_object_type(srv) != PROP_TYPE_DICTIONARY) + continue; + + obj = prop_dictionary_get(srv, BTHIDEVdescriptor); + if (prop_object_type(obj) != PROP_TYPE_DATA) + continue; + + obj = prop_string_create_cstring_nocopy(hid_mode(obj)); + if (obj == NULL || !prop_dictionary_set(srv, BTDEVmode, obj)) + err(EXIT_FAILURE, "Cannot set %s", BTDEVmode); + + prop_object_release(obj); + } + + prop_object_iterator_release(iter1); + } + + prop_object_iterator_release(iter0); +} diff --git a/usr.sbin/btdevctl/print.c b/usr.sbin/btdevctl/print.c index 821abd6019cf..e6904738d0cf 100644 --- a/usr.sbin/btdevctl/print.c +++ b/usr.sbin/btdevctl/print.c @@ -1,4 +1,4 @@ -/* $NetBSD: print.c,v 1.7 2006/09/29 18:48:17 plunky Exp $ */ +/* $NetBSD: print.c,v 1.8 2007/04/21 06:15:24 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -58,7 +58,7 @@ */ #include -__RCSID("$NetBSD: print.c,v 1.7 2006/09/29 18:48:17 plunky Exp $"); +__RCSID("$NetBSD: print.c,v 1.8 2007/04/21 06:15:24 plunky Exp $"); #include @@ -99,6 +99,10 @@ cfg_print(prop_dictionary_t dict) } printf("remote bdaddr: %s\n", bt_ntoa(prop_data_data_nocopy(obj), NULL)); + obj = prop_dictionary_get(dict, BTDEVmode); + if (prop_object_type(obj) == PROP_TYPE_STRING) + printf("link mode: %s\n", prop_string_cstring_nocopy(obj)); + obj = prop_dictionary_get(dict, BTDEVtype); if (prop_object_type(obj) != PROP_TYPE_STRING) { printf("No device type!\n"); @@ -166,7 +170,7 @@ hid_parse(prop_data_t desc) hid_init(NULL); - r = hid_use_report_desc((unsigned char *)prop_data_data_nocopy(desc), + r = hid_use_report_desc(prop_data_data_nocopy(desc), prop_data_size(desc)); if (r == NULL) return; diff --git a/usr.sbin/btdevctl/sdp.c b/usr.sbin/btdevctl/sdp.c index 9a1af7f3f716..51c28e1d3935 100644 --- a/usr.sbin/btdevctl/sdp.c +++ b/usr.sbin/btdevctl/sdp.c @@ -1,4 +1,4 @@ -/* $NetBSD: sdp.c,v 1.2 2007/04/11 20:01:01 plunky Exp $ */ +/* $NetBSD: sdp.c,v 1.3 2007/04/21 06:15:24 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -55,7 +55,7 @@ */ #include -__RCSID("$NetBSD: sdp.c,v 1.2 2007/04/11 20:01:01 plunky Exp $"); +__RCSID("$NetBSD: sdp.c,v 1.3 2007/04/21 06:15:24 plunky Exp $"); #include @@ -213,6 +213,7 @@ config_hid(prop_dictionary_t dict) reconnect_initiate, battery_power, normally_connectable, hid_length; uint8_t *hid_descriptor; + const char *mode; int i; control_psm = -1; @@ -286,6 +287,13 @@ config_hid(prop_dictionary_t dict) if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj)) return errno; + mode = hid_mode(obj); + prop_object_release(obj); + + obj = prop_string_create_cstring_nocopy(mode); + if (obj == NULL || !prop_dictionary_set(dict, BTDEVmode, obj)) + return errno; + prop_object_release(obj); if (!reconnect_initiate) { @@ -764,3 +772,37 @@ parse_rfcomm_channel(sdp_attr_t *a) return (channel); } + +/* + * return appropriate mode for HID descriptor + */ +const char * +hid_mode(prop_data_t desc) +{ + report_desc_t r; + hid_data_t d; + struct hid_item h; + const char *mode; + + hid_init(NULL); + + mode = BTDEVauth; /* default */ + + r = hid_use_report_desc(prop_data_data_nocopy(desc), + prop_data_size(desc)); + if (r == NULL) + err(EXIT_FAILURE, "hid_use_report_desc"); + + d = hid_start_parse(r, ~0, -1); + while (hid_get_item(d, &h) > 0) { + if (h.kind == hid_collection + && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP + && HID_USAGE(h.usage) == HUG_KEYBOARD) + mode = BTDEVencrypt; + } + + hid_end_parse(d); + hid_dispose_report_desc(r); + + return mode; +}