~ubuntu-branches/debian/squeeze/libnice/squeeze

« back to all changes in this revision

Viewing changes to docs/design.txt

  • Committer: Bazaar Package Importer
  • Author(s): Laurent Bigonville
  • Date: 2009-01-04 17:45:34 UTC
  • Revision ID: james.westby@ubuntu.com-20090104174534-dh5u1pfonumqa99c
Tags: upstream-0.0.4
ImportĀ upstreamĀ versionĀ 0.0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Nice: Design documentation
 
2
==========================
 
3
 
 
4
Socket ownership
 
5
----------------
 
6
 
 
7
For UDP candidates, one socket is created for each component and bound
 
8
to INADDR_ANY. The same local socket is used for the host candidate,
 
9
STUN candidate as well as the TURN candidate. The socket handles are
 
10
stored to the Component structure.
 
11
 
 
12
The library will use the source address of incoming packets in order
 
13
to identify from which remote candidates, if any (peer-derived
 
14
candidates), packets were sent.
 
15
 
 
16
XXX: Describe the subtle issues with ICMP error handling when one
 
17
socket is used to send to multiple destinations.
 
18
 
 
19
Real-time considerations
 
20
------------------------
 
21
 
 
22
One potential use for libnice code is providing network connectivity
 
23
for media transport in voice and video telephony applications. This
 
24
means that the libnice code is potentially run in real-time context
 
25
(for instance under POSIX SCHED_FIFO/SHCED_RR scheduling policy) and
 
26
ideally has deterministic execution time.
 
27
 
 
28
To be real-time friendly, operations with non-deterministic execution
 
29
time (dynamic memory allocation, file and other resource access) should
 
30
be done at startup/initialization phase. During an active session
 
31
(connectivity has been established and non-STUN traffic is being sent),
 
32
code should be as deterministic as possible.
 
33
 
 
34
Memory management
 
35
-----------------
 
36
 
 
37
To work on platforms where available memory may be constrained, libnice
 
38
should gracefully handle out of memory situations. If memory allocation
 
39
fails, the library should return an error via the originating public
 
40
library API function.
 
41
 
 
42
Use of glib creates some challenges to meet the above:
 
43
 
 
44
- A lot of glib's internal code assumes memory allocations will
 
45
  always work. Use of these glib facilities should be limited.
 
46
  While the glib default policy (see g_malloc() documentation) of terminating 
 
47
  the process is ok for applications, this is not acceptable for library 
 
48
  components.
 
49
- Glib has weak support for preallocating structures needed at
 
50
  runtime (for instance use of timers creates a lot of memory 
 
51
  allocation activity). 
 
52
 
 
53
To work around the above limitations, the following guidelines need
 
54
to be followed:
 
55
 
 
56
- Always check return values of glib functions.
 
57
- Use safe variants: g_malloc_try(), etc
 
58
- Current issues (last update 2007-05-04)
 
59
     - g_slist_append() will crash if alloc fails
 
60
 
 
61
Timers
 
62
------
 
63
 
 
64
Management of timers is handled by the 'agent' module. Other modules 
 
65
may use timer APIs to get timestamps, but they do not run timers. 
 
66
 
 
67
Glib's timer interface has some problems that have affected the design:
 
68
 
 
69
 - an expired timer will destroy the source (a potentially costly
 
70
   operation)
 
71
 - it is not possible to cancel, or adjust the timer expiration
 
72
   timer without destroying the associated source and creating 
 
73
   a new one, which again causes malloc/frees and is potentially
 
74
   a costly operation
 
75
 - on Linux, glib uses gettimeofday() which is subject to clock
 
76
   skew, and no monotonic timer API is available
 
77
 
 
78
Due to the above, 'agent' code runs fixed interval periodic timers
 
79
(started with g_timeout_add()) during candidate gathering, connectivity
 
80
check, and session keepalive phases. Timer frequency is set separately
 
81
for each phase of processing. A more elegant design would use dynamic
 
82
timeouts, but this would be too expensive with glib timer
 
83
infrastructure.
 
84
 
 
85
Control flow for NICE agent API (NiceAgentClass)
 
86
------------------------------------------------
 
87
 
 
88
The main library interface for applications using libnice is the
 
89
NiceAgent GObject interface defined in 'nice/agent.h'.
 
90
 
 
91
The rough order of control follow is as follows:
 
92
 
 
93
- client should initialize glib with g_type_init()
 
94
- creation of NiceAgent object instance
 
95
- setting agent properties such as STUN and TURN server addresses
 
96
- connecting the GObject signals with g_signal_connect() to application
 
97
  callback functions
 
98
- adding local interface addresses to use with
 
99
  nice_agent_add_local_address()
 
100
 
 
101
And continues when making an initial offer:
 
102
 
 
103
- creating the streams with nice_agent_add_stream()
 
104
- attach the mainloop context to connect the NiceAgent sockets to
 
105
  the application's event loop (using nice_agent_attach_recv())
 
106
- start candidate gathering by calling nice_agent_gather_candidates()
 
107
- the application should wait for the "candidate-gathering-done" signal
 
108
  before going forward (so that ICE can gather the needed set of local
 
109
  connectiviy candidates) 
 
