BlueZ kernel的连接机制分析

A2DP SOURCE CONNECT

简单总结:

struct l2cap_chan :表示应用层一个socket + PSM,比如SDP/AVDTP/AVCTP
struct hci_conn :表示kernel底层一个实际的物理连接,以唯一的MAC地址为区分

struct hci_conn *conn;
状态机:
hci_acl_create_connection
    conn->state = BT_CONNECT;

hci_conn_complete_evt
    conn->state = BT_CONFIG;
    hci_remote_ext_features_evt

hci_remote_ext_features_evt
    conn->state = BT_CONNECTED;

//底层连接通道建立后:
hci_connect_cfm //反馈给L2CAP进行处理
    l2cap_connect_cfm
        struct l2cap_conn *conn;  指向 struct hci_conn *conn;
        /* Find fixed channels and notify them of the new connection.*/
        //给l2cap_conn 找一个 fix chn 的 l2cap_chan ,主要用来做smp相关的
        __l2cap_chan_add(conn, chan);
            chan->conn = conn;
    l2cap_conn_ready(conn);

//[l2cap_state_change] chan 00000000950c1207 BT_BOUND -> BT_CONNECT  //l2cap chan的状态机
l2cap_conn_ready
    //首先做一个l2cap info 交互
    if (hcon->type == ACL_LINK)
        l2cap_request_info(conn);
    [l2cap_conn_ready] fix chan [7, 4, 2, 4]     state: BT_OPEN
    [l2cap_conn_ready] co chan [64, 3, 5, 4] state: BT_CONNECT //l2cap chan的状态机

    if (chan->state == BT_CONNECT)
            l2cap_do_start(chan);
                //由于L2CAP_INFO_REQ没有完成,所以啥也不做
                if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
                    return;

//l2cap info 交互完成
l2cap_information_rsp
    case L2CAP_IT_FEAT_MASK:
    case L2CAP_IT_FIXED_CHAN:
    l2cap_conn_start(conn);

//开始建立l2cap conn
l2cap_conn_start(struct l2cap_conn *conn)
    //便利挂在l2cap_conn上面的所有的l2cap_chan
    list_for_each_entry_safe(chan, tmp, &conn->chan_l, list)
        //fix chan
        if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) 
           l2cap_chan_ready(chan);
                chan->state = BT_CONNECTED; //fix chan connected
                smp_ready_cb
                    conn->smp = chan;
                    if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
                        bredr_pairing(chan); //如果满足条件,进行smp

        //conn oriented
        if (chan->state == BT_CONNECT)
            l2cap_chan_check_security
                hci_conn_security //由于是SDP,无需加密
            l2cap_start_connection(chan);//启动l2cap连接


l2cap_connect_create_rsp
    l2cap_state_change(chan, BT_CONFIG); //l2cap chan的状态机
    //连接成功后,发起配置请求
    l2cap_send_cmd(L2CAP_CONF_REQ)

//对端回应配置请求
l2cap_config_rsp
//我们回应对方的配置请求
l2cap_config_req
    CONF_OUTPUT_DONE
    CONF_INPUT_DONE
    l2cap_chan_ready(chan);

//l2cap_chan 就绪的前提是l2cap conf 配置完成
l2cap_chan_ready
    chan->state = BT_CONNECTED;//l2cap chan的状态机
    chan->ops->ready(chan);
    l2cap_sock_ready_cb
        sk->sk_state = BT_CONNECTED;
        sk->sk_state_change(sk);
    
sk->sk_state = BT_CONNECTED;
//至此,完成了一次上层的SDP的 socket connect 操作,包括两个状态机,由于SDP无需加密,所以比较简单

//下面相同的流程再走一遍,这次是AVDTP socket connect操作,不同点用红色表示,相比SDP而已
[l2cap_chan_create] chan 00000000db3a6a9d
[l2cap_chan_connect] 4d:11:f2:1e:3c:c9 -> f0:13:c3:50:ff:26 (type 0) psm 0x19 (3, 0 3) //PSM 0X19 AVDTP
    //对应同一个ACL连接而已,这两个结构体都是对所有的L2CAP共用!
    struct l2cap_conn *conn;
    struct hci_conn *hcon;
    //把新建立的l2cap_chan 挂到 l2cap_conn上面
    [__l2cap_chan_add] conn 0000000080dc1834, psm 0x19, dcid 0x0000, scid 0x0041, ct: 3, flags: 0x2
        list_add(&chan->list, &conn->chan_l);
    //AVDTP chan的状态机
    l2cap_state_change(chan, BT_CONNECT);
    //struct hci_conn *hcon; ACL连接再SDP阶段已经完成,所有是connected!
    if (hcon->state == BT_CONNECTED)
        l2cap_do_start(chan);

