res_stun_monitor.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2010, Digium, Inc.
  5. *
  6. * David Vossel <dvossel@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*!
  19. * \file
  20. * \brief STUN Network Monitor
  21. *
  22. * \author David Vossel <dvossel@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  29. #include "asterisk/module.h"
  30. #include "asterisk/sched.h"
  31. #include "asterisk/config.h"
  32. #include "asterisk/stun.h"
  33. #include "asterisk/netsock2.h"
  34. #include "asterisk/lock.h"
  35. #include "asterisk/acl.h"
  36. #include "asterisk/cli.h"
  37. #include "asterisk/json.h"
  38. #include "asterisk/stasis.h"
  39. #include "asterisk/stasis_system.h"
  40. #include "asterisk/astobj2.h"
  41. #include <fcntl.h>
  42. #define DEFAULT_MONITOR_REFRESH 30 /*!< Default refresh period in seconds */
  43. #define DEFAULT_RETRIES 3 /*!< retries shown in stun show status
  44. matching static retries in stun.c */
  45. static const char stun_conf_file[] = "res_stun_monitor.conf";
  46. static struct ast_sched_context *sched;
  47. static struct {
  48. /*! STUN monitor protection lock. */
  49. ast_mutex_t lock;
  50. /*! Current perceived external address. */
  51. struct sockaddr_in external_addr;
  52. /*! STUN server host name. */
  53. const char *server_hostname;
  54. /*! Port of STUN server to use */
  55. unsigned int stun_port;
  56. /*! Number of seconds between polls to the STUN server for the external address. */
  57. unsigned int refresh;
  58. /*! Monitoring STUN socket. */
  59. int stun_sock;
  60. /*! TRUE if the STUN monitor is enabled. */
  61. unsigned int monitor_enabled:1;
  62. /*! TRUE if the perceived external address is valid/known. */
  63. unsigned int external_addr_known:1;
  64. /*! TRUE if we have already griped about a STUN poll failing. */
  65. unsigned int stun_poll_failed_gripe:1;
  66. } args;
  67. static void stun_close_sock(void)
  68. {
  69. if (0 <= args.stun_sock) {
  70. close(args.stun_sock);
  71. args.stun_sock = -1;
  72. }
  73. }
  74. /* \brief called by scheduler to send STUN request */
  75. static int stun_monitor_request(const void *blarg)
  76. {
  77. int res;
  78. struct sockaddr_in answer;
  79. static const struct sockaddr_in no_addr = { 0, };
  80. ast_mutex_lock(&args.lock);
  81. if (!args.monitor_enabled) {
  82. goto monitor_request_cleanup;
  83. }
  84. if (args.stun_sock < 0) {
  85. struct ast_sockaddr stun_addr;
  86. /* STUN socket not open. Refresh the server DNS address resolution. */
  87. if (!args.server_hostname) {
  88. /* No STUN hostname? */
  89. goto monitor_request_cleanup;
  90. }
  91. /* Lookup STUN address. */
  92. memset(&stun_addr, 0, sizeof(stun_addr));
  93. stun_addr.ss.ss_family = AF_INET;
  94. if (ast_get_ip(&stun_addr, args.server_hostname)) {
  95. /* Lookup failed. */
  96. ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
  97. args.server_hostname);
  98. goto monitor_request_cleanup;
  99. }
  100. ast_sockaddr_set_port(&stun_addr, args.stun_port);
  101. /* open socket binding */
  102. args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
  103. if (args.stun_sock < 0) {
  104. ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
  105. goto monitor_request_cleanup;
  106. }
  107. if (ast_connect(args.stun_sock, &stun_addr)) {
  108. ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
  109. ast_sockaddr_stringify(&stun_addr), strerror(errno));
  110. stun_close_sock();
  111. goto monitor_request_cleanup;
  112. }
  113. }
  114. res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
  115. if (res) {
  116. /*
  117. * STUN request timed out or errored.
  118. *
  119. * Refresh the server DNS address resolution next time around.
  120. */
  121. if (!args.stun_poll_failed_gripe) {
  122. args.stun_poll_failed_gripe = 1;
  123. ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
  124. res < 0 ? "failed" : "got no response");
  125. }
  126. stun_close_sock();
  127. } else {
  128. args.stun_poll_failed_gripe = 0;
  129. if (memcmp(&no_addr, &answer, sizeof(no_addr))
  130. && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
  131. const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
  132. int newport = ntohs(answer.sin_port);
  133. ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
  134. ast_inet_ntoa(args.external_addr.sin_addr),
  135. ntohs(args.external_addr.sin_port), newaddr, newport);
  136. args.external_addr = answer;
  137. if (args.external_addr_known) {
  138. RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
  139. RAII_VAR(struct ast_json_payload *, json_payload, NULL, ao2_cleanup);
  140. RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
  141. if (!ast_network_change_type()) {
  142. goto publish_failure;
  143. }
  144. /* This json_object doesn't actually contain anything yet. We have to reference something
  145. * for stasis, and this is useful for if we want to ever add data for any reason. */
  146. json_object = ast_json_object_create();
  147. if (!json_object) {
  148. goto publish_failure;
  149. }
  150. if (!(json_payload = ast_json_payload_create(json_object))) {
  151. goto publish_failure;
  152. }
  153. msg = stasis_message_create(ast_network_change_type(), json_payload);
  154. publish_failure:
  155. if (msg) {
  156. stasis_publish(ast_system_topic(), msg);
  157. } else {
  158. ast_log(LOG_ERROR, "Failed to issue network change message.\n");
  159. }
  160. } else {
  161. /* this was the first external address we found, do not alert listeners
  162. * until this address changes to something else. */
  163. args.external_addr_known = 1;
  164. }
  165. }
  166. }
  167. monitor_request_cleanup:
  168. /* always refresh this scheduler item. It will be removed elsewhere when
  169. * it is supposed to go away */
  170. res = args.refresh * 1000;
  171. ast_mutex_unlock(&args.lock);
  172. return res;
  173. }
  174. /*!
  175. * \internal
  176. * \brief Stops the STUN monitor thread.
  177. *
  178. * \note do not hold the args->lock while calling this
  179. *
  180. * \return Nothing
  181. */
  182. static void stun_stop_monitor(void)
  183. {
  184. ast_mutex_lock(&args.lock);
  185. args.monitor_enabled = 0;
  186. ast_free((char *) args.server_hostname);
  187. args.server_hostname = NULL;
  188. stun_close_sock();
  189. ast_mutex_unlock(&args.lock);
  190. if (sched) {
  191. ast_sched_context_destroy(sched);
  192. sched = NULL;
  193. ast_log(LOG_NOTICE, "STUN monitor stopped\n");
  194. }
  195. }
  196. /*!
  197. * \internal
  198. * \brief Starts the STUN monitor thread.
  199. *
  200. * \note The args->lock MUST be held when calling this function
  201. *
  202. * \return Nothing
  203. */
  204. static int stun_start_monitor(void)
  205. {
  206. /* if scheduler thread is not started, make sure to start it now */
  207. if (sched) {
  208. return 0; /* already started */
  209. }
  210. if (!(sched = ast_sched_context_create())) {
  211. ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
  212. return -1;
  213. }
  214. if (ast_sched_start_thread(sched)) {
  215. ast_sched_context_destroy(sched);
  216. sched = NULL;
  217. stun_close_sock();
  218. return -1;
  219. }
  220. if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
  221. ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
  222. ast_sched_context_destroy(sched);
  223. sched = NULL;
  224. return -1;
  225. }
  226. ast_log(LOG_NOTICE, "STUN monitor started\n");
  227. return 0;
  228. }
  229. /*!
  230. * \internal
  231. * \brief Parse and setup the stunaddr parameter.
  232. *
  233. * \param value Configuration parameter variable value.
  234. *
  235. * \retval 0 on success.
  236. * \retval -1 on error.
  237. */
  238. static int setup_stunaddr(const char *value)
  239. {
  240. char *val;
  241. char *host_str;
  242. char *port_str;
  243. unsigned int port;
  244. struct ast_sockaddr stun_addr;
  245. if (ast_strlen_zero(value)) {
  246. /* Setting to an empty value disables STUN monitoring. */
  247. args.monitor_enabled = 0;
  248. return 0;
  249. }
  250. val = ast_strdupa(value);
  251. if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
  252. || ast_strlen_zero(host_str)) {
  253. return -1;
  254. }
  255. /* Determine STUN port */
  256. if (ast_strlen_zero(port_str)
  257. || 1 != sscanf(port_str, "%30u", &port)) {
  258. port = STANDARD_STUN_PORT;
  259. }
  260. host_str = ast_strdup(host_str);
  261. if (!host_str) {
  262. return -1;
  263. }
  264. /* Lookup STUN address. */
  265. memset(&stun_addr, 0, sizeof(stun_addr));
  266. stun_addr.ss.ss_family = AF_INET;
  267. if (ast_get_ip(&stun_addr, host_str)) {
  268. ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
  269. ast_free(host_str);
  270. return -1;
  271. }
  272. /* Save STUN server information. */
  273. ast_free((char *) args.server_hostname);
  274. args.server_hostname = host_str;
  275. args.stun_port = port;
  276. /* Enable STUN monitor */
  277. args.monitor_enabled = 1;
  278. return 0;
  279. }
  280. static int load_config(int startup)
  281. {
  282. struct ast_flags config_flags = { 0, };
  283. struct ast_config *cfg;
  284. struct ast_variable *v;
  285. if (!startup) {
  286. ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
  287. }
  288. cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
  289. if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
  290. ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
  291. return -1;
  292. }
  293. if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
  294. return 0;
  295. }
  296. /* clean up any previous open socket */
  297. stun_close_sock();
  298. args.stun_poll_failed_gripe = 0;
  299. /* set defaults */
  300. args.monitor_enabled = 0;
  301. args.refresh = DEFAULT_MONITOR_REFRESH;
  302. for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
  303. if (!strcasecmp(v->name, "stunaddr")) {
  304. if (setup_stunaddr(v->value)) {
  305. ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
  306. v->value, v->lineno);
  307. }
  308. } else if (!strcasecmp(v->name, "stunrefresh")) {
  309. if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
  310. ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
  311. args.refresh = DEFAULT_MONITOR_REFRESH;
  312. }
  313. } else {
  314. ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
  315. v->value, v->lineno);
  316. }
  317. }
  318. ast_config_destroy(cfg);
  319. return 0;
  320. }
  321. /*! \brief Execute stun show status command */
  322. static void _stun_show_status(int fd)
  323. {
  324. const char *status;
  325. #define DATALN "%-25s %-5u %-7u %-8d %-7s %-16s %-d\n"
  326. #define HEADER "%-25s %-5s %-7s %-8s %-7s %-16s %-s\n"
  327. /*! we only have one stun server, but start to play well with more */
  328. ast_cli(fd, HEADER, "Hostname", "Port", "Period", "Retries", "Status", "ExternAddr", "ExternPort");
  329. if (args.stun_poll_failed_gripe) {
  330. status = "FAIL";
  331. } else if (args.external_addr_known) {
  332. status = "OK";
  333. } else {
  334. status = "INIT";
  335. }
  336. ast_cli( fd, DATALN,
  337. args.server_hostname,
  338. args.stun_port,
  339. args.refresh,
  340. DEFAULT_RETRIES,
  341. status,
  342. ast_inet_ntoa(args.external_addr.sin_addr),
  343. ntohs(args.external_addr.sin_port)
  344. );
  345. #undef HEADER
  346. #undef DATALN
  347. }
  348. static char *handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  349. {
  350. switch (cmd) {
  351. case CLI_INIT:
  352. e->command = "stun show status";
  353. e->usage =
  354. "Usage: stun show status\n"
  355. " List all known STUN servers and statuses.\n";
  356. return NULL;
  357. case CLI_GENERATE:
  358. return NULL;
  359. }
  360. if (a->argc != 3) {
  361. return CLI_SHOWUSAGE;
  362. }
  363. _stun_show_status(a->fd);
  364. return CLI_SUCCESS;
  365. }
  366. static struct ast_cli_entry cli_stun[] = {
  367. AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
  368. };
  369. static int __reload(int startup)
  370. {
  371. int res;
  372. ast_mutex_lock(&args.lock);
  373. if (!(res = load_config(startup)) && args.monitor_enabled) {
  374. res = stun_start_monitor();
  375. }
  376. ast_mutex_unlock(&args.lock);
  377. if (res < 0 || !args.monitor_enabled) {
  378. stun_stop_monitor();
  379. }
  380. return res;
  381. }
  382. static int reload(void)
  383. {
  384. return __reload(0);
  385. }
  386. static int unload_module(void)
  387. {
  388. stun_stop_monitor();
  389. ast_mutex_destroy(&args.lock);
  390. /*! Unregister CLI commands */
  391. ast_cli_unregister_multiple(cli_stun, ARRAY_LEN(cli_stun));
  392. return 0;
  393. }
  394. static int load_module(void)
  395. {
  396. ast_mutex_init(&args.lock);
  397. args.stun_sock = -1;
  398. if (__reload(1)) {
  399. ast_mutex_destroy(&args.lock);
  400. return AST_MODULE_LOAD_DECLINE;
  401. }
  402. /*! Register CLI commands */
  403. ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
  404. return AST_MODULE_LOAD_SUCCESS;
  405. }
  406. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
  407. .support_level = AST_MODULE_SUPPORT_CORE,
  408. .load = load_module,
  409. .unload = unload_module,
  410. .reload = reload,
  411. .load_pri = AST_MODPRI_CHANNEL_DEPEND
  412. );