ble- ATT profile詳解

閒話

  • 熟視無睹的東西,更需要更加深入的研究,ATT作爲最基本的ble常識,很多時候並沒有深刻的總結
  • 專精自己熟悉的領域,其次瞭解更多周邊領域,要足夠體現自己的專業性,而不是浮於表面
  • 溝通的前提,是要思路清晰,明白對方的意圖,有待加強

從數據格式瞭解ATT

在這裏插入圖片描述

Opcode

  • 總體分爲6種大類型的Opcode

request / response
indicate / confirm
command
notify

  • 所有的ATT數據,都是屬於這6種類型的子類
  • request 和command從client 端發起,區別是request 必須要有response ,而command 不需要,單向
  • indicate和notify從server 端發起,區別是indicate必須要有confirm 返回,而notify不需要,單向
  • opcode 佔用1個字節

ATT 參數

att 參數主要分爲三個部分,但是並不是所有ATT 數據包都需要全部包含這三部分內容

- ATT TYPE
由uuid 決定,uuid有16bit ,32bit 和128bit long uuid幾種格式 ,一些常用的uuid,比如primary service,characteristic 等等由SIG 官方發佈,可以查表。用戶也可以自定義uuid

32bit uuid在PDU發送的時候,需要轉成128bit發送,轉換公式如下:
128_bit_value = 16_bit_value * 296 + Bluetooth_Base_UUID
128_bit_value = 32_bit_value * 296 + Bluetooth_Base_UUID

16bit uuid 在發送的時候,沒有這個要求,ATT解析uuid 支持16bit 和128bit 兩種格式

- ATT handle
所有ATT數據都是通過handle 去訪問,handle 類似於數組的index,每個att 數據都有唯一的handle去與之對應,
handle 是一個1-0xFFFF直接的數據,每個handle依次增加

- ATT value
ATT 數據值是根據ATT type決定存儲的格式,可以是一個字符串或者一個handle值。
根據該條ATT type,也可以解析其數據
數據的整體空間可以爲定長,也可以爲變化長度

數據加密

  • 可以通過設置opcode bit7,需要加密的ATT數據,將會佔有12個字節的空間
  • 如果有打開,加密信息會附加到ATT PDU末尾,對端設備需要有key 才能讀取該條ATT數據
  • ATT數據的實際使用空間爲 未加密:ATT_MTU-1 加密:ATT_MTU-13

從opcode瞭解ATT操作流程

在這裏插入圖片描述

0x01 ERROR_RSP

  • 報錯的opcode,handle,是什麼錯誤(error code,1個字節)
  • 經常error rsp用於表示流程結束標誌
  • 具體的error code ,此處不貼圖,可以查閱spec
  • 所佔字節數1+1+2+1

0x02 EXCHANGE_MTU_REQ/ 0x03 EXCHANGE_MTU_RSP

  • request ,client向server發起交換mtu 的請求,request中攜帶自己支持的mtu值(2字節)
  • server 端收到之後,會根據自己所支持的mtu值與接收到的比較,取較小的值,回覆給client端
  • 在沒有收到exchange rsp 之前,雙方用默認值ATT_MTU=23
  • ATT_MTU大小範圍在23-512之間
  • 如果ATT 鏈接建立過程中沒有MTU request ,雙方使用默認值

0x04 FIND_INFORMATION_REQ /0x05 FIND_INFORMATION_RSP

  • request,client 發給server,大海撈針。
  • client 不知道server有定義哪些服務,告訴一個區間,返回server 定義的ATT類型
  • att支持的類型是有uuid 決定的,uuid有兩種,16bit,128bit,response 中format 決定
  • 返回的信息需要告訴handle 和uuid 成組的信息,handle-uuid成對
  • requset 格式: opcode(1)+start handle(2)+end handle(2)
  • response格式:opcode(1)+ format(1)+ [ handle- uudi ] +[handle -uuid]
  • 一般用於discover具體charteritic 的值,搜索範圍會很小,精確讀取未知handle的att 類型