l2cap_do_start
    //重点:发起l2cap conn之前,要检查安全级别
    l2cap_chan_check_security(chan, true)
        //由于是AVDTP,则加密级别提高为:BT_SECURITY_MEDIUM -> HCI_AT_GENERAL_BONDING
        [l2cap_get_auth_type] sl: 2, psm: 25, ct: 3
        //ACL LINK, BT_SECURITY_MEDIUM HCI_AT_GENERAL_BONDING
        [hci_conn_security] hcon 000000007c312d12, te: 1, sl: 2, at: 4, ir: 1, kt: 0xff, flags: 0x4c0
            /* For other security levels we need the link key. */
            if (!test_bit(HCI_CONN_AUTH, &conn->flags))   
                //如果此ACL链路没有认证过
                hci_conn_auth(conn, sec_level, auth_type)
                    //发起认证
                    [hci_send_cmd] hci0 opcode 0x0411 plen 2
                    //并且设置加密等待
                    set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
    

//下面开始一个认证流程

//首先LMP会向host询问是否有配对过的有效的LINK KEY
[hci_link_key_request_evt] hci0 link key not found for f0:13:c3:50:ff:26

//没有,则询问host的io capa
[hci_io_capa_request_evt] 0x10ca18030, 0x204c5, 0xff, 0x3
[hci_io_capa_request_evt] capa: 0x3, authentication: 0x4
    //reply给LMP
    hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
                 sizeof(cp), &cp);

//LMP给的反馈
[hci_io_capa_reply_evt] hci0, 0x3, 0x4
    conn->remote_cap = ev->capability;  0x3
    conn->remote_auth = ev->authentication; 0x4

[hci_user_confirm_request_evt] hci0 0x4, 0x4, flags: 0x204c5
[hci_user_confirm_request_evt] hci0 lm: 0, rm: 0, psl: 0x2, rcap: 0x3
[hci_user_confirm_request_evt] Auto-accept of user confirmation with 0ms delay
    /* If no side requires MITM protection; auto-accept */
    HCI_IO_NO_INPUT_OUTPUT 无需 MITM,自动接收确认
    hci_send_cmd: HCI_OP_USER_CONFIRM_REPLY

//Event Code    Simple Pairing Complete
hci_simple_pair_complete_evt

//HCI Link Key Notification (F0:13:C3:50:FF:26, Key=4B120A2E:0E5F443D:145E8660:F851DF75, Type=Unauthenticated-192)
hci_link_key_notify_evt
    hci_add_link_key(hdev, conn, &ev->bdaddr, ev->link_key,
                    ev->key_type, pin_len, &persistent);
    mgmt_new_link_key(hdev, key, persistent);

[hci_auth_complete_evt] hci0 status 0x00 state 1
    //完成认证
    set_bit(HCI_CONN_AUTH, &conn->flags);
    conn->sec_level = conn->pending_sec_level;

//开始加密相关

hci_encrypt_change_evt
    hci_auth_cfm(conn, ev->status);
        if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
            return; //认证还没有完成,需要去加密
    if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)
        hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT
        [hci_send_cmd] hci0 opcode 0x0413 plen 3  //发起加密

