stun_tester.c 9.76 KB
Newer Older
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
	belle-sip - SIP (RFC3261) library.
	Copyright (C) 2014  Belledonne Communications SARL

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

19
#include "linphone/core.h"
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
20
#include "liblinphone_tester.h"
jehan's avatar
jehan committed
21
#include "mediastreamer2/stun.h"
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
22 23 24
#include "ortp/port.h"


Ghislain MARY's avatar
Ghislain MARY committed
25
static const char *stun_address = "stun.linphone.org";
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
26 27


Ghislain MARY's avatar
Ghislain MARY committed
28
static size_t test_stun_encode(char **buffer)
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
29
{
Ghislain MARY's avatar
Ghislain MARY committed
30 31 32 33 34
	MSStunMessage *req = ms_stun_binding_request_create();
	UInt96 tr_id = ms_stun_message_get_tr_id(req);
	tr_id.octet[0] = 11;
	ms_stun_message_set_tr_id(req, tr_id);
	return ms_stun_message_encode(req, buffer);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
35 36
}

37
static void linphone_stun_test_encode(void)
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
38
{
Ghislain MARY's avatar
Ghislain MARY committed
39 40
	char *buffer = NULL;
	size_t len = test_stun_encode(&buffer);
41
	BC_ASSERT(len > 0);
Ghislain MARY's avatar
Ghislain MARY committed
42 43
	BC_ASSERT_PTR_NOT_NULL(buffer);
	if (buffer != NULL) ms_free(buffer);
44
	ms_message("STUN message encoded in %i bytes", (int)len);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
45 46
}

47
static void linphone_stun_test_grab_ip(void) {
Ghislain MARY's avatar
Ghislain MARY committed
48
	LinphoneCoreManager* lc_stun = linphone_core_manager_new2("stun_rc", FALSE);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
49
	int ping_time;
Ghislain MARY's avatar
Ghislain MARY committed
50
	int tmp = 0;
51 52 53 54 55 56 57 58 59 60
	char audio_addr[LINPHONE_IPADDR_SIZE] = { 0 };
	char video_addr[LINPHONE_IPADDR_SIZE] = { 0 };
	char text_addr[LINPHONE_IPADDR_SIZE] = { 0 };
	int audio_port = 0;
	int video_port = 0;
	int text_port = 0;

	/* This test verifies the very basic STUN support of liblinphone, which is deprecated.
	 * It works only in IPv4 mode and there is no plan to make it work over ipv6. */
	if (liblinphone_tester_ipv4_available())
Simon Morlat's avatar
Simon Morlat committed
61 62
		goto end;
	linphone_core_enable_ipv6(lc_stun->lc, FALSE);
63
	linphone_core_enable_realtime_text(lc_stun->lc, TRUE);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
64
	linphone_core_set_stun_server(lc_stun->lc, stun_address);
65
	BC_ASSERT_STRING_EQUAL(stun_address, linphone_core_get_stun_server(lc_stun->lc));
Ghislain MARY's avatar
Ghislain MARY committed
66
	wait_for(lc_stun->lc, lc_stun->lc, &tmp, 1);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
67

68
	ping_time = linphone_run_stun_tests(lc_stun->lc, 7078, 9078, 11078, audio_addr, &audio_port, video_addr, &video_port, text_addr, &text_port);
69
	BC_ASSERT(ping_time != -1);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
70 71 72

	ms_message("Round trip to STUN: %d ms", ping_time);

73 74
	BC_ASSERT(audio_addr[0] != '\0');
	BC_ASSERT(audio_port != 0);
75
#ifdef VIDEO_ENABLED
76 77
	BC_ASSERT(video_addr[0] != '\0');
	BC_ASSERT(video_port != 0);
78
#endif
79 80
	BC_ASSERT(text_addr[0] != '\0');
	BC_ASSERT(text_port != 0);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
81

82
	ms_message("STUN test result: local audio port maps to %s:%i", audio_addr, audio_port);
83
#ifdef VIDEO_ENABLED
84
	ms_message("STUN test result: local video port maps to %s:%i", video_addr, video_port);
85
#endif
86
	ms_message("STUN test result: local text port maps to %s:%i", text_addr, text_port);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
87

Simon Morlat's avatar
Simon Morlat committed
88
end:
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
89 90 91
	linphone_core_manager_destroy(lc_stun);
}