0x06 FIND_BY_TYPE_VALUE_REQ/0x07 FIND_BY_TYPE_VALUE_RSP

  • 另外一種大海撈針的查找方式,較於find information request,多一個uuid和value 限制條件
  • start ,end 定義一個查找區間
  • 要查找什麼類型,設置uuid過濾條件,比如primary service
  • att value,設置att value過濾條件,比如0x1801,定義GAP service值
  • 一般常用於discover service 開頭,將範圍設置爲0-0xffff,返回GAP祥光的primary service,過濾條件能夠減少返回的數量
  • 0x04,0x05,0x06,x07主要用於初次獲取對端ATT 的類型,返回值都是type類型。拿到對端的類型之後,才能通過恰當的方式去操作ATT,讀取ATT攜帶的數據

0x08 READ_BY_TYPE_REQ/ 0x09 READ_BY_TYPE_RSP

  • 需要知道server段關於目標類型的ATT 數據分佈情況
  • 常見的UUID type 包括:include,characteristic,PNP ID等等
  • 此類UUID一般是廣泛的搜索,宏觀的定義
  • 返回信息 handle+value,返回信息value是搜索類型的申明ATT信息,handle是指搜索類型對應的handle。
  • 所有定義的類型,第一條ATT就是申明
    在這裏插入圖片描述
    以一個栗子說明,RSP中的數據結構
    handle = 50, 該characteristic 申明對應的handle
    value :包含三部分:
    properties屬性,包括這個特徵的操作方式,每個characteristic中都包含這個部分
    value handle, 指定這個charteristic實際值保存的位置,可以被操作的對象
    characteristic uuid,指定這個可操作值value 的類型,可以理解爲characteristic的子類
    特徵申明handle一般和實際value handle相鄰,discover 的最終目的是通過申明找到操作value的方法,
    然後通過數據所在的handle去操作

0x0A READ_REQ / 0x0B READ_RSP

  • 通過read by type獲取到了操作方法及value對應的handle 值,下一步就是直接去獲取handle對應的value

  • 在讀這個handle之前,必須要先獲取到對端的類型,只有這樣,返回的值才能夠被正確解析

  • SIG有規定部分類型的值 的數據格式,用戶也可以自定義數據類型及其對應值的格式

  • reuqust: opcode(1)+handle(2)

  • response:opcode(1)+value(最長爲mtu-1)

0x0C READ_BLOB_REQ / 0x0D READ_BLOB_RSP

  • 如果ATT攜帶數據超過了MTU最大值,第一次Read 肯定讀取不完,這種情況下需要使用多次讀取。

  • 如何判斷數據超長,一次沒有讀取完? 第一次read response中,數據 size 等於(最大值-1),則馬上改用 read
    blob req讀取餘下的所有值, 直到response中 數據size 小於(最大值-1)

  • 讀取的過程中,request 會傳遞offest=(最大值-1)的計數,保證數據完整性。每讀取一次offset累加(最大值-1)

  • 因此read blob request一般都在第一次read request 之後用

  • request:opcode(1)+handle(2)+offset(2)

  • response:opcode(1)+value(最長爲mtu-1)

0x0e READ_MULTIPLE_REQ / 0x0f READ_MULTIPLE_RSP

  • 如果遇到多個value size較小,多次讀取顯然不是一個明智的選擇,
  • read mutil request能夠實現一次讀取多個value值
  • 但是有一些風險需要注意:
    1. 任何一個handle,單獨read 有error,整個rsp都會fail,如: Insufficient Authorization,Insufficient Encryption Key Size.
    2. 必須要保證每個value size在上層都是已知且固定的,並且總大小不超過mtu-1,方便解析接收的數據
    3. 並不要求每個value size一致
    4. value總大小 = mtu-1,避免使用該命令,無法判斷數據有全部讀完
    5. 如果value 列表總長超過了mtu-1, 只會返回第一個value值

