ICE學習筆記 -- RFC 5245

RFC 5245 ICE  
1, offer/answer model
2, ICE Step:
   1) 產生候選地址(1.公網 2.NAT反射 3.Relay轉發地址) Generate candidates ( host candidates, server reflexive candidates, peer reflexive candidates, and relayed candidates )
   2) 本地對端交換候選地址,完成鏈接性檢查。(用RTP包,一個四次握手的過程,以下圖:)

        

   3) 根據優先級進行排序                              Sorting Candidates
   4) 凍結候選地址                                    Frozen Candidates
   5) 安全性檢查                                      Security for Checks
   6) 結束ICE過程                                     Concluding ICE
 
3. STUN  (RFC 5389)
   1) binding message format
   2) binding request/binding response
   3) 目前定義了三種STUN用途:
      Interactive Connectivity Establishment(ICE)[MMUSIC-ICE],交互式鏈接創建
      Client-initiated connections for SIP [SIP-OUTBOUND],用於SIP的客戶端初始化鏈接
      NAT Behavior Discovery [BEHAVE-NAT],NAT行爲發現 

   4)STUN Message Typegit

      - request
      - success response
      - failure response
      - indication  github

4. TURN (RFC 5766) (TURN協議的缺點是 服務器負載過高,容易成爲性能瓶頸)
   1)Relayed Transport Address:TURN服務器上的傳輸地址,用於客戶端和對端中繼數據。
     TURN Server Transport Address:TURN服務器上的傳輸地址,用於客戶端發送STUN消息給服務器。
     Peer Transport Address:服務器看到的對端的傳輸地址,當對端是在NAT後面,則是對端的服務器反射傳輸地址。
     Allocation:經過Allocate請求將中繼傳輸地址提供給客戶端,除了中繼狀態外,還有許可和超時定時器等。
     5-tuple:五元組,包括客戶端IP地址和端口,服務器IP地址和端口和傳輸協議(包括UDP、TCP、TLS)的組合。
     Channel:通道號與對端傳輸地址的關聯,一旦一個通道號與一個對端的傳輸地址綁定,客戶端和服務器就可以利用帶寬效應更大的通道數據消息來交換數據。
     Permission:一個對端容許使用它的IP地址和傳輸協議來發送數據到TURN服務器,服務器只爲從對端發來的而且匹配一個已經存在的許可的流量中繼到相應的客戶端。
     Realm:服務器內用於描述服務器或內容的一個字符串,這個realm告訴客戶端哪些用戶名和密碼的組合可用於認證請求。
     Nonce:服務器隨機選擇的一個字符串,包含在報文摘要中。爲了防止中繼攻擊,服務器應該有規律的改變這個nonce。
    
   2)方法:
     0x003    Allocate
     0x004    Refresh
     0x006    Send
     0x007    Data
     0x008    CreatePermission
     0x009    ChannelBind  

    一、A向S發出Allocate Request,請求S在本身的IP地址上爲A分配一個端口。chrome

    二、S收到A的Allocate請求後,爲A分配一個端口aport。並向A返回一個Allocate Response。
    三、A向S發出Channel Bind請求,請求將B的(IP地址UDP端口)對綁定到一個Channel號ano上。
    四、S收到Channel Bind請求後,將Channel號ano和B的(IP地址UDP端口)對綁定,並向A返回一個Channel Bind Success回覆。
    五、以後A能夠用Channel Data命令經過Channel號ano向B發消息。Channel Data命令實際上將消息發給了S,S再經過爲A分配的端口aport向B轉發。
    六、B收到的A的消息中,源地址顯示的是S爲A分配的中轉地址(S的IP地址:aport),B能夠直接向這個中轉地址發消息,S會將其轉發給A。
    

5. 開源實現 https://github.com/NATTools安全

ICELIB_INSTANCE *m_iceInst;
static ICELIB_Result OnConnectivityChecksComplete(void *pUserData, uint32_t userValue1, bool isControlling, bool iceFailed);
static ICELIB_Result OnOutgoingBindingRequest(void *pUserData, const struct sockaddr *destination,
const struct sockaddr *source, uint32_t transport, uint32_t userValue1, uint32_t userValue2,
uint32_t componentId, bool useRelay, const char *pUfragPair, const char *pPasswd,
uint32_t peerPriority, bool useCandidate, bool iceControlling, bool iceControlled,
uint64_t tieBreaker, StunMsgId transactionId, const char *szFingerPrint);
static ICELIB_Result OutgoingBindingResponse(void *pUserData, uint32_t userValue1,
uint32_t userValue2, uint32_t componentId, const struct sockaddr *source,
const struct sockaddr *destination, const struct sockaddr *MappedAddress,
uint16_t errorResponse, StunMsgId transactionId, bool useRelay, const char *pUfragPair,
const char *pPasswd);
/*
static ICELIB_Result OnSendKeepAlive(void *pUserData, uint32_t userValue1, uint32_t userValue2,
uint32_t mediaIdx);
*/服務器

 