hci_encrypt_change_evt
    Encryption Enabled    On (BR/EDR E0, LE AES-CCM)
    /* Encryption implies authentication */
    set_bit(HCI_CONN_AUTH, &conn->flags);
    set_bit(HCI_CONN_ENCRYPT, &conn->flags);
    conn->sec_level = conn->pending_sec_level;
    //sec_level: 2, conn->flags: 0x1234c4
    clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);

    /* Try reading the encryption key size for encrypted ACL links */
    if (!ev->status && ev->encrypt && conn->type == ACL_LINK)
        hci_req_add(&req, HCI_OP_READ_ENC_KEY_SIZE
        [hci_req_add_ev] hci0 opcode 0x1408 plen 2

read_enc_key_size_complete
    hci_encrypt_cfm(conn, 0);
        list_for_each_entry(cb, &hci_cb_list, list)
        cb->security_cfm(conn, status, encrypt);

l2cap_security_cfm
    //遍历l2cap_conn上面的所有l2cap_chan (PSM的代码表示)
    list_for_each_entry(chan, &conn->chan_l, list)
    [l2cap_security_cfm] conn 0000000080dc1834 status 0x00 encrypt 1
    [l2cap_security_cfm] chan 00000000db3a6a9d AVDTP scid 0x0041 state BT_CONNECT
        if (chan->state == BT_CONNECT) 
            l2cap_start_connection(chan); //至此,AVDTP的安全机制已完成,则现在可以开始L2CAP CONN!!!,流程参考上面的SDP

    [l2cap_security_cfm] chan 0000000061cd910d SMP OVER BR/EDR scid 0x0007 state BT_CONNECTED
    [l2cap_security_cfm] chan 00000000ee31465e SDP scid 0x0040 state BT_CONNECTED

l2cap_start_connection
l2cap_connect_create_rsp

    [l2cap_connect_create_rsp] dcid 0x0601 scid 0x0041 result 0x01 status 0x02
    [l2cap_connect_create_rsp] dcid 0x0601 scid 0x0041 result 0x00 status 0x00
    l2cap_state_change(chan, BT_CONFIG);
    [l2cap_state_change] chan 00000000db3a6a9d BT_CONNECT -> BT_CONFIG
    l2cap_send_cmd L2CAP_CONF_REQ

//对端回应配置请求
l2cap_config_rsp
//我们回应对方的配置请求
l2cap_config_req
    CONF_OUTPUT_DONE
    CONF_INPUT_DONE
    l2cap_chan_ready(chan);

l2cap_chan_ready
[l2cap_chan_ready] chan: 00000000db3a6a9d
[l2cap_chan_ready] [00000000db3a6a9d] chan->state = BT_CONNECTED
sk->sk_state = BT_CONNECTED;
//至此,完成了AVDTP的 socket connect 操作,包括两个状态机,由于AVDTP需要加密,所以比较复杂!

疑问:为什么AVDTP没有触发FIXED CHAN 的 SMP机制?
//一 只有下面的case才能跑到SMP,但l2cap_information_req/rsp只有在ACL建立后仅执行一次,所以第二次无法跑到这里!
l2cap_information_rsp
    l2cap_conn_start
        list_for_each_entry_safe(chan, tmp, &conn->chan_l, list)
        if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
            l2cap_chan_ready(chan);
            l2cap_chan_unlock(chan);
            continue;
        }   

//二 

hci_encrypt_cfm
    if (conn->state == BT_CONFIG) {
        if (!status)
            conn->state = BT_CONNECTED;

        hci_connect_cfm(conn, status);
        hci_conn_drop(conn);
        return;
    }


        
//接下来第三次AVDTP TRANSPORT,上面那个是AVDTP SIG通道,一个传输控制数据,另一个传输音频数据(两个L2CAP的连接,L2CAP的作用)

//类比:TCP/UDP 层
//L2CAP的SCID/DCID,就是端口的概念!!!
//ACL链路类比 数据链路层!!!,蓝牙没有IP层的类比,它不想以太网有IP地址及MAC地址!

[l2cap_chan_create] chan 00000000a34acb53
[l2cap_sock_bind] sk 000000004f83a7ce, l2_cid: 0, l2_psm: 0, sl: 1, ct: 3, mode: 0
[l2cap_chan_connect] 4d:11:f2:1e:3c:c9 -> f0:13:c3:50:ff:26 (type 0) psm 0x19 (3, 0 3)
[l2cap_get_auth_type] sl: 2, psm: 25, ct: 3
[l2cap_state_change] chan 00000000a34acb53 BT_BOUND -> BT_CONNECT
[l2cap_chan_connect] hcon->state: 1
[l2cap_chan_connect] l2cap_do_start

