TAS
TCP Acceleration as an OS Service
control.c
1 /*
2  * Copyright 2019 University of Washington, Max Planck Institute for
3  * Software Systems, and The University of Texas at Austin
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <stddef.h>
29 #include <stdarg.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36 
37 #include <utils.h>
38 #include <tas_sockets.h>
39 #include <tas_ll.h>
40 
41 #include "internal.h"
42 
43 static void conn_close(struct flextcp_context *ctx, struct socket *s);
44 
45 int tas_init(void)
46 {
47  if (flextcp_fd_init() != 0) {
48  fprintf(stderr, "flextcp_fd_init failed\n");
49  return -1;
50  }
51 
52  if (flextcp_init() != 0) {
53  fprintf(stderr, "flextcp_init failed\n");
54  return -1;
55  }
56 
57  return 0;
58 }
59 
60 int tas_socket(int domain, int type, int protocol)
61 {
62  struct socket *s;
63  int fd;
64  int nonblock = 0, cloexec = 0;
65 
66  if ((type & SOCK_NONBLOCK) == SOCK_NONBLOCK) {
67  nonblock = 1;
68  }
69  if ((type & SOCK_CLOEXEC) == SOCK_CLOEXEC) {
70  cloexec = 1;
71  }
72 
73  type &= ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
74  if (domain != AF_INET || type != SOCK_STREAM) {
75  errno = EINVAL;
76  return -1;
77  }
78 
79  if ((fd = flextcp_fd_salloc(&s)) < 0) {
80  return -1;
81  }
82 
83  s->type = SOCK_SOCKET;
84  s->flags = 0;
85  flextcp_epoll_sockinit(s);
86 
87  if (nonblock) {
88  s->flags |= SOF_NONBLOCK;
89  }
90  if (cloexec) {
91  s->flags |= SOF_CLOEXEC;
92  }
93 
94  flextcp_fd_srelease(fd, s);
95  return fd;
96 }
97 
98 int tas_close(int sockfd)
99 {
100  struct socket *s;
101  struct epoll *ep;
102 
103  if (flextcp_fd_slookup(sockfd, &s) == 0) {
104  flextcp_fd_close(sockfd);
105 
106  /* there is another fd associated with this socket */
107  if (s->refcnt != 0) {
108  flextcp_fd_srelease(sockfd, s);
109  return 0;
110  }
111 
112  tas_sock_close(s);
113  } else if (flextcp_fd_elookup(sockfd, &ep) == 0) {
114  flextcp_fd_close(sockfd);
115 
116  /* there is another fd associated with this epoll */
117  if (ep->refcnt != 0) {
118  flextcp_fd_erelease(sockfd, ep);
119  return 0;
120  }
121 
122  /* destroy epoll */
123  flextcp_epoll_destroy(ep);
124  } else {
125  errno = EBADF;
126  return -1;
127  }
128 
129  return 0;
130 }
131 
132 int tas_sock_close(struct socket *s)
133 {
134  struct flextcp_context *ctx;
135 
136  assert(s->refcnt == 0);
137 
138  /* remove from epoll */
139  flextcp_epoll_sockclose(s);
140 
141  ctx = flextcp_sockctx_get();
142  if (s->type == SOCK_CONNECTION) {
143  conn_close(ctx, s);
144  } else if (s->type == SOCK_SOCKET) {
145  free(s);
146  } else {
147  fprintf(stderr, "TODO: close for non-connections. (leak)\n");
148  }
149 
150  return 0;
151 }
152 
153 /* called with lock on s held, takes over ownership of s struct */
154 static void conn_close(struct flextcp_context *ctx, struct socket *s)
155 {
156  s->data.connection.status = SOC_CLOSED;
157 
158  if ((s->data.connection.st_flags & CSTF_TXCLOSED_ACK) &&
159  (s->data.connection.st_flags & CSTF_RXCLOSED))
160  {
161  /* rx and tx already closed */
162  flextcp_sockclose_finish(ctx, s);
163  } else if (!(s->data.connection.st_flags & CSTF_TXCLOSED)) {
164  if (flextcp_connection_tx_close(ctx, &s->data.connection.c) != 0) {
165  fprintf(stderr, "conn_close: flextcp_connection_tx_close failed\n");
166  abort();
167  }
168 
169  s->data.connection.st_flags |= CSTF_TXCLOSED;
170  } else {
171  /* TX close already initiated, so we're waiting either for TX close to
172  * be acknowledged or an RX close to arrive.
173  *
174  * TODO: send reset if data arrives after this */
175  }
176  socket_unlock(s);
177 }
178 
179 void flextcp_sockclose_finish(struct flextcp_context *ctx, struct socket *s)
180 {
181  /* socket struct will be freed after asynchronous completion */
182 
183  if (flextcp_connection_close(ctx, &s->data.connection.c) != 0) {
184  fprintf(stderr, "close: flextcp_connection_close failed (unhandled, "
185  "results in leak)\n");
186  return;
187  }
188 
189 }
190 
191 int tas_shutdown(int sockfd, int how)
192 {
193  struct socket *s;
194  struct flextcp_context *ctx;
195  int ret = 0;
196 
197  if (flextcp_fd_slookup(sockfd, &s) != 0) {
198  errno = EBADF;
199  return -1;
200  }
201 
202  tas_sock_move(s);
203 
204  if (s->type != SOCK_CONNECTION) {
205  /* TODO: probably the wrong thing for listeners */
206  errno = ENOTSOCK;
207  ret = -1;
208  goto out;
209  }
210 
211  if (s->data.connection.status != SOC_CONNECTED) {
212  errno = ENOTCONN;
213  ret = -1;
214  goto out;
215  }
216 
217  if (how != SHUT_WR) {
218  fprintf(stderr, "flextcp shutdown: TODO how != SHUT_WR\n");
219  errno = EINVAL;
220  ret = -1;
221  goto out;
222  }
223 
224  /* already closed for tx -> NOP */
225  if ((s->data.connection.st_flags & CSTF_TXCLOSED) == CSTF_TXCLOSED) {
226  goto out;
227  }
228 
229  ctx = flextcp_sockctx_get();
230  if (flextcp_connection_tx_close(ctx, &s->data.connection.c) != 0) {
231  /* a bit fishy.... */
232  errno = ENOBUFS;
233  ret = -1;
234  goto out;
235  }
236 
237  s->data.connection.st_flags |= CSTF_TXCLOSED;
238 
239 out:
240  flextcp_fd_srelease(sockfd, s);
241  return ret;
242 }
243 
244 int tas_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
245 {
246  struct socket *s;
247  int ret = 0;
248 
249  if (flextcp_fd_slookup(sockfd, &s) != 0) {
250  errno = EBADF;
251  return -1;
252  }
253 
254  if (addrlen != sizeof(s->addr) || addr->sa_family != AF_INET) {
255  errno = EINVAL;
256  ret = -1;
257  goto out;
258  }
259 
260  memcpy(&s->addr, addr, sizeof(s->addr));
261  s->flags |= SOF_BOUND;
262 
263 out:
264  flextcp_fd_srelease(sockfd, s);
265  return ret;
266 }
267 
268 int tas_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
269 {
270  struct socket *s;
271  int ret = 0, block;
272  struct sockaddr_in *sin = (struct sockaddr_in *) addr;
273  struct flextcp_context *ctx;
274 
275  if (flextcp_fd_slookup(sockfd, &s) != 0) {
276  errno = EBADF;
277  return -1;
278  }
279 
280  /* socket already used */
281  if (s->type == SOCK_LISTENER ||
282  (s->type == SOCK_CONNECTION && s->data.connection.status == SOC_CONNECTED))
283  {
284  errno = EISCONN;
285  ret = -1;
286  goto out;
287  }
288 
289  /* non blocking socket connecting */
290  if (s->type == SOCK_CONNECTION && s->data.connection.status == SOC_CONNECTING) {
291  errno = EALREADY;
292  ret = -1;
293  goto out;
294  }
295 
296  /* filter out invalid address types */
297  if (addrlen != sizeof(s->addr) || addr->sa_family != AF_INET) {
298  errno = EINVAL;
299  ret = -1;
300  goto out;
301  }
302 
303  /* open flextcp connection */
304  ctx = flextcp_sockctx_get();
305  if (flextcp_connection_open(ctx, &s->data.connection.c,
306  ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port)))
307  {
308  /* TODO */
309  errno = ECONNREFUSED;
310  ret = -1;
311  goto out;
312  }
313 
314  assert(s->type == SOCK_CONNECTION || s->type == SOCK_SOCKET);
315  s->type = SOCK_CONNECTION;
316  s->data.connection.status = SOC_CONNECTING;
317  s->data.connection.listener = NULL;
318  s->data.connection.rx_len_1 = 0;
319  s->data.connection.rx_len_2 = 0;
320  s->data.connection.ctx = ctx;
321 
322  /* check whether the socket is blocking */
323  if ((s->flags & SOF_NONBLOCK) == SOF_NONBLOCK) {
324  /* if non-blocking, just return */
325  errno = EINPROGRESS;
326  ret = -1;
327  goto out;
328  } else {
329  /* if this is blocking, wait for connection to complete */
330  block = 0;
331  do {
332  socket_unlock(s);
333  if (block)
334  flextcp_context_wait(ctx, -1);
335  block = 1;
336  flextcp_sockctx_poll(ctx);
337  socket_lock(s);
338  } while (s->data.connection.status == SOC_CONNECTING);
339  }
340 
341  if (s->data.connection.status == SOC_FAILED) {
342  /* TODO */
343  errno = ECONNREFUSED;
344  ret = -1;
345  goto out;
346  }
347 
348 out:
349  flextcp_fd_srelease(sockfd, s);
350  return ret;
351 }
352 
353 int tas_listen(int sockfd, int backlog)
354 {
355  struct socket *s;
356  struct flextcp_context *ctx;
357  int ret = 0, block;
358  uint32_t flags = 0;
359 
360  if (flextcp_fd_slookup(sockfd, &s) != 0) {
361  errno = EBADF;
362  return -1;
363  }
364 
365  /* socket already used */
366  if (s->type != SOCK_SOCKET) {
367  errno = EOPNOTSUPP;
368  ret = -1;
369  goto out;
370  }
371 
372  /* socket not bound */
373  /* TODO: technically sohuld probably bind to an ephemeral port */
374  if ((s->flags & SOF_BOUND) != SOF_BOUND) {
375  errno = EADDRINUSE;
376  ret = -1;
377  goto out;
378  }
379 
380  /* pass on reuseport flags */
381  if ((s->flags & SOF_REUSEPORT) == SOF_REUSEPORT) {
382  flags |= FLEXTCP_LISTEN_REUSEPORT;
383  }
384 
385  /* make sure we have a reasonable backlog */
386  if (backlog < 8) {
387  backlog = 8;
388  }
389 
390  /* open flextcp listener */
391  ctx = flextcp_sockctx_get();
392  if (flextcp_listen_open(ctx, &s->data.listener.l, ntohs(s->addr.sin_port),
393  backlog, flags))
394  {
395  /* TODO */
396  errno = ECONNREFUSED;
397  ret = -1;
398  goto out;
399  }
400 
401  s->type = SOCK_LISTENER;
402  s->data.listener.backlog = backlog;
403  s->data.listener.status = SOL_OPENING;
404  s->data.listener.pending = NULL;
405 
406  /* wait for listen to complete */
407  block = 0;
408  do {
409  socket_unlock(s);
410  if (block)
411  flextcp_context_wait(ctx, -1);
412  block = 1;
413  flextcp_sockctx_poll(ctx);
414  socket_lock(s);
415  } while (s->data.listener.status == SOL_OPENING);
416 
417  /* check whether listen failed */
418  if (s->data.listener.status == SOL_FAILED) {
419  /* TODO */
420  errno = ENOBUFS;
421  ret = -1;
422  goto out;
423  }
424 
425 out:
426  flextcp_fd_srelease(sockfd, s);
427  return ret;
428 }
429 
430 int tas_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
431  int flags)
432 {
433  struct socket *s, *ns;
434  struct flextcp_context *ctx;
435  struct socket_pending *sp, *spp;
436  int ret = 0, nonblock = 0, cloexec = 0, newfd, block;
437 
438  if (flextcp_fd_slookup(sockfd, &s) != 0) {
439  errno = EBADF;
440  return -1;
441  }
442 
443  /* validate flags */
444  if ((flags & SOCK_NONBLOCK) == SOCK_NONBLOCK) {
445  nonblock = 1;
446  }
447  if ((flags & SOCK_CLOEXEC) == SOCK_CLOEXEC) {
448  cloexec = 1;
449  }
450 
451  flags &= ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
452  if (flags != 0) {
453  errno = EINVAL;
454  ret = -1;
455  goto out;
456  }
457 
458  /* socket is not a listening socket */
459  if (s->type != SOCK_LISTENER) {
460  errno = EOPNOTSUPP;
461  ret = -1;
462  goto out;
463  }
464 
465  ctx = flextcp_sockctx_get();
466 
467  /* lookup pending connection for this context/thread */
468  for (sp = s->data.listener.pending; sp != NULL; sp = sp->next) {
469  if (sp->ctx == ctx) {
470  break;
471  }
472  }
473 
474  /* if there is no pending request, send out a request */
475  if (sp == NULL) {
476  if ((sp = malloc(sizeof(*sp))) == NULL) {
477  errno = ENOMEM;
478  ret = -1;
479  goto out;
480  }
481 
482  /* allocate socket structure */
483  if ((newfd = flextcp_fd_salloc(&ns)) < 0) {
484  free(sp);
485  ret = -1;
486  goto out;
487  }
488 
489  ns->type = SOCK_CONNECTION;
490  ns->flags = (nonblock ? SOF_NONBLOCK : 0) | (cloexec ? SOF_CLOEXEC : 0);
491  ns->data.connection.status = SOC_CONNECTING;
492  ns->data.connection.listener = s;
493  ns->data.connection.rx_len_1 = 0;
494  ns->data.connection.rx_len_2 = 0;
495  ns->data.connection.ctx = ctx;
496 
497  sp->fd = newfd;
498  sp->s = ns;
499  sp->ctx = ctx;
500  sp->next = NULL;
501 
502  /* send accept request to kernel */
503  if (flextcp_listen_accept(ctx, &s->data.listener.l,
504  &ns->data.connection.c) != 0)
505  {
506  /* TODO */
507  errno = ENOBUFS;
508  ret = -1;
509  free(sp);
510  flextcp_fd_close(newfd);
511  free(s);
512  goto out;
513  }
514 
515  /* append entry to pending list */
516  spp = s->data.listener.pending;
517  if (spp == NULL) {
518  s->data.listener.pending = sp;
519  } else {
520  while (spp->next != NULL) {
521  spp = spp->next;
522  }
523  spp->next = sp;
524  }
525  } else {
526  ns = sp->s;
527  newfd = sp->fd;
528  }
529 
530  /* check if connection is still pending */
531  if (ns->data.connection.status == SOC_CONNECTING) {
532  flextcp_epoll_clear(s, EPOLLIN);
533  if ((s->flags & SOF_NONBLOCK) == SOF_NONBLOCK) {
534  /* if non-blocking, just return */
535  errno = EAGAIN;
536  ret = -1;
537  flextcp_fd_srelease(newfd, ns);
538  goto out;
539  } else {
540  /* if this is blocking, wait for connection to complete */
541  block = 0;
542  do {
543  socket_unlock(ns);
544  socket_unlock(s);
545  if (block)
546  flextcp_context_wait(ctx, -1);
547  flextcp_sockctx_poll(ctx);
548  block = 1;
549  socket_lock(s);
550  socket_lock(ns);
551  } while (ns->data.connection.status == SOC_CONNECTING);
552  }
553  }
554 
555  /* connection is opened now */
556  assert(ns->data.connection.status == SOC_CONNECTED);
557 
558  /* remove entry from pending list */
559  if (s->data.listener.pending == sp) {
560  s->data.listener.pending = sp->next;
561  } else {
562  spp = s->data.listener.pending;
563  assert(spp != NULL);
564  while (spp->next != sp) {
565  assert(spp->next != NULL);
566  spp = spp->next;
567  }
568  spp->next = sp->next;
569  }
570  free(sp);
571  flextcp_fd_srelease(newfd, ns);
572 
573  // fill in addr if given
574  if(addr != NULL) {
575  int r = tas_getpeername(newfd, addr, addrlen);
576  assert(r == 0);
577  }
578 
579  ret = newfd;
580 out:
581  flextcp_fd_srelease(sockfd, s);
582  return ret;
583 }
584 
586 int tas_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
587 {
588  return tas_accept4(sockfd, addr, addrlen, 0);
589 }
590 
591 int tas_fcntl(int sockfd, int cmd, ...)
592 {
593  struct socket *s;
594  int ret = 0;
595  int iarg;
596  va_list arg;
597 
598  if (flextcp_fd_slookup(sockfd, &s) != 0) {
599  errno = EBADF;
600  return -1;
601  }
602 
603  tas_sock_move(s);
604 
605  switch (cmd) {
606  case F_GETFL:
607  /* return flags */
608  if ((s->flags & SOF_NONBLOCK) == SOF_NONBLOCK) {
609  ret |= O_NONBLOCK;
610  }
611  break;
612 
613  case F_SETFL:
614  /* update flags */
615  va_start(arg, cmd);
616  iarg = va_arg(arg, int);
617  va_end(arg);
618 
619  /* make sure only supported flags are set */
620  if ((iarg & ~(O_NONBLOCK | O_ACCMODE)) != 0) {
621  fprintf(stderr, "flextcp fcntl: unsupported flags set (%x)\n",
622  iarg);
623  /* not sure if that's the right error code */
624  errno = EINVAL;
625  ret = -1;
626  goto out;
627  }
628 
629  /* set or clear nonblocking socket flags */
630  if ((iarg & O_NONBLOCK) == 0) {
631  s->flags &= ~SOF_NONBLOCK;
632  } else {
633  s->flags |= SOF_NONBLOCK;
634  }
635  break;
636 
637  case F_GETFD:
638  if ((s->flags & SOF_CLOEXEC) == SOF_CLOEXEC) {
639  ret |= FD_CLOEXEC;
640  }
641  break;
642 
643  case F_SETFD:
644  /* set/clear cloexec flag */
645  va_start(arg, cmd);
646  iarg = va_arg(arg, int);
647  va_end(arg);
648 
649  if ((iarg & ~FD_CLOEXEC) != 0) {
650  fprintf(stderr, "flextcp fcntl: setfd unsupported flag (%x)\n",
651  iarg);
652  errno = EINVAL;
653  ret = -1;
654  goto out;
655  }
656 
657  if ((iarg & FD_CLOEXEC) == FD_CLOEXEC)
658  s->flags |= SOF_CLOEXEC;
659  else
660  s->flags &= ~SOF_CLOEXEC;
661  break;
662 
663  default:
664  fprintf(stderr, "flextcp fcntl: unsupported cmd\n");
665  errno = EINVAL;
666  ret = -1;
667  goto out;
668  }
669 
670 out:
671  flextcp_fd_srelease(sockfd, s);
672  return ret;
673 }
674 
675 int tas_getsockopt(int sockfd, int level, int optname, void *optval,
676  socklen_t *optlen)
677 {
678  struct socket *s;
679  int ret = 0, res, len;
680 
681  if (flextcp_fd_slookup(sockfd, &s) != 0) {
682  errno = EBADF;
683  return -1;
684  }
685 
686  tas_sock_move(s);
687 
688  if(level == IPPROTO_TCP && optname == TCP_NODELAY) {
689  /* check nodelay flag: always set */
690  res = 1;
691 
692  } else if(level == SOL_SOCKET &&
693  (optname == SO_RCVBUF || optname == SO_SNDBUF))
694  {
695  /* read receive or transmit buffer size: TODO */
696  res = 1024 * 1024;
697  } else if (level == SOL_SOCKET && optname == SO_ERROR) {
698  /* check socket error */
699  if (s->type == SOCK_LISTENER) {
700  res = (s->data.listener.status == SOL_OPEN ? 0 : EINPROGRESS);
701  } else if (s->type == SOCK_CONNECTION) {
702  /* if connection is opening, make sure to poll context to make busy loops
703  * work */
704  if (s->data.connection.status == SOC_CONNECTING) {
705  socket_unlock(s);
706  flextcp_sockctx_poll(flextcp_sockctx_get());
707  socket_lock(s);
708  }
709 
710  if (s->data.connection.status == SOC_CONNECTED) {
711  res = 0;
712  } else if (s->data.connection.status == SOC_CONNECTING) {
713  res = EINPROGRESS;
714  } else {
715  /* TODO */
716  res = ECONNREFUSED;
717  }
718  } else {
719  /* TODO */
720  res = ENOTSUP;
721  }
722  } else if (level == SOL_SOCKET && optname == SO_REUSEPORT) {
723  res = !!(s->flags & SOF_REUSEPORT);
724  } else if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
725  /* reuseaddr is always on */
726  res = 1;
727  } else if (level == SOL_SOCKET && optname == SO_KEEPALIVE) {
728  /* keepalive is always disabled */
729  res = 0;
730  } else if (level == IPPROTO_TCP && (optname == TCP_KEEPIDLE ||
731  optname == TCP_KEEPINTVL || optname == TCP_KEEPCNT)) {
732  res = 0;
733  } else if (level == SOL_SOCKET && optname == SO_LINGER) {
734  fprintf(stderr, "flextcp getsockopt: SO_LINGER not implemented\n");
735  errno = ENOPROTOOPT;
736  ret = -1;
737  goto out;
738  } else {
739  /* unknown option */
740  fprintf(stderr, "flextcp getsockopt: unknown level optname combination "
741  "(l=%u, o=%u)\n", level, optname);
742  errno = ENOPROTOOPT;
743  ret = -1;
744  goto out;
745  }
746 
747  /* copy result to optval, truncate if necessary */
748  len = MIN(*optlen, sizeof(res));
749  memcpy(optval, &res, len);
750  *optlen = res;
751 
752 out:
753  flextcp_fd_srelease(sockfd, s);
754  return ret;
755 }
756 
757 int tas_setsockopt(int sockfd, int level, int optname, const void *optval,
758  socklen_t optlen)
759 {
760  struct socket *s;
761  int ret = 0, res;
762 
763  if (flextcp_fd_slookup(sockfd, &s) != 0) {
764  errno = EBADF;
765  return -1;
766  }
767 
768  tas_sock_move(s);
769 
770  if(level == IPPROTO_TCP && optname == TCP_NODELAY) {
771  /* do nothing */
772  if (optlen != sizeof(int)) {
773  errno = EINVAL;
774  ret = -1;
775  goto out;
776  }
777  } else if(level == SOL_SOCKET &&
778  (optname == SO_RCVBUF || optname == SO_SNDBUF))
779  {
780  if (optlen < sizeof(int)) {
781  errno = EINVAL;
782  ret = -1;
783  goto out;
784  }
785 
786  /* we allow "resizing" up to 1MB */
787  res = * ((int *) optval);
788  if (res <= 1024 * 1024) {
789  ret = 0;
790  } else {
791  errno = ENOMEM;
792  ret = -1;
793  goto out;
794  }
795  } else if (level == SOL_SOCKET && optname == SO_REUSEPORT) {
796  if (optlen != sizeof(int)) {
797  errno = EINVAL;
798  ret = -1;
799  goto out;
800  }
801 
802  if (*(int *) optval != 0) {
803  s->flags |= SOF_REUSEPORT;
804  } else {
805  s->flags &= ~SOF_REUSEPORT;
806  }
807  } else if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
808  /* ignore silently */
809  } else if (level == SOL_SOCKET && optname == SO_KEEPALIVE) {
810  /* ignore silently */
811  } else if (level == SOL_SOCKET && optname == SO_PRIORITY) {
812  /* ignore silently */
813  } else if (level == IPPROTO_TCP && (optname == TCP_KEEPIDLE ||
814  optname == TCP_KEEPINTVL || optname == TCP_KEEPCNT)) {
815  /* ignore silently */
816  } else if (level == SOL_SOCKET && optname == SO_LINGER) {
817  fprintf(stderr, "flextcp setsockopt: SO_LINGER not implemented\n");
818  errno = ENOPROTOOPT;
819  ret = -1;
820  goto out;
821  } else {
822  /* unknown option */
823  fprintf(stderr, "flextcp setsockopt: unknown level optname combination "
824  "(l=%u, o=%u)\n", level, optname);
825  errno = ENOPROTOOPT;
826  ret = -1;
827  goto out;
828  }
829 
830 out:
831  flextcp_fd_srelease(sockfd, s);
832  return ret;
833 }
834 
835 int tas_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
836 {
837  struct socket *s;
838  int ret = 0;
839  socklen_t len;
840  struct sockaddr_in sin;
841 
842  if (flextcp_fd_slookup(sockfd, &s) != 0) {
843  errno = EBADF;
844  return -1;
845  }
846 
847  tas_sock_move(s);
848 
849  if ((s->flags & SOF_BOUND) == SOF_BOUND) {
850  sin = s->addr;
851  } else if (s->type == SOCK_CONNECTION &&
852  s->data.connection.status == SOC_CONNECTED)
853  {
854  memset(&sin, 0, sizeof(sin));
855  sin.sin_family = AF_INET;
856  /* FIXME: without breaking abstraction */
857  sin.sin_addr.s_addr = htonl(s->data.connection.c.local_ip);
858  sin.sin_port = htons(s->data.connection.c.local_port);
859  } else {
860  errno = ENOTCONN;
861  ret = -1;
862  goto out;
863  }
864 
865  len = MIN(*addrlen, sizeof(sin));
866  *addrlen = len;
867  memcpy(addr, &sin, len);
868 
869 out:
870  flextcp_fd_srelease(sockfd, s);
871  return ret;
872 }
873 
874 int tas_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
875 {
876  struct socket *s;
877  int ret = 0;
878  socklen_t len;
879  struct sockaddr_in sin;
880 
881  if (flextcp_fd_slookup(sockfd, &s) != 0) {
882  errno = EBADF;
883  return -1;
884  }
885 
886  tas_sock_move(s);
887 
888  /* if not connection or not currently connected then there is no peername */
889  if (s->type != SOCK_CONNECTION ||
890  s->data.connection.status != SOC_CONNECTED)
891  {
892  errno = ENOTCONN;
893  ret = -1;
894  goto out;
895  }
896 
897  memset(&sin, 0, sizeof(sin));
898  sin.sin_family = AF_INET;
899  /* FIXME: without breaking abstraction */
900  sin.sin_addr.s_addr = htonl(s->data.connection.c.remote_ip);
901  sin.sin_port = htons(s->data.connection.c.remote_port);
902 
903  len = MIN(*addrlen, sizeof(sin));
904  *addrlen = len;
905  memcpy(addr, &sin, len);
906 
907 out:
908  flextcp_fd_srelease(sockfd, s);
909  return ret;
910 }
911 
912 int tas_move_conn(int sockfd)
913 {
914  struct socket *s;
915  int ret = 0;
916 
917  if (flextcp_fd_slookup(sockfd, &s) != 0) {
918  errno = EBADF;
919  return -1;
920  }
921 
922  ret = tas_sock_move(s);
923 
924  flextcp_fd_srelease(sockfd, s);
925  return ret;
926 }
927 
928 int tas_sock_move(struct socket *s)
929 {
930  int ret, block;
931  struct flextcp_context *ctx;
932 
933  /* if not connection or not currently connected then there is no peername */
934  if (s->type != SOCK_CONNECTION ||
935  s->data.connection.status != SOC_CONNECTED)
936  {
937  errno = ENOTCONN;
938  return -1;
939  }
940 
941  /* no-op if already on the right core */
942  ctx = flextcp_sockctx_get();
943  if (s->data.connection.ctx == ctx) {
944  return 0;
945  }
946 
947  s->data.connection.move_status = INT_MIN;
948  if (flextcp_connection_move(ctx, &s->data.connection.c) != 0) {
949  /* TODO */
950  errno = EINVAL;
951  return -1;
952  }
953 
954  block = 0;
955  do {
956  socket_unlock(s);
957  if (block)
958  flextcp_context_wait(ctx, -1);
959  block = 1;
960  flextcp_sockctx_poll(ctx);
961  socket_lock(s);
962  } while (s->data.connection.move_status == INT_MIN);
963  ret = s->data.connection.move_status;
964  if (ret == 0) {
965  s->data.connection.ctx = ctx;
966  }
967 
968  return ret;
969 }
int flextcp_context_wait(struct flextcp_context *ctx, int timeout_ms)
Definition: init.c:995
int tas_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
Definition: control.c:586
int flextcp_listen_open(struct flextcp_context *ctx, struct flextcp_listener *lst, uint16_t port, uint32_t backlog, uint32_t flags)
Definition: conn.c:41
int flextcp_init(void)
Definition: init.c:75
int flextcp_connection_tx_close(struct flextcp_context *ctx, struct flextcp_connection *conn)
Definition: conn.c:337
int flextcp_connection_close(struct flextcp_context *ctx, struct flextcp_connection *conn)
Definition: conn.c:161
Public low-level application interface for TAS.
int flextcp_connection_move(struct flextcp_context *ctx, struct flextcp_connection *conn)
Definition: conn.c:397
int flextcp_connection_open(struct flextcp_context *ctx, struct flextcp_connection *conn, uint32_t dst_ip, uint16_t dst_port)
Definition: conn.c:123
TAS sockets emulation.
int flextcp_listen_accept(struct flextcp_context *ctx, struct flextcp_listener *lst, struct flextcp_connection *conn)
Definition: conn.c:89