6. wilson同窗的分享記錄網絡

ICEsession

1. 什麼是NAT?(網絡地址轉換)
2. NAT帶來的問題(連通性問題,並無定義或推薦公網或私網IP地址影射的方法,及互相通迅的問題,徹底由應用本身解決)
3. STUN解決的問題(解決兩個NAT以後設備的連通)
4. 源地址、目標地址、源端口,目標端口,協議 5個元素
5. NAT暴力猜想方法,兩邊各猜64個鏈接,連通的機率就很大了
6. ALG,根據SDP裏的信息,幫你分配端口,路由器要支持SDP,且SDP不能被加密
7. Connectivity check,能夠作不少並行的嘗試,間隔必定的delay,按優先級發包
8. STUN包作了擴展。PRIORITY, USE-CANDIDATE, ICE-CONTROLLED, and ICE-CONTROLLING
9. Nominate的過程:(Nomination 一直在作,priority每過100毫秒加100,達到1000,便可以conclude.)
收到包之後,好幾個都通了,就須要選擇一個。Aggressive/Regular nomination, Aggressive nomination已經淘汰,由於協議有bug。
Aggressive 原本是想更快的創建鏈接,後來你們發現Regular徹底能夠作到一樣的快, Early media。

Regular Nominate過程,發的STUN包有兩種包,USE-CANDIDATE=0(Nominate前), USE-CANDIDATE=1(Nominate後)
兩我的,不能兩我的都Nominate, 要選擇一個主動,一個被動。SDP裏面有個屬性,叫ICE-CONTROLLING(主動), ICE-CONTROLLED(被動)。
被動的一方收到response,看到USE-CANDIDATE=1,就知道了選擇好了,之後用這個transport來通信。
角色的選擇,第一個STUN包的裏時候,裏面有個tie-breaking,是一個64bit的隨機數,誰的大誰就作CONTROLLING,另外一個作CONTROLLED

10. LITE實現(當作爲服務器時,有公網IP的狀況下,申明我是LITE,就不須要connectivity check,由client來check)
firefox 對LITE支持的不太好,chrome還能夠。app

11. Restart/Reconnect
12. Keep live(定時,client發一個包,server回一個包,作有效性檢查)
13. Conclude:(爲何Aggressive Nominate不須要了,是由於有Early media)
Early media就是說,有一個transport連通之後,就能夠發送一些數據回去,因此只要你發,對方確定能收的到.
由於這時候尚未Conclue,一旦conclue這後,非nominate的transport會被關閉。
Conclude,是爲了保證RTP的transport是symmetric的,即發過去和發過來的transport是同一個。
14. ICE, RTP, DTLS是複用在同一個transport上的。
15. AddMediaStream,一個SDP裏面有多個Media(Audio/Video),要建多條鏈接,一個MediaStream裏面有兩個component(RTP1 /RTCP 2).
一個session裏面有多個MediaStream(Audio/Video/Sharing)。
咱們ICE session裏面的實現,三個是分開的,一個Media就用了一個Session,是以爲放在一個session裏,能夠作優化的很少。
Audio/Video/Sharing各有一個ICEConnector,各用一個transport。ide

16. 優先級 0123456 Nattool裏fundation有bug(不一樣的IP用相同的fundation).
17. OnICEComplete會來兩回,第一次是Early meida(最先的連通的一個transport), 第二次是Conclude. bUpdate=0 bUpdate=1。性能


ICE

//
//----- ICE configuration data
//
typedef struct {
unsigned int tickIntervalMS;
unsigned int keepAliveIntervalS;
unsigned int maxCheckListPairs;
bool aggressiveNomination;
bool iceLite;
ICELIB_logLevel logLevel;
} ICELIB_CONFIGURATION;


//
//----- ICE instance data
//
typedef struct tag_ICELIB_INSTANCE {
ICELIB_STATE iceState;
ICELIB_CONFIGURATION iceConfiguration;
ICELIB_CALLBACKS callbacks;
ICE_MEDIA localIceMedia;
ICE_MEDIA remoteIceMedia;
bool iceControlling;
bool iceControlled;
bool iceSupportVerified;
uint64_t tieBreaker;
ICELIB_STREAM_CONTROLLER streamControllers[ ICE_MAX_MEDIALINES];
unsigned int numberOfMediaStreams;
unsigned int roundRobinStreamControllerIndex;
uint32_t tickCount;
uint32_t keepAliveTickCount;
} ICELIB_INSTANCE;