92
static void configure_nat_policy(LinphoneCore *lc, bool_t turn_enabled) {
Ghislain MARY's avatar
Ghislain MARY committed
93 94 95 96 97
	const char *username = "liblinphone-tester";
	const char *password = "retset-enohpnilbil";
	LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, username, NULL, password, NULL, "sip.linphone.org", NULL);
	LinphoneNatPolicy *nat_policy = linphone_core_create_nat_policy(lc);
	linphone_nat_policy_enable_ice(nat_policy, TRUE);
98 99 100 101 102 103 104 105
	if (turn_enabled) {
		linphone_nat_policy_enable_turn(nat_policy, TRUE);
		linphone_nat_policy_set_stun_server(nat_policy, "sip1.linphone.org:3479");
		linphone_nat_policy_set_stun_server_username(nat_policy, username);
	} else {
		linphone_nat_policy_enable_stun(nat_policy, TRUE);
		linphone_nat_policy_set_stun_server(nat_policy, "stun.linphone.org");
	}
Ghislain MARY's avatar
Ghislain MARY committed
106 107
	linphone_core_set_nat_policy(lc, nat_policy);
	linphone_core_add_auth_info(lc, auth_info);
Ghislain MARY's avatar
Ghislain MARY committed
108
	linphone_nat_policy_unref(nat_policy);
Ghislain MARY's avatar
Ghislain MARY committed
109
	linphone_auth_info_unref(auth_info);
Ghislain MARY's avatar
Ghislain MARY committed
110 111
}

112 113 114 115 116 117 118 119 120 121 122 123 124
static void check_turn_context_statistics(MSTurnContext *turn_context, bool_t forced_relay) {
	BC_ASSERT_TRUE(turn_context->stats.nb_successful_allocate > 1);
	if (forced_relay == TRUE) {
		BC_ASSERT_TRUE(turn_context->stats.nb_send_indication > 0);
		BC_ASSERT_TRUE(turn_context->stats.nb_data_indication > 0);
		BC_ASSERT_TRUE(turn_context->stats.nb_received_channel_msg > 0);
		BC_ASSERT_TRUE(turn_context->stats.nb_sent_channel_msg > 0);
		BC_ASSERT_TRUE(turn_context->stats.nb_successful_refresh > 0);
		BC_ASSERT_TRUE(turn_context->stats.nb_successful_create_permission > 1);
		BC_ASSERT_TRUE(turn_context->stats.nb_successful_channel_bind > 1);
	}
}