110
- get the information needed for sending offer using
 
111
  nice_agent_get_local_candidates() and
 
112
  nice_agent_get_local_credentials()
 
113
- client should now send the session offer
 
114
- once it receives an answer, it can pass the information to NiceAgent
 
115
  using nice_agent_set_remote_candidates() and
 
116
  nice_agent_set_remote_credentials()
 
117
 
 
118
Alternatively, when answering to an initial offer:
 
119
 
 
120
- the first five steps are the same as above (making initial offer)
 
121
- pass the remote session information to NiceAgent using 
 
122
  nice_agent_set_remote_candidates() and
 
123
  nice_agent_set_remote_credentials()
 
124
- client can send the answer to session offer
 
125
 
 
126
Special considerations for a SIP client:
 
127
 
 
128
- Upon sending the initial offer/answer, client should pick one
 
129
  local candidate as the default one, and encode it to the SDP
 
130
  "m" and "c" lines, in addition to the ICE "a=candidate" lines.
 
131
- Client should connect to "new-selected-pair" signals. If this
 
132
  signal is received, a new candidate pair has been set as 
 
133
  a selected pair (highest priority nominated pair). See 
 
134
  ICE specification for a definition of "nominated pairs". 
 
135
- Once all components of a stream have reached the
 
136
  "NICE_COMPONENT_STATE_READY" state (as reported by 
 
137
  "component-state-changed" signals), the client should check
 
138
  whether its original default candidate matches the latest 
 
139
  selected pair. If not, it needs to send an updated offer
 
140
  it is in controlling mode. Before sending the offer, client
 
141
  should check the "controlling-mode" property to check that
 
142
  it still is in controlling mode (might change during ICE
 
143
  processing due to ICE role conflicts).
 
144
- The "remote-attributes" SDP attribute can be created from
 
145
  the information provided by "component-state-changed" (which
 
146
  components are ready), "new-selected-pair" (which candidates
 
147
  are selected) and "new-remote-candidate" (peer-reflexive
 
148
  candidates discovered during processing) signals.
 
149
- Supporting forked calls is not yet supported by the API (multiple
 
150
  sets of remote candidates for one local set of candidates).
 
151
 
 
152
Restarting ICE:
 
153
 
 
154
- ICE processing can be restarted by calling nice_agent_restart()
 
155
- Restart will clean the set of remote candidates, so client must
 
156
  afterwards call nice_agent_set_remote_candidates() after receiving 
 
157
  a new offer/answer for the restarted ICE session.
 
158
- Restart will reinitialize the local credentials (see 
 
159
  nice_agent_get_local_credentials()).
 
160
- Note that to modify the set of local candidates, a new stream 
 
161
  has to be created. For the remote party, this looks like a ICE
 
162
  restart as well.
 
163
 
 
164
Handling fallback to non-ICE operation:
 
165
 
 
166
- If we are the offering party, and the remote party indicates
 
167
  it doesn't support ICE, we can use nice_agent_set_selected_pair()
 
168
  to force selection of a candidate pair (for remote party, 
 
169
  the information on SDP 'm=' and 'c=' lines needs to be used
 
170
  to generate one remote candidate for each component of the
 
171
  streams). This function will halt all ICE processing (excluding
 
172
  keepalives), while still allowing to send and receive media (assuming
 
173
  NATs won't interfere).
 
174
 
 
175
Notes about sending media:
 
176
 
 
177
- Client may send media once all components of a stream have reached
 
178
  state of NICE_COMPONENT_STATE_CONNECTED or NICE_COMPONENT_STATE_READY,
 
179
  (as reported by "component-state-changed" signals), and a selected pair 
 
180
  is set for all components (as reported by "new-selected-pair" signals).
 
181
 
 
182
STUN API
 
183
--------
 
184
 
 
185
The underlying STUN library takes care of formatting and parsing STUN
 
186
messages (lower layer),
 
187
 
 
188
Applications should only need to use the higher layer API which then
 
189
uses the lower layer API.
 
190
 
 
191
The following STUN usages are currently implemented by the
 
192
transaction layer:
 
193
- Binding discovery (RFC5389 with RFC3489 backward compatibility)
 
194
- Binding keep-alive
 
195
- ICE connectivity checks
 
196
- TURN
 
197
- STUN retransmission timers
 
198
 
 
199
 
 
200
STUN message API
 
201
----------------
 
202
 
 
203
STUN message API provide thin wrappers to parse and format STUN
 
204
messages. To achieve maximum cross-architectures portability and retain
 
205
real-time friendliness, these functions are fully "computational" [1].
 
206
They also make no assumption about endianess or memory alignment
 
207
(reading single bytes or using memcpy()).
 
208
 
 
209
Message buffers are provided by the caller (so these can be
 
210
preallocated). Because STUN uses a relatively computer-friendly binary
 
211
format, STUN messages are stored in wire format within the buffers.
 
212
There is no intermediary translation, so the APIs can operate directly
 
213
with data received from or sent to the network.
 
214
 
 
215
[1] With one exception: The random number generated might access the
 
216
system entropy pool (/dev/urandom) if available.