statistics.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2006 Voice Sistem SRL
  5. *
  6. * This file is part of Kamailio, a free SIP server.
  7. *
  8. * Kamailio is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * Kamailio is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. *
  22. *
  23. * History:
  24. * ---------
  25. * 2006-01-16 first version (bogdan)
  26. * 2006-11-28 added get_stat_var_from_num_code() (Jeffrey Magder -
  27. * SOMA Networks)
  28. * 2010-08-08 removed all the parts emulated by kstats_wrapper.[ch] (andrei)
  29. */
  30. /*!
  31. * \file
  32. * \brief Statistics support
  33. */
  34. #include <string.h>
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include "../../ut.h"
  38. #include "../../dprint.h"
  39. #include "../../socket_info.h"
  40. #include "statistics.h"
  41. #ifdef STATISTICS
  42. /*! \brief
  43. * Returns the statistic associated with 'numerical_code' and 'out_codes'.
  44. * Specifically:
  45. *
  46. * - if out_codes is nonzero, then the stat_var for the number of messages
  47. * _sent out_ with the 'numerical_code' will be returned if it exists.
  48. * - otherwise, the stat_var for the number of messages _received_ with the
  49. * 'numerical_code' will be returned, if the stat exists.
  50. */
  51. stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int out_codes)
  52. {
  53. static char msg_code[INT2STR_MAX_LEN+4];
  54. str stat_name;
  55. stat_name.s = int2bstr( (unsigned long)numerical_code, msg_code,
  56. &stat_name.len);
  57. stat_name.s[stat_name.len++] = '_';
  58. if (out_codes) {
  59. stat_name.s[stat_name.len++] = 'o';
  60. stat_name.s[stat_name.len++] = 'u';
  61. stat_name.s[stat_name.len++] = 't';
  62. } else {
  63. stat_name.s[stat_name.len++] = 'i';
  64. stat_name.s[stat_name.len++] = 'n';
  65. }
  66. return get_stat(&stat_name);
  67. }
  68. #endif /*STATISTICS*/
  69. #define MAX_PROC_BUFFER 256
  70. /*!
  71. * This function will retrieve a list of all ip addresses and ports that Kamailio
  72. * is listening on, with respect to the transport protocol specified with
  73. * 'protocol'.
  74. *
  75. * The first parameter, ipList, is a pointer to a pointer. It will be assigned a
  76. * new block of memory holding the IP Addresses and ports being listened to with
  77. * respect to 'protocol'. The array maps a 2D array into a 1 dimensional space,
  78. * and is layed out as follows:
  79. *
  80. * The first NUM_IP_OCTETS indices will be the IP address, and the next index
  81. * the port. So if NUM_IP_OCTETS is equal to 4 and there are two IP addresses
  82. * found, then:
  83. *
  84. * - ipList[0] will be the first octet of the first ip address
  85. * - ipList[3] will be the last octet of the first ip address.
  86. * - iplist[4] will be the port of the first ip address
  87. * -
  88. * - iplist[5] will be the first octet of the first ip address,
  89. * - and so on.
  90. *
  91. * The function will return the number of sockets which were found. This can be
  92. * used to index into ipList.
  93. *
  94. * \note This function assigns a block of memory equal to:
  95. *
  96. * returnedValue * (NUM_IP_OCTETS + 1) * sizeof(int);
  97. *
  98. * Therefore it is CRUCIAL that you free ipList when you are done with its
  99. * contents, to avoid a nasty memory leak.
  100. */
  101. int get_socket_list_from_proto(int **ipList, int protocol) {
  102. return get_socket_list_from_proto_and_family(ipList, protocol, AF_INET);
  103. }
  104. /*!
  105. * This function will retrieve a list of all ip addresses and ports that Kamailio
  106. * is listening on, with respect to the transport protocol specified with
  107. * 'protocol'. This function supports both IPv4 and IPv6
  108. *
  109. * The first parameter, ipList, is a pointer to a pointer. It will be assigned a
  110. * new block of memory holding the IP Addresses and ports being listened to with
  111. * respect to 'protocol'. The array maps a 2D array into a 1 dimensional space,
  112. * and is layed out as follows:
  113. *
  114. * The first NUM_IP_OCTETS indices will be the IP address, and the next index
  115. * the port. So if NUM_IP_OCTETS is equal to 4 and there are two IP addresses
  116. * found, then:
  117. *
  118. * - ipList[0] will be the first octet of the first ip address
  119. * - ipList[3] will be the last octet of the first ip address.
  120. * - iplist[4] will be the port of the first ip address
  121. * -
  122. * - iplist[5] will be the first octet of the first ip address,
  123. * - and so on.
  124. */
  125. int get_socket_list_from_proto_and_family(int **ipList, int protocol, int family) {
  126. struct socket_info *si;
  127. struct socket_info** list;
  128. int num_ip_octets = family == AF_INET ? NUM_IP_OCTETS : NUM_IPV6_OCTETS;
  129. int numberOfSockets = 0;
  130. int currentRow = 0;
  131. /* I hate to use #ifdefs, but this is necessary because of the way
  132. * get_sock_info_list() is defined. */
  133. #ifndef USE_TCP
  134. if (protocol == PROTO_TCP)
  135. {
  136. return 0;
  137. }
  138. #endif
  139. #ifndef USE_TLS
  140. if (protocol == PROTO_TLS)
  141. {
  142. return 0;
  143. }
  144. #endif
  145. #ifndef USE_SCTP
  146. if (protocol == PROTO_SCTP)
  147. {
  148. return 0;
  149. }
  150. #endif
  151. /* We have no "interfaces" for websockets */
  152. if (protocol == PROTO_WS || protocol == PROTO_WSS)
  153. return 0;
  154. /* Retrieve the list of sockets with respect to the given protocol. */
  155. list=get_sock_info_list(protocol);
  156. /* Find out how many sockets are in the list. We need to know this so
  157. * we can malloc an array to assign to ipList. */
  158. for(si=list?*list:0; si; si=si->next){
  159. if (si->address.af == family) {
  160. numberOfSockets++;
  161. }
  162. }
  163. /* There are no open sockets with respect to the given protocol. */
  164. if (numberOfSockets == 0)
  165. {
  166. return 0;
  167. }
  168. *ipList = pkg_malloc(numberOfSockets * (num_ip_octets + 1) * sizeof(int));
  169. /* We couldn't allocate memory for the IP List. So all we can do is
  170. * fail. */
  171. if (*ipList == NULL) {
  172. LM_ERR("no more pkg memory");
  173. return 0;
  174. }
  175. /* We need to search the list again. So find the front of the list. */
  176. list=get_sock_info_list(protocol);
  177. /* Extract out the IP Addresses and ports. */
  178. for(si=list?*list:0; si; si=si->next){
  179. int i;
  180. /* We currently only support IPV4. */
  181. if (si->address.af != family) {
  182. continue;
  183. }
  184. for (i = 0; i < num_ip_octets; i++) {
  185. (*ipList)[currentRow*(num_ip_octets + 1) + i ] =
  186. si->address.u.addr[i];
  187. }
  188. (*ipList)[currentRow*(num_ip_octets + 1) + i] =
  189. si->port_no;
  190. currentRow++;
  191. }
  192. return numberOfSockets;
  193. }
  194. /*!
  195. * Takes a 'line' (from the proc file system), parses out the ipAddress,
  196. * address, and stores the number of bytes waiting in 'rx_queue'
  197. *
  198. * Returns 1 on success, and 0 on a failed parse.
  199. *
  200. * Note: The format of ipAddress is as defined in the comments of
  201. * get_socket_list_from_proto() in this file.
  202. *
  203. */
  204. static int parse_proc_net_line(char *line, int *ipAddress, int *rx_queue)
  205. {
  206. int i;
  207. int ipOctetExtractionMask = 0xFF;
  208. char *currColonLocation;
  209. char *nextNonNumericalChar;
  210. char *currentLocationInLine = line;
  211. int parsedInteger[4];
  212. /* Example line from /proc/net/tcp or /proc/net/udp:
  213. *
  214. * sl local_address rem_address st tx_queue rx_queue
  215. * 21: 5A0A0B0A:CAC7 1C016E0A:0016 01 00000000:00000000
  216. *
  217. * Algorithm:
  218. *
  219. * 1) Find the location of the first ':'
  220. * 2) Parse out the IP Address into an integer
  221. * 3) Find the location of the second ':'
  222. * 4) Parse out the port number.
  223. * 5) Find the location of the fourth ':'
  224. * 6) Parse out the rx_queue.
  225. */
  226. for (i = 0; i < 4; i++) {
  227. currColonLocation = strchr(currentLocationInLine, ':');
  228. /* We didn't find all the needed ':', so fail. */
  229. if (currColonLocation == NULL) {
  230. return 0;
  231. }
  232. /* Parse out the integer, keeping the location of the next
  233. * non-numerical character. */
  234. parsedInteger[i] =
  235. (int) strtol(++currColonLocation, &nextNonNumericalChar,
  236. 16);
  237. /* strtol()'s specifications specify that the second parameter
  238. * is set to the first parameter when a number couldn't be
  239. * parsed out. This means the parse was unsuccesful. */
  240. if (nextNonNumericalChar == currColonLocation) {
  241. return 0;
  242. }
  243. /* Reset the currentLocationInLine to the last non-numerical
  244. * character, so that next iteration of this loop, we can find
  245. * the next colon location. */
  246. currentLocationInLine = nextNonNumericalChar;
  247. }
  248. /* Extract out the segments of the IP Address. They are stored in
  249. * reverse network byte order. */
  250. for (i = 0; i < NUM_IP_OCTETS; i++) {
  251. ipAddress[i] =
  252. parsedInteger[0] & (ipOctetExtractionMask << i*8);
  253. ipAddress[i] >>= i*8;
  254. }
  255. ipAddress[NUM_IP_OCTETS] = parsedInteger[1];
  256. *rx_queue = parsedInteger[3];
  257. return 1;
  258. }
  259. /*!
  260. * Returns 1 if ipOne was found in ipArray, and 0 otherwise.
  261. *
  262. * The format of ipOne and ipArray are described in the comments of
  263. * get_socket_list_from_proto() in this file.
  264. *
  265. * */
  266. static int match_ip_and_port(int *ipOne, int *ipArray, int sizeOf_ipArray)
  267. {
  268. int curIPAddrIdx;
  269. int curOctetIdx;
  270. int ipArrayIndex;
  271. /* Loop over every IP Address */
  272. for (curIPAddrIdx = 0; curIPAddrIdx < sizeOf_ipArray; curIPAddrIdx++) {
  273. /* Check for octets that don't match. If one is found, skip the
  274. * rest. */
  275. for (curOctetIdx = 0; curOctetIdx < NUM_IP_OCTETS + 1; curOctetIdx++) {
  276. /* We've encoded a 2D array as a 1D array. So find out
  277. * our position in the 1D array. */
  278. ipArrayIndex =
  279. curIPAddrIdx * (NUM_IP_OCTETS + 1) + curOctetIdx;
  280. if (ipOne[curOctetIdx] != ipArray[ipArrayIndex]) {
  281. break;
  282. }
  283. }
  284. /* If the index from the inner loop is equal to NUM_IP_OCTETS
  285. * + 1, then that means that every octet (and the port with the
  286. * + 1) matched. */
  287. if (curOctetIdx == NUM_IP_OCTETS + 1) {
  288. return 1;
  289. }
  290. }
  291. return 0;
  292. }
  293. /*!
  294. * Returns the number of bytes waiting to be consumed on the network interfaces
  295. * assigned the IP Addresses specified in interfaceList. The check will be
  296. * limited to the TCP or UDP transport exclusively. Specifically:
  297. *
  298. * - If forTCP is non-zero, the check involves only the TCP transport.
  299. * - if forTCP is zero, the check involves only the UDP transport.
  300. *
  301. * Note: This only works on linux systems supporting the /proc/net/[tcp|udp]
  302. * interface. On other systems, zero will always be returned.
  303. */
  304. static int get_used_waiting_queue(
  305. int forTCP, int *interfaceList, int listSize)
  306. {
  307. FILE *fp;
  308. char *fileToOpen;
  309. char lineBuffer[MAX_PROC_BUFFER];
  310. int ipAddress[NUM_IP_OCTETS+1];
  311. int rx_queue;
  312. int waitingQueueSize = 0;
  313. /* Set up the file we want to open. */
  314. if (forTCP) {
  315. fileToOpen = "/proc/net/tcp";
  316. } else {
  317. fileToOpen = "/proc/net/udp";
  318. }
  319. fp = fopen(fileToOpen, "r");
  320. if (fp == NULL) {
  321. LM_ERR("Could not open %s. kamailioMsgQueueDepth and its related"
  322. " alarms will not be available.\n", fileToOpen);
  323. return 0;
  324. }
  325. /* Read in every line of the file, parse out the ip address, port, and
  326. * rx_queue, and compare to our list of interfaces we are listening on.
  327. * Add up rx_queue for those lines which match our known interfaces. */
  328. while (fgets(lineBuffer, MAX_PROC_BUFFER, fp)!=NULL) {
  329. /* Parse out the ip address, port, and rx_queue. */
  330. if(parse_proc_net_line(lineBuffer, ipAddress, &rx_queue)) {
  331. /* Only add rx_queue if the line just parsed corresponds
  332. * to an interface we are listening on. We do this
  333. * check because it is possible that this system has
  334. * other network interfaces that Kamailio has been told
  335. * to ignore. */
  336. if (match_ip_and_port(ipAddress, interfaceList, listSize)) {
  337. waitingQueueSize += rx_queue;
  338. }
  339. }
  340. }
  341. fclose(fp);
  342. return waitingQueueSize;
  343. }
  344. /*!
  345. * Returns the sum of the number of bytes waiting to be consumed on all network
  346. * interfaces and transports that Kamailio is listening on.
  347. *
  348. * Note: This currently only works on systems supporting the /proc/net/[tcp|udp]
  349. * interface. On other systems, zero will always be returned. To change
  350. * this in the future, add an equivalent for get_used_waiting_queue().
  351. */
  352. int get_total_bytes_waiting(void)
  353. {
  354. int bytesWaiting = 0;
  355. int *UDPList = NULL;
  356. int *TCPList = NULL;
  357. int *TLSList = NULL;
  358. int *UDP6List = NULL;
  359. int *TCP6List = NULL;
  360. int *TLS6List = NULL;
  361. int numUDPSockets = 0;
  362. int numTCPSockets = 0;
  363. int numTLSSockets = 0;
  364. int numUDP6Sockets = 0;
  365. int numTCP6Sockets = 0;
  366. int numTLS6Sockets = 0;
  367. /* Extract out the IP address address for UDP, TCP, and TLS, keeping
  368. * track of the number of IP addresses from each transport */
  369. numUDPSockets = get_socket_list_from_proto(&UDPList, PROTO_UDP);
  370. numTCPSockets = get_socket_list_from_proto(&TCPList, PROTO_TCP);
  371. numTLSSockets = get_socket_list_from_proto(&TLSList, PROTO_TLS);
  372. numUDP6Sockets = get_socket_list_from_proto_and_family(&UDP6List, PROTO_UDP, AF_INET6);
  373. numTCP6Sockets = get_socket_list_from_proto_and_family(&TCP6List, PROTO_TCP, AF_INET6);
  374. numTLS6Sockets = get_socket_list_from_proto_and_family(&TLS6List, PROTO_TLS, AF_INET6);
  375. /* Deliberately not looking at PROTO_WS or PROTO_WSS here as they are
  376. just upgraded TCP/TLS connections */
  377. /* Find out the number of bytes waiting on our interface list over all
  378. * UDP and TCP transports. */
  379. bytesWaiting += get_used_waiting_queue(0, UDPList, numUDPSockets);
  380. bytesWaiting += get_used_waiting_queue(1, TCPList, numTCPSockets);
  381. bytesWaiting += get_used_waiting_queue(1, TLSList, numTLSSockets);
  382. bytesWaiting += get_used_waiting_queue(0, UDP6List, numUDP6Sockets);
  383. bytesWaiting += get_used_waiting_queue(1, TCP6List, numTCP6Sockets);
  384. bytesWaiting += get_used_waiting_queue(1, TLS6List, numTLS6Sockets);
  385. /* get_socket_list_from_proto() allocated a chunk of memory, so we need
  386. * to free it. */
  387. if (numUDPSockets > 0)
  388. {
  389. pkg_free(UDPList);
  390. }
  391. if (numUDP6Sockets > 0)
  392. {
  393. pkg_free(UDP6List);
  394. }
  395. if (numTCPSockets > 0)
  396. {
  397. pkg_free(TCPList);
  398. }
  399. if (numTCP6Sockets > 0)
  400. {
  401. pkg_free(TCP6List);
  402. }
  403. if (numTLSSockets > 0)
  404. {
  405. pkg_free(TLSList);
  406. }
  407. if (numTLS6Sockets > 0)
  408. {
  409. pkg_free(TLS6List);
  410. }
  411. return bytesWaiting;
  412. }