Changeset 1499 for pjproject/trunk
- Timestamp:
- Oct 13, 2007 9:27:21 AM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjnath/src/pjnath/nat_detect.c
r1498 r1499 24 24 #include <pj/os.h> 25 25 #include <pj/pool.h> 26 #include <pj/rand.h> 26 27 #include <pj/string.h> 27 28 #include <pj/timer.h> … … 42 43 43 44 44 #define CHANGE_ ADDR445 #define CHANGE_PORT 46 #define CHANGE_ ADDR_PORT (CHANGE_ADDR | CHANGE_PORT)47 48 49 enum state45 #define CHANGE_IP_FLAG 4 46 #define CHANGE_PORT_FLAG 2 47 #define CHANGE_IP_PORT_FLAG (CHANGE_IP_FLAG | CHANGE_PORT_FLAG) 48 #define TEST_INTERVAL 50 49 50 enum test_type 50 51 { 51 52 ST_TEST_1, 52 53 ST_TEST_2, 54 ST_TEST_3, 53 55 ST_TEST_1B, 54 ST_ TEST_356 ST_MAX 55 57 }; 56 58 … … 59 61 "Test I: Binding request", 60 62 "Test II: Binding request with change address and port request", 61 "Test IB: Binding request to alternate address", 62 "Test III: Binding request with change port request" 63 "Test III: Binding request with change port request", 64 "Test IB: Binding request to alternate address" 65 }; 66 67 enum timer_type 68 { 69 TIMER_TEST = 1, 70 TIMER_DESTROY = 2 63 71 }; 64 72 … … 69 77 70 78 pj_timer_heap_t *timer_heap; 71 pj_timer_entry destroy_timer;79 pj_timer_entry timer; 72 80 73 81 void *user_data; … … 79 87 pj_sockaddr_in *cur_server; 80 88 pj_stun_session *stun_sess; 81 enum state state;82 89 83 90 pj_ioqueue_op_key_t read_op, write_op; … … 87 94 int src_addr_len; 88 95 89 pj_bool_t test1_same_ip; 90 pj_sockaddr_in test1_ma; /* MAPPED-ADDRESS */ 91 pj_sockaddr_in test1_ca; /* CHANGED-ADDRESS */ 96 struct result 97 { 98 pj_bool_t executed; 99 pj_bool_t complete; 100 pj_status_t status; 101 pj_sockaddr_in ma; 102 pj_sockaddr_in ca; 103 } result[ST_MAX]; 92 104 93 105 } nat_detect_session; … … 109 121 unsigned addr_len); 110 122 111 static pj_status_t s tart_test(nat_detect_session *sess,112 enum state state,113 114 115 static void on_ timer_destroy(pj_timer_heap_t *th,123 static pj_status_t send_test(nat_detect_session *sess, 124 enum test_type test_id, 125 const pj_sockaddr_in *alt_addr, 126 pj_uint32_t change_flag); 127 static void on_sess_timer(pj_timer_heap_t *th, 116 128 pj_timer_entry *te); 117 129 static void sess_destroy(nat_detect_session *sess); 130 131 132 static int test_executed(nat_detect_session *sess) 133 { 134 unsigned i, count; 135 for (i=0, count=0; i<PJ_ARRAY_SIZE(sess->result); ++i) { 136 if (sess->result[i].executed) 137 ++count; 138 } 139 return count; 140 } 141 142 static int test_completed(nat_detect_session *sess) 143 { 144 unsigned i, count; 145 for (i=0, count=0; i<PJ_ARRAY_SIZE(sess->result); ++i) { 146 if (sess->result[i].complete) 147 ++count; 148 } 149 return count; 150 } 118 151 119 152 static pj_status_t get_local_interface(const pj_sockaddr_in *server, … … 193 226 */ 194 227 sess->timer_heap = stun_cfg->timer_heap; 195 sess-> destroy_timer.cb = &on_timer_destroy;196 sess-> destroy_timer.user_data = sess;228 sess->timer.cb = &on_sess_timer; 229 sess->timer.user_data = sess; 197 230 198 231 … … 232 265 pj_inet_ntoa(sess->local_addr.sin_addr), 233 266 pj_ntohs(sess->local_addr.sin_port))); 267 268 PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d", 269 pj_inet_ntoa(server->sin_addr), 270 pj_ntohs(server->sin_port))); 234 271 235 272 /* … … 269 306 * Start TEST_1 270 307 */ 271 PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d", 272 pj_inet_ntoa(server->sin_addr), 273 pj_ntohs(server->sin_port))); 274 275 status = start_test(sess, ST_TEST_1, NULL, 0); 276 if (status != PJ_SUCCESS) 277 goto on_error; 308 sess->timer.id = TIMER_TEST; 309 on_sess_timer(stun_cfg->timer_heap, &sess->timer); 278 310 279 311 return PJ_SUCCESS; … … 307 339 308 340 309 static pj_status_t start_test(nat_detect_session *sess,310 enum state state,311 const pj_sockaddr_in *alt_addr,312 pj_uint32_t change_flag)313 {314 pj_stun_tx_data *tdata;315 pj_status_t status;316 317 /* Create BIND request */318 status = pj_stun_session_create_req(sess->stun_sess,319 PJ_STUN_BINDING_REQUEST, 0x83224,320 NULL, &tdata);321 if (status != PJ_SUCCESS)322 return status;323 324 /* Add CHANGE-REQUEST attribute */325 status = pj_stun_msg_add_uint_attr(sess->pool, tdata->msg,326 PJ_STUN_ATTR_CHANGE_REQUEST,327 change_flag);328 if (status != PJ_SUCCESS)329 return status;330 331 /* Configure alternate address */332 if (alt_addr)333 sess->cur_server = (pj_sockaddr_in*) alt_addr;334 else335 sess->cur_server = &sess->server;336 337 PJ_LOG(5,(sess->pool->obj_name,338 "Performing %s to %s:%d",339 test_names[state],340 pj_inet_ntoa(sess->cur_server->sin_addr),341 pj_ntohs(sess->cur_server->sin_port)));342 343 /* Send the request */344 status = pj_stun_session_send_msg(sess->stun_sess, PJ_TRUE,345 sess->cur_server,346 sizeof(pj_sockaddr_in),347 tdata);348 if (status != PJ_SUCCESS)349 return status;350 351 sess->state = state;352 353 return PJ_SUCCESS;354 }355 356 357 341 static void end_session(nat_detect_session *sess, 358 342 pj_status_t status, … … 378 362 delay.msec = 0; 379 363 380 pj_timer_heap_schedule(sess->timer_heap, &sess->destroy_timer, &delay); 364 sess->timer.id = TIMER_DESTROY; 365 pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay); 381 366 } 382 367 … … 441 426 nat_detect_session *sess; 442 427 pj_ssize_t pkt_len; 428 pj_status_t status; 443 429 444 430 sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); 445 431 446 432 pkt_len = pkt_size; 447 return pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0, 448 dst_addr, addr_len); 433 status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0, 434 dst_addr, addr_len); 435 436 return status; 449 437 450 438 } … … 462 450 nat_detect_session *sess; 463 451 pj_stun_sockaddr_attr *mattr = NULL; 452 pj_stun_changed_addr_attr *ca = NULL; 453 pj_uint32_t *tsx_id; 454 int cmp; 455 unsigned test_id; 464 456 465 457 PJ_UNUSED_ARG(tdata); … … 503 495 status = PJNATH_ESTUNNOMAPPEDADDR; 504 496 } 497 498 /* Get CHANGED-ADDRESS attribute */ 499 ca = (pj_stun_changed_addr_attr*) 500 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0); 501 502 if (ca == NULL) { 503 status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR); 504 } 505 505 506 } 506 507 } 508 509 /* Save the result */ 510 tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id; 511 test_id = tsx_id[2]; 512 513 if (test_id >= ST_MAX) { 514 PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response", 515 test_id)); 516 end_session(sess, status, 517 PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR)); 518 return; 519 } 520 521 PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d", 522 test_names[test_id], status)); 523 524 sess->result[test_id].complete = PJ_TRUE; 525 sess->result[test_id].status = status; 526 if (status == PJ_SUCCESS) { 527 pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4, 528 sizeof(pj_sockaddr_in)); 529 pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4, 530 sizeof(pj_sockaddr_in)); 531 } 532 533 if (test_id == ST_TEST_1 && status == PJ_SUCCESS) { 534 cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, 535 sizeof(pj_sockaddr_in)); 536 if (cmp != 0) 537 send_test(sess, ST_TEST_1B, &sess->result[test_id].ca, 0); 538 } 539 540 if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess)) 541 return; 507 542 508 543 /* Handle the test result according to RFC 3489 page 22: … … 555 590 */ 556 591 557 switch (sess->state) { 558 case ST_TEST_1: 559 if (status == PJ_SUCCESS) { 560 pj_stun_changed_addr_attr *ca; 561 562 /* Get CHANGED-ADDRESS attribute */ 563 ca = (pj_stun_changed_addr_attr*) 564 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0); 565 566 if (ca) { 567 pj_memcpy(&sess->test1_ca, &ca->sockaddr, 568 sizeof(pj_sockaddr_in)); 569 } 570 571 /* Save mapped address */ 572 pj_memcpy(&sess->test1_ma, &mattr->sockaddr, 573 sizeof(pj_sockaddr_in)); 574 575 /* Compare mapped address with local address */ 576 sess->test1_same_ip=(pj_memcmp(&sess->local_addr, &mattr->sockaddr, 577 sizeof(pj_sockaddr_in))==0); 578 579 /* Execute test 2: 580 * Send BINDING_REQUEST with both the "change IP" and "change port" 581 * flags from the CHANGE-REQUEST attribute set 592 switch (sess->result[ST_TEST_1].status) { 593 case PJNATH_ESTUNTIMEDOUT: 594 /* 595 * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. 596 */ 597 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); 598 break; 599 case PJ_SUCCESS: 600 /* 601 * Test 1 is successful. Further tests are needed to detect 602 * NAT type. Compare the MAPPED-ADDRESS with the local address. 603 */ 604 cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, 605 sizeof(pj_sockaddr_in)); 606 if (cmp==0) { 607 /* 608 * MAPPED-ADDRESS and local address is equal. Need one more 609 * test to determine NAT type. 582 610 */ 583 start_test(sess, ST_TEST_2, NULL, CHANGE_ADDR_PORT); 584 585 } else { 586 /* Test 1 has completed with error. 587 * Terminate our test session. 588 */ 589 if (status == PJNATH_ESTUNTIMEDOUT) 590 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); 591 else 592 end_session(sess, status, PJ_STUN_NAT_TYPE_UNKNOWN); 593 } 594 break; 595 596 case ST_TEST_2: 597 if (sess->test1_same_ip) { 598 if (status == PJ_SUCCESS) { 611 switch (sess->result[ST_TEST_2].status) { 612 case PJ_SUCCESS: 613 /* 614 * Test 2 is also successful. We're in the open. 615 */ 599 616 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN); 600 } else if (status == PJNATH_ESTUNTIMEDOUT) { 617 break; 618 case PJNATH_ESTUNTIMEDOUT: 619 /* 620 * Test 2 has timed out. We're behind somekind of UDP 621 * firewall. 622 */ 601 623 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP); 602 } else { 603 end_session(sess, status, PJ_STUN_NAT_TYPE_UNKNOWN); 624 break; 625 default: 626 /* 627 * We've got other error with Test 2. 628 */ 629 end_session(sess, sess->result[ST_TEST_2].status, 630 PJ_STUN_NAT_TYPE_UNKNOWN); 631 break; 604 632 } 605 633 } else { 606 if (status == PJ_SUCCESS) { 634 /* 635 * MAPPED-ADDRESS is different than local address. 636 * We're behind NAT. 637 */ 638 switch (sess->result[ST_TEST_2].status) { 639 case PJ_SUCCESS: 640 /* 641 * Test 2 is successful. We're behind a full-cone NAT. 642 */ 607 643 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE); 608 } else if (status == PJNATH_ESTUNTIMEDOUT) { 609 if (sess->test1_ca.sin_family == 0) { 610 PJ_LOG(4,(sess->pool->obj_name, 611 "CHANGED-ADDRESS attribute not present in " 612 "Binding response, unable to continue test")); 613 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_UNKNOWN); 614 } else { 615 /* Execute TEST_1B */ 616 start_test(sess, ST_TEST_1B, &sess->test1_ca, 0); 644 break; 645 case PJNATH_ESTUNTIMEDOUT: 646 /* 647 * Test 2 has timed-out Check result of test 1B.. 648 */ 649 switch (sess->result[ST_TEST_1B].status) { 650 case PJ_SUCCESS: 651 /* 652 * Compare the MAPPED-ADDRESS of test 1B with the 653 * MAPPED-ADDRESS returned in test 1.. 654 */ 655 cmp = pj_memcmp(&sess->result[ST_TEST_1].ma, 656 &sess->result[ST_TEST_1B].ma, 657 sizeof(pj_sockaddr_in)); 658 if (cmp != 0) { 659 /* 660 * MAPPED-ADDRESS is different, we're behind a 661 * symmetric NAT. 662 */ 663 end_session(sess, PJ_SUCCESS, 664 PJ_STUN_NAT_TYPE_SYMMETRIC); 665 } else { 666 /* 667 * MAPPED-ADDRESS is equal. We're behind a restricted 668 * or port-restricted NAT, depending on the result of 669 * test 3. 670 */ 671 switch (sess->result[ST_TEST_3].status) { 672 case PJ_SUCCESS: 673 /* 674 * Test 3 is successful, we're behind a restricted 675 * NAT. 676 */ 677 end_session(sess, PJ_SUCCESS, 678 PJ_STUN_NAT_TYPE_RESTRICTED); 679 break; 680 case PJNATH_ESTUNTIMEDOUT: 681 /* 682 * Test 3 failed, we're behind a port restricted 683 * NAT. 684 */ 685 end_session(sess, PJ_SUCCESS, 686 PJ_STUN_NAT_TYPE_PORT_RESTRICTED); 687 break; 688 default: 689 /* 690 * Got other error with test 3. 691 */ 692 end_session(sess, sess->result[ST_TEST_3].status, 693 PJ_STUN_NAT_TYPE_UNKNOWN); 694 break; 695 } 696 } 697 break; 698 case PJNATH_ESTUNTIMEDOUT: 699 /* 700 * Strangely test 1B has failed. Maybe connectivity was 701 * lost? 702 */ 703 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); 704 break; 705 default: 706 /* 707 * Got other error with test 1B. 708 */ 709 end_session(sess, sess->result[ST_TEST_1B].status, 710 PJ_STUN_NAT_TYPE_UNKNOWN); 711 break; 617 712 } 618 } else { 619 end_session(sess, status, PJ_STUN_NAT_TYPE_UNKNOWN); 713 break; 714 default: 715 /* 716 * We've got other error with Test 2. 717 */ 718 end_session(sess, sess->result[ST_TEST_2].status, 719 PJ_STUN_NAT_TYPE_UNKNOWN); 720 break; 620 721 } 621 722 } 622 723 break; 623 624 case ST_TEST_1B: 625 if (status == PJ_SUCCESS) { 626 int cmp; 627 628 /* Compare MAPPED-ADDRESS with the one from TEST_1 */ 629 cmp = pj_memcmp(&mattr->sockaddr, &sess->test1_ma, 630 sizeof(pj_sockaddr_in)); 631 632 if (cmp!=0) { 633 /* Different address, this is symmetric NAT */ 634 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC); 635 } else { 636 /* Same address. Check if this is port restricted. 637 * Execute TEST_3 638 */ 639 start_test(sess, ST_TEST_3, NULL, CHANGE_PORT); 640 } 724 default: 725 /* 726 * We've got other error with Test 1. 727 */ 728 end_session(sess, sess->result[ST_TEST_1].status, 729 PJ_STUN_NAT_TYPE_UNKNOWN); 730 break; 731 } 732 733 pj_mutex_unlock(sess->mutex); 734 } 735 736 737 /* Perform test */ 738 static pj_status_t send_test(nat_detect_session *sess, 739 enum test_type test_id, 740 const pj_sockaddr_in *alt_addr, 741 pj_uint32_t change_flag) 742 { 743 pj_stun_tx_data *tdata; 744 pj_uint32_t magic, tsx_id[3]; 745 pj_status_t status; 746 747 sess->result[test_id].executed = PJ_TRUE; 748 749 /* Randomize tsx id */ 750 do { 751 magic = pj_rand(); 752 } while (magic == PJ_STUN_MAGIC); 753 754 tsx_id[0] = pj_rand(); 755 tsx_id[1] = pj_rand(); 756 tsx_id[2] = test_id; 757 758 /* Create BIND request */ 759 status = pj_stun_session_create_req(sess->stun_sess, 760 PJ_STUN_BINDING_REQUEST, magic, 761 (pj_uint8_t*)tsx_id, &tdata); 762 if (status != PJ_SUCCESS) 763 goto on_error; 764 765 /* Add CHANGE-REQUEST attribute */ 766 status = pj_stun_msg_add_uint_attr(sess->pool, tdata->msg, 767 PJ_STUN_ATTR_CHANGE_REQUEST, 768 change_flag); 769 if (status != PJ_SUCCESS) 770 goto on_error; 771 772 /* Configure alternate address */ 773 if (alt_addr) 774 sess->cur_server = (pj_sockaddr_in*) alt_addr; 775 else 776 sess->cur_server = &sess->server; 777 778 PJ_LOG(5,(sess->pool->obj_name, 779 "Performing %s to %s:%d", 780 test_names[test_id], 781 pj_inet_ntoa(sess->cur_server->sin_addr), 782 pj_ntohs(sess->cur_server->sin_port))); 783 784 /* Send the request */ 785 status = pj_stun_session_send_msg(sess->stun_sess, PJ_TRUE, 786 sess->cur_server, 787 sizeof(pj_sockaddr_in), 788 tdata); 789 if (status != PJ_SUCCESS) 790 goto on_error; 791 792 return PJ_SUCCESS; 793 794 on_error: 795 sess->result[test_id].complete = PJ_TRUE; 796 sess->result[test_id].status = status; 797 798 return status; 799 } 800 801 802 /* Timer callback */ 803 static void on_sess_timer(pj_timer_heap_t *th, 804 pj_timer_entry *te) 805 { 806 nat_detect_session *sess; 807 808 sess = (nat_detect_session*) te->user_data; 809 810 if (te->id == TIMER_DESTROY) { 811 pj_mutex_lock(sess->mutex); 812 pj_ioqueue_unregister(sess->key); 813 sess->key = NULL; 814 te->id = 0; 815 pj_mutex_unlock(sess->mutex); 816 817 sess_destroy(sess); 818 819 } else if (te->id == TIMER_TEST) { 820 821 int executed; 822 pj_bool_t next_timer; 823 824 pj_mutex_lock(sess->mutex); 825 826 executed = test_executed(sess); 827 next_timer = PJ_FALSE; 828 829 if (executed == ST_TEST_1) { 830 send_test(sess, ST_TEST_1, NULL, 0); 831 next_timer = PJ_TRUE; 832 } else if (executed == ST_TEST_2) { 833 send_test(sess, ST_TEST_2, NULL, CHANGE_IP_PORT_FLAG); 834 next_timer = PJ_TRUE; 835 } else if (executed == ST_TEST_3) { 836 send_test(sess, ST_TEST_3, NULL, CHANGE_PORT_FLAG); 641 837 } else { 642 end_session(sess, status, PJ_STUN_NAT_TYPE_UNKNOWN);838 pj_assert(!"Shouldn't have timer at this state"); 643 839 } 644 break; 645 646 case ST_TEST_3: 647 if (status == PJ_SUCCESS) { 648 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); 649 } else if (status == PJNATH_ESTUNTIMEDOUT) { 650 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_PORT_RESTRICTED); 840 841 if (next_timer) { 842 pj_time_val delay = {0, TEST_INTERVAL}; 843 pj_timer_heap_schedule(th, te, &delay); 651 844 } else { 652 end_session(sess, status, PJ_STUN_NAT_TYPE_UNKNOWN);845 te->id = 0; 653 846 } 654 break; 655 } 656 657 pj_mutex_unlock(sess->mutex); 658 } 659 660 661 static void on_timer_destroy(pj_timer_heap_t *th, 662 pj_timer_entry *te) 663 { 664 nat_detect_session *sess; 665 666 PJ_UNUSED_ARG(th); 667 668 sess = (nat_detect_session*) te->user_data; 669 670 pj_mutex_lock(sess->mutex); 671 pj_ioqueue_unregister(sess->key); 672 sess->key = NULL; 673 pj_mutex_unlock(sess->mutex); 674 675 sess_destroy(sess); 676 } 677 847 848 pj_mutex_unlock(sess->mutex); 849 850 } else { 851 pj_assert(!"Invalid timer ID"); 852 } 853 } 854
Note: See TracChangeset
for help on using the changeset viewer.