l2cap_do_start
[l2cap_get_auth_type] sl: 2, psm: 25, ct: 3
[hci_conn_security] hcon 000000007c312d12, te: 1, sl: 2, at: 4, ir: 1, kt: 0x4, flags: 0x1234c0
    HCI_CONN_AUTH 已经置位
    //这是之前的link key
    HCI Link Key Notification (F0:13:C3:50:FF:26, Key=4B120A2E:0E5F443D:145E8660:F851DF75, Type=Unauthenticated-192)
    /* An unauthenticated combination key has sufficient security for security level 1 and 2. */
    if ((conn->key_type == HCI_LK_UNAUTH_COMBINATION_P192 ||
         conn->key_type == HCI_LK_UNAUTH_COMBINATION_P256) &&
        (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW))
        goto encrypt;
    encrypt:
        //之前已经加密完成!
        if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))    
            if (!conn->enc_key_size)
                return 0;
            /* Nothing else needed, all requirements are met */
            return 1;

if (l2cap_check_enc_key_size(conn->hcon)) //检查下enc key
        l2cap_start_connection(chan);
... ...

SPP 被动监听

SPP
被动连接:
hci_cs_create_conn
    conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr, HCI_ROLE_MASTER);
    struct hci_conn *conn = kzalloc(sizeof(*conn), GFP_KERNEL);
    conn->state = BT_OPEN;
    conn->role  = HCI_ROLE_MASTER;
    conn->auth_type = HCI_AT_GENERAL_BONDING;
    if (conn->role == HCI_ROLE_MASTER)
        conn->out = true;
    case ACL_LINK:
        conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
    hci_conn_hash_add(hdev, conn);

hci_conn_complete_evt
    [hci_conn_complete_evt]  flags: 0x1d, cflags: 0x400, at: 4, out: 0, 0x5,0xd,0x6,0xc
    /* Get remote features */
    hci_send_cmd HCI_OP_READ_REMOTE_FEATURES

hci_remote_features_evt
    hci_send_cmd HCI_OP_READ_REMOTE_EXT_FEATURES

hci_remote_ext_features_evt
    [hci_remote_ext_features_evt] flags: 0x580, 0x8,0x7
    set_bit(HCI_CONN_SC_ENABLED, &conn->flags);
    set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
    hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ,

    //[hci_outgoing_auth_needed] flags: 0x580, 0x7,0x0,0x0,0x4.
    if (!hci_outgoing_auth_needed(hdev, conn))
        conn->state = BT_CONNECTED;
        BT_INFO(" %d BT_CONNECTED to hci_connect_cfm", __LINE__);
        hci_connect_cfm(conn, ev->status);

hci_connect_cfm
    l2cap_connect_cfm
        [l2cap_connect_cfm] hcon 000000001cf0a359 bdaddr 94:87:e0:b6:6d:ae status 0
        [l2cap_connect_cfm] hcon flags: 0x580, 0x1,0x0,0x0,0x4
        struct hci_conn *hcon
        struct l2cap_conn *conn;
        //这里仅创建l2cap_conn/hci_chan,并没有l2cap_chan
        conn = l2cap_conn_add(hcon);
            hchan = hci_chan_create(hcon);
            conn = kzalloc(sizeof(*conn), GFP_KERNEL);
            hcon->l2cap_data = conn;
            conn->hcon = hci_conn_get(hcon);
            conn->hchan = hchan;

        struct l2cap_chan *pchan;
        //找到smp root
        pchan = l2cap_global_fixed_chan(NULL, hcon);
            chan = pchan->ops->new_connection(pchan);
            smp_new_conn_cb
            //创建l2cap_chan
            __l2cap_chan_add(conn, chan);

        l2cap_conn_ready
            if (hcon->type == ACL_LINK)
                l2cap_request_info(conn);  //发起L2CAP初始的INFO交互
            list_for_each_entry(chan, &conn->chan_l, list)
               [l2cap_conn_ready] chan SMP 00000000cee8bdf1 [7, 4, 2, 4]

l2cap_information_rsp
[l2cap_information_rsp] type 0x0002 result 0x00
[l2cap_information_rsp] type 0x0003 result 0x00
    l2cap_conn_start
        list_for_each_entry_safe(chan, tmp, &conn->chan_l, list)
            [l2cap_conn_start] chan 00000000cee8bdf1, type: 4, state: 2
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
                    l2cap_chan_ready(chan);

l2cap_chan_ready
 [l2cap_chan_ready]  SMP [00000000cee8bdf1] chan->state = BT_CONNECTED
    chan->ops->ready(chan);
    [smp_ready_cb] chan 00000000cee8bdf1

//