Ghislain MARY's avatar
Ghislain MARY committed
125
static void ice_turn_call_base(bool_t video_enabled, bool_t forced_relay, bool_t caller_turn_enabled, bool_t callee_turn_enabled, bool_t rtcp_mux_enabled, bool_t ipv6) {
Ghislain MARY's avatar
Ghislain MARY committed
126 127
	LinphoneCoreManager *marie;
	LinphoneCoreManager *pauline;
128
	LinphoneCall *lcall;
Ghislain MARY's avatar
Ghislain MARY committed
129
	LinphoneIceState expected_ice_state = LinphoneIceStateHostConnection;
Ghislain MARY's avatar
Ghislain MARY committed
130
	LinphoneMediaDirection expected_video_dir = LinphoneMediaDirectionInactive;
131
	bctbx_list_t *lcs = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
132

Ghislain MARY's avatar
Ghislain MARY committed
133
	marie = linphone_core_manager_new2("marie_rc", FALSE);
134
	lcs = bctbx_list_append(lcs, marie->lc);
Ghislain MARY's avatar
Ghislain MARY committed
135
	pauline = linphone_core_manager_new2(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc", FALSE);
136
	lcs = bctbx_list_append(lcs, pauline->lc);
Ghislain MARY's avatar
Ghislain MARY committed
137

Ghislain MARY's avatar
Ghislain MARY committed
138 139 140
	if (ipv6) {
		linphone_core_enable_ipv6(marie->lc, TRUE);
		linphone_core_enable_ipv6(pauline->lc, TRUE);
141 142 143
	} else {
		linphone_core_enable_ipv6(marie->lc, FALSE);
		linphone_core_enable_ipv6(pauline->lc, FALSE);
Ghislain MARY's avatar
Ghislain MARY committed
144 145
	}

146 147
	configure_nat_policy(marie->lc, caller_turn_enabled);
	configure_nat_policy(pauline->lc, callee_turn_enabled);
Ghislain MARY's avatar
Ghislain MARY committed
148 149 150
	if (forced_relay == TRUE) {
		linphone_core_enable_forced_ice_relay(marie->lc, TRUE);
		linphone_core_enable_forced_ice_relay(pauline->lc, TRUE);
151 152
		linphone_core_enable_short_turn_refresh(marie->lc, TRUE);
		linphone_core_enable_short_turn_refresh(pauline->lc, TRUE);
Ghislain MARY's avatar
Ghislain MARY committed
153 154
		expected_ice_state = LinphoneIceStateRelayConnection;
	}
155 156 157 158
	if (rtcp_mux_enabled == TRUE) {
		lp_config_set_int(linphone_core_get_config(marie->lc), "rtp", "rtcp_mux", 1);
		lp_config_set_int(linphone_core_get_config(pauline->lc), "rtp", "rtcp_mux", 1);
	}
Ghislain MARY's avatar
Ghislain MARY committed
159

Ghislain MARY's avatar
Ghislain MARY committed
160 161 162
	linphone_core_manager_start(marie, TRUE);
	linphone_core_manager_start(pauline, TRUE);

Ghislain MARY's avatar
Ghislain MARY committed
163
	if (video_enabled) {
Simon Morlat's avatar
Simon Morlat committed
164
#ifdef VIDEO_ENABLED
165 166
		linphone_core_set_video_device(pauline->lc,liblinphone_tester_mire_id);
		linphone_core_set_video_device(marie->lc,liblinphone_tester_mire_id);
Ghislain MARY's avatar
Ghislain MARY committed
167 168
		video_call_base_2(marie, pauline, FALSE, LinphoneMediaEncryptionNone, TRUE, TRUE);
		expected_video_dir = LinphoneMediaDirectionSendRecv;
Simon Morlat's avatar
Simon Morlat committed
169
#endif
Ghislain MARY's avatar
Ghislain MARY committed
170 171 172
	} else {
		BC_ASSERT_TRUE(call(marie, pauline));
	}
Ghislain MARY's avatar
Ghislain MARY committed
173 174 175 176 177 178

	/* Wait for the ICE reINVITE to complete */
	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2));
	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2));
	BC_ASSERT_TRUE(check_ice(pauline, marie, expected_ice_state));
	check_nb_media_starts(pauline, marie, 1, 1);
Ghislain MARY's avatar
Ghislain MARY committed
179 180
	check_media_direction(marie, linphone_core_get_current_call(marie->lc), lcs, LinphoneMediaDirectionSendRecv, expected_video_dir);
	check_media_direction(pauline, linphone_core_get_current_call(pauline->lc), lcs, LinphoneMediaDirectionSendRecv, expected_video_dir);
Ghislain MARY's avatar
Ghislain MARY committed
181
	liblinphone_tester_check_rtcp(marie, pauline);
182
	lcall = linphone_core_get_current_call(marie->lc);
183 184
	BC_ASSERT_PTR_NOT_NULL(lcall);
	if (lcall != NULL) {
185 186 187 188
		IceSession *ice_session = linphone_call_get_ice_session(lcall);
		BC_ASSERT_PTR_NOT_NULL(ice_session);
		if (ice_session != NULL) {
			IceCheckList *cl = ice_session_check_list(ice_session, 0);
189 190 191 192 193
			BC_ASSERT_PTR_NOT_NULL(cl);
			if (cl != NULL) {
				check_turn_context_statistics(cl->rtp_turn_context, forced_relay);
				if (!rtcp_mux_enabled) check_turn_context_statistics(cl->rtcp_turn_context, forced_relay);
			}
194 195
		}
	}
Ghislain MARY's avatar
Ghislain MARY committed
196 197 198 199 200

	end_call(marie, pauline);

	linphone_core_manager_destroy(pauline);
	linphone_core_manager_destroy(marie);
201
	bctbx_list_free(lcs);
Ghislain MARY's avatar
Ghislain MARY committed
202 203 204
}

