1
/* $Id: nat_detect.c 4360 2013-02-21 11:26:35Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjnath/nat_detect.h>
21
#include <pjnath/errno.h>
22
#include <pj/assert.h>
23
#include <pj/ioqueue.h>
28
#include <pj/string.h>
30
#include <pj/compat/socket.h>
33
static const char *nat_type_names[] =
47
#define CHANGE_IP_FLAG 4
48
#define CHANGE_PORT_FLAG 2
49
#define CHANGE_IP_PORT_FLAG (CHANGE_IP_FLAG | CHANGE_PORT_FLAG)
50
#define TEST_INTERVAL 50
61
static const char *test_names[] =
63
"Test I: Binding request",
64
"Test II: Binding request with change address and port request",
65
"Test III: Binding request with change port request",
66
"Test IB: Binding request to alternate address"
75
typedef struct nat_detect_session
80
pj_timer_heap_t *timer_heap;
82
unsigned timer_executed;
85
pj_stun_nat_detect_cb *cb;
87
pj_sockaddr_in local_addr;
88
pj_ioqueue_key_t *key;
89
pj_sockaddr_in server;
90
pj_sockaddr_in *cur_server;
91
pj_stun_session *stun_sess;
93
pj_ioqueue_op_key_t read_op, write_op;
94
pj_uint8_t rx_pkt[PJ_STUN_MAX_PKT_LEN];
95
pj_ssize_t rx_pkt_len;
96
pj_sockaddr_in src_addr;
106
pj_stun_tx_data *tdata;
109
} nat_detect_session;
112
static void on_read_complete(pj_ioqueue_key_t *key,
113
pj_ioqueue_op_key_t *op_key,
114
pj_ssize_t bytes_read);
115
static void on_request_complete(pj_stun_session *sess,
118
pj_stun_tx_data *tdata,
119
const pj_stun_msg *response,
120
const pj_sockaddr_t *src_addr,
121
unsigned src_addr_len);
122
static pj_status_t on_send_msg(pj_stun_session *sess,
126
const pj_sockaddr_t *dst_addr,
129
static pj_status_t send_test(nat_detect_session *sess,
130
enum test_type test_id,
131
const pj_sockaddr_in *alt_addr,
132
pj_uint32_t change_flag);
133
static void on_sess_timer(pj_timer_heap_t *th,
135
static void sess_destroy(nat_detect_session *sess);
139
* Get the NAT name from the specified NAT type.
141
PJ_DEF(const char*) pj_stun_get_nat_name(pj_stun_nat_type type)
143
PJ_ASSERT_RETURN(type >= 0 && type <= PJ_STUN_NAT_TYPE_PORT_RESTRICTED,
146
return nat_type_names[type];
149
static int test_executed(nat_detect_session *sess)
152
for (i=0, count=0; i<PJ_ARRAY_SIZE(sess->result); ++i) {
153
if (sess->result[i].executed)
159
static int test_completed(nat_detect_session *sess)
162
for (i=0, count=0; i<PJ_ARRAY_SIZE(sess->result); ++i) {
163
if (sess->result[i].complete)
169
static pj_status_t get_local_interface(const pj_sockaddr_in *server,
170
pj_in_addr *local_addr)
177
status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock);
178
if (status != PJ_SUCCESS)
181
status = pj_sock_bind_in(sock, 0, 0);
182
if (status != PJ_SUCCESS) {
187
status = pj_sock_connect(sock, server, sizeof(pj_sockaddr_in));
188
if (status != PJ_SUCCESS) {
193
addr_len = sizeof(pj_sockaddr_in);
194
status = pj_sock_getsockname(sock, &tmp, &addr_len);
195
if (status != PJ_SUCCESS) {
200
local_addr->s_addr = tmp.sin_addr.s_addr;
207
PJ_DEF(pj_status_t) pj_stun_detect_nat_type(const pj_sockaddr_in *server,
208
pj_stun_config *stun_cfg,
210
pj_stun_nat_detect_cb *cb)
213
nat_detect_session *sess;
214
pj_stun_session_cb sess_cb;
215
pj_ioqueue_callback ioqueue_cb;
219
PJ_ASSERT_RETURN(server && stun_cfg, PJ_EINVAL);
220
PJ_ASSERT_RETURN(stun_cfg->pf && stun_cfg->ioqueue && stun_cfg->timer_heap,
224
* Init NAT detection session.
226
pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK,
227
PJNATH_POOL_INC_NATCK, NULL);
231
sess = PJ_POOL_ZALLOC_T(pool, nat_detect_session);
233
sess->user_data = user_data;
236
status = pj_mutex_create_recursive(pool, pool->obj_name, &sess->mutex);
237
if (status != PJ_SUCCESS)
240
pj_memcpy(&sess->server, server, sizeof(pj_sockaddr_in));
243
* Init timer to self-destroy.
245
sess->timer_heap = stun_cfg->timer_heap;
246
sess->timer.cb = &on_sess_timer;
247
sess->timer.user_data = sess;
253
status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sess->sock);
254
if (status != PJ_SUCCESS)
260
pj_bzero(&sess->local_addr, sizeof(pj_sockaddr_in));
261
sess->local_addr.sin_family = pj_AF_INET();
262
status = pj_sock_bind(sess->sock, &sess->local_addr,
263
sizeof(pj_sockaddr_in));
264
if (status != PJ_SUCCESS)
268
* Get local/bound address.
270
addr_len = sizeof(sess->local_addr);
271
status = pj_sock_getsockname(sess->sock, &sess->local_addr, &addr_len);
272
if (status != PJ_SUCCESS)
276
* Find out which interface is used to send to the server.
278
status = get_local_interface(server, &sess->local_addr.sin_addr);
279
if (status != PJ_SUCCESS)
282
PJ_LOG(5,(sess->pool->obj_name, "Local address is %s:%d",
283
pj_inet_ntoa(sess->local_addr.sin_addr),
284
pj_ntohs(sess->local_addr.sin_port)));
286
PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d",
287
pj_inet_ntoa(server->sin_addr),
288
pj_ntohs(server->sin_port)));
291
* Register socket to ioqueue to receive asynchronous input
294
pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
295
ioqueue_cb.on_read_complete = &on_read_complete;
297
status = pj_ioqueue_register_sock(sess->pool, stun_cfg->ioqueue,
298
sess->sock, sess, &ioqueue_cb,
300
if (status != PJ_SUCCESS)
304
* Create STUN session.
306
pj_bzero(&sess_cb, sizeof(sess_cb));
307
sess_cb.on_request_complete = &on_request_complete;
308
sess_cb.on_send_msg = &on_send_msg;
309
status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb,
310
PJ_FALSE, NULL, &sess->stun_sess);
311
if (status != PJ_SUCCESS)
314
pj_stun_session_set_user_data(sess->stun_sess, sess);
317
* Kick-off ioqueue reading.
319
pj_ioqueue_op_key_init(&sess->read_op, sizeof(sess->read_op));
320
pj_ioqueue_op_key_init(&sess->write_op, sizeof(sess->write_op));
321
on_read_complete(sess->key, &sess->read_op, 0);
326
sess->timer.id = TIMER_TEST;
327
on_sess_timer(stun_cfg->timer_heap, &sess->timer);
337
static void sess_destroy(nat_detect_session *sess)
339
if (sess->stun_sess) {
340
pj_stun_session_destroy(sess->stun_sess);
344
pj_ioqueue_unregister(sess->key);
345
} else if (sess->sock && sess->sock != PJ_INVALID_SOCKET) {
346
pj_sock_close(sess->sock);
350
pj_mutex_destroy(sess->mutex);
354
pj_pool_release(sess->pool);
359
static void end_session(nat_detect_session *sess,
361
pj_stun_nat_type nat_type)
363
pj_stun_nat_detect_result result;
364
char errmsg[PJ_ERR_MSG_SIZE];
367
if (sess->timer.id != 0) {
368
pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
372
pj_bzero(&result, sizeof(result));
374
result.status_text = errmsg;
376
result.status = status;
377
pj_strerror(status, errmsg, sizeof(errmsg));
378
result.nat_type = nat_type;
379
result.nat_type_name = nat_type_names[result.nat_type];
382
(*sess->cb)(sess->user_data, &result);
387
sess->timer.id = TIMER_DESTROY;
388
pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
393
* Callback upon receiving packet from network.
395
static void on_read_complete(pj_ioqueue_key_t *key,
396
pj_ioqueue_op_key_t *op_key,
397
pj_ssize_t bytes_read)
399
nat_detect_session *sess;
402
sess = (nat_detect_session *) pj_ioqueue_get_user_data(key);
403
pj_assert(sess != NULL);
405
pj_mutex_lock(sess->mutex);
407
if (bytes_read < 0) {
408
if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
409
-bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) &&
410
-bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
412
/* Permanent error */
413
end_session(sess, -bytes_read, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
417
} else if (bytes_read > 0) {
418
pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read,
419
PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET,
421
&sess->src_addr, sess->src_addr_len);
425
sess->rx_pkt_len = sizeof(sess->rx_pkt);
426
sess->src_addr_len = sizeof(sess->src_addr);
427
status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len,
428
PJ_IOQUEUE_ALWAYS_ASYNC,
429
&sess->src_addr, &sess->src_addr_len);
431
if (status != PJ_EPENDING) {
432
pj_assert(status != PJ_SUCCESS);
433
end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
437
pj_mutex_unlock(sess->mutex);
442
* Callback to send outgoing packet from STUN session.
444
static pj_status_t on_send_msg(pj_stun_session *stun_sess,
448
const pj_sockaddr_t *dst_addr,
451
nat_detect_session *sess;
455
PJ_UNUSED_ARG(token);
457
sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
460
status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0,
468
* Callback upon request completion.
470
static void on_request_complete(pj_stun_session *stun_sess,
473
pj_stun_tx_data *tdata,
474
const pj_stun_msg *response,
475
const pj_sockaddr_t *src_addr,
476
unsigned src_addr_len)
478
nat_detect_session *sess;
479
pj_stun_sockaddr_attr *mattr = NULL;
480
pj_stun_changed_addr_attr *ca = NULL;
485
PJ_UNUSED_ARG(token);
486
PJ_UNUSED_ARG(tdata);
487
PJ_UNUSED_ARG(src_addr);
488
PJ_UNUSED_ARG(src_addr_len);
490
sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
492
pj_mutex_lock(sess->mutex);
494
/* Find errors in the response */
495
if (status == PJ_SUCCESS) {
497
/* Check error message */
498
if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
499
pj_stun_errcode_attr *eattr;
502
eattr = (pj_stun_errcode_attr*)
503
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
506
err_code = eattr->err_code;
508
err_code = PJ_STUN_SC_SERVER_ERROR;
510
status = PJ_STATUS_FROM_STUN_CODE(err_code);
515
/* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */
516
mattr = (pj_stun_sockaddr_attr*)
517
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
519
mattr = (pj_stun_sockaddr_attr*)
520
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0);
524
status = PJNATH_ESTUNNOMAPPEDADDR;
527
/* Get CHANGED-ADDRESS attribute */
528
ca = (pj_stun_changed_addr_attr*)
529
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0);
532
status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR);
538
/* Save the result */
539
tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id;
542
if (test_id >= ST_MAX) {
543
PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response",
545
end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR),
546
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
550
PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d",
551
test_names[test_id], status));
553
sess->result[test_id].complete = PJ_TRUE;
554
sess->result[test_id].status = status;
555
if (status == PJ_SUCCESS) {
556
pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4,
557
sizeof(pj_sockaddr_in));
558
pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4,
559
sizeof(pj_sockaddr_in));
562
/* Send Test 1B only when Test 2 completes. Must not send Test 1B
563
* before Test 2 completes to avoid creating mapping on the NAT.
565
if (!sess->result[ST_TEST_1B].executed &&
566
sess->result[ST_TEST_2].complete &&
567
sess->result[ST_TEST_2].status != PJ_SUCCESS &&
568
sess->result[ST_TEST_1].complete &&
569
sess->result[ST_TEST_1].status == PJ_SUCCESS)
571
cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
572
sizeof(pj_sockaddr_in));
574
send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0);
577
if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess))
580
/* Handle the test result according to RFC 3489 page 22:
591
N / \ Y / \ Y +--------+
592
UDP <-------/Resp\--------->/ IP \------------->| Test |
593
Blocked \ ? / \Same/ | 2 |
599
+--------+ Sym. N / \
600
| Test | UDP <---/Resp\
606
Symmetric N / \ +--------+ N / \ V
607
NAT <--- / IP \<-----| Test |<--- /Resp\ Open
608
\Same/ | 1B | \ ? / Internet
618
| Test |------>/Resp\---->Restricted
626
Figure 2: Flow for type discovery process
629
switch (sess->result[ST_TEST_1].status) {
630
case PJNATH_ESTUNTIMEDOUT:
632
* Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED.
634
end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED);
638
* Test 1 is successful. Further tests are needed to detect
639
* NAT type. Compare the MAPPED-ADDRESS with the local address.
641
cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
642
sizeof(pj_sockaddr_in));
645
* MAPPED-ADDRESS and local address is equal. Need one more
646
* test to determine NAT type.
648
switch (sess->result[ST_TEST_2].status) {
651
* Test 2 is also successful. We're in the open.
653
end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN);
655
case PJNATH_ESTUNTIMEDOUT:
657
* Test 2 has timed out. We're behind somekind of UDP
660
end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP);
664
* We've got other error with Test 2.
666
end_session(sess, sess->result[ST_TEST_2].status,
667
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
672
* MAPPED-ADDRESS is different than local address.
675
switch (sess->result[ST_TEST_2].status) {
678
* Test 2 is successful. We're behind a full-cone NAT.
680
end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE);
682
case PJNATH_ESTUNTIMEDOUT:
684
* Test 2 has timed-out Check result of test 1B..
686
switch (sess->result[ST_TEST_1B].status) {
689
* Compare the MAPPED-ADDRESS of test 1B with the
690
* MAPPED-ADDRESS returned in test 1..
692
cmp = pj_memcmp(&sess->result[ST_TEST_1].ma,
693
&sess->result[ST_TEST_1B].ma,
694
sizeof(pj_sockaddr_in));
697
* MAPPED-ADDRESS is different, we're behind a
700
end_session(sess, PJ_SUCCESS,
701
PJ_STUN_NAT_TYPE_SYMMETRIC);
704
* MAPPED-ADDRESS is equal. We're behind a restricted
705
* or port-restricted NAT, depending on the result of
708
switch (sess->result[ST_TEST_3].status) {
711
* Test 3 is successful, we're behind a restricted
714
end_session(sess, PJ_SUCCESS,
715
PJ_STUN_NAT_TYPE_RESTRICTED);
717
case PJNATH_ESTUNTIMEDOUT:
719
* Test 3 failed, we're behind a port restricted
722
end_session(sess, PJ_SUCCESS,
723
PJ_STUN_NAT_TYPE_PORT_RESTRICTED);
727
* Got other error with test 3.
729
end_session(sess, sess->result[ST_TEST_3].status,
730
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
735
case PJNATH_ESTUNTIMEDOUT:
737
* Strangely test 1B has failed. Maybe connectivity was
738
* lost? Or perhaps port 3489 (the usual port number in
739
* CHANGED-ADDRESS) is blocked?
741
switch (sess->result[ST_TEST_3].status) {
743
/* Although test 1B failed, test 3 was successful.
744
* It could be that port 3489 is blocked, while the
745
* NAT itself looks to be a Restricted one.
747
end_session(sess, PJ_SUCCESS,
748
PJ_STUN_NAT_TYPE_RESTRICTED);
751
/* Can't distinguish between Symmetric and Port
752
* Restricted, so set the type to Unknown
754
end_session(sess, PJ_SUCCESS,
755
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
761
* Got other error with test 1B.
763
end_session(sess, sess->result[ST_TEST_1B].status,
764
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
770
* We've got other error with Test 2.
772
end_session(sess, sess->result[ST_TEST_2].status,
773
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
780
* We've got other error with Test 1.
782
end_session(sess, sess->result[ST_TEST_1].status,
783
PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
788
pj_mutex_unlock(sess->mutex);
793
static pj_status_t send_test(nat_detect_session *sess,
794
enum test_type test_id,
795
const pj_sockaddr_in *alt_addr,
796
pj_uint32_t change_flag)
798
pj_uint32_t magic, tsx_id[3];
801
sess->result[test_id].executed = PJ_TRUE;
803
/* Randomize tsx id */
806
} while (magic == PJ_STUN_MAGIC);
808
tsx_id[0] = pj_rand();
809
tsx_id[1] = pj_rand();
812
/* Create BIND request */
813
status = pj_stun_session_create_req(sess->stun_sess,
814
PJ_STUN_BINDING_REQUEST, magic,
816
&sess->result[test_id].tdata);
817
if (status != PJ_SUCCESS)
820
/* Add CHANGE-REQUEST attribute */
821
status = pj_stun_msg_add_uint_attr(sess->pool,
822
sess->result[test_id].tdata->msg,
823
PJ_STUN_ATTR_CHANGE_REQUEST,
825
if (status != PJ_SUCCESS)
828
/* Configure alternate address */
830
sess->cur_server = (pj_sockaddr_in*) alt_addr;
832
sess->cur_server = &sess->server;
834
PJ_LOG(5,(sess->pool->obj_name,
835
"Performing %s to %s:%d",
837
pj_inet_ntoa(sess->cur_server->sin_addr),
838
pj_ntohs(sess->cur_server->sin_port)));
840
/* Send the request */
841
status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE,
842
PJ_TRUE, sess->cur_server,
843
sizeof(pj_sockaddr_in),
844
sess->result[test_id].tdata);
845
if (status != PJ_SUCCESS)
851
sess->result[test_id].complete = PJ_TRUE;
852
sess->result[test_id].status = status;
859
static void on_sess_timer(pj_timer_heap_t *th,
862
nat_detect_session *sess;
864
sess = (nat_detect_session*) te->user_data;
866
if (te->id == TIMER_DESTROY) {
867
pj_mutex_lock(sess->mutex);
868
pj_ioqueue_unregister(sess->key);
870
sess->sock = PJ_INVALID_SOCKET;
872
pj_mutex_unlock(sess->mutex);
876
} else if (te->id == TIMER_TEST) {
878
pj_bool_t next_timer;
880
pj_mutex_lock(sess->mutex);
882
next_timer = PJ_FALSE;
884
if (sess->timer_executed == 0) {
885
send_test(sess, ST_TEST_1, NULL, 0);
886
next_timer = PJ_TRUE;
887
} else if (sess->timer_executed == 1) {
888
send_test(sess, ST_TEST_2, NULL, CHANGE_IP_PORT_FLAG);
889
next_timer = PJ_TRUE;
890
} else if (sess->timer_executed == 2) {
891
send_test(sess, ST_TEST_3, NULL, CHANGE_PORT_FLAG);
893
pj_assert(!"Shouldn't have timer at this state");
896
++sess->timer_executed;
899
pj_time_val delay = {0, TEST_INTERVAL};
900
pj_timer_heap_schedule(th, te, &delay);
905
pj_mutex_unlock(sess->mutex);
908
pj_assert(!"Invalid timer ID");