hci_io_capa_reply_evt
[hci_io_capa_reply_evt] hci0, 0x1, 0x5
//手机侧发来它的io capa
//HCI IO Capability Response (94:87:E0:B6:6D:AE, IoCapability=Yes/No, RemoteOobDataPresent=None, Auth=MITM+General)
    conn->remote_cap = ev->capability; 
    conn->remote_auth = ev->authentication;

//手机侧询问我们的io
[hci_io_capa_request_evt] 0x10cb18030, 0x5c0, 0x5, 0x3
[hci_get_auth_req] 0x5, 0x4, 0x1, 0x3
[hci_io_capa_request_evt] capa: 0x3, authentication: 0x4
//HCI IO Capability Request Reply (94:87:E0:B6:6D:AE, IoCapability=None, HostOobDataPresent=None, Auth=!MITM+General)

[hci_user_confirm_request_evt] hci0 0x4, 0x5, flags: 0x5c0
[hci_user_confirm_request_evt] hci0 lm: 0, rm: 1, psl: 0x0, rcap: 0x1
[hci_user_confirm_request_evt] Auto-accept of user confirmation with 0ms delay

[hci_simple_pair_complete_evt] hci0
[hci_link_key_notify_evt] hci0
[hci_add_link_key] hci0 key for 94:87:e0:b6:6d:ae type 7
[hci_encrypt_change_evt] 2878, sec_level: 2, conn->flags: 0x1035c0
            /* Encryption implies authentication */
            set_bit(HCI_CONN_AUTH, &conn->flags);
            set_bit(HCI_CONN_ENCRYPT, &conn->flags);
[read_enc_key_size_complete] hci0 status 0x00

read_enc_key_size_complete
    if (conn->state == BT_CONFIG) {  //这就是为什么单独SPP连接可以进SMP流程的原因!!!
        if (!status)
            conn->state = BT_CONNECTED;

        hci_connect_cfm(conn, status);
        hci_conn_drop(conn);
        return;
    }
[hci_encrypt_cfm] 1297, conn->state: 1, conn->sec_level: 2
    list_for_each_entry(cb, &hci_cb_list, list) {
        if (cb->security_cfm)
            cb->security_cfm(conn, status, encrypt);
    }

    [l2cap_security_cfm] conn 00000000ee457177 status 0x00 encrypt 2
    [l2cap_security_cfm] chan 00000000cee8bdf1 scid 0x0007 state BT_CONNECTED
        if (!status && (chan->state == BT_CONNECTED ||
                chan->state == BT_CONFIG)) {
            chan->ops->resume(chan);
            smp_resume_cb
        }

    [rfcomm_security_cfm] conn 000000001cf0a359 status 0x00 encrypt 0x02


smp_resume_cb
    if (hcon->type == ACL_LINK) {
        bredr_pairing(chan);
        return;
    }    

bredr_pairing
    ... ...

l2cap_connect_req

    l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
        /* Check if we have socket listening on psm */
        //rfcomm_run 创建l2cap_chan 
        pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, &conn->hcon->dst, ACL_LINK);
        //创建新的sock 及 l2cap_chan
        chan = pchan->ops->new_connection(pchan);
            //.name            = "L2CAP Socket Interface",
            //.new_connection        = l2cap_sock_new_connection_cb, 

            l2cap_sock_new_connection_cb
                    sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC, 0);
                    l2cap_sock_init(sk, parent);
                    bt_accept_enqueue(parent, sk, false);
						list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q);
						bt_sk(sk)->parent = parent;
                    return l2cap_pi(sk)->chan;

            bacpy(&chan->src, &conn->hcon->src);
            bacpy(&chan->dst, &conn->hcon->dst);
            chan->src_type = bdaddr_src_type(conn->hcon);
            chan->dst_type = bdaddr_dst_type(conn->hcon);
            chan->psm  = psm;
            chan->dcid = scid;
            __l2cap_chan_add(conn, chan);
            l2cap_state_change(chan, BT_CONFIG);
            result = L2CAP_CR_SUCCESS;

l2cap_config_rsp
[l2cap_chan_ready] [00000000bef51779] chan->state = BT_CONNECTED
    chan->state = BT_CONNECTED;//l2cap chan的状态机
    chan->ops->ready(chan);
    l2cap_sock_ready_cb

l2cap_sock_ready_cb
    sk->sk_state = BT_CONNECTED;
    sk->sk_state_change(sk);

    if (parent)
        parent->sk_data_ready(parent);

sk_data_ready
    rfcomm_l2data_ready

... ...

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值