/*!
* ICE single candidate
*
* From draft-ietf-mmusic-ice-18:
*
* foundation = 1*32 ice-char
* componentid = 1*5 digit (0..65535)
* priority = 1*10 digit (0..2147483647)
* connectionAddr = address including port
* relAddr = host addres when sending relayed candidates (Optional, used for debugging)
*/
typedef struct {
char foundation[ ICE_MAX_FOUNDATION_LENGTH];
uint32_t componentid;
uint32_t priority;
struct sockaddr_storage connectionAddr;
ICE_CANDIDATE_TYPE type;
struct sockaddr_storage relAddr;
uint32_t userValue1;
uint32_t userValue2;
uint32_t transport;
char fingerprint[ICE_MAX_FINGERPRINT];
} ICE_CANDIDATE;

typedef struct {
uint32_t componentId;
struct sockaddr_storage connectionAddr;
ICE_CANDIDATE_TYPE type;
} ICE_REMOTE_CANDIDATE;

typedef struct {
ICE_REMOTE_CANDIDATE remoteCandidate[ICE_MAX_COMPONENTS];
uint32_t numberOfComponents;
} ICE_REMOTE_CANDIDATES;

/*!
* ICE candidates for a single media stream
*/
typedef struct {
char ufrag [ ICE_MAX_UFRAG_LENGTH];
char passwd [ ICE_MAX_PASSWD_LENGTH];
ICE_CANDIDATE candidate[ ICE_MAX_CANDIDATES];
uint32_t numberOfCandidates;
ICE_TURN_STATE turnState;
uint32_t userValue1;
uint32_t userValue2;
struct sockaddr_storage defaultAddr;
ICE_CANDIDATE_TYPE defaultCandType;
} ICE_MEDIA_STREAM;

 

ICELIB_INSTANCE
ICELIB_Constructor
ICELIB_Destructor
ICELIB_Start
ICELIB_Stop
ICELIB_ReStart
ICELIB_Tick

ICELIB_setCallbackLog

ICELIB_addLocalMediaStream
ICELIB_setLocalMediaStream
ICELIB_getLocalMediaStream

ICELIB_addRemoteMediaStream
ICELIB_getRemoteMediaStream

ICELIB_setCallbackConnecitivityChecksComplete
ICELIB_setCallbackOutgoingBindingRequest
ICELIB_setCallbackOutgoingBindingResponse
ICELIB_setCallbackKeepAlive
ICELIB_doKeepAlive

ICELIB_addLocalCandidate
ICELIB_getActiveCandidate
ICELIB_getActiveRemoteCandidates

ICELIB_incomingBindingRequest
ICELIB_isIceComplete
ICELIB_isRunning
ICELIB_incomingBindingResponse

ICELIB_getRemoteComponentId
ICELIB_generateTransactionId


ICELIBTYPES_ICE_CANDIDATE_TYPE_toString

Frozen已經廢棄不用了


ICELIB_setCallbackConnecitivityChecksComplete(m_iceInst, OnConnectivityChecksComplete, this);
ICELIB_setCallbackOutgoingBindingRequest(m_iceInst, OnOutgoingBindingRequest, this);
ICELIB_setCallbackOutgoingBindingResponse(m_iceInst, OutgoingBindingResponse, this);

static ICELIB_Result OnConnectivityChecksComplete(void *pUserData, uint32_t userValue1, bool isControlling, bool iceFailed); //onicecomplete, aReason static ICELIB_Result OnOutgoingBindingRequest(void *pUserData, const struct sockaddr *destination, const struct sockaddr *source, uint32_t transport, uint32_t userValue1, uint32_t userValue2, uint32_t componentId, bool useRelay, const char *pUfragPair, const char *pPasswd, uint32_t peerPriority, bool useCandidate, bool iceControlling, bool iceControlled, uint64_t tieBreaker, StunMsgId transactionId, const char *szFingerPrint); //start connectivity checking, a Request --->> a check. static ICELIB_Result OutgoingBindingResponse(void *pUserData, uint32_t userValue1, uint32_t userValue2, uint32_t componentId, const struct sockaddr *source, const struct sockaddr *destination, const struct sockaddr *MappedAddress, uint16_t errorResponse, StunMsgId transactionId, bool useRelay, const char *pUfragPair, const char *pPasswd); //有一個response回來能夠作early media conclude at last, select a transport /* static ICELIB_Result OnSendKeepAlive(void *pUserData, uint32_t userValue1, uint32_t userValue2, uint32_t mediaIdx); */

相關文章
相關標籤/搜索