0x10 READ_BY_GROUP_TYPE_REQ / 0x11 READ_BY_GROUP_TYPE_RSP

  • ATT group 是一個虛擬的概念,並沒有指定的數據,明確表示具體的ATT屬於哪一個service group

  • ATT數據結構中最大的單元是service。以service 爲例, 每個service,以service declaration開頭,以下一個service declaration結束,在這兩個handle直接就屬於同一個service group
    以GATT數據框架爲例說明:在這裏插入圖片描述
    每一個矩形框就是一個ATT數據,不同的ATT數據根據類型分佈組成各個不同group

  • rsp 返回的是改group 的start handle 和end handle

  • 並沒有特殊的group type 定義,一般搜索的也是宏觀的uuid,例如:primary service 0x0028

  • req 中指定 搜索範圍, group type 類型
    opcode(1)+start handle(2)+end handle(2)+uuid(2或16) 0x0028

  • resp 如果總長超過mtu-1,只會第一組數據,每組數據格式如下:start handle(2)+end handle(2)+uuid(2或16)0x180f
    返回的uuid 是服務聲明的類型,具體的service 類型,例如:電量服務0x180f

  • 重點理解這兩個uuid 的含義:
    1.primary service 0x0028 服務類型,具體信息:電量服務0x180f
    2.一個服務的聲明,是一個ATT數據,包括一個handle,type(電量服務0x180f),服務類型(0x2800/0x2801)

0x12 WRITE_REQ / 0x13 WRITE_RSP

  • 用於向已知handle 寫入值
  • 如果寫成功,會有write RSP返回
  • write 用於已經知道所操作handle 的數據類型,get db 結束之後
  • opcode(1)+handle(2)+value(最多mtu-3)

0x52 WRITE_CMD / 0xD2 SIGNED_WRITE_CMD

  • 用於client向 service寫入數據,單向寫入,沒有反饋
  • 典型的 點控ATT,control-point attribute
  • 兩者的區別,後者攜帶加密信息,opcode bit7 置 1,所以可攜帶的數據大小最多爲(mtu-15)

0x16 PREPARE_WRITE_REQ/ 0x17 PREPARE_WRITE_RSP

  • 針對超長ATT value的操作,可以分批次多次寫入
  • req 中寫的寫入目標handle ,offset,value
  • rsp 中攜帶已經完成寫入的handle ,offset
  • 如果req 和rsp 中handle 和offset相同,則表明寫入成功
  • 這個命令執行完必須搭配下一個命令,才能生效,server端僅僅只是cache這個value,還沒有寫入。

0x18 EXECUTE_WRITE_REQ / 0x19 EXECUTE_WRITE_RSP

  • 搭配上一個命令使用,包含兩種狀態,生效或者取消
  • server接受到這個命令後,會根據狀態,執行對應的操作,寫入或者取消
  • 在server 端,這個命令是原子操作,不可以被中斷

0x1b HANDLE_VALUE_NTF

  • server主動發起,notify 可以在任何時候發出,不需要client 回覆
  • 一個notify中攜帶 opcode(1)+handle(2)+value(最多mtu-3)
  • 常見的hid device 都是使用這種方式發送鍵值信息

0x1d HANDLE_VALUE_IND / 0x1e HANDLE_VALUE_CFM

  • 又一種主動從server 端發起的ATT類型
  • 一個indication攜帶的數據 opcode(1)+handle(2)+value(最多mtu-3)
  • 與notify最大的區別是,需要client回覆comfirm,如果回覆超時,ACL 對斷開
  • 如果client 接收到indication,會回覆confirm,攜帶opcode(1)
  • 一次只能發送一個indication,前一個indication對應的confirm沒有拿到之前,不會發送下一個,處於block狀態

0x23 MULTIPLE_HANDLE_VALUE_NTF

  • 一個notify 中可以包含多個handle value值
  • 一組數據中需要攜帶每一個handle value 長度值,告訴client 如何分割數據
  • 如果對應的handle 無效,client 會忽略該值
  • 任何時候server都可以發送該notify,不需要client回覆
  • length 長度爲2個字節
  • 一組數據的格式爲length(2)+ handle(2)+value(length-4)

安全相關

  1. ATT 的訪問時建立在一條已經加密過的acl 之上
  2. 加密的相關流程在GAP中完成
  3. 在air log中,輸入link key,就能夠解析出ATT層的具體數據信息
  4. 每條ATT可以設置獨立的安全機制,可以設置被寫或者被讀
  5. 當client端操作一個ATT之前,server端需要先檢查其具備的權限
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章