PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/platform/FNET/fnet_stack/services/ping/fnet_ping.c

https://gitlab.com/fuggles/ucos
C | 373 lines | 226 code | 57 blank | 90 comment | 48 complexity | c5b2ff643fce6cc29441bfbc90e44a19 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0
  1. /**************************************************************************
  2. *
  3. * Copyright 2011-2015 by Andrey Butok. FNET Community.
  4. *
  5. ***************************************************************************
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License Version 3
  8. * or later (the "LGPL").
  9. *
  10. * As a special exception, the copyright holders of the FNET project give you
  11. * permission to link the FNET sources with independent modules to produce an
  12. * executable, regardless of the license terms of these independent modules,
  13. * and to copy and distribute the resulting executable under terms of your
  14. * choice, provided that you also meet, for each linked independent module,
  15. * the terms and conditions of the license of that module.
  16. * An independent module is a module which is not derived from or based
  17. * on this library.
  18. * If you modify the FNET sources, you may extend this exception
  19. * to your version of the FNET sources, but you are not obligated
  20. * to do so. If you do not wish to do so, delete this
  21. * exception statement from your version.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  26. *
  27. * You should have received a copy of the GNU General Public License
  28. * and the GNU Lesser General Public License along with this program.
  29. * If not, see <http://www.gnu.org/licenses/>.
  30. *
  31. **********************************************************************/ /*!
  32. *
  33. * @file fnet_ping.c
  34. *
  35. * @author Andrey Butok
  36. *
  37. * @brief PING implementation.
  38. *
  39. ***************************************************************************/
  40. #include "fnet.h"
  41. #if FNET_CFG_PING
  42. #include "stack/fnet_checksum.h"
  43. #include "stack/fnet_icmp.h"
  44. #include "stack/fnet_ip6_prv.h"
  45. #if FNET_CFG_DEBUG_PING
  46. #define FNET_DEBUG_PING FNET_DEBUG
  47. #else
  48. #define FNET_DEBUG_PING(...)
  49. #endif
  50. /************************************************************************
  51. * Definitions
  52. *************************************************************************/
  53. #define FNET_PING_ERR_PARAMS "ERROR: Wrong input parameters."
  54. #define FNET_PING_ERR_SOCKET_CREATION "ERROR: Socket creation error."
  55. #define FNET_PING_ERR_SOCKET_CONNECT "ERROR: Socket Error during connect."
  56. #define FNET_PING_ERR_SERVICE "ERROR: Service registration is failed."
  57. #define FNET_PING_ERR_IS_INITIALIZED "ERROR: PING is already initialized."
  58. #define FNET_PING_ERR_GETSOCKNAME "ERROR: Socket getsockname error."
  59. #define FNET_PING_BUFFER_SIZE (sizeof(fnet_icmp_echo_header_t) + FNET_CFG_PING_PACKET_MAX)
  60. static void fnet_ping_state_machine(void *fnet_ping_if_p);
  61. /************************************************************************
  62. * PING service interface structure.
  63. *************************************************************************/
  64. typedef struct
  65. {
  66. SOCKET socket_foreign; /* Foreign socket.*/
  67. fnet_address_family_t family;
  68. unsigned short sequence_number;
  69. fnet_poll_desc_t service_descriptor;
  70. fnet_ping_state_t state; /* Current state. */
  71. fnet_ping_handler_t handler; /* Callback function. */
  72. long handler_cookie; /* Callback-handler specific parameter. */
  73. char buffer[FNET_PING_BUFFER_SIZE]; /* Message buffer. */
  74. unsigned long timeout_clk; /* Timeout value in clocks, that ping request waits for reply.*/
  75. unsigned long send_time; /* Last send time, used for timeout detection. */
  76. unsigned int packet_count; /* Number of packets to be sent.*/
  77. unsigned long packet_size;
  78. unsigned char pattern;
  79. struct sockaddr target_addr;
  80. }
  81. fnet_ping_if_t;
  82. /* PING interface structure */
  83. static fnet_ping_if_t fnet_ping_if;
  84. /************************************************************************
  85. * NAME: fnet_ping_request
  86. *
  87. * DESCRIPTION: Initializes PING service.
  88. ************************************************************************/
  89. int fnet_ping_request( struct fnet_ping_params *params )
  90. {
  91. const unsigned long bufsize_option = FNET_PING_BUFFER_SIZE;
  92. /* Check input parameters. */
  93. if((params == 0) || (params->packet_count==0) || fnet_socket_addr_is_unspecified(&params->target_addr))
  94. {
  95. FNET_DEBUG_PING(FNET_PING_ERR_PARAMS);
  96. goto ERROR;
  97. }
  98. /* Check if PING service is free.*/
  99. if(fnet_ping_if.state != FNET_PING_STATE_DISABLED)
  100. {
  101. FNET_DEBUG_PING(FNET_PING_ERR_IS_INITIALIZED);
  102. goto ERROR;
  103. }
  104. /* Save input parmeters.*/
  105. fnet_ping_if.handler = params->handler;
  106. fnet_ping_if.handler_cookie = params->cookie;
  107. fnet_ping_if.timeout_clk = params->timeout/FNET_TIMER_PERIOD_MS;
  108. if(fnet_ping_if.timeout_clk == 0)
  109. fnet_ping_if.timeout_clk = 1;
  110. fnet_ping_if.family = params->target_addr.sa_family;
  111. fnet_ping_if.packet_count = params->packet_count;
  112. fnet_ping_if.pattern = params->pattern;
  113. fnet_ping_if.packet_size = params->packet_size;
  114. if(fnet_ping_if.packet_size > FNET_CFG_PING_PACKET_MAX)
  115. fnet_ping_if.packet_size = FNET_CFG_PING_PACKET_MAX;
  116. fnet_ping_if.target_addr = params->target_addr;
  117. /* Create socket */
  118. if((fnet_ping_if.socket_foreign = socket(fnet_ping_if.family, SOCK_RAW, (params->target_addr.sa_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6)) == SOCKET_INVALID)
  119. {
  120. FNET_DEBUG_PING(FNET_PING_ERR_SOCKET_CREATION);
  121. goto ERROR;
  122. }
  123. /* Set Socket options. */
  124. #if FNET_CFG_IP4
  125. if(fnet_ping_if.family == AF_INET)
  126. setsockopt(fnet_ping_if.socket_foreign, IPPROTO_IP, IP_TTL, (char *) &params->ttl, sizeof(params->ttl));
  127. #endif
  128. #if FNET_CFG_IP6
  129. if(fnet_ping_if.family == AF_INET6)
  130. setsockopt(fnet_ping_if.socket_foreign, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &params->ttl, sizeof(params->ttl));
  131. #endif
  132. setsockopt(fnet_ping_if.socket_foreign, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize_option, sizeof(bufsize_option));
  133. setsockopt(fnet_ping_if.socket_foreign, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize_option, sizeof(bufsize_option));
  134. /* Register PING service. */
  135. fnet_ping_if.service_descriptor = fnet_poll_service_register(fnet_ping_state_machine, (void *) &fnet_ping_if);
  136. if(fnet_ping_if.service_descriptor == (fnet_poll_desc_t)FNET_ERR)
  137. {
  138. FNET_DEBUG_PING(FNET_PING_ERR_SERVICE);
  139. goto ERROR_1;
  140. }
  141. fnet_ping_if.state = FNET_PING_STATE_SENDING_REQUEST;
  142. return FNET_OK;
  143. ERROR_1:
  144. closesocket(fnet_ping_if.socket_foreign);
  145. ERROR:
  146. return FNET_ERR;
  147. }
  148. /************************************************************************
  149. * NAME: fnet_ping_state_machine
  150. *
  151. * DESCRIPTION: PING service state machine.
  152. ************************************************************************/
  153. static void fnet_ping_state_machine(void *fnet_ping_if_p)
  154. {
  155. int received;
  156. fnet_icmp_echo_header_t *hdr;
  157. fnet_ping_if_t *ping_if = (fnet_ping_if_t *)fnet_ping_if_p;
  158. struct sockaddr addr;
  159. unsigned int addr_len = sizeof(addr);
  160. switch(ping_if->state)
  161. {
  162. /*===================================*/
  163. case FNET_PING_STATE_SENDING_REQUEST:
  164. /* Build message.*/
  165. hdr = (fnet_icmp_echo_header_t *)&fnet_ping_if.buffer[0];
  166. /* Fill ICMP Echo request header.*/
  167. fnet_memset_zero(hdr, sizeof(*hdr));
  168. hdr->header.type = (unsigned char)((fnet_ping_if.family == AF_INET) ? FNET_ICMP_ECHO: FNET_ICMP6_TYPE_ECHO_REQ);
  169. hdr->identifier = FNET_CFG_PING_IDENTIFIER;
  170. fnet_ping_if.sequence_number++;
  171. hdr->sequence_number = fnet_htons(fnet_ping_if.sequence_number);
  172. /* Fill payload data by pattern.*/
  173. fnet_memset(&fnet_ping_if.buffer[sizeof(*hdr)], ping_if->pattern, ping_if->packet_size);
  174. /* Checksum.*/
  175. #if FNET_CFG_IP4
  176. if(ping_if->family == AF_INET)
  177. {
  178. hdr->header.checksum = fnet_checksum_buf(&fnet_ping_if.buffer[0], (int)(sizeof(*hdr) + ping_if->packet_size));
  179. }
  180. else
  181. #endif
  182. #if FNET_CFG_IP6
  183. if(ping_if->family == AF_INET6)
  184. {
  185. fnet_ip6_addr_t *src_ip = (fnet_ip6_addr_t *)fnet_ip6_select_src_addr(FNET_NULL, (fnet_ip6_addr_t *)ping_if->target_addr.sa_data); /*TBD Check result.*/
  186. hdr->header.checksum = fnet_checksum_pseudo_buf(&fnet_ping_if.buffer[0],
  187. (unsigned short)(sizeof(*hdr) + ping_if->packet_size),
  188. FNET_HTONS(IPPROTO_ICMPV6),
  189. (char *)src_ip,
  190. ping_if->target_addr.sa_data,
  191. sizeof(fnet_ip6_addr_t));
  192. }
  193. else
  194. #endif
  195. {};
  196. /* Send request.*/
  197. sendto(fnet_ping_if.socket_foreign, (char*)(&fnet_ping_if.buffer[0]), (int)(sizeof(*hdr) + ping_if->packet_size), 0, &ping_if->target_addr, sizeof(ping_if->target_addr));
  198. ping_if->packet_count--;
  199. fnet_ping_if.send_time = fnet_timer_ticks();
  200. ping_if->state = FNET_PING_STATE_WAITING_REPLY;
  201. break;
  202. /*===================================*/
  203. case FNET_PING_STATE_WAITING_REPLY:
  204. /* Receive data */
  205. received = recvfrom(ping_if->socket_foreign, (char*)(&ping_if->buffer[0]), FNET_PING_BUFFER_SIZE, 0, &addr, &addr_len );
  206. if(received > 0 )
  207. {
  208. unsigned short checksum = 0;
  209. hdr = (fnet_icmp_echo_header_t *)(ping_if->buffer);
  210. /* Check checksum.*/
  211. #if FNET_CFG_IP4
  212. if(ping_if->family == AF_INET)
  213. {
  214. checksum = fnet_checksum_buf(&fnet_ping_if.buffer[0], received);
  215. }
  216. else
  217. #endif
  218. #if 0 /* #if FNET_CFG_IP6 */ /* TBD case to receive from multicast address ff02::1*/
  219. if(ping_if->family == AF_INET6)
  220. {
  221. checksum = fnet_checksum_pseudo_buf(&fnet_ping_if.buffer[0],
  222. (unsigned short)(received),
  223. IPPROTO_ICMPV6,
  224. ping_if->local_addr.sa_data,
  225. ping_if->target_addr.sa_data,
  226. sizeof(fnet_ip6_addr_t));
  227. }
  228. else
  229. #endif
  230. {};
  231. /* Check header.*/
  232. if( checksum
  233. ||(hdr->header.type != (addr.sa_family == AF_INET) ? FNET_ICMP_ECHOREPLY: FNET_ICMP6_TYPE_ECHO_REPLY)
  234. ||(hdr->identifier != FNET_CFG_PING_IDENTIFIER)
  235. ||(hdr->sequence_number != fnet_htons(ping_if->sequence_number)) )
  236. {
  237. goto NO_DATA;
  238. }
  239. /* Call handler.*/
  240. if(ping_if->handler)
  241. ping_if->handler(FNET_OK, ping_if->packet_count, &addr, ping_if->handler_cookie);
  242. if(ping_if->packet_count)
  243. ping_if->state = FNET_PING_STATE_WAITING_TIMEOUT;
  244. else
  245. fnet_ping_release();
  246. }
  247. else if(received == SOCKET_ERROR)
  248. {
  249. /* Call handler.*/
  250. if(ping_if->handler)
  251. {
  252. int sock_err ;
  253. unsigned int option_len;
  254. /* Get socket error.*/
  255. option_len = sizeof(sock_err);
  256. getsockopt(ping_if->socket_foreign, SOL_SOCKET, SO_ERROR, (char*)&sock_err, &option_len);
  257. ping_if->handler(sock_err, ping_if->packet_count, FNET_NULL, ping_if->handler_cookie);
  258. }
  259. if(ping_if->packet_count)
  260. ping_if->state = FNET_PING_STATE_WAITING_TIMEOUT;
  261. else
  262. fnet_ping_release();
  263. }
  264. else /* No data. Check timeout */
  265. {
  266. NO_DATA:
  267. if(fnet_timer_get_interval(fnet_ping_if.send_time, fnet_timer_ticks()) > fnet_ping_if.timeout_clk)
  268. {
  269. /* Call handler.*/
  270. if(ping_if->handler)
  271. ping_if->handler(FNET_ERR_TIMEDOUT, ping_if->packet_count, FNET_NULL, ping_if->handler_cookie);
  272. if(ping_if->packet_count)
  273. ping_if->state = FNET_PING_STATE_SENDING_REQUEST;
  274. else
  275. fnet_ping_release();
  276. }
  277. }
  278. break;
  279. /*===================================*/
  280. case FNET_PING_STATE_WAITING_TIMEOUT:
  281. if(fnet_timer_get_interval(fnet_ping_if.send_time, fnet_timer_ticks()) > fnet_ping_if.timeout_clk)
  282. {
  283. ping_if->state = FNET_PING_STATE_SENDING_REQUEST;
  284. }
  285. break;
  286. default:
  287. break; /* do nothing, avoid compiler warning "enumeration value not handled in switch" */
  288. }
  289. }
  290. /************************************************************************
  291. * NAME: fnet_ping_release
  292. *
  293. * DESCRIPTION: Releases the PING service.
  294. ************************************************************************/
  295. void fnet_ping_release( void )
  296. {
  297. if(fnet_ping_if.state != FNET_PING_STATE_DISABLED)
  298. {
  299. /* Close socket. */
  300. closesocket(fnet_ping_if.socket_foreign);
  301. /* Unregister the tftp service. */
  302. fnet_poll_service_unregister(fnet_ping_if.service_descriptor);
  303. fnet_ping_if.state = FNET_PING_STATE_DISABLED;
  304. }
  305. }
  306. /************************************************************************
  307. * NAME: fnet_ping_state
  308. *
  309. * DESCRIPTION: Retrieves the current state of the PING service
  310. * (for debugging purposes).
  311. ************************************************************************/
  312. fnet_ping_state_t fnet_ping_state( void )
  313. {
  314. return fnet_ping_if.state;
  315. }
  316. #endif /* FNET_CFG_PING */