PageRenderTime 44ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/common/fh_tcp.c

https://github.com/csinitiative/fhce
C | 623 lines | 399 code | 100 blank | 124 comment | 112 complexity | 4fbc3da863419ecba343ce9d66887e8b MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. * Copyright (C) 2008, 2009, 2010 The Collaborative Software Foundation.
  3. *
  4. * This file is part of FeedHandlers (FH).
  5. *
  6. * FH is free software: you can redistribute it and/or modify it under the terms of the
  7. * GNU Lesser General Public License as published by the Free Software Foundation, either version 3
  8. * of the License, or (at your option) any later version.
  9. *
  10. * FH is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
  11. * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License
  15. * along with FH. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. /*
  18. * System includes
  19. */
  20. #include <stdio.h>
  21. #include <errno.h>
  22. #include <time.h>
  23. #include <string.h>
  24. #include <netinet/ip.h>
  25. #include <netinet/tcp.h>
  26. #include <netinet/in.h>
  27. #include <sys/ioctl.h>
  28. /*
  29. * Shared common includes
  30. */
  31. #include "fh_log.h"
  32. #include "fh_sock.h"
  33. #include "fh_tcp.h"
  34. #include "fh_time.h"
  35. static int should_retry(int err, uint32_t *ts, char * dir);
  36. static int fh_tcp_readex(int s, void *buf, int nbytes, int flags);
  37. static int fh_tcp_writeex(int s, const void *buf, int nbytes, int flags);
  38. #define FH_TCP_TIMEOUT (2) /* In secs */
  39. #define FH_TCP_CLIENT_TIMEOUT (200) /* In millisecs */
  40. /*
  41. * should_retry
  42. *
  43. * Ask if the error is non-fatal or if it is necessary to retry.
  44. */
  45. static int should_retry(int err, uint32_t *start_ts, char *dir)
  46. {
  47. uint32_t now_ts = (uint32_t)time(NULL);
  48. switch (err) {
  49. case EINTR:
  50. return 1;
  51. #if EWOULDBLOCK != EAGAIN
  52. case EWOULDBLOCK:
  53. #endif
  54. case EAGAIN:
  55. if (*start_ts == 0) {
  56. *start_ts = now_ts;
  57. return 1;
  58. }
  59. if ((now_ts - *start_ts) >= FH_TCP_TIMEOUT) {
  60. FH_LOG(NET, ERR, ("NET> %s: retry timeout after %d secs", dir, FH_TCP_TIMEOUT));
  61. return 0;
  62. }
  63. return 1;
  64. default:
  65. break;
  66. }
  67. return 0;
  68. }
  69. /*
  70. * fh_tcp_listen
  71. *
  72. * Configure a socket as a listening socket.
  73. */
  74. FH_STATUS fh_tcp_listen(int s)
  75. {
  76. if (listen(s, SOMAXCONN) < 0) {
  77. FH_LOG(NET, ERR, ("NET> listen failed on socket #%d: %s (%d)",
  78. s, strerror(errno), errno));
  79. return FH_ERROR;
  80. }
  81. return FH_OK;
  82. }
  83. /*
  84. * fh_tcp_accept
  85. *
  86. * Performs a TCP accept on a listening socket.
  87. */
  88. FH_STATUS fh_tcp_accept(int s, uint32_t *daddr, uint16_t *dport, int *cs)
  89. {
  90. socklen_t peerlen = sizeof(struct sockaddr_in);
  91. struct sockaddr_in peer;
  92. int sock;
  93. sock = accept(s, (struct sockaddr *) &peer, &peerlen);
  94. if (sock < 0) {
  95. FH_LOG(NET, ERR, ("NET> accept failed on socket #%d: %s (%d)",
  96. s, strerror(errno), errno));
  97. return FH_ERROR;
  98. }
  99. if (fh_tcp_nodelay(sock, 1)) {
  100. close(sock);
  101. return FH_ERROR;
  102. }
  103. *daddr = peer.sin_addr.s_addr;
  104. *dport = ntohs(peer.sin_port);
  105. *cs = sock;
  106. return FH_OK;
  107. }
  108. /*
  109. * fh_tcp_tconnect
  110. *
  111. * Timed connect. The connect will timeout after usec microsecs.
  112. * If usec is set to zero, then timed-connect is equivalent to connect.
  113. */
  114. FH_STATUS fh_tcp_tconnect(int s, uint32_t daddr, uint16_t dport, uint32_t usec, int quiet)
  115. {
  116. struct sockaddr_in peer;
  117. FH_STATUS rc;
  118. int ret;
  119. /*
  120. * For timed-connect, make sure that we are not blocking on connect
  121. */
  122. if (usec != 0) {
  123. rc = fh_sock_block(s, 0);
  124. if (rc != FH_OK) {
  125. return rc;
  126. }
  127. }
  128. memset(&peer, 0, sizeof(peer));
  129. peer.sin_addr.s_addr = daddr;
  130. peer.sin_port = htons(dport);
  131. peer.sin_family = AF_INET;
  132. ret = connect(s, (struct sockaddr *) &peer, sizeof(peer));
  133. if (usec != 0) {
  134. if (fh_sock_block(s, 1) < 0) {
  135. return FH_ERROR;
  136. }
  137. }
  138. if (ret != 0) {
  139. struct timeval *ptv, tv;
  140. fd_set wrfds;
  141. int ret;
  142. if (errno != EINPROGRESS) {
  143. if (!quiet) {
  144. FH_LOG(NET, ERR, ("NET> connect failed on socket %d: err=%d", s, errno));
  145. }
  146. return FH_ERROR;
  147. }
  148. if (usec != 0) {
  149. tv.tv_sec = (int) (usec/1000000);
  150. tv.tv_usec = (int) (usec%1000000);
  151. ptv = &tv;
  152. }
  153. else {
  154. ptv = NULL;
  155. }
  156. FD_ZERO(&wrfds);
  157. FD_SET(s, &wrfds);
  158. ret = select((int)(s+1), NULL, &wrfds, NULL, ptv);
  159. if (ret < 0) {
  160. if (!quiet) {
  161. FH_LOG(NET, ERR, ("NET> select failed on socket #%d: %d", s, errno));
  162. }
  163. return FH_ERROR;
  164. }
  165. if (ret == 0) {
  166. if (!quiet) {
  167. FH_LOG(NET, ERR, ("NET> Connect timed out on socket #%d", s));
  168. }
  169. return FH_ERROR;
  170. }
  171. if (fh_sock_error(s, &ret) != FH_OK) {
  172. return FH_ERROR;
  173. }
  174. if (ret != 0) {
  175. if (!quiet) {
  176. FH_LOG(NET, ERR, ("NET> Connect failed on socket #%d: ret=%d", s, ret));
  177. }
  178. return FH_ERROR;
  179. }
  180. }
  181. return FH_OK;
  182. }
  183. /*
  184. * fh_tcp_connect
  185. */
  186. FH_STATUS fh_tcp_connect(int s, uint32_t daddr, uint16_t dport)
  187. {
  188. return fh_tcp_tconnect(s, daddr, dport, 0, 0);
  189. }
  190. /*
  191. * fh_tcp_keepalive
  192. *
  193. * Set the TCP socket to send some keepalives to keep socket from being
  194. * disconnected when connection is idle.
  195. */
  196. FH_STATUS fh_tcp_keepalive(int s, int on)
  197. {
  198. if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)) < 0) {
  199. FH_LOG(NET, ERR, ("NET> setsockopt SO_KEEPALIVE failed on socket #%d: %d",
  200. s, errno));
  201. return FH_ERROR;
  202. }
  203. return FH_OK;
  204. }
  205. /*
  206. * fh_tcp_nodelay
  207. *
  208. * Configures a socket to not wait before sending data. This reduces overall
  209. * latency due to TCP buffering.
  210. */
  211. FH_STATUS fh_tcp_nodelay(int s, int on)
  212. {
  213. int ret;
  214. ret = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on));
  215. if (ret < 0) {
  216. FH_LOG(NET, ERR, ("NET> setsockopt TCP_NODELAY failed on socket #%d: %d",
  217. s, errno));
  218. return FH_ERROR;
  219. }
  220. return FH_OK;
  221. }
  222. /*
  223. * fh_tcp_open
  224. */
  225. FH_STATUS fh_tcp_open(uint32_t addr, uint16_t port, int *s)
  226. {
  227. int sock;
  228. sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  229. if (sock == -1) {
  230. FH_LOG(NET, ERR, ("NET> Failed to create TCP socket: %d", errno));
  231. return FH_ERROR;
  232. }
  233. if (fh_sock_block(sock, 0) < 0) {
  234. goto error;
  235. }
  236. if (fh_sock_reuse(sock, 1) < 0) {
  237. goto error;
  238. }
  239. if (addr != 0 || port != 0) {
  240. if (fh_sock_bind(sock, addr, port) < 0) {
  241. goto error;
  242. }
  243. }
  244. if (fh_tcp_keepalive(sock, 1) != FH_OK) {
  245. goto error;
  246. }
  247. *s = sock;
  248. return FH_OK;
  249. error:
  250. close(sock);
  251. return FH_ERROR;
  252. }
  253. /*
  254. * fh_tcp_client_ex
  255. *
  256. * Creates a TCP Client connection to a given TCP server {daddr, dport}.
  257. * It is possible to optionally configure the saddr and/or sport to bind
  258. * to locally.
  259. */
  260. FH_STATUS fh_tcp_client_ex(uint32_t saddr, uint16_t sport, uint32_t daddr,
  261. uint16_t dport, int *s, int quiet)
  262. {
  263. int sock = -1;
  264. FH_STATUS rc;
  265. rc = fh_tcp_open(saddr, sport, &sock);
  266. if (rc != FH_OK) {
  267. return rc;
  268. }
  269. rc = fh_tcp_tconnect(sock, daddr, dport, FH_TCP_CLIENT_TIMEOUT, quiet);
  270. if (rc != FH_OK) {
  271. close(sock);
  272. return rc;
  273. }
  274. /*
  275. * Reset the non-blocking flag if needed since the timed-connect
  276. * uses the non-blocking flag to never block on connect
  277. */
  278. rc = fh_sock_block(sock, 0);
  279. if (rc != FH_OK) {
  280. close(sock);
  281. return rc;
  282. }
  283. rc = fh_tcp_nodelay(sock, 1);
  284. if (rc != FH_OK) {
  285. close(sock);
  286. return rc;
  287. }
  288. *s = sock;
  289. return FH_OK;
  290. }
  291. /*
  292. * fh_tcp_client
  293. *
  294. * Creates a TCP client connection to the server {daddr,dport}.
  295. */
  296. FH_STATUS fh_tcp_client(uint32_t daddr, uint16_t dport, int *s, int quiet)
  297. {
  298. return fh_tcp_client_ex(0, 0, daddr, dport, s, quiet);
  299. }
  300. /*
  301. * fh_tcp_server
  302. *
  303. * Creates a TCP server by opening on TCP listening socket for {saddr,sport}
  304. */
  305. FH_STATUS fh_tcp_server(uint32_t saddr, uint16_t sport, int *s)
  306. {
  307. int sock;
  308. FH_STATUS rc;
  309. rc = fh_tcp_open(saddr, sport, &sock);
  310. if (rc != FH_OK) {
  311. return rc;
  312. }
  313. rc = fh_tcp_listen(sock);
  314. if (rc != FH_OK) {
  315. return rc;
  316. }
  317. *s = sock;
  318. return 0;
  319. }
  320. /*
  321. * fh_tcp_readblk
  322. *
  323. * Performs the read() system call on the socket, waiting for all the data
  324. * before returning.
  325. */
  326. int fh_tcp_readblk(int s, void *buf, int nbytes)
  327. {
  328. int nleft, flags = MSG_WAITALL;
  329. nleft = nbytes;
  330. while (nleft > 0) {
  331. int nread = recv(s, (char*)buf, nleft, flags);
  332. if (nread < 0) {
  333. if (EINTR != errno) {
  334. FH_LOG(NET, WARN, ("NET> fh_tcp_readblk: exit early from error %d", errno));
  335. break;
  336. }
  337. continue;
  338. }
  339. else if (nread == 0) {
  340. break;
  341. }
  342. if (flags != MSG_PEEK) {
  343. nleft -= nread;
  344. buf = (char *) buf + nread;
  345. }
  346. else if (nleft == nread) {
  347. nleft = 0;
  348. }
  349. }
  350. FH_LOG(NET, INFO, ("NET> Read %d bytes from socket #%d", nbytes - nleft, s));
  351. return nbytes - nleft;
  352. }
  353. /*
  354. * fh_tcp_writeblk
  355. *
  356. * Performs the write() system call on the socket, waiting for all the data
  357. * to be sent before returning.
  358. */
  359. int fh_tcp_writeblk(int s, void *buf, int nbytes)
  360. {
  361. int nleft, flags;
  362. flags = MSG_WAITALL;
  363. nleft = nbytes;
  364. while (nleft > 0) {
  365. int nwritten = send(s, (char*)buf, nleft, flags);
  366. if (nwritten < 0) {
  367. if (EINTR != errno) {
  368. FH_LOG(NET, WARN, ("NET> fh_tcp_writeblk: exit early from error %d", errno));
  369. break;
  370. }
  371. continue;
  372. }
  373. else if (nwritten == 0) {
  374. break;
  375. }
  376. nleft -= nwritten;
  377. buf = (char *) buf + nwritten;
  378. }
  379. FH_LOG(NET, INFO, ("NET> fh_tcp_writeblk: written: %d socket # %d left: %d",
  380. nbytes - nleft, s, nleft));
  381. return nbytes - nleft;
  382. }
  383. /*
  384. * fh_tcp_readex
  385. *
  386. * Performs the read() system call on the socket, waiting for all the data
  387. * before returning.
  388. */
  389. static int fh_tcp_readex(int s, void *buf, int nbytes, int flags)
  390. {
  391. int nleft, nread;
  392. uint32_t start_ts = 0;
  393. uint64_t beg_ts = 0;
  394. uint64_t end_ts = 0;
  395. uint32_t retry = 0;
  396. if (FH_LL_OK(NET, STATS)) {
  397. fh_time_get(&beg_ts);
  398. }
  399. nleft = nbytes;
  400. while (nleft > 0) {
  401. nread = recv(s, (char*)buf, nleft, flags | MSG_DONTWAIT);
  402. if (nread < 0) {
  403. if ((errno == ECONNRESET) ||
  404. (errno == EPIPE)) {
  405. FH_LOG(NET, WARN, ("NET> fh_tcp_readex: ret 0 due to error %d", errno));
  406. return 0;
  407. }
  408. if (should_retry(errno, &start_ts, "Rx")) {
  409. if (beg_ts == 0) {
  410. fh_time_get(&beg_ts);
  411. }
  412. usleep(1000);
  413. retry++;
  414. continue;
  415. }
  416. else {
  417. FH_LOG(NET, ERR, ("NET> fh_tcp_readex failed: %d", errno));
  418. break;
  419. }
  420. }
  421. else if (nread == 0) {
  422. break;
  423. }
  424. if (flags != MSG_PEEK) {
  425. nleft -= nread;
  426. buf = (char *) buf + nread;
  427. }
  428. else if (nleft == nread) {
  429. nleft = 0;
  430. }
  431. }
  432. if (beg_ts) {
  433. fh_time_get(&end_ts);
  434. if ((end_ts - beg_ts) > 1000000) {
  435. FH_LOG_PGEN(WARN, ("NET> Delayed TCP Read: Sock:%d, Elapsed:%lld us, Retry:%d, Pending:%d bytes",
  436. s, (long long) (end_ts-beg_ts), retry, fh_sock_pending(s)));
  437. }
  438. }
  439. FH_LOG(NET, INFO, ("NET> Read %d bytes from socket #%d", nbytes - nleft, s));
  440. return nbytes - nleft;
  441. }
  442. /*
  443. * fh_tcp_read
  444. *
  445. * Performs the read() system call on the socket, waiting for all the data
  446. * before returning.
  447. */
  448. int fh_tcp_read(int s, void *buf, int nbytes)
  449. {
  450. return fh_tcp_readex(s, buf, nbytes, 0);
  451. }
  452. /*
  453. * fh_tcp_peek
  454. */
  455. int fh_tcp_peek(int s, void *buf, int nbytes)
  456. {
  457. return fh_tcp_readex(s, buf, nbytes, MSG_PEEK);
  458. }
  459. /*
  460. * fh_tcp_writeex
  461. *
  462. * Perform the write() system call on the socket, waiting for all the data
  463. * to be sent before returning.
  464. */
  465. static int fh_tcp_writeex(int s, const void *buf, int nbytes, int flags)
  466. {
  467. int nleft, nwritten;
  468. uint32_t start_ts = 0;
  469. uint64_t beg_ts = 0;
  470. uint64_t end_ts = 0;
  471. uint32_t retry = 0;
  472. if (FH_LL_OK(NET, STATS)) {
  473. fh_time_get(&beg_ts);
  474. }
  475. nleft = nbytes;
  476. while (nleft > 0) {
  477. nwritten = send(s, (char*)buf, nleft, flags | MSG_DONTWAIT);
  478. if (nwritten < 0) {
  479. if ((errno == ECONNRESET) ||
  480. (errno == EPIPE)) {
  481. FH_LOG(NET, WARN, ("NET> fh_tcp_writeex: ret 0 due to error %d", errno));
  482. return 0;
  483. }
  484. if (should_retry(errno, &start_ts, "Tx")) {
  485. if (beg_ts == 0) {
  486. fh_time_get(&beg_ts);
  487. }
  488. usleep(1000);
  489. retry++;
  490. continue;
  491. }
  492. else {
  493. FH_LOG(NET, ERR, ("NET> fh_tcp_readex failed: %d", errno));
  494. break;
  495. }
  496. }
  497. else if (nwritten == 0) {
  498. break;
  499. }
  500. nleft -= nwritten;
  501. buf = (char *) buf + nwritten;
  502. }
  503. if (beg_ts) {
  504. fh_time_get(&end_ts);
  505. if ((end_ts - beg_ts) > 1000000) {
  506. FH_LOG_PGEN(WARN, ("NET> Delayed TCP Write: Sock:%d, Elapsed:%lld us, Retry:%d",
  507. s, (long long) (end_ts-beg_ts), retry));
  508. }
  509. }
  510. FH_LOG(NET, INFO, ("NET> fh_tcp_writeex: written: %d socket # %d left: %d",
  511. nbytes - nleft, s, nleft));
  512. return nbytes - nleft;
  513. }
  514. /*
  515. * fh_tcp_write
  516. *
  517. * Perform the write() system call on the socket, waiting for all the data
  518. * to be sent before returning.
  519. */
  520. int fh_tcp_write(int s, const void *buf, int nbytes)
  521. {
  522. return fh_tcp_writeex(s, buf, nbytes, 0);
  523. }