static void basic_ice_turn_call(void) {
Ghislain MARY's avatar
Ghislain MARY committed
205 206 207 208 209 210 211 212 213
	ice_turn_call_base(FALSE, FALSE, TRUE, TRUE, FALSE, FALSE);
}

static void basic_ipv6_ice_turn_call(void) {
	if (liblinphone_tester_ipv6_available()) {
		ice_turn_call_base(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE);
	} else {
		ms_warning("Test skipped, no ipv6 available");
	}
Ghislain MARY's avatar
Ghislain MARY committed
214 215
}

Simon Morlat's avatar
Simon Morlat committed
216
#ifdef VIDEO_ENABLED
Ghislain MARY's avatar
Ghislain MARY committed
217
static void video_ice_turn_call(void) {
Ghislain MARY's avatar
Ghislain MARY committed
218
	ice_turn_call_base(TRUE, FALSE, TRUE, TRUE, FALSE, FALSE);
Ghislain MARY's avatar
Ghislain MARY committed
219
}
Simon Morlat's avatar
Simon Morlat committed
220
#endif
Ghislain MARY's avatar
Ghislain MARY committed
221 222

static void relayed_ice_turn_call(void) {
Ghislain MARY's avatar
Ghislain MARY committed
223
	ice_turn_call_base(FALSE, TRUE, TRUE, TRUE, FALSE, FALSE);
Ghislain MARY's avatar
Ghislain MARY committed
224 225
}

Simon Morlat's avatar
Simon Morlat committed
226
#ifdef VIDEO_ENABLED
Ghislain MARY's avatar
Ghislain MARY committed
227
static void relayed_video_ice_turn_call(void) {
Ghislain MARY's avatar
Ghislain MARY committed
228
	ice_turn_call_base(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE);
229
}
Simon Morlat's avatar
Simon Morlat committed
230
#endif
231 232

static void relayed_ice_turn_call_with_rtcp_mux(void) {
Ghislain MARY's avatar
Ghislain MARY committed
233
	ice_turn_call_base(FALSE, TRUE, TRUE, TRUE, TRUE, FALSE);
234 235 236
}

static void relayed_ice_turn_to_ice_stun_call(void) {
Ghislain MARY's avatar
Ghislain MARY committed
237
	ice_turn_call_base(FALSE, TRUE, TRUE, FALSE, FALSE, FALSE);
Ghislain MARY's avatar
Ghislain MARY committed
238 239
}

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
240 241

test_t stun_tests[] = {
Ghislain MARY's avatar
Ghislain MARY committed
242 243 244
	TEST_ONE_TAG("Basic Stun test (Ping/public IP)", linphone_stun_test_grab_ip, "STUN"),
	TEST_ONE_TAG("STUN encode", linphone_stun_test_encode, "STUN"),
	TEST_TWO_TAGS("Basic ICE+TURN call", basic_ice_turn_call, "ICE", "TURN"),
Ghislain MARY's avatar
Ghislain MARY committed
245
	TEST_TWO_TAGS("Basic IPv6 ICE+TURN call", basic_ipv6_ice_turn_call, "ICE", "TURN"),
Simon Morlat's avatar
Simon Morlat committed
246
#ifdef VIDEO_ENABLED
Ghislain MARY's avatar
Ghislain MARY committed
247 248
	TEST_TWO_TAGS("Video ICE+TURN call", video_ice_turn_call, "ICE", "TURN"),
	TEST_TWO_TAGS("Relayed video ICE+TURN call", relayed_video_ice_turn_call, "ICE", "TURN"),
Simon Morlat's avatar
Simon Morlat committed
249 250
#endif
	TEST_TWO_TAGS("Relayed ICE+TURN call", relayed_ice_turn_call, "ICE", "TURN"),
251
	TEST_TWO_TAGS("Relayed ICE+TURN call with rtcp-mux", relayed_ice_turn_call_with_rtcp_mux, "ICE", "TURN"),
252
	TEST_TWO_TAGS("Relayed ICE+TURN to ICE+STUN call", relayed_ice_turn_to_ice_stun_call, "ICE", "TURN")
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
253 254
};

255
test_suite_t stun_test_suite = {"Stun", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,
256
								sizeof(stun_tests) / sizeof(stun_tests[0]), stun_tests};