]>
Commit | Line | Data |
---|---|---|
f6b25ca5 JM |
1 | wpa_supplicant and Wi-Fi P2P |
2 | ============================ | |
3 | ||
4 | This document describes how the Wi-Fi P2P implementation in | |
5 | wpa_supplicant can be configured and how an external component on the | |
6 | client (e.g., management GUI) is used to enable WPS enrollment and | |
7 | registrar registration. | |
8 | ||
9 | ||
10 | Introduction to Wi-Fi P2P | |
11 | ------------------------- | |
12 | ||
13 | TODO | |
14 | ||
15 | More information about Wi-Fi P2P is available from Wi-Fi Alliance: | |
16 | http://www.wi-fi.org/Wi-Fi_Direct.php | |
17 | ||
18 | ||
19 | wpa_supplicant implementation | |
20 | ----------------------------- | |
21 | ||
22 | TODO | |
23 | ||
24 | ||
25 | wpa_supplicant configuration | |
26 | ---------------------------- | |
27 | ||
28 | Wi-Fi P2P is an optional component that needs to be enabled in the | |
29 | wpa_supplicant build configuration (.config). Here is an example | |
30 | configuration that includes Wi-Fi P2P support and Linux nl80211 | |
31 | -based driver interface: | |
32 | ||
33 | CONFIG_DRIVER_NL80211=y | |
34 | CONFIG_CTRL_IFACE=y | |
35 | CONFIG_P2P=y | |
36 | CONFIG_AP=y | |
37 | CONFIG_WPS=y | |
38 | ||
39 | ||
40 | In run-time configuration file (wpa_supplicant.conf), some parameters | |
41 | for P2P may be set. In order to make the devices easier to recognize, | |
42 | device_name and device_type should be specified. For example, | |
43 | something like this should be included: | |
44 | ||
45 | ctrl_interface=/var/run/wpa_supplicant | |
46 | device_name=My P2P Device | |
47 | device_type=1-0050F204-1 | |
48 | ||
49 | ||
50 | wpa_cli | |
51 | ------- | |
52 | ||
53 | Actual Wi-Fi P2P operations are requested during runtime. These can be | |
54 | done for example using wpa_cli (which is described below) or a GUI | |
55 | like wpa_gui-qt4. | |
56 | ||
57 | ||
58 | wpa_cli starts in interactive mode if no command string is included on | |
59 | the command line. By default, it will select the first network interface | |
60 | that it can find (and that wpa_supplicant controls). If more than one | |
61 | interface is in use, it may be necessary to select one of the explicitly | |
62 | by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1'). | |
63 | ||
64 | Most of the P2P operations are done on the main interface (e.g., the | |
65 | interface that is automatically added when the driver is loaded, e.g., | |
66 | wlan0). When using a separate virtual interface for group operations | |
67 | (e.g., wlan1), the control interface for that group interface may need | |
68 | to be used for some operations (mainly WPS activation in GO). This may | |
69 | change in the future so that all the needed operations could be done | |
70 | over the main control interface. | |
71 | ||
72 | Device Discovery | |
73 | ||
37448ede JM |
74 | p2p_find [timeout in seconds] [type=<social|progressive>] \ |
75 | [dev_id=<addr>] [delay=<search delay in ms>] | |
f6b25ca5 JM |
76 | |
77 | The default behavior is to run a single full scan in the beginning and | |
78 | then scan only social channels. type=social will scan only social | |
79 | channels, i.e., it skips the initial full scan. type=progressive is | |
80 | like the default behavior, but it will scan through all the channels | |
81 | progressively one channel at the time in the Search state rounds. This | |
82 | will help in finding new groups or groups missed during the initial | |
83 | full scan. | |
84 | ||
37448ede JM |
85 | The optional dev_id option can be used to specify a single P2P peer to |
86 | search for. The optional delay parameter can be used to request an extra | |
87 | delay to be used between search iterations (e.g., to free up radio | |
88 | resources for concurrent operations). | |
89 | ||
f6b25ca5 JM |
90 | p2p_listen [timeout in seconds] |
91 | ||
92 | Start Listen-only state (become discoverable without searching for | |
93 | other devices). Optional parameter can be used to specify the duration | |
94 | for the Listen operation in seconds. This command may not be of that | |
95 | much use during normal operations and is mainly designed for | |
96 | testing. It can also be used to keep the device discoverable without | |
97 | having to maintain a group. | |
98 | ||
99 | p2p_stop_find | |
100 | ||
101 | Stop ongoing P2P device discovery or other operation (connect, listen | |
102 | mode). | |
103 | ||
104 | p2p_flush | |
105 | ||
106 | Flush P2P peer table and state. | |
107 | ||
108 | Group Formation | |
109 | ||
0918c4bf | 110 | p2p_prov_disc <peer device address> <display|keypad|pbc> [join|auto] |
f6b25ca5 JM |
111 | |
112 | Send P2P provision discovery request to the specified peer. The | |
113 | parameters for this command are the P2P device address of the peer and | |
114 | the desired configuration method. For example, "p2p_prov_disc | |
115 | 02:01:02:03:04:05 display" would request the peer to display a PIN for | |
116 | us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer | |
117 | to enter a PIN that we display. | |
118 | ||
8c5f7309 JJ |
119 | The optional "join" parameter can be used to indicate that this command |
120 | is requesting an already running GO to prepare for a new client. This is | |
0918c4bf JM |
121 | mainly used with "display" to request it to display a PIN. The "auto" |
122 | parameter can be used to request wpa_supplicant to automatically figure | |
123 | out whether the peer device is operating as a GO and if so, use | |
124 | join-a-group style PD instead of GO Negotiation style PD. | |
8c5f7309 | 125 | |
61ff2c80 | 126 | p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad] |
23c84252 | 127 | [persistent|persistent=<network id>] [join|auth] |
3bc462cb | 128 | [go_intent=<0..15>] [freq=<in MHz>] [provdisc] |
f6b25ca5 JM |
129 | |
130 | Start P2P group formation with a discovered P2P peer. This includes | |
131 | optional group owner negotiation, group interface setup, provisioning, | |
132 | and establishing data connection. | |
133 | ||
134 | The <pbc|pin|PIN#> parameter specifies the WPS provisioning | |
135 | method. "pbc" string starts pushbutton method, "pin" string start PIN | |
136 | method using an automatically generated PIN (which will be returned as | |
137 | the command return code), PIN# means that a pre-selected PIN can be | |
61ff2c80 JM |
138 | used (e.g., 12345670). [display|keypad] is used with PIN method |
139 | to specify which PIN is used (display=dynamically generated random PIN | |
140 | from local display, keypad=PIN entered from peer display). "persistent" | |
23c84252 JM |
141 | parameter can be used to request a persistent group to be formed. The |
142 | "persistent=<network id>" alternative can be used to pre-populate | |
143 | SSID/passphrase configuration based on a previously used persistent | |
144 | group where this device was the GO. The previously used parameters will | |
145 | then be used if the local end becomes the GO in GO Negotiation (which | |
146 | can be forced with go_intent=15). | |
f6b25ca5 JM |
147 | |
148 | "join" indicates that this is a command to join an existing group as a | |
149 | client. It skips the GO Negotiation part. This will send a Provision | |
150 | Discovery Request message to the target GO before associating for WPS | |
151 | provisioning. | |
152 | ||
153 | "auth" indicates that the WPS parameters are authorized for the peer | |
154 | device without actually starting GO Negotiation (i.e., the peer is | |
155 | expected to initiate GO Negotiation). This is mainly for testing | |
156 | purposes. | |
157 | ||
158 | "go_intent" can be used to override the default GO Intent for this GO | |
159 | Negotiation. | |
160 | ||
161 | "freq" can be used to set a forced operating channel (e.g., freq=2412 | |
162 | to select 2.4 GHz channel 1). | |
163 | ||
3bc462cb JM |
164 | "provdisc" can be used to request a Provision Discovery exchange to be |
165 | used prior to starting GO Negotiation as a workaround with some deployed | |
166 | P2P implementations that require this to allow the user to accept the | |
167 | connection. | |
168 | ||
f6b25ca5 JM |
169 | p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] |
170 | ||
171 | Set up a P2P group owner manually (i.e., without group owner | |
172 | negotiation with a specific peer). This is also known as autonomous | |
173 | GO. Optional persistent=<network id> can be used to specify restart of | |
174 | a persistent group. Optional freq=<freq in MHz> can be used to force | |
175 | the GO to be started on a specific frequency. Special freq=2 or freq=5 | |
176 | options can be used to request the best 2.4 GHz or 5 GHz band channel | |
177 | to be selected automatically. | |
178 | ||
179 | p2p_reject <peer device address> | |
180 | ||
181 | Reject connection attempt from a peer (specified with a device | |
182 | address). This is a mechanism to reject a pending GO Negotiation with | |
183 | a peer and request to automatically block any further connection or | |
184 | discovery of the peer. | |
185 | ||
186 | p2p_group_remove <group interface> | |
187 | ||
188 | Terminate a P2P group. If a new virtual network interface was used for | |
189 | the group, it will also be removed. The network interface name of the | |
190 | group interface is used as a parameter for this command. | |
191 | ||
192 | p2p_cancel | |
193 | ||
194 | Cancel an ongoing P2P group formation related operation. | |
195 | ||
196 | Service Discovery | |
197 | ||
198 | p2p_serv_disc_req | |
199 | ||
200 | Schedule a P2P service discovery request. The parameters for this | |
201 | command are the device address of the peer device (or 00:00:00:00:00:00 | |
202 | for wildcard query that is sent to every discovered P2P peer that | |
203 | supports service discovery) and P2P Service Query TLV(s) as hexdump. For | |
204 | example, | |
205 | ||
206 | p2p_serv_disc_req 00:00:00:00:00:00 02000001 | |
207 | ||
208 | schedules a request for listing all available services of all service | |
209 | discovery protocols and requests this to be sent to all discovered | |
210 | peers (note: this can result in long response frames). The pending | |
211 | requests are sent during device discovery (see p2p_find). | |
212 | ||
213 | Only a single pending wildcard query is supported, but there can be | |
214 | multiple pending peer device specific queries (each will be sent in | |
215 | sequence whenever the peer is found). | |
216 | ||
217 | This command returns an identifier for the pending query (e.g., | |
218 | "1f77628") that can be used to cancel the request. Directed requests | |
219 | will be automatically removed when the specified peer has replied to | |
220 | it. | |
221 | ||
222 | For UPnP, an alternative command format can be used to specify a | |
223 | single query TLV (i.e., a service discovery for a specific UPnP | |
224 | service): | |
225 | ||
226 | p2p_serv_disc_req 00:00:00:00:00:00 upnp <version hex> <ST: from M-SEARCH> | |
227 | ||
228 | For example: | |
229 | ||
230 | p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 | |
231 | ||
232 | Additional examples for queries: | |
233 | ||
234 | # list of all Bonjour services | |
235 | p2p_serv_disc_req 00:00:00:00:00:00 02000101 | |
236 | ||
237 | # list of all UPnP services | |
238 | p2p_serv_disc_req 00:00:00:00:00:00 02000201 | |
239 | ||
240 | # list of all WS-Discovery services | |
241 | p2p_serv_disc_req 00:00:00:00:00:00 02000301 | |
242 | ||
243 | # list of all Bonjour and UPnP services | |
244 | p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202 | |
245 | ||
246 | # Apple File Sharing over TCP | |
247 | p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01 | |
248 | ||
249 | # Bonjour SSTH (supported service type hash) | |
250 | p2p_serv_disc_req 00:00:00:00:00:00 05000101000000 | |
251 | ||
252 | # UPnP examples | |
253 | p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all | |
254 | p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice | |
255 | p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2 | |
256 | p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 | |
257 | p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 | |
258 | ||
259 | p2p_serv_disc_cancel_req <query identifier> | |
260 | ||
261 | Cancel a pending P2P service discovery request. This command takes a | |
262 | single parameter: identifier for the pending query (the value returned | |
263 | by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628". | |
264 | ||
265 | p2p_serv_disc_resp | |
266 | ||
267 | Reply to a service discovery query. This command takes following | |
268 | parameters: frequency in MHz, destination address, dialog token, | |
269 | response TLV(s). The first three parameters are copied from the | |
270 | request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7 | |
271 | 1 0300000101". This command is used only if external program is used | |
272 | to process the request (see p2p_serv_disc_external). | |
273 | ||
274 | p2p_service_update | |
275 | ||
276 | Indicate that local services have changed. This is used to increment | |
277 | the P2P service indicator value so that peers know when previously | |
278 | cached information may have changed. This is only needed when external | |
279 | service discovery processing is enabled since the commands to | |
280 | pre-configure services for internal processing will increment the | |
281 | indicator automatically. | |
282 | ||
283 | p2p_serv_disc_external <0|1> | |
284 | ||
285 | Configure external processing of P2P service requests: 0 (default) = | |
286 | no external processing of requests (i.e., internal code will process | |
287 | each request based on pre-configured services), 1 = external | |
288 | processing of requests (external program is responsible for replying | |
289 | to service discovery requests with p2p_serv_disc_resp). Please note | |
290 | that there is quite strict limit on how quickly the response needs to | |
291 | be transmitted, so use of the internal processing is strongly | |
292 | recommended. | |
293 | ||
294 | p2p_service_add bonjour <query hexdump> <RDATA hexdump> | |
295 | ||
296 | Add a local Bonjour service for internal SD query processing. | |
297 | ||
298 | Examples: | |
299 | ||
300 | # AFP Over TCP (PTR) | |
301 | p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027 | |
302 | # AFP Over TCP (TXT) (RDATA=null) | |
303 | p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00 | |
304 | ||
305 | # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.) | |
306 | p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027 | |
307 | # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript) | |
308 | p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074 | |
309 | ||
310 | # Supported Service Type Hash (SSTH) | |
311 | p2p_service_add bonjour 000000 <32-byte bitfield as hexdump> | |
312 | (note: see P2P spec Annex E.4 for information on how to construct the bitfield) | |
313 | ||
314 | p2p_service_del bonjour <query hexdump> | |
315 | ||
316 | Remove a local Bonjour service from internal SD query processing. | |
317 | ||
318 | p2p_service_add upnp <version hex> <service> | |
319 | ||
320 | Add a local UPnP service for internal SD query processing. | |
321 | ||
322 | Examples: | |
323 | ||
324 | p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice | |
325 | p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice | |
326 | p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2 | |
327 | p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2 | |
328 | p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1 | |
329 | ||
330 | p2p_service_del upnp <version hex> <service> | |
331 | ||
332 | Remove a local UPnP service from internal SD query processing. | |
333 | ||
334 | p2p_service_flush | |
335 | ||
336 | Remove all local services from internal SD query processing. | |
337 | ||
338 | Invitation | |
339 | ||
340 | p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address] | |
341 | [go_dev_addr=address] | |
342 | ||
343 | Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a | |
344 | persistent group (e.g., persistent=4). If the peer device is the GO of | |
781d8e0f | 345 | the persistent group, the peer parameter is not needed. Otherwise it is |
f6b25ca5 JM |
346 | used to specify which device to invite. go_dev_addr parameter can be |
347 | used to override the GO device address for Invitation Request should | |
348 | it be not known for some reason (this should not be needed in most | |
349 | cases). | |
350 | ||
351 | Group Operations | |
352 | ||
353 | (These are used on the group interface.) | |
354 | ||
355 | wps_pin <any|address> <PIN> | |
356 | ||
357 | Start WPS PIN method. This allows a single WPS Enrollee to connect to | |
358 | the AP/GO. This is used on the GO when a P2P client joins an existing | |
359 | group. The second parameter is the address of the Enrollee or a string | |
360 | "any" to allow any station to use the entered PIN (which will restrict | |
361 | the PIN for one-time-use). PIN is the Enrollee PIN read either from a | |
362 | label or display on the P2P Client/WPS Enrollee. | |
363 | ||
364 | wps_pbc | |
365 | ||
366 | Start WPS PBC method (i.e., push the button). This allows a single WPS | |
367 | Enrollee to connect to the AP/GO. This is used on the GO when a P2P | |
368 | client joins an existing group. | |
369 | ||
370 | p2p_get_passphrase | |
371 | ||
372 | Get the passphrase for a group (only available when acting as a GO). | |
373 | ||
374 | p2p_presence_req [<duration> <interval>] [<duration> <interval>] | |
375 | ||
376 | Send a P2P Presence Request to the GO (this is only available when | |
377 | acting as a P2P client). If no duration/interval pairs are given, the | |
378 | request indicates that this client has no special needs for GO | |
379 | presence. the first parameter pair gives the preferred duration and | |
380 | interval values in microseconds. If the second pair is included, that | |
381 | indicates which value would be acceptable. | |
382 | ||
383 | Parameters | |
384 | ||
385 | p2p_ext_listen [<period> <interval>] | |
386 | ||
387 | Configure Extended Listen Timing. If the parameters are omitted, this | |
388 | feature is disabled. If the parameters are included, Listen State will | |
389 | be entered every interval msec for at least period msec. Both values | |
390 | have acceptable range of 1-65535 (with interval obviously having to be | |
391 | larger than or equal to duration). If the P2P module is not idle at | |
392 | the time the Extended Listen Timing timeout occurs, the Listen State | |
393 | operation will be skipped. | |
394 | ||
395 | The configured values will also be advertised to other P2P Devices. The | |
396 | received values are available in the p2p_peer command output: | |
397 | ||
398 | ext_listen_period=100 ext_listen_interval=5000 | |
399 | ||
400 | p2p_set <field> <value> | |
401 | ||
402 | Change dynamic P2P parameters | |
403 | ||
404 | p2p_set discoverability <0/1> | |
405 | ||
406 | Disable/enable advertisement of client discoverability. This is | |
407 | enabled by default and this parameter is mainly used to allow testing | |
408 | of device discoverability. | |
409 | ||
410 | p2p_set managed <0/1> | |
411 | ||
412 | Disable/enable managed P2P Device operations. This is disabled by | |
413 | default. | |
414 | ||
415 | p2p_set listen_channel <1/6/11> | |
416 | ||
417 | Set P2P Listen channel. This is mainly meant for testing purposes and | |
418 | changing the Listen channel during normal operations can result in | |
419 | protocol failures. | |
420 | ||
421 | p2p_set ssid_postfix <postfix> | |
422 | ||
423 | Set postfix string to be added to the automatically generated P2P SSID | |
424 | (DIRECT-<two random characters>). For example, postfix of "-testing" | |
425 | could result in the SSID becoming DIRECT-ab-testing. | |
426 | ||
427 | set <field> <value> | |
428 | ||
429 | Set global configuration parameters which may also affect P2P | |
430 | operations. The format on these parameters is same as is used in | |
431 | wpa_supplicant.conf. Only the parameters listen here should be | |
432 | changed. Modifying other parameters may result in incorrect behavior | |
433 | since not all existing users of the parameters are updated. | |
434 | ||
435 | set uuid <UUID> | |
436 | ||
437 | Set WPS UUID (by default, this is generated based on the MAC address). | |
438 | ||
439 | set device_name <device name> | |
440 | ||
441 | Set WPS Device Name (also included in some P2P messages). | |
442 | ||
443 | set manufacturer <manufacturer> | |
444 | ||
445 | Set WPS Manufacturer. | |
446 | ||
447 | set model_name <model name> | |
448 | ||
449 | Set WPS Model Name. | |
450 | ||
451 | set model_number <model number> | |
452 | ||
453 | Set WPS Model Number. | |
454 | ||
455 | set serial_number <serial number> | |
456 | ||
457 | Set WPS Serial Number. | |
458 | ||
459 | set device_type <device type> | |
460 | ||
461 | Set WPS Device Type. | |
462 | ||
463 | set os_version <OS version> | |
464 | ||
465 | Set WPS OS Version. | |
466 | ||
467 | set config_methods <config methods> | |
468 | ||
469 | Set WPS Configuration Methods. | |
470 | ||
471 | set sec_device_type <device type> | |
472 | ||
473 | Add a new Secondary Device Type. | |
474 | ||
475 | set p2p_go_intent <GO intent> | |
476 | ||
477 | Set the default P2P GO Intent. Note: This value can be overridden in | |
478 | p2p_connect command and as such, there should be no need to change the | |
479 | default value here during normal operations. | |
480 | ||
481 | set p2p_ssid_postfix <P2P SSID postfix> | |
482 | ||
483 | Set P2P SSID postfix. | |
484 | ||
485 | set persistent_reconnect <0/1> | |
486 | ||
487 | Disable/enabled persistent reconnect for reinvocation of persistent | |
488 | groups. If enabled, invitations to reinvoke a persistent group will be | |
489 | accepted without separate authorization (e.g., user interaction). | |
490 | ||
491 | set country <two character country code> | |
492 | ||
493 | Set country code (this is included in some P2P messages). | |
494 | ||
495 | Status | |
496 | ||
497 | p2p_peers [discovered] | |
498 | ||
499 | List P2P Device Addresses of all the P2P peers we know. The optional | |
500 | "discovered" parameter filters out the peers that we have not fully | |
501 | discovered, i.e., which we have only seen in a received Probe Request | |
502 | frame. | |
503 | ||
504 | p2p_peer <P2P Device Address> | |
505 | ||
506 | Fetch information about a known P2P peer. | |
507 | ||
508 | Group Status | |
509 | ||
510 | (These are used on the group interface.) | |
511 | ||
512 | status | |
513 | ||
514 | Show status information (connection state, role, use encryption | |
515 | parameters, IP address, etc.). | |
516 | ||
517 | sta | |
518 | ||
519 | Show information about an associated station (when acting in AP/GO role). | |
520 | ||
521 | all_sta | |
522 | ||
523 | Lists the currently associated stations. | |
524 | ||
525 | Configuration data | |
526 | ||
527 | list_networks | |
528 | ||
529 | Lists the configured networks, including stored information for | |
530 | persistent groups. The identifier in this list is used with | |
531 | p2p_group_add and p2p_invite to indicate which persistent group is to | |
532 | be reinvoked. | |
533 | ||
534 | remove_network <network id> | |
535 | ||
536 | Remove a network entry from configuration. | |
537 | ||
538 | ||
539 | wpa_cli action script | |
540 | --------------------- | |
541 | ||
542 | See examples/p2p-action.sh | |
543 | ||
544 | TODO: describe DHCP/DNS setup | |
545 | TODO: cross-connection |