Initial push of vcl-ldpreload to extras
[vpp.git] / extras / vcl-ldpreload / src / libvcl-ldpreload / vcom_socket.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <sys/uio.h>
18 #include <limits.h>
19 #define __need_IOV_MAX
20 #include <bits/stdio_lim.h>
21
22 #include <vppinfra/types.h>
23 #include <vppinfra/hash.h>
24 #include <vppinfra/pool.h>
25
26 #include <libvcl-ldpreload/vcom_socket.h>
27 #include <libvcl-ldpreload/vcom_socket_wrapper.h>
28 #include <libvcl-ldpreload/vcom.h>
29
30 #include <uri/vppcom.h>
31
32
33 /*
34  * VCOM_SOCKET Private definitions and functions.
35  */
36
37 typedef struct vcom_socket_main_t_
38 {
39   u8 init;
40
41   /* vcom_socket pool */
42   vcom_socket_t *vsockets;
43
44   /* Hash table for socketidx to fd mapping */
45   uword *sockidx_by_fd;
46
47   /* vcom_epoll pool */
48   vcom_epoll_t *vepolls;
49
50   /* Hash table for epollidx to epfd mapping */
51   uword *epollidx_by_epfd;
52
53
54   /* common epitem poll for all epfd */
55   /* TBD: epitem poll per epfd */
56   /* vcom_epitem pool */
57   vcom_epitem_t *vepitems;
58
59   /* Hash table for epitemidx to epfdfd mapping */
60   uword *epitemidx_by_epfdfd;
61
62   /* Hash table - key:epfd, value:vec of epitemidx */
63   uword *epitemidxs_by_epfd;
64   /* Hash table - key:fd, value:vec of epitemidx */
65   uword *epitemidxs_by_fd;
66
67 } vcom_socket_main_t;
68
69 vcom_socket_main_t vcom_socket_main;
70
71
72 static int
73 vcom_socket_open_socket (int domain, int type, int protocol)
74 {
75   int rv = -1;
76
77   /* handle domains implemented by vpp */
78   switch (domain)
79     {
80     case AF_INET:
81     case AF_INET6:
82       /* get socket type and
83        * handle the socket types supported by vpp */
84       switch (type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
85         {
86         case SOCK_STREAM:
87         case SOCK_DGRAM:
88           /* the type argument serves a second purpose,
89            * in addition to specifying a socket type,
90            * it may include the bitwise OR of any of
91            * SOCK_NONBLOCK and SOCK_CLOEXEC, to modify
92            * the behavior of socket. */
93           rv = libc_socket (domain, type, protocol);
94           if (rv == -1)
95             rv = -errno;
96           break;
97
98         default:
99           break;
100         }
101
102       break;
103
104     default:
105       break;
106     }
107
108   return rv;
109 }
110
111 static int
112 vcom_socket_open_epoll (int flags)
113 {
114   int rv = -1;
115
116   if (flags < 0)
117     {
118       return -EINVAL;
119     }
120   if (flags && (flags & ~EPOLL_CLOEXEC))
121     {
122       return -EINVAL;
123     }
124
125   /* flags can be either zero or EPOLL_CLOEXEC */
126   rv = libc_epoll_create1 (flags);
127   if (rv == -1)
128     rv = -errno;
129
130   return rv;
131 }
132
133 static int
134 vcom_socket_close_socket (int fd)
135 {
136   int rv;
137
138   rv = libc_close (fd);
139   if (rv == -1)
140     rv = -errno;
141
142   return rv;
143 }
144
145 static int
146 vcom_socket_close_epoll (int epfd)
147 {
148   int rv;
149
150   rv = libc_close (epfd);
151   if (rv == -1)
152     rv = -errno;
153
154   return rv;
155 }
156
157 /*
158  * Public API functions
159  */
160
161
162 int
163 vcom_socket_is_vcom_fd (int fd)
164 {
165   vcom_socket_main_t *vsm = &vcom_socket_main;
166   uword *p;
167   vcom_socket_t *vsock;
168
169   p = hash_get (vsm->sockidx_by_fd, fd);
170
171   if (p)
172     {
173       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
174       if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND)
175         return 1;
176     }
177   return 0;
178 }
179
180 int
181 vcom_socket_is_vcom_epfd (int epfd)
182 {
183   vcom_socket_main_t *vsm = &vcom_socket_main;
184   uword *p;
185   vcom_epoll_t *vepoll;
186
187   p = hash_get (vsm->epollidx_by_epfd, epfd);
188
189   if (p)
190     {
191       vepoll = pool_elt_at_index (vsm->vepolls, p[0]);
192       if (vepoll && vepoll->type == EPOLL_TYPE_VPPCOM_BOUND)
193         return 1;
194     }
195   return 0;
196 }
197
198 static inline int
199 vcom_socket_get_sid (int fd)
200 {
201   vcom_socket_main_t *vsm = &vcom_socket_main;
202   uword *p;
203   vcom_socket_t *vsock;
204
205   p = hash_get (vsm->sockidx_by_fd, fd);
206
207   if (p)
208     {
209       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
210       if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND)
211         return vsock->sid;
212     }
213   return INVALID_SESSION_ID;
214 }
215
216 static inline int
217 vcom_socket_get_vep_idx (int epfd)
218 {
219   vcom_socket_main_t *vsm = &vcom_socket_main;
220   uword *p;
221   vcom_epoll_t *vepoll;
222
223   p = hash_get (vsm->epollidx_by_epfd, epfd);
224
225   if (p)
226     {
227       vepoll = pool_elt_at_index (vsm->vepolls, p[0]);
228       if (vepoll && vepoll->type == EPOLL_TYPE_VPPCOM_BOUND)
229         return vepoll->vep_idx;
230     }
231   return INVALID_VEP_IDX;
232 }
233
234 static inline int
235 vcom_socket_get_sid_and_vsock (int fd, vcom_socket_t ** vsockp)
236 {
237   vcom_socket_main_t *vsm = &vcom_socket_main;
238   uword *p;
239   vcom_socket_t *vsock;
240
241   p = hash_get (vsm->sockidx_by_fd, fd);
242
243   if (p)
244     {
245       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
246       if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND)
247         {
248           *vsockp = vsock;
249           return vsock->sid;
250         }
251     }
252   return INVALID_SESSION_ID;
253 }
254
255 static inline int
256 vcom_socket_get_vep_idx_and_vepoll (int epfd, vcom_epoll_t ** vepollp)
257 {
258   vcom_socket_main_t *vsm = &vcom_socket_main;
259   uword *p;
260   vcom_epoll_t *vepoll;
261
262   p = hash_get (vsm->epollidx_by_epfd, epfd);
263
264   if (p)
265     {
266       vepoll = pool_elt_at_index (vsm->vepolls, p[0]);
267       if (vepoll && vepoll->type == EPOLL_TYPE_VPPCOM_BOUND)
268         {
269           *vepollp = vepoll;
270           return vepoll->vep_idx;
271         }
272     }
273   return INVALID_VEP_IDX;
274 }
275
276
277 static int
278 vcom_socket_close_vepoll (int epfd)
279 {
280   int rv = -1;
281   vcom_socket_main_t *vsm = &vcom_socket_main;
282   uword *p;
283   vcom_epoll_t *vepoll;
284
285   p = hash_get (vsm->epollidx_by_epfd, epfd);
286   if (!p)
287     return -EBADF;
288
289   vepoll = pool_elt_at_index (vsm->vepolls, p[0]);
290   if (!vepoll)
291     return -EBADF;
292
293   if (vepoll->type != EPOLL_TYPE_VPPCOM_BOUND)
294     return -EINVAL;
295
296   if (vepoll->count)
297     {
298       if (!vepoll->close)
299         {
300           vepoll->close = 1;
301           return 0;
302         }
303       else
304         {
305           return -EBADF;
306         }
307     }
308
309   /* count is zero */
310   rv = vppcom_session_close (vepoll->vep_idx);
311   rv = vcom_socket_close_epoll (vepoll->epfd);
312
313   vepoll_init (vepoll);
314   hash_unset (vsm->epollidx_by_epfd, epfd);
315   pool_put (vsm->vepolls, vepoll);
316
317   return rv;
318 }
319
320 static int
321 vcom_socket_close_vsock (int fd)
322 {
323   int rv = -1;
324   vcom_socket_main_t *vsm = &vcom_socket_main;
325   uword *p;
326   vcom_socket_t *vsock;
327
328   vcom_epitem_t *vepitem;
329
330   i32 *vepitemidxs = 0;
331   i32 *vepitemidxs_var = 0;
332
333   p = hash_get (vsm->sockidx_by_fd, fd);
334   if (!p)
335     return -EBADF;
336
337   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
338   if (!vsock)
339     return -ENOTSOCK;
340
341   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
342     return -EINVAL;
343
344   rv = vppcom_session_close (vsock->sid);
345   rv = vcom_socket_close_socket (vsock->fd);
346
347   vsocket_init (vsock);
348   hash_unset (vsm->sockidx_by_fd, fd);
349   pool_put (vsm->vsockets, vsock);
350
351   /*
352    * NOTE:
353    * Before calling close(), user should remove
354    * this fd from the epoll-set of all epoll instances,
355    * otherwise resource(epitems) leaks ensues.
356    */
357
358   /*
359    * 00. close all epoll instances that are marked as "close"
360    *     of which this fd is the "last" remaining member.
361    * 01. epitems associated with this fd are intentionally
362    *     not removed, see NOTE: above.
363    * */
364
365   /* does this fd participate in epoll */
366   p = hash_get (vsm->epitemidxs_by_fd, fd);
367   if (p)
368     {
369       vepitemidxs = *(i32 **) p;
370       vec_foreach (vepitemidxs_var, vepitemidxs)
371       {
372         vepitem = pool_elt_at_index (vsm->vepitems, vepitemidxs_var[0]);
373         if (vepitem && vepitem->fd == fd &&
374             vepitem->type == FD_TYPE_VCOM_SOCKET)
375           {
376             i32 vep_idx;
377             vcom_epoll_t *vepoll;
378             if ((vep_idx =
379                  vcom_socket_get_vep_idx_and_vepoll (vepitem->epfd,
380                                                      &vepoll)) !=
381                 INVALID_VEP_IDX)
382               {
383                 if (vepoll->close)
384                   {
385                     if (vepoll->count == 1)
386                       {
387                         /*
388                          * force count to zero and
389                          * close this epoll instance
390                          * */
391                         vepoll->count = 0;
392                         vcom_socket_close_vepoll (vepoll->epfd);
393                       }
394                     else
395                       {
396                         vepoll->count -= 1;
397                       }
398                   }
399               }
400           }
401
402       }
403     }
404
405   return rv;
406 }
407
408 int
409 vcom_socket_close (int __fd)
410 {
411   int rv;
412
413   if (vcom_socket_is_vcom_fd (__fd))
414     {
415       rv = vcom_socket_close_vsock (__fd);
416     }
417   else if (vcom_socket_is_vcom_epfd (__fd))
418     {
419       rv = vcom_socket_close_vepoll (__fd);
420     }
421   else
422     {
423       rv = -EBADF;
424     }
425
426   return rv;
427 }
428
429 ssize_t
430 vcom_socket_read (int __fd, void *__buf, size_t __nbytes)
431 {
432   int rv = -1;
433   vcom_socket_main_t *vsm = &vcom_socket_main;
434   uword *p;
435   vcom_socket_t *vsock;
436
437   p = hash_get (vsm->sockidx_by_fd, __fd);
438   if (!p)
439     return -EBADF;
440
441   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
442   if (!vsock)
443     return -ENOTSOCK;
444
445   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
446     return -EINVAL;
447
448   if (!__buf || __nbytes < 0)
449     {
450       return -EINVAL;
451     }
452
453   rv = vcom_fcntl (__fd, F_GETFL, 0);
454   if (rv < 0)
455     {
456       return rv;
457
458     }
459
460   /* is blocking */
461   if (!(rv & O_NONBLOCK))
462     {
463       do
464         {
465           rv = vppcom_session_read (vsock->sid, __buf, __nbytes);
466         }
467       while (rv == -EAGAIN || rv == -EWOULDBLOCK);
468       return rv;
469     }
470   /* The file descriptor refers to a socket and has been
471    * marked nonblocking(O_NONBLOCK) and the read would
472    * block.
473    * */
474   /* is non blocking */
475   rv = vppcom_session_read (vsock->sid, __buf, __nbytes);
476   return rv;
477 }
478
479 ssize_t
480 vcom_socket_readv (int __fd, const struct iovec * __iov, int __iovcnt)
481 {
482   int rv;
483   vcom_socket_main_t *vsm = &vcom_socket_main;
484   uword *p;
485   vcom_socket_t *vsock;
486   ssize_t total = 0, len = 0;
487
488   p = hash_get (vsm->sockidx_by_fd, __fd);
489   if (!p)
490     return -EBADF;
491
492   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
493   if (!vsock)
494     return -ENOTSOCK;
495
496   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
497     return -EINVAL;
498
499   if (__iov == 0 || __iovcnt == 0 || __iovcnt > IOV_MAX)
500     return -EINVAL;
501
502   /* Sanity check */
503   for (int i = 0; i < __iovcnt; ++i)
504     {
505       if (SSIZE_MAX - len < __iov[i].iov_len)
506         return -EINVAL;
507       len += __iov[i].iov_len;
508     }
509
510   rv = vcom_fcntl (__fd, F_GETFL, 0);
511   if (rv < 0)
512     {
513       return rv;
514     }
515
516   /* is blocking */
517   if (!(rv & O_NONBLOCK))
518     {
519       do
520         {
521           for (int i = 0; i < __iovcnt; ++i)
522             {
523               rv = vppcom_session_read (vsock->sid, __iov[i].iov_base,
524                                         __iov[i].iov_len);
525               if (rv < 0)
526                 break;
527               else
528                 {
529                   total += rv;
530                   if (rv < __iov[i].iov_len)
531                     /* Read less than buffer provided, no point to continue */
532                     break;
533                 }
534             }
535         }
536       while ((rv == -EAGAIN || rv == -EWOULDBLOCK) && total == 0);
537       return total;
538     }
539
540   /* is non blocking */
541   for (int i = 0; i < __iovcnt; ++i)
542     {
543       rv = vppcom_session_read (vsock->sid, __iov[i].iov_base,
544                                 __iov[i].iov_len);
545       if (rv < 0)
546         {
547           if (total > 0)
548             break;
549           else
550             {
551               errno = rv;
552               return rv;
553             }
554         }
555       else
556         {
557           total += rv;
558           if (rv < __iov[i].iov_len)
559             /* Read less than buffer provided, no point to continue */
560             break;
561         }
562     }
563   return total;
564 }
565
566 ssize_t
567 vcom_socket_write (int __fd, const void *__buf, size_t __n)
568 {
569   int rv = -1;
570   vcom_socket_main_t *vsm = &vcom_socket_main;
571   uword *p;
572   vcom_socket_t *vsock;
573
574   p = hash_get (vsm->sockidx_by_fd, __fd);
575   if (!p)
576     return -EBADF;
577
578   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
579   if (!vsock)
580     return -ENOTSOCK;
581
582   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
583     return -EINVAL;
584
585   if (!__buf || __n < 0)
586     {
587       return -EINVAL;
588     }
589
590   rv = vppcom_session_write (vsock->sid, (void *) __buf, __n);
591   return rv;
592 }
593
594 ssize_t
595 vcom_socket_writev (int __fd, const struct iovec * __iov, int __iovcnt)
596 {
597   int rv = -1;
598   ssize_t total = 0;
599   vcom_socket_main_t *vsm = &vcom_socket_main;
600   uword *p;
601   vcom_socket_t *vsock;
602
603   p = hash_get (vsm->sockidx_by_fd, __fd);
604   if (!p)
605     return -EBADF;
606
607   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
608   if (!vsock)
609     return -ENOTSOCK;
610
611   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
612     return -EINVAL;
613
614   if (__iov == 0 || __iovcnt == 0 || __iovcnt > IOV_MAX)
615     return -EINVAL;
616
617   for (int i = 0; i < __iovcnt; ++i)
618     {
619       rv = vppcom_session_write (vsock->sid, __iov[i].iov_base,
620                                  __iov[i].iov_len);
621       if (rv < 0)
622         {
623           if (total > 0)
624             break;
625           else
626             return rv;
627         }
628       else
629         total += rv;
630     }
631   return total;
632 }
633
634 /*
635  * RETURN:  0 - invalid cmd
636  *          1 - cmd not handled by vcom and vppcom
637  *          2 - cmd handled by vcom socket resource
638  *          3 - cmd handled by vppcom
639  * */
640 /* TBD: incomplete list of cmd */
641 static int
642 vcom_socket_check_fcntl_cmd (int __cmd)
643 {
644   switch (__cmd)
645     {
646       /*cmd not handled by vcom and vppcom */
647       /* Fallthrough */
648     case F_DUPFD:
649     case F_DUPFD_CLOEXEC:
650       return 1;
651
652       /* cmd handled by vcom socket resource */
653       /* Fallthrough */
654     case F_GETFD:
655     case F_SETFD:
656     case F_GETFL:
657     case F_SETFL:
658     case F_GETLK:
659     case F_SETLK:
660     case F_SETLKW:
661     case F_GETOWN:
662     case F_SETOWN:
663       return 2;
664
665 #if 0
666       /* cmd handled by vppcom */
667     case F_XXXXX:
668       return 3;
669 #endif
670       /* invalid cmd */
671     default:
672       return 0;
673     }
674   return 0;
675 }
676
677 /* TBD: move it to vppcom */
678 static int
679 vppcom_session_fcntl_va (int __fd, int __cmd, va_list __ap)
680 {
681   int rv;
682
683   rv = -EINVAL;
684
685   return rv;
686 }
687
688 int
689 vcom_socket_fcntl_va (int __fd, int __cmd, va_list __ap)
690 {
691   int rv = -EBADF;
692   vcom_socket_main_t *vsm = &vcom_socket_main;
693   uword *p;
694   vcom_socket_t *vsock;
695
696   p = hash_get (vsm->sockidx_by_fd, __fd);
697   if (!p)
698     return -EBADF;
699
700   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
701   if (!vsock)
702     return -ENOTSOCK;
703
704   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
705     return -EINVAL;
706
707   switch (vcom_socket_check_fcntl_cmd (__cmd))
708     {
709       /* invalid cmd */
710     case 0:
711       rv = -EBADF;
712       break;
713       /*cmd not handled by vcom and vppcom */
714     case 1:
715       rv = -EBADF;
716       break;
717       /* cmd handled by vcom socket resource */
718     case 2:
719       rv = libc_vfcntl (vsock->fd, __cmd, __ap);
720       break;
721       /* cmd handled by vppcom */
722     case 3:
723       rv = vppcom_session_fcntl_va (vsock->sid, __cmd, __ap);
724       break;
725
726     default:
727       rv = -EINVAL;
728       break;
729     }
730
731   return rv;
732 }
733
734 static inline int
735 vcom_socket_fds_2_sid_fds (
736                             /* dest */
737                             int *vcom_nsid_fds,
738                             fd_set * __restrict vcom_rd_sid_fds,
739                             fd_set * __restrict vcom_wr_sid_fds,
740                             fd_set * __restrict vcom_ex_sid_fds,
741                             /* src */
742                             int vcom_nfds,
743                             fd_set * __restrict vcom_readfds,
744                             fd_set * __restrict vcom_writefds,
745                             fd_set * __restrict vcom_exceptfds)
746 {
747   int rv = 0;
748   int fd;
749   int sid;
750   /* invalid max_sid is -1 */
751   int max_sid = -1;
752   int nsid = 0;
753
754   /*
755    *  set sid in sid sets corresponding to fd's in fd sets
756    *  compute nsid and vcom_nsid_fds from sid sets
757    */
758
759   for (fd = 0; fd < vcom_nfds; fd++)
760     {
761       /*
762        * F fd set, src
763        * S sid set, dest
764        */
765 #define _(S,F)                              \
766       if ((F) && (S) && FD_ISSET (fd, (F))) \
767         {                                   \
768           sid = vcom_socket_get_sid (fd);   \
769           if (sid != INVALID_SESSION_ID)    \
770             {                               \
771               FD_SET (sid, (S));            \
772               if (sid > max_sid)            \
773                 {                           \
774                   max_sid = sid;            \
775                 }                           \
776               ++nsid;                       \
777             }                               \
778           else                              \
779             {                               \
780               rv = -EBADFD;                 \
781               goto done;                    \
782             }                               \
783         }
784
785
786       _(vcom_rd_sid_fds, vcom_readfds);
787       _(vcom_wr_sid_fds, vcom_writefds);
788       _(vcom_ex_sid_fds, vcom_exceptfds);
789 #undef _
790     }
791
792   *vcom_nsid_fds = max_sid != -1 ? max_sid + 1 : 0;
793   rv = nsid;
794
795 done:
796   return rv;
797 }
798
799 /*
800  * PRE: 00. sid sets were derived from fd sets
801  *      01. sid sets were updated with sids that actually changed
802  *          status
803  *      02. fd sets still has watched fds
804  *
805  * This function will modify in place fd sets to indicate which fd's
806  * actually changed status(inferred from sid sets)
807  */
808 static inline int
809 vcom_socket_sid_fds_2_fds (
810                             /* dest */
811                             int *new_vcom_nfds,
812                             int vcom_nfds,
813                             fd_set * __restrict vcom_readfds,
814                             fd_set * __restrict vcom_writefds,
815                             fd_set * __restrict vcom_exceptfds,
816                             /* src */
817                             int vcom_nsid_fds,
818                             fd_set * __restrict vcom_rd_sid_fds,
819                             fd_set * __restrict vcom_wr_sid_fds,
820                             fd_set * __restrict vcom_ex_sid_fds)
821 {
822   int rv = 0;
823   int fd;
824   int sid;
825   /* invalid max_fd is -1 */
826   int max_fd = -1;
827   int nfd = 0;
828
829
830   /*
831    *  modify in place fd sets to indicate which fd's
832    * actually changed status(inferred from sid sets)
833    */
834   for (fd = 0; fd < vcom_nfds; fd++)
835     {
836       /*
837        * F fd set, dest
838        * S sid set, src
839        */
840 #define _(S,F)                              \
841       if ((F) && (S) && FD_ISSET (fd, (F))) \
842         {                                   \
843           sid = vcom_socket_get_sid (fd);   \
844           if (sid != INVALID_SESSION_ID)    \
845             {                               \
846               if (!FD_ISSET (sid, (S)))     \
847                 {                           \
848                    FD_CLR(fd, (F));         \
849                 }                           \
850             }                               \
851           else                              \
852             {                               \
853               rv = -EBADFD;                 \
854               goto done;                    \
855             }                               \
856         }
857
858
859       _(vcom_rd_sid_fds, vcom_readfds);
860       _(vcom_wr_sid_fds, vcom_writefds);
861       _(vcom_ex_sid_fds, vcom_exceptfds);
862 #undef _
863     }
864
865   /*
866    *  compute nfd and new_vcom_nfds from fd sets
867    */
868   for (fd = 0; fd < vcom_nfds; fd++)
869     {
870
871 #define _(F)                                \
872       if ((F) && FD_ISSET (fd, (F)))        \
873         {                                   \
874           if (fd > max_fd)                  \
875             {                               \
876               max_fd = fd;                  \
877             }                               \
878           ++nfd;                            \
879         }
880
881
882       _(vcom_readfds);
883       _(vcom_writefds);
884       _(vcom_exceptfds);
885 #undef _
886
887     }
888
889   *new_vcom_nfds = max_fd != -1 ? max_fd + 1 : 0;
890   rv = nfd;
891
892 done:
893   return rv;
894 }
895
896 /*
897  * PRE:
898  * vom_socket_select is always called with
899  * timeout->tv_sec and timeout->tv_usec set to zero.
900  * hence vppcom_select return immediately.
901  */
902 /*
903  * TBD: do{body;} while(timeout conditional); timeout loop
904  */
905 int
906 vcom_socket_select (int vcom_nfds, fd_set * __restrict vcom_readfds,
907                     fd_set * __restrict vcom_writefds,
908                     fd_set * __restrict vcom_exceptfds,
909                     struct timeval *__restrict timeout)
910 {
911   int rv = -EBADF;
912   pid_t pid = getpid ();
913
914   int new_vcom_nfds = 0;
915   int new_vcom_nfd = 0;
916
917   /* vcom sid fds */
918   fd_set vcom_rd_sid_fds;
919   fd_set vcom_wr_sid_fds;
920   fd_set vcom_ex_sid_fds;
921   unsigned long vcom_nsid_fds = 0;
922   int vcom_nsid = 0;
923
924   /* in seconds eg. 3.123456789 seconds */
925   double time_to_wait = (double) 0;
926
927   /* validate inputs */
928   if (vcom_nfds < 0)
929     {
930       return -EINVAL;
931     }
932
933   /* convert timeval timeout to double time_to_wait */
934   if (timeout)
935     {
936       if (timeout->tv_sec == 0 && timeout->tv_usec == 0)
937         {
938           /* polling: vppcom_select returns immediately */
939           time_to_wait = (double) 0;
940         }
941       else
942         {
943           /*TBD:  use timeval api */
944           time_to_wait = (double) timeout->tv_sec +
945             (double) timeout->tv_usec / (double) 1000000 +
946             (double) (timeout->tv_usec % 1000000) / (double) 1000000;
947         }
948     }
949   else
950     {
951       /*
952        * no timeout: vppcom_select can block indefinitely
953        * waiting for a file descriptor to become ready
954        * */
955       /* set to a phantom value */
956       time_to_wait = ~0;
957     }
958
959   /* zero the sid_sets */
960   /*
961    * F fd set
962    * S sid set
963    */
964 #define _(S,F)                          \
965   if ((F))                              \
966     {                                   \
967       FD_ZERO ((S));                    \
968     }
969
970
971   _(&vcom_rd_sid_fds, vcom_readfds);
972   _(&vcom_wr_sid_fds, vcom_writefds);
973   _(&vcom_ex_sid_fds, vcom_exceptfds);
974 #undef _
975
976   /* populate read, write and except sid_sets */
977   vcom_nsid = vcom_socket_fds_2_sid_fds (
978                                           /* dest */
979                                           vcom_readfds || vcom_writefds
980                                           || vcom_exceptfds ? (int *)
981                                           &vcom_nsid_fds : NULL,
982                                           vcom_readfds ? &vcom_rd_sid_fds :
983                                           NULL,
984                                           vcom_writefds ? &vcom_wr_sid_fds :
985                                           NULL,
986                                           vcom_exceptfds ? &vcom_ex_sid_fds :
987                                           NULL,
988                                           /* src */
989                                           vcom_nfds,
990                                           vcom_readfds,
991                                           vcom_writefds, vcom_exceptfds);
992   if (vcom_nsid < 0)
993     {
994       return vcom_nsid;
995     }
996   if (vcom_nsid_fds < 0)
997     {
998       return -EINVAL;
999     }
1000
1001   rv = vppcom_select (vcom_nsid_fds,
1002                       vcom_readfds ? (unsigned long *) &vcom_rd_sid_fds :
1003                       NULL,
1004                       vcom_writefds ? (unsigned long *) &vcom_wr_sid_fds :
1005                       NULL,
1006                       vcom_exceptfds ? (unsigned long *) &vcom_ex_sid_fds :
1007                       NULL, time_to_wait);
1008   if (VCOM_DEBUG > 0)
1009     fprintf (stderr, "[%d] vppcom_select: "
1010              "'%04d'='%04d'\n", pid, rv, (int) vcom_nsid_fds);
1011
1012   /* check if any file descriptors changed status */
1013   if (rv > 0)
1014     {
1015       /*
1016        * on exit, sets are modified in place to indicate which
1017        * file descriptors actually changed status
1018        * */
1019
1020       /*
1021        * comply with pre-condition
1022        * do not clear vcom fd sets befor calling
1023        * vcom_socket_sid_fds_2_fds
1024        */
1025       new_vcom_nfd = vcom_socket_sid_fds_2_fds (
1026                                                  /* dest */
1027                                                  &new_vcom_nfds,
1028                                                  vcom_nfds,
1029                                                  vcom_readfds,
1030                                                  vcom_writefds,
1031                                                  vcom_exceptfds,
1032                                                  /* src */
1033                                                  vcom_nsid_fds,
1034                                                  vcom_readfds ?
1035                                                  &vcom_rd_sid_fds : NULL,
1036                                                  vcom_writefds ?
1037                                                  &vcom_wr_sid_fds : NULL,
1038                                                  vcom_exceptfds ?
1039                                                  &vcom_ex_sid_fds : NULL);
1040       if (new_vcom_nfd < 0)
1041         {
1042           return new_vcom_nfd;
1043         }
1044       if (new_vcom_nfds < 0)
1045         {
1046           return -EINVAL;
1047         }
1048       rv = new_vcom_nfd;
1049     }
1050   return rv;
1051 }
1052
1053
1054 int
1055 vcom_socket_socket (int __domain, int __type, int __protocol)
1056 {
1057   int rv = -1;
1058   vcom_socket_main_t *vsm = &vcom_socket_main;
1059   vcom_socket_t *vsock;
1060
1061   i32 fd;
1062   i32 sid;
1063   i32 sockidx;
1064   u8 is_nonblocking = __type & SOCK_NONBLOCK ? 1 : 0;
1065   int type = __type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
1066
1067   fd = vcom_socket_open_socket (__domain, __type, __protocol);
1068   if (fd < 0)
1069     {
1070       rv = fd;
1071       goto out;
1072     }
1073
1074   sid = vppcom_session_create (VPPCOM_VRF_DEFAULT,
1075                                (type == SOCK_DGRAM) ?
1076                                VPPCOM_PROTO_UDP : VPPCOM_PROTO_TCP,
1077                                is_nonblocking);
1078   if (sid < 0)
1079     {
1080       rv = sid;
1081       goto out_close_socket;
1082     }
1083
1084   pool_get (vsm->vsockets, vsock);
1085   vsocket_init (vsock);
1086
1087   sockidx = vsock - vsm->vsockets;
1088   hash_set (vsm->sockidx_by_fd, fd, sockidx);
1089
1090   vsocket_set (vsock, fd, sid, SOCKET_TYPE_VPPCOM_BOUND);
1091   return fd;
1092
1093 out_close_socket:
1094   vcom_socket_close_socket (fd);
1095 out:
1096   return rv;
1097 }
1098
1099 int
1100 vcom_socket_socketpair (int __domain, int __type, int __protocol,
1101                         int __fds[2])
1102 {
1103 /* TBD: */
1104   return 0;
1105 }
1106
1107 int
1108 vcom_socket_bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
1109 {
1110   int rv = -1;
1111   vcom_socket_main_t *vsm = &vcom_socket_main;
1112   uword *p;
1113   vcom_socket_t *vsock;
1114
1115   vppcom_endpt_t ep;
1116
1117   p = hash_get (vsm->sockidx_by_fd, __fd);
1118   if (!p)
1119     return -EBADF;
1120
1121   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1122   if (!vsock)
1123     return -ENOTSOCK;
1124
1125   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1126     return -EINVAL;
1127
1128   if (!__addr)
1129     {
1130       return -EINVAL;
1131     }
1132
1133   ep.vrf = VPPCOM_VRF_DEFAULT;
1134   switch (__addr->sa_family)
1135     {
1136     case AF_INET:
1137       if (__len != sizeof (struct sockaddr_in))
1138         {
1139           return -EINVAL;
1140         }
1141       ep.is_ip4 = VPPCOM_IS_IP4;
1142       ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr;
1143       ep.port = (u16) ((const struct sockaddr_in *) __addr)->sin_port;
1144       break;
1145
1146     case AF_INET6:
1147       if (__len != sizeof (struct sockaddr_in6))
1148         {
1149           return -EINVAL;
1150         }
1151       ep.is_ip4 = VPPCOM_IS_IP6;
1152       ep.ip = (u8 *) & ((const struct sockaddr_in6 *) __addr)->sin6_addr;
1153       ep.port = (u16) ((const struct sockaddr_in6 *) __addr)->sin6_port;
1154       break;
1155
1156     default:
1157       return -1;
1158       break;
1159     }
1160
1161   rv = vppcom_session_bind (vsock->sid, &ep);
1162   /* TBD: remove libc_bind code snippet
1163    * once vppcom implements vppcom_session_getsockname */
1164   if (rv == 0)
1165     {
1166       rv = libc_bind (__fd, __addr, __len);
1167       if (rv != 0)
1168         {
1169           rv = -errno;
1170         }
1171     }
1172   return rv;
1173 }
1174
1175 int
1176 vppcom_session_getsockname (int sid, vppcom_endpt_t * ep)
1177 {
1178   /* TBD: move it to vppcom */
1179   return 0;
1180 }
1181
1182 int
1183 vcom_socket_getsockname (int __fd, __SOCKADDR_ARG __addr,
1184                          socklen_t * __restrict __len)
1185 {
1186   int rv = -1;
1187   vcom_socket_main_t *vsm = &vcom_socket_main;
1188   uword *p;
1189   vcom_socket_t *vsock;
1190
1191
1192   p = hash_get (vsm->sockidx_by_fd, __fd);
1193   if (!p)
1194     return -EBADF;
1195
1196   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1197   if (!vsock)
1198     return -ENOTSOCK;
1199
1200   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1201     return -EINVAL;
1202
1203   if (!__addr || !__len)
1204     return -EFAULT;
1205
1206   if (*__len < 0)
1207     {
1208       return -EINVAL;
1209     }
1210
1211   /* TBD: remove libc_getsockname code snippet
1212    * once vppcom implements vppcom_session_getsockname */
1213   rv = libc_getsockname (__fd, __addr, __len);
1214   if (rv != 0)
1215     {
1216       rv = -errno;
1217       return rv;
1218     }
1219
1220   /* TBD: use the below code snippet when vppcom
1221    * implements vppcom_session_getsockname */
1222 #if 0
1223   vppcom_endpt_t ep;
1224   ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr;
1225   rv = vppcom_session_getsockname (vsock->sid, &ep);
1226   if (rv == 0)
1227     {
1228       if (ep.vrf == VPPCOM_VRF_DEFAULT)
1229         {
1230           __addr->sa_family = ep.is_ip4 == VPPCOM_IS_IP4 ? AF_INET : AF_INET6;
1231           switch (__addr->sa_family)
1232             {
1233             case AF_INET:
1234               ((struct sockaddr_in *) __addr)->sin_port = ep.port;
1235               *__len = sizeof (struct sockaddr_in);
1236               break;
1237
1238             case AF_INET6:
1239               ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port;
1240               *__len = sizeof (struct sockaddr_in6);
1241               break;
1242
1243             default:
1244               break;
1245             }
1246         }
1247     }
1248 #endif
1249
1250   return rv;
1251 }
1252
1253 int
1254 vcom_socket_connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
1255 {
1256   int rv = -1;
1257   vcom_socket_main_t *vsm = &vcom_socket_main;
1258   uword *p;
1259   vcom_socket_t *vsock;
1260
1261   vppcom_endpt_t ep;
1262
1263   p = hash_get (vsm->sockidx_by_fd, __fd);
1264   if (p)
1265     {
1266       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1267
1268       ep.vrf = VPPCOM_VRF_DEFAULT;
1269       switch (__addr->sa_family)
1270         {
1271         case AF_INET:
1272           ep.is_ip4 = VPPCOM_IS_IP4;
1273           ep.ip =
1274             (uint8_t *) & ((const struct sockaddr_in *) __addr)->sin_addr;
1275           ep.port =
1276             (uint16_t) ((const struct sockaddr_in *) __addr)->sin_port;
1277           break;
1278
1279         case AF_INET6:
1280           ep.is_ip4 = VPPCOM_IS_IP6;
1281           ep.ip =
1282             (uint8_t *) & ((const struct sockaddr_in6 *) __addr)->sin6_addr;
1283           ep.port =
1284             (uint16_t) ((const struct sockaddr_in6 *) __addr)->sin6_port;
1285           break;
1286
1287         default:
1288           return -1;
1289           break;
1290         }
1291
1292       rv = vppcom_session_connect (vsock->sid, &ep);
1293     }
1294   return rv;
1295 }
1296
1297 int
1298 vppcom_session_getpeername (int sid, vppcom_endpt_t * ep)
1299 {
1300   /* TBD: move it to vppcom */
1301   return 0;
1302 }
1303
1304 int
1305 vcom_socket_getpeername (int __fd, __SOCKADDR_ARG __addr,
1306                          socklen_t * __restrict __len)
1307 {
1308   int rv = -1;
1309   vcom_socket_main_t *vsm = &vcom_socket_main;
1310   uword *p;
1311   vcom_socket_t *vsock;
1312
1313
1314   p = hash_get (vsm->sockidx_by_fd, __fd);
1315   if (!p)
1316     return -EBADF;
1317
1318   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1319   if (!vsock)
1320     return -ENOTSOCK;
1321
1322   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1323     return -EINVAL;
1324
1325   if (!__addr || !__len)
1326     return -EFAULT;
1327
1328   if (*__len < 0)
1329     {
1330       return -EINVAL;
1331     }
1332
1333   /* DAW: hack to allow iperf3 to be happy w/ getpeername output */
1334   {
1335     uint8_t *a;
1336     ((struct sockaddr_in *) __addr)->sin_family = AF_INET;
1337     ((struct sockaddr_in *) __addr)->sin_port = 0x1000;
1338     a = (uint8_t *) & ((struct sockaddr_in *) __addr)->sin_addr;
1339     a[0] = 0x7f;
1340     a[1] = 0x00;
1341     a[2] = 0x00;
1342     a[3] = 0x01;
1343     *__len = sizeof (struct sockaddr_in);
1344     return 0;
1345   }
1346
1347   /* TBD: remove libc_getpeername code snippet
1348    * once vppcom implements vppcom_session_getpeername */
1349   rv = libc_getpeername (__fd, __addr, __len);
1350   if (rv != 0)
1351     {
1352       rv = -errno;
1353       return rv;
1354     }
1355
1356   /* TBD: use the below code snippet when vppcom
1357    * implements vppcom_session_getpeername */
1358 #if 0
1359   vppcom_endpt_t ep;
1360   ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr;
1361   rv = vppcom_session_getpeername (vsock->sid, &ep);
1362   if (rv == 0)
1363     {
1364       if (ep.vrf == VPPCOM_VRF_DEFAULT)
1365         {
1366           __addr->sa_family = ep.is_ip4 == VPPCOM_IS_IP4 ? AF_INET : AF_INET6;
1367           switch (__addr->sa_family)
1368             {
1369             case AF_INET:
1370               ((struct sockaddr_in *) __addr)->sin_port = ep.port;
1371               *__len = sizeof (struct sockaddr_in);
1372               break;
1373
1374             case AF_INET6:
1375               ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port;
1376               *__len = sizeof (struct sockaddr_in6);
1377               break;
1378
1379             default:
1380               break;
1381             }
1382         }
1383     }
1384 #endif
1385
1386   return rv;
1387 }
1388
1389 ssize_t
1390 vcom_socket_send (int __fd, const void *__buf, size_t __n, int __flags)
1391 {
1392   return vcom_socket_sendto (__fd, __buf, __n, __flags, NULL, 0);
1393 }
1394
1395 ssize_t
1396 vcom_socket_recv (int __fd, void *__buf, size_t __n, int __flags)
1397 {
1398   int rv = -1;
1399   rv = vcom_socket_recvfrom (__fd, __buf, __n, __flags, NULL, 0);
1400   return rv;
1401 }
1402
1403 /*
1404  * RETURN   1 if __fd is (SOCK_STREAM, SOCK_SEQPACKET),
1405  * 0 otherwise
1406  * */
1407 int
1408 vcom_socket_is_connection_mode_socket (int __fd)
1409 {
1410   int rv = -1;
1411   /* TBD define new vppcom api */
1412   vcom_socket_main_t *vsm = &vcom_socket_main;
1413   uword *p;
1414   vcom_socket_t *vsock;
1415
1416   int type;
1417   socklen_t optlen;
1418
1419   p = hash_get (vsm->sockidx_by_fd, __fd);
1420
1421   if (p)
1422     {
1423       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1424       if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND)
1425         {
1426           optlen = sizeof (type);
1427           rv = libc_getsockopt (__fd, SOL_SOCKET, SO_TYPE, &type, &optlen);
1428           if (rv != 0)
1429             {
1430               return 0;
1431             }
1432           /* get socket type */
1433           switch (type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
1434             {
1435             case SOCK_STREAM:
1436             case SOCK_SEQPACKET:
1437               return 1;
1438               break;
1439
1440             default:
1441               return 0;
1442               break;
1443             }
1444         }
1445     }
1446   return 0;
1447 }
1448
1449 ssize_t
1450 vvppcom_session_sendto (int __sid, const void *__buf, size_t __n,
1451                         int __flags, __CONST_SOCKADDR_ARG __addr,
1452                         socklen_t __addr_len)
1453 {
1454   int rv = -1;
1455   /* TBD add new vpp api  */
1456   /* TBD add flags parameter */
1457   rv = vppcom_session_write (__sid, (void *) __buf, (int) __n);
1458   return rv;
1459 }
1460
1461 ssize_t
1462 vcom_socket_sendto (int __fd, const void *__buf, size_t __n,
1463                     int __flags, __CONST_SOCKADDR_ARG __addr,
1464                     socklen_t __addr_len)
1465 {
1466   int rv = -1;
1467   vcom_socket_main_t *vsm = &vcom_socket_main;
1468   uword *p;
1469   vcom_socket_t *vsock;
1470
1471   p = hash_get (vsm->sockidx_by_fd, __fd);
1472   if (!p)
1473     return -EBADF;
1474
1475   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1476   if (!vsock)
1477     return -ENOTSOCK;
1478
1479   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1480     return -EINVAL;
1481
1482   if (!__buf || __n < 0)
1483     {
1484       return -EINVAL;
1485     }
1486
1487   if (vcom_socket_is_connection_mode_socket (__fd))
1488     {
1489       /* ignore __addr and _addr_len */
1490       /* and EISCONN may be returned when they are not NULL and 0 */
1491       if ((__addr != NULL) || (__addr_len != 0))
1492         {
1493           return -EISCONN;
1494         }
1495     }
1496   else
1497     {
1498       if (!__addr || __addr_len < 0)
1499         {
1500           return -EDESTADDRREQ;
1501         }
1502       /* not a vppcom supported address family */
1503       if ((__addr->sa_family != AF_INET) || (__addr->sa_family != AF_INET6))
1504         {
1505           return -EINVAL;
1506         }
1507     }
1508
1509   rv = vvppcom_session_sendto (vsock->sid, (void *) __buf, (int) __n,
1510                                __flags, __addr, __addr_len);
1511   return rv;
1512 }
1513
1514 /* TBD: move it to vppcom */
1515 static ssize_t
1516 vppcom_session_recvfrom (int __sid, void *__restrict __buf, size_t __n,
1517                          int __flags, __SOCKADDR_ARG __addr,
1518                          socklen_t * __restrict __addr_len)
1519 {
1520   int rv = -1;
1521
1522   /* TBD add flags parameter */
1523   rv = vppcom_session_read (__sid, __buf, __n);
1524   return rv;
1525 }
1526
1527 ssize_t
1528 vcom_socket_recvfrom (int __fd, void *__restrict __buf, size_t __n,
1529                       int __flags, __SOCKADDR_ARG __addr,
1530                       socklen_t * __restrict __addr_len)
1531 {
1532   int rv = -1;
1533   vcom_socket_main_t *vsm = &vcom_socket_main;
1534   uword *p;
1535   vcom_socket_t *vsock;
1536
1537   p = hash_get (vsm->sockidx_by_fd, __fd);
1538   if (!p)
1539     return -EBADF;
1540
1541   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1542   if (!vsock)
1543     return -ENOTSOCK;
1544
1545   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1546     return -EINVAL;
1547
1548   if (!__buf || __n < 0)
1549     {
1550       return -EINVAL;
1551     }
1552
1553   if (__addr || __addr_len < 0)
1554     {
1555       return -EINVAL;
1556     }
1557
1558   rv = vppcom_session_recvfrom (vsock->sid, __buf, __n,
1559                                 __flags, __addr, __addr_len);
1560   return rv;
1561 }
1562
1563 /* TBD: move it to vppcom */
1564 static ssize_t
1565 vppcom_sendmsg (int __sid, const struct msghdr *__message, int __flags)
1566 {
1567   int rv = -1;
1568   /* rv = vppcom_session_write (__sid, (void *) __message->__buf,
1569      (int)__n); */
1570   return rv;
1571 }
1572
1573 ssize_t
1574 vcom_socket_sendmsg (int __fd, const struct msghdr * __message, int __flags)
1575 {
1576   int rv = -1;
1577   vcom_socket_main_t *vsm = &vcom_socket_main;
1578   uword *p;
1579   vcom_socket_t *vsock;
1580
1581   p = hash_get (vsm->sockidx_by_fd, __fd);
1582   if (!p)
1583     return -EBADF;
1584
1585   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1586   if (!vsock)
1587     return -ENOTSOCK;
1588
1589   if (vcom_socket_is_connection_mode_socket (__fd))
1590     {
1591       /* ignore __addr and _addr_len */
1592       /* and EISCONN may be returned when they are not NULL and 0 */
1593       if ((__message->msg_name != NULL) || (__message->msg_namelen != 0))
1594         {
1595           return -EISCONN;
1596         }
1597     }
1598   else
1599     {
1600       /* TBD: validate __message->msg_name and __message->msg_namelen
1601        * and return -EINVAL on validation error
1602        * */
1603       ;
1604     }
1605
1606   rv = vppcom_sendmsg (vsock->sid, __message, __flags);
1607
1608   return rv;
1609 }
1610
1611 #ifdef __USE_GNU
1612 int
1613 vcom_socket_sendmmsg (int __fd, struct mmsghdr *__vmessages,
1614                       unsigned int __vlen, int __flags)
1615 {
1616
1617   /* TBD: define a new vppcom api */
1618   return 0;
1619 }
1620 #endif
1621
1622 /* TBD: move it to vppcom */
1623 static ssize_t
1624 vppcom_recvmsg (int __sid, struct msghdr *__message, int __flags)
1625 {
1626   int rv = -1;
1627   /* rv = vppcom_session_read (__sid, (void *) __message->__buf,
1628      (int)__n); */
1629   rv = -EOPNOTSUPP;
1630   return rv;
1631 }
1632
1633 ssize_t
1634 vcom_socket_recvmsg (int __fd, struct msghdr * __message, int __flags)
1635 {
1636   int rv = -1;
1637   vcom_socket_main_t *vsm = &vcom_socket_main;
1638   uword *p;
1639   vcom_socket_t *vsock;
1640
1641   p = hash_get (vsm->sockidx_by_fd, __fd);
1642   if (!p)
1643     return -EBADF;
1644
1645   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1646   if (!vsock)
1647     return -ENOTSOCK;
1648
1649   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1650     return -EINVAL;
1651
1652   if (!__message)
1653     {
1654       return -EINVAL;
1655     }
1656
1657   /* validate __flags */
1658
1659   rv = vppcom_recvmsg (vsock->sid, __message, __flags);
1660   return rv;
1661 }
1662
1663 #ifdef __USE_GNU
1664 int
1665 vcom_socket_recvmmsg (int __fd, struct mmsghdr *__vmessages,
1666                       unsigned int __vlen, int __flags,
1667                       struct timespec *__tmo)
1668 {
1669   /* TBD: define a new vppcom api */
1670   return 0;
1671 }
1672 #endif
1673
1674 /* TBD: move it to vppcom */
1675 static int
1676 vppcom_getsockopt (int __sid, int __level, int __optname,
1677                    void *__restrict __optval, socklen_t * __restrict __optlen)
1678 {
1679   /* 1. for socket level options that are NOT socket attributes
1680    *    and that has corresponding vpp options get from vppcom */
1681 #if 0
1682   return 0;
1683 #endif
1684
1685   /* 2. unhandled options */
1686   return -ENOPROTOOPT;
1687 }
1688
1689 int
1690 vcom_socket_getsockopt (int __fd, int __level, int __optname,
1691                         void *__restrict __optval,
1692                         socklen_t * __restrict __optlen)
1693 {
1694   int rv = -1;
1695   vcom_socket_main_t *vsm = &vcom_socket_main;
1696   uword *p;
1697   vcom_socket_t *vsock;
1698
1699   p = hash_get (vsm->sockidx_by_fd, __fd);
1700   if (!p)
1701     return -EBADF;
1702
1703   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1704   if (!vsock)
1705     return -ENOTSOCK;
1706
1707   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1708     return -EINVAL;
1709
1710   if (!__optval && !__optlen)
1711     return -EFAULT;
1712
1713   if (*__optlen < 0)
1714     {
1715       return -EINVAL;
1716     }
1717
1718   switch (__level)
1719     {
1720       /* handle options at socket level */
1721     case SOL_SOCKET:
1722       switch (__optname)
1723         {
1724 /*
1725  *  1. for socket level options that are socket attributes,
1726  *     get from libc_getsockopt.
1727  *  2. for socket level options that are NOT socket
1728  *     attributes and that has corresponding vpp options
1729  *     get from vppcom.
1730  *  3. for socket level options unimplemented
1731  *     return -ENOPROTOOPT */
1732         case SO_DEBUG:
1733         case SO_DONTROUTE:
1734         case SO_BROADCAST:
1735         case SO_SNDBUF:
1736         case SO_RCVBUF:
1737         case SO_REUSEADDR:
1738         case SO_REUSEPORT:
1739         case SO_KEEPALIVE:
1740         case SO_TYPE:
1741         case SO_PROTOCOL:
1742         case SO_DOMAIN:
1743         case SO_ERROR:
1744         case SO_OOBINLINE:
1745         case SO_NO_CHECK:
1746         case SO_PRIORITY:
1747         case SO_LINGER:
1748         case SO_BSDCOMPAT:
1749         case SO_TIMESTAMP:
1750         case SO_TIMESTAMPNS:
1751         case SO_TIMESTAMPING:
1752         case SO_RCVTIMEO:
1753         case SO_SNDTIMEO:
1754         case SO_RCVLOWAT:
1755         case SO_SNDLOWAT:
1756         case SO_PASSCRED:
1757         case SO_PEERCRED:
1758         case SO_PEERNAME:
1759         case SO_ACCEPTCONN:
1760         case SO_PASSSEC:
1761         case SO_PEERSEC:
1762         case SO_MARK:
1763         case SO_RXQ_OVFL:
1764         case SO_WIFI_STATUS:
1765         case SO_PEEK_OFF:
1766         case SO_NOFCS:
1767         case SO_BINDTODEVICE:
1768         case SO_GET_FILTER:
1769         case SO_LOCK_FILTER:
1770         case SO_BPF_EXTENSIONS:
1771         case SO_SELECT_ERR_QUEUE:
1772 #ifdef CONFIG_NET_RX_BUSY_POLL
1773         case SO_BUSY_POLL:
1774 #endif
1775         case SO_MAX_PACING_RATE:
1776         case SO_INCOMING_CPU:
1777           rv = libc_getsockopt (__fd, __level, __optname, __optval, __optlen);
1778           if (rv != 0)
1779             {
1780               rv = -errno;
1781               return rv;
1782             }
1783           break;
1784
1785         default:
1786           /* We implement the SO_SNDLOWAT etc to not be settable
1787            * (1003.1g 7).
1788            */
1789           return -ENOPROTOOPT;
1790         }
1791
1792       break;
1793
1794     default:
1795       /* 1. handle options that are NOT socket level options,
1796        *    but have corresponding vpp otions. */
1797       rv = vppcom_getsockopt (vsock->sid, __level, __optname,
1798                               __optval, __optlen);
1799
1800       return rv;
1801 #if 0
1802       /* 2. unhandled options */
1803       return -ENOPROTOOPT;
1804 #endif
1805     }
1806
1807   return rv;
1808 }
1809
1810 /* TBD: move it to vppcom */
1811 int
1812 vppcom_setsockopt (int __fd, int __level, int __optname,
1813                    const void *__optval, socklen_t __optlen)
1814 {
1815   /* 1. for socket level options that are NOT socket attributes
1816    *    and that has corresponding vpp options set it from vppcom */
1817 #if 0
1818   return 0;
1819 #endif
1820
1821   /* 2. unhandled options */
1822   return -ENOPROTOOPT;
1823 }
1824
1825 int
1826 vcom_socket_setsockopt (int __fd, int __level, int __optname,
1827                         const void *__optval, socklen_t __optlen)
1828 {
1829   int rv = -1;
1830   vcom_socket_main_t *vsm = &vcom_socket_main;
1831   uword *p;
1832   vcom_socket_t *vsock;
1833
1834   p = hash_get (vsm->sockidx_by_fd, __fd);
1835   if (!p)
1836     return -EBADF;
1837
1838   vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1839   if (!vsock)
1840     return -ENOTSOCK;
1841
1842   if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
1843     return -EINVAL;
1844
1845   /*
1846    *      Options without arguments
1847    */
1848
1849   if (__optname == SO_BINDTODEVICE)
1850     {
1851       rv = libc_setsockopt (__fd, __level, __optname, __optval, __optlen);
1852       if (rv != 0)
1853         {
1854           rv = -errno;
1855         }
1856       return rv;
1857     }
1858
1859   if (!__optval)
1860     return -EFAULT;
1861
1862   if ((__optlen < 0) || (__optlen < sizeof (int)))
1863     return -EINVAL;
1864
1865   switch (__level)
1866     {
1867       /* handle options at socket level */
1868     case SOL_SOCKET:
1869       switch (__optname)
1870         {
1871           /*
1872            * 1. for socket level options that are socket attributes,
1873            *    set it from libc_getsockopt
1874            * 2. for socket level options that are NOT socket
1875            *    attributes and that has corresponding vpp options
1876            *    set it from vppcom
1877            * 3. for socket level options unimplemented
1878            *    return -ENOPROTOOPT */
1879         case SO_DEBUG:
1880         case SO_DONTROUTE:
1881         case SO_BROADCAST:
1882         case SO_SNDBUF:
1883         case SO_RCVBUF:
1884         case SO_REUSEADDR:
1885         case SO_REUSEPORT:
1886         case SO_KEEPALIVE:
1887         case SO_TYPE:
1888         case SO_PROTOCOL:
1889         case SO_DOMAIN:
1890         case SO_ERROR:
1891         case SO_OOBINLINE:
1892         case SO_NO_CHECK:
1893         case SO_PRIORITY:
1894         case SO_LINGER:
1895         case SO_BSDCOMPAT:
1896         case SO_TIMESTAMP:
1897         case SO_TIMESTAMPNS:
1898         case SO_TIMESTAMPING:
1899         case SO_RCVTIMEO:
1900         case SO_SNDTIMEO:
1901         case SO_RCVLOWAT:
1902         case SO_SNDLOWAT:
1903         case SO_PASSCRED:
1904         case SO_PEERCRED:
1905         case SO_PEERNAME:
1906         case SO_ACCEPTCONN:
1907         case SO_PASSSEC:
1908         case SO_PEERSEC:
1909         case SO_MARK:
1910         case SO_RXQ_OVFL:
1911         case SO_WIFI_STATUS:
1912         case SO_PEEK_OFF:
1913         case SO_NOFCS:
1914           /*
1915            * SO_BINDTODEVICE already handled as
1916            * "Options without arguments" */
1917           /* case SO_BINDTODEVICE: */
1918         case SO_GET_FILTER:
1919         case SO_LOCK_FILTER:
1920         case SO_BPF_EXTENSIONS:
1921         case SO_SELECT_ERR_QUEUE:
1922 #ifdef CONFIG_NET_RX_BUSY_POLL
1923         case SO_BUSY_POLL:
1924 #endif
1925         case SO_MAX_PACING_RATE:
1926         case SO_INCOMING_CPU:
1927           rv = libc_setsockopt (__fd, __level, __optname, __optval, __optlen);
1928           if (rv != 0)
1929             {
1930               rv = -errno;
1931               return rv;
1932             }
1933           break;
1934
1935         default:
1936           /* We implement the SO_SNDLOWAT etc to not be settable
1937            * (1003.1g 7).
1938            */
1939           return -ENOPROTOOPT;
1940         }
1941
1942       break;
1943
1944     default:
1945       /* 1. handle options that are NOT socket level options,
1946        *    but have corresponding vpp otions. */
1947       rv = vppcom_setsockopt (vsock->sid, __level, __optname,
1948                               __optval, __optlen);
1949       return rv;
1950 #if 0
1951       /* 2. unhandled options */
1952       return -ENOPROTOOPT;
1953 #endif
1954     }
1955
1956   return rv;
1957 }
1958
1959 int
1960 vcom_socket_listen (int __fd, int __n)
1961 {
1962   int rv = -1;
1963   vcom_socket_main_t *vsm = &vcom_socket_main;
1964   uword *p;
1965   vcom_socket_t *vsock;
1966
1967   p = hash_get (vsm->sockidx_by_fd, __fd);
1968   if (p)
1969     {
1970       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
1971
1972       /* TBD vppcom to accept __n parameter */
1973       rv = vppcom_session_listen (vsock->sid, __n);
1974     }
1975
1976   return rv;
1977 }
1978
1979 static int
1980 vcom_socket_connected_socket (int __fd, int __sid,
1981                               int *__domain,
1982                               int *__type, int *__protocol, int flags)
1983 {
1984   int rv = -1;
1985   vcom_socket_main_t *vsm = &vcom_socket_main;
1986   vcom_socket_t *vsock;
1987
1988   i32 fd;
1989   i32 sockidx;
1990
1991   socklen_t optlen;
1992
1993   optlen = sizeof (*__domain);
1994   rv = libc_getsockopt (__fd, SOL_SOCKET, SO_DOMAIN, __domain, &optlen);
1995   if (rv != 0)
1996     {
1997       rv = -errno;
1998       goto out;
1999     }
2000
2001   optlen = sizeof (*__type);
2002   rv = libc_getsockopt (__fd, SOL_SOCKET, SO_TYPE, __type, &optlen);
2003   if (rv != 0)
2004     {
2005       rv = -errno;
2006       goto out;
2007     }
2008
2009   optlen = sizeof (*__protocol);
2010   rv = libc_getsockopt (__fd, SOL_SOCKET, SO_PROTOCOL, __protocol, &optlen);
2011   if (rv != 0)
2012     {
2013       rv = -errno;
2014       goto out;
2015     }
2016
2017   fd = vcom_socket_open_socket (*__domain, *__type | flags, *__protocol);
2018   if (fd < 0)
2019     {
2020       rv = fd;
2021       goto out;
2022     }
2023
2024   pool_get (vsm->vsockets, vsock);
2025   vsocket_init (vsock);
2026
2027   sockidx = vsock - vsm->vsockets;
2028   hash_set (vsm->sockidx_by_fd, fd, sockidx);
2029
2030   vsocket_set (vsock, fd, __sid, SOCKET_TYPE_VPPCOM_BOUND);
2031   return fd;
2032
2033 out:
2034   return rv;
2035 }
2036
2037 /* If flag is 0, then accept4() is the same as accept().
2038  * SOCK_NONBLOCK and SOCK_CLOEXEC can be bitwise ORed in flags
2039  */
2040 static int
2041 vcom_socket_accept_flags (int __fd, __SOCKADDR_ARG __addr,
2042                           socklen_t * __restrict __addr_len, int flags)
2043 {
2044   int rv = -1;
2045   vcom_socket_main_t *vsm = &vcom_socket_main;
2046   uword *p;
2047   vcom_socket_t *vsock;
2048
2049   int fd;
2050   int sid;
2051   int domain;
2052   int type;
2053   int protocol;
2054
2055   uint8_t addr8[sizeof (struct in6_addr)];
2056   vppcom_endpt_t ep;
2057
2058   ep.ip = addr8;
2059
2060   /* validate flags */
2061
2062   /*
2063    * for documentation
2064    *  switch (flags)
2065    *   {
2066    *   case 0:
2067    *   case SOCK_NONBLOCK:
2068    *   case SOCK_CLOEXEC:
2069    *   case SOCK_NONBLOCK | SOCK_CLOEXEC:
2070    *     break;
2071    *
2072    *   default:
2073    *     return -1;
2074    *   }
2075    */
2076   /* flags can be 0 or can be bitwise OR
2077    * of any of SOCK_NONBLOCK and SOCK_CLOEXEC */
2078
2079   if (!(!flags || (flags & (SOCK_NONBLOCK | SOCK_CLOEXEC))))
2080     {
2081       /* TBD: return proper error code */
2082       return -1;
2083     }
2084
2085   /* TBD: return proper error code */
2086
2087   if (!vcom_socket_is_connection_mode_socket (__fd))
2088     {
2089       return -EOPNOTSUPP;
2090     }
2091
2092   p = hash_get (vsm->sockidx_by_fd, __fd);
2093   if (p)
2094     {
2095       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
2096
2097
2098       rv = vcom_fcntl (vsock->fd, F_GETFL, 0);
2099       if (rv < 0)
2100         {
2101           return rv;
2102         }
2103
2104       /* is blocking */
2105       if (!(rv & O_NONBLOCK))
2106         {
2107           /* socket is not marked as nonblocking
2108            * and no pending connections are present
2109            * on the queue, accept () blocks the caller
2110            * until a connection is present.
2111            */
2112           rv = vppcom_session_accept (vsock->sid, &ep,
2113                                       -1.0 /* wait forever */ );
2114         }
2115       else
2116         {
2117           /* The file descriptor refers to a socket and has been
2118            * marked nonblocking(O_NONBLOCK) and the accept would
2119            * block.
2120            * */
2121           /* is non blocking */
2122           rv = vppcom_session_accept (vsock->sid, &ep, 0);
2123           /* If the socket is marked nonblocking and
2124            * no pending connections are present on the
2125            * queue, accept fails with the error
2126            * EAGAIN or EWOULDBLOCK
2127            */
2128           if (rv == VPPCOM_ETIMEDOUT)
2129             {
2130               rv = VPPCOM_EAGAIN;
2131             }
2132         }
2133       if (rv < 0)
2134         {
2135           return rv;
2136         }
2137
2138       sid = rv;
2139
2140       /* create a new connected socket resource and set flags
2141        * on the new file descriptor.
2142        * update vsockets and sockidx_by_fd table
2143        * */
2144       fd = vcom_socket_connected_socket (__fd, sid,
2145                                          &domain, &type, &protocol, flags);
2146       if (fd < 0)
2147         {
2148           return fd;
2149         }
2150
2151       rv = fd;
2152
2153       /* TBD populate __addr and __addr_len */
2154       /* TBD: The returned address is truncated if the buffer
2155        * provided is too small, in this case, __addr_len will
2156        * return a value greater than was supplied to the call.*/
2157       if (__addr)
2158         {
2159           if (ep.is_cut_thru)
2160             {
2161               /* TBD populate __addr and __addr_len */
2162               switch (domain)
2163                 {
2164                 case AF_INET:
2165                   ((struct sockaddr_in *) __addr)->sin_family = AF_INET;
2166                   ((struct sockaddr_in *) __addr)->sin_port = ep.port;
2167                   memcpy (&((struct sockaddr_in *) __addr)->sin_addr,
2168                           addr8, sizeof (struct in_addr));
2169                   /* TBD: populate __addr_len */
2170                   if (__addr_len)
2171                     {
2172                       *__addr_len = sizeof (struct sockaddr_in);
2173                     }
2174                   break;
2175
2176                 case AF_INET6:
2177                   ((struct sockaddr_in6 *) __addr)->sin6_family = AF_INET6;
2178                   ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port;
2179                   memcpy (((struct sockaddr_in6 *) __addr)->sin6_addr.
2180                           __in6_u.__u6_addr8, addr8,
2181                           sizeof (struct in6_addr));
2182                   /* TBD: populate __addr_len */
2183                   if (__addr_len)
2184                     {
2185                       *__addr_len = sizeof (struct sockaddr_in6);
2186                     }
2187                   break;
2188
2189                 default:
2190                   return -EAFNOSUPPORT;
2191                 }
2192             }
2193           else
2194             {
2195               switch (ep.is_ip4)
2196                 {
2197                 case VPPCOM_IS_IP4:
2198                   ((struct sockaddr_in *) __addr)->sin_family = AF_INET;
2199                   ((struct sockaddr_in *) __addr)->sin_port = ep.port;
2200                   memcpy (&((struct sockaddr_in *) __addr)->sin_addr,
2201                           addr8, sizeof (struct in_addr));
2202                   /* TBD: populate __addr_len */
2203                   if (__addr_len)
2204                     {
2205                       *__addr_len = sizeof (struct sockaddr_in);
2206                     }
2207                   break;
2208
2209                 case VPPCOM_IS_IP6:
2210                   ((struct sockaddr_in6 *) __addr)->sin6_family = AF_INET6;
2211                   ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port;
2212                   memcpy (((struct sockaddr_in6 *) __addr)->sin6_addr.
2213                           __in6_u.__u6_addr8, addr8,
2214                           sizeof (struct in6_addr));
2215                   /* TBD: populate __addr_len */
2216                   if (__addr_len)
2217                     {
2218                       *__addr_len = sizeof (struct sockaddr_in6);
2219                     }
2220                   break;
2221
2222                 default:
2223                   return -EAFNOSUPPORT;
2224                 }
2225             }
2226         }
2227       else
2228         {
2229           /* when __addr is NULL, nothing is filled in,
2230            * in this case, __addr_len is not used,
2231            * and should also be null
2232            * */
2233           if (__addr_len)
2234             {
2235               /* TBD: return proper error code */
2236               return -1;
2237             }
2238         }
2239     }
2240
2241   return rv;
2242 }
2243
2244 int
2245 vcom_socket_accept (int __fd, __SOCKADDR_ARG __addr,
2246                     socklen_t * __restrict __addr_len)
2247 {
2248   /* set flags to 0 for accept() */
2249   return vcom_socket_accept_flags (__fd, __addr, __addr_len, 0);
2250 }
2251
2252 #ifdef __USE_GNU
2253 int
2254 vcom_socket_accept4 (int __fd, __SOCKADDR_ARG __addr,
2255                      socklen_t * __restrict __addr_len, int __flags)
2256 {
2257   /*  SOCK_NONBLOCK and SOCK_CLOEXEC can be bitwise ORed in flags */
2258   return vcom_socket_accept_flags (__fd, __addr, __addr_len, __flags);
2259 }
2260 #endif
2261
2262 /* TBD: move it to vppcom */
2263 int
2264 vppcom_session_shutdown (int __fd, int __how)
2265 {
2266   return 0;
2267 }
2268
2269 int
2270 vcom_socket_shutdown (int __fd, int __how)
2271 {
2272   int rv = -1;
2273   vcom_socket_main_t *vsm = &vcom_socket_main;
2274   uword *p;
2275   vcom_socket_t *vsock;
2276
2277   p = hash_get (vsm->sockidx_by_fd, __fd);
2278   if (p)
2279     {
2280       vsock = pool_elt_at_index (vsm->vsockets, p[0]);
2281       switch (__how)
2282         {
2283         case SHUT_RD:
2284         case SHUT_WR:
2285         case SHUT_RDWR:
2286           rv = vppcom_session_shutdown (vsock->sid, __how);
2287           return rv;
2288           break;
2289
2290         default:
2291           return -EINVAL;
2292           break;
2293         }
2294     }
2295
2296   return rv;
2297 }
2298
2299 int
2300 vcom_socket_epoll_create1 (int __flags)
2301 {
2302   int rv = -1;
2303   vcom_socket_main_t *vsm = &vcom_socket_main;
2304   vcom_epoll_t *vepoll;
2305
2306   i32 epfd;
2307   i32 vep_idx;
2308   i32 epollidx;
2309
2310   epfd = vcom_socket_open_epoll (__flags);
2311   if (epfd < 0)
2312     {
2313       rv = epfd;
2314       goto out;
2315     }
2316
2317   vep_idx = vppcom_epoll_create ();
2318   if (vep_idx < 0)
2319     {
2320       rv = vep_idx;
2321       goto out_close_epoll;
2322     }
2323
2324   pool_get (vsm->vepolls, vepoll);
2325   vepoll_init (vepoll);
2326
2327   epollidx = vepoll - vsm->vepolls;
2328   hash_set (vsm->epollidx_by_epfd, epfd, epollidx);
2329
2330   vepoll_set (vepoll, epfd, vep_idx, EPOLL_TYPE_VPPCOM_BOUND, __flags, 0, 0);
2331
2332   return epfd;
2333
2334 out_close_epoll:
2335   vcom_socket_close_epoll (epfd);
2336 out:
2337   return rv;
2338 }
2339
2340 /*
2341  * PRE: vppcom_epoll_ctl() is successful
2342  * free_vepitem_on_del : 0 - no_pool_put, 1 - pool_put
2343  */
2344 int
2345 vcom_socket_ctl_vepitem (int __epfd, int __op, int __fd,
2346                          struct epoll_event *__event,
2347                          i32 vep_idx, vcom_epoll_t * vepoll,
2348                          i32 vfd_id, void *vfd, vcom_fd_type_t type,
2349                          int free_vepitem_on_del)
2350 {
2351   int rv = -1;
2352   vcom_socket_main_t *vsm = &vcom_socket_main;
2353   vcom_epitem_t *vepitem;
2354
2355   vcom_epitem_key_t epfdfd = {.epfd = __epfd,.fd = __fd };
2356   uword *p;
2357   i32 vepitemidx;
2358
2359   i32 *vepitemidxs = 0;
2360
2361   struct epoll_event revent = {.events = 0,.data.fd = INVALID_FD };
2362
2363   i32 vec_idx;
2364
2365   /* perform control operations on the epoll instance */
2366   switch (__op)
2367     {
2368     case EPOLL_CTL_ADD:
2369       /*
2370        * supplied file descriptor is already
2371        * registered with this epoll instance
2372        * */
2373       /* vepitem exists */
2374       p = hash_get (vsm->epitemidx_by_epfdfd, epfdfd.key);
2375       if (p)
2376         {
2377           rv = -EEXIST;
2378           goto out;
2379         }
2380
2381       /* add a new vepitem */
2382       pool_get (vsm->vepitems, vepitem);
2383       vepitem_init (vepitem);
2384
2385       vepitemidx = vepitem - vsm->vepitems;
2386       hash_set (vsm->epitemidx_by_epfdfd, epfdfd.key, vepitemidx);
2387       vepitem_set (vepitem, __epfd, __fd, __fd, __fd, type, *__event, revent);
2388
2389       /* update epitemidxs */
2390       /* by_epfd */
2391       p = hash_get (vsm->epitemidxs_by_epfd, __epfd);
2392       if (!p)                   /*  not exist */
2393         {
2394           vepitemidxs = 0;
2395           vec_add1 (vepitemidxs, vepitemidx);
2396           hash_set (vsm->epitemidxs_by_epfd, __epfd, vepitemidxs);
2397         }
2398       else                      /*  exists */
2399         {
2400           vepitemidxs = *(i32 **) p;
2401           vec_add1 (vepitemidxs, vepitemidx);
2402           hash_set3 (vsm->epitemidxs_by_epfd, __epfd, vepitemidxs, 0);
2403         }
2404       /* update epitemidxs */
2405       /* by_fd */
2406       p = hash_get (vsm->epitemidxs_by_fd, __fd);
2407       if (!p)                   /*  not exist */
2408         {
2409           vepitemidxs = 0;
2410           vec_add1 (vepitemidxs, vepitemidx);
2411           hash_set (vsm->epitemidxs_by_fd, __fd, vepitemidxs);
2412         }
2413       else                      /*  exists */
2414         {
2415           vepitemidxs = *(i32 **) p;
2416           vec_add1 (vepitemidxs, vepitemidx);
2417           hash_set3 (vsm->epitemidxs_by_fd, __fd, vepitemidxs, 0);
2418         }
2419
2420       /* increment vepoll fd count by 1 */
2421       vepoll->count += 1;
2422
2423       rv = 0;
2424       goto out;
2425       break;
2426
2427     case EPOLL_CTL_MOD:
2428       /*
2429        * supplied file descriptor is not
2430        * registered with this epoll instance
2431        * */
2432       /* vepitem not exist */
2433       p = hash_get (vsm->epitemidx_by_epfdfd, epfdfd.key);
2434       if (!p)
2435         {
2436           rv = -ENOENT;
2437           goto out;
2438         }
2439       vepitem = pool_elt_at_index (vsm->vepitems, p[0]);
2440       if (vepitem)
2441         {
2442           vepitem->event = *__event;
2443           vepitem->revent = revent;
2444         }
2445
2446       rv = 0;
2447       goto out;
2448       break;
2449
2450     case EPOLL_CTL_DEL:
2451       /*
2452        * supplied file descriptor is not
2453        * registered with this epoll instance
2454        * */
2455       /* vepitem not exist */
2456       p = hash_get (vsm->epitemidx_by_epfdfd, epfdfd.key);
2457       if (!p)
2458         {
2459           rv = -ENOENT;
2460           goto out;
2461         }
2462       vepitemidx = *(i32 *) p;
2463       hash_unset (vsm->epitemidx_by_epfdfd, epfdfd.key);
2464
2465       /* update epitemidxs */
2466       /* by_epfd */
2467       p = hash_get (vsm->epitemidxs_by_epfd, __epfd);
2468       if (!p)                   /*  not exist */
2469         {
2470           rv = -ENOENT;
2471           goto out;
2472         }
2473       else                      /*  exists */
2474         {
2475           vepitemidxs = *(i32 **) p;
2476           vec_idx = vec_search (vepitemidxs, vepitemidx);
2477           if (vec_idx != ~0)
2478             {
2479               vec_del1 (vepitemidxs, vec_idx);
2480               if (!vec_len (vepitemidxs))
2481                 {
2482                   vec_free (vepitemidxs);
2483                   hash_unset (vsm->epitemidxs_by_epfd, __epfd);
2484                 }
2485             }
2486         }
2487
2488       /* update epitemidxs */
2489       /* by_fd */
2490       p = hash_get (vsm->epitemidxs_by_fd, __fd);
2491       if (!p)                   /*  not exist */
2492         {
2493           rv = -ENOENT;
2494           goto out;
2495         }
2496       else                      /*  exists */
2497         {
2498           vepitemidxs = *(i32 **) p;
2499           vec_idx = vec_search (vepitemidxs, vepitemidx);
2500           if (vec_idx != ~0)
2501             {
2502               vec_del1 (vepitemidxs, vec_idx);
2503               if (!vec_len (vepitemidxs))
2504                 {
2505                   vec_free (vepitemidxs);
2506                   hash_unset (vsm->epitemidxs_by_fd, __fd);
2507                 }
2508             }
2509         }
2510
2511       /* pool put vepitem */
2512       vepitem = pool_elt_at_index (vsm->vepitems, vepitemidx);
2513       if (free_vepitem_on_del)
2514         {
2515           if (!vepitem)
2516             {
2517               rv = -ENOENT;
2518               goto out;
2519             }
2520           vepitem_init (vepitem);
2521           pool_put (vsm->vepitems, vepitem);
2522         }
2523       else
2524         {
2525           if (!vepitem)
2526             {
2527               vepitem_init (vepitem);
2528             }
2529         }
2530
2531       /* decrement vepoll fd count by 1 */
2532       vepoll->count -= 1;
2533
2534       rv = 0;
2535       goto out;
2536       break;
2537
2538     default:
2539       rv = -EINVAL;
2540       goto out;
2541       break;
2542     }
2543
2544 out:
2545   return rv;
2546 }
2547
2548 /*
2549  * PRE: 00. null pointer check on __event
2550  *      01. all other parameters are validated
2551  */
2552
2553 static int
2554 vcom_socket_epoll_ctl_internal (int __epfd, int __op, int __fd,
2555                                 struct epoll_event *__event,
2556                                 int free_vepitem_on_del)
2557 {
2558   int rv = -1;
2559
2560   /* vcom_socket_main_t *vsm = &vcom_socket_main; */
2561   vcom_epoll_t *vepoll;
2562
2563   /*__fd could could be vcom socket or vcom epoll or kernel fd */
2564   void *vfd;
2565   vcom_epoll_t *vfd_vepoll;
2566   vcom_socket_t *vfd_vsock;
2567
2568   i32 vep_idx;
2569   i32 vfd_id;
2570
2571   vcom_fd_type_t type = FD_TYPE_INVALID;
2572
2573   /* validate __event */
2574
2575   /* get vep_idx and vepoll */
2576   vep_idx = vcom_socket_get_vep_idx_and_vepoll (__epfd, &vepoll);
2577   if (vep_idx == INVALID_VEP_IDX)
2578     {
2579       return -EBADF;
2580     }
2581
2582   /* get vcom fd type, vfd_id and vfd */
2583   vfd_id = vcom_socket_get_sid_and_vsock (__fd, &vfd_vsock);
2584   if (vfd_id != INVALID_SESSION_ID)
2585     {
2586       type = FD_TYPE_VCOM_SOCKET;
2587       vfd = vfd_vsock;
2588     }
2589   else if ((vfd_id = vcom_socket_get_vep_idx_and_vepoll (__fd, &vfd_vepoll))
2590            != INVALID_VEP_IDX)
2591     {
2592       type = FD_TYPE_EPOLL;
2593       vfd = vfd_vepoll;
2594     }
2595   else
2596     {
2597       /* FD_TYPE_KERNEL not supported by epoll instance */
2598       type = FD_TYPE_INVALID;
2599       return -EBADF;
2600     }
2601
2602
2603   /* vepoll and vsock are now valid */
2604   rv = vppcom_epoll_ctl (vep_idx, __op, vfd_id, __event);
2605   if (rv < 0)
2606     {
2607       return rv;
2608     }
2609
2610   rv = vcom_socket_ctl_vepitem (__epfd, __op, __fd,
2611                                 __event,
2612                                 vep_idx, vepoll,
2613                                 vfd_id, vfd, type, free_vepitem_on_del);
2614   return rv;
2615 }
2616
2617 int
2618 vcom_socket_epoll_ctl (int __epfd, int __op, int __fd,
2619                        struct epoll_event *__event)
2620 {
2621   int rv = -1;
2622
2623   rv = vcom_socket_epoll_ctl_internal (__epfd, __op, __fd, __event, 1);
2624   return rv;
2625 }
2626
2627 static int
2628 vcom_socket_epoll_ctl1 (int __epfd, int __op, int __fd,
2629                         struct epoll_event *__event)
2630 {
2631   int rv = -1;
2632
2633   rv = vcom_socket_epoll_ctl_internal (__epfd, __op, __fd, __event, 0);
2634   return rv;
2635 }
2636
2637 int
2638 vcom_socket_epoll_pwait (int __epfd, struct epoll_event *__events,
2639                          int __maxevents, int __timeout,
2640                          const __sigset_t * __ss)
2641 {
2642   int rv = -EBADF;
2643
2644   /* in seconds eg. 3.123456789 seconds */
2645   double time_to_wait = (double) 0;
2646
2647   i32 vep_idx;
2648
2649   /* validate __event */
2650   if (!__events)
2651     {
2652       rv = -EFAULT;
2653       goto out;
2654     }
2655
2656   /* validate __timeout */
2657   if (__timeout > 0)
2658     {
2659       time_to_wait = (double) __timeout / (double) 1000;
2660     }
2661   else if (__timeout == 0)
2662     {
2663       time_to_wait = (double) 0;
2664     }
2665   else if (__timeout == -1)
2666     {
2667       time_to_wait = ~0;
2668     }
2669   else
2670     {
2671       rv = -EBADF;
2672       goto out;
2673     }
2674
2675   /* get vep_idx */
2676   vep_idx = vcom_socket_get_vep_idx (__epfd);
2677   if (vep_idx != INVALID_VEP_IDX)
2678     {
2679       rv = vppcom_epoll_wait (vep_idx, __events, __maxevents, time_to_wait);
2680     }
2681 out:
2682   return rv;
2683 }
2684
2685 int
2686 vcom_socket_main_init (void)
2687 {
2688   vcom_socket_main_t *vsm = &vcom_socket_main;
2689
2690   if (VCOM_DEBUG > 0)
2691     printf ("vcom_socket_main_init\n");
2692
2693   if (!vsm->init)
2694     {
2695       /* TBD: define FD_MAXSIZE and use it here */
2696       pool_alloc (vsm->vsockets, FD_SETSIZE);
2697       vsm->sockidx_by_fd = hash_create (0, sizeof (i32));
2698
2699       pool_alloc (vsm->vepolls, FD_SETSIZE);
2700       vsm->epollidx_by_epfd = hash_create (0, sizeof (i32));
2701
2702       pool_alloc (vsm->vepitems, FD_SETSIZE);
2703       vsm->epitemidx_by_epfdfd = hash_create (0, sizeof (i32));
2704
2705       vsm->epitemidxs_by_epfd = hash_create (0, sizeof (i32 *));
2706       vsm->epitemidxs_by_fd = hash_create (0, sizeof (i32 *));
2707
2708       vsm->init = 1;
2709     }
2710
2711   return 0;
2712 }
2713
2714
2715 void
2716 vcom_socket_main_show (void)
2717 {
2718   vcom_socket_main_t *vsm = &vcom_socket_main;
2719   vcom_socket_t *vsock;
2720
2721   vcom_epoll_t *vepoll;
2722
2723   vcom_epitem_t *vepitem;
2724
2725   i32 epfd;
2726   i32 fd;
2727   i32 *vepitemidxs, *vepitemidxs_var;
2728
2729   if (vsm->init)
2730     {
2731       /* from active list of vsockets show vsock */
2732
2733       /* *INDENT-OFF* */
2734       pool_foreach (vsock, vsm->vsockets,
2735         ({
2736           printf(
2737                  "fd='%04d', sid='%08x',type='%-30s'\n",
2738                  vsock->fd, vsock->sid,
2739                  vcom_socket_type_str (vsock->type));
2740         }));
2741       /* *INDENT-ON* */
2742
2743       /* from active list of vepolls, show vepoll */
2744
2745       /* *INDENT-OFF* */
2746       pool_foreach (vepoll, vsm->vepolls,
2747         ({
2748           printf(
2749                  "epfd='%04d', vep_idx='%08x', "
2750                  "type='%-30s', "
2751                  "flags='%d', count='%d', close='%d'\n",
2752                  vepoll->epfd, vepoll->vep_idx,
2753                  vcom_socket_epoll_type_str (vepoll->type),
2754                  vepoll->flags, vepoll->count, vepoll->close);
2755         }));
2756       /* *INDENT-ON* */
2757
2758       /* from active list of vepitems, show vepitem */
2759
2760       /* *INDENT-OFF* */
2761       pool_foreach (vepitem, vsm->vepitems,
2762         ({
2763           printf(
2764                  "epfd='%04d', fd='%04d', "
2765                  "next_fd='%04d', prev_fd='%04d', "
2766                  "type='%-30s', "
2767                  "events='%04x', revents='%04x'\n",
2768                  vepitem->epfd, vepitem->fd,
2769                  vepitem->next_fd, vepitem->prev_fd,
2770                  vcom_socket_vcom_fd_type_str (vepitem->type),
2771                  vepitem->event.events, vepitem->revent.events);
2772         }));
2773
2774       /* *INDENT-ON* */
2775
2776       /* show epitemidxs for epfd */
2777       /* *INDENT-OFF* */
2778       hash_foreach (epfd, vepitemidxs,
2779                     vsm->epitemidxs_by_epfd,
2780       ({
2781         printf("\n[ '%04d': ", epfd);
2782         vec_foreach (vepitemidxs_var,vepitemidxs)
2783         {
2784           printf("'%04d' ", (int)vepitemidxs_var[0]);
2785         }
2786         printf("]\n");
2787       }));
2788       /* *INDENT-ON* */
2789
2790       /* show epitemidxs for fd */
2791       /* *INDENT-OFF* */
2792       hash_foreach (fd, vepitemidxs,
2793                     vsm->epitemidxs_by_fd,
2794       ({
2795         printf("\n{ '%04d': ", fd);
2796         vec_foreach (vepitemidxs_var,vepitemidxs)
2797         {
2798           printf("'%04d' ", (int)vepitemidxs_var[0]);
2799         }
2800         printf("}\n");
2801       }));
2802       /* *INDENT-ON* */
2803
2804     }
2805 }
2806
2807 void
2808 vcom_socket_main_destroy (void)
2809 {
2810   vcom_socket_main_t *vsm = &vcom_socket_main;
2811   vcom_socket_t *vsock;
2812
2813   vcom_epoll_t *vepoll;
2814
2815   vcom_epitem_t *vepitem;
2816
2817   i32 epfd;
2818   i32 fd;
2819   i32 *vepitemidxs;
2820
2821
2822   if (VCOM_DEBUG > 0)
2823     printf ("vcom_socket_main_destroy\n");
2824
2825   if (vsm->init)
2826     {
2827
2828       /*
2829        * from active list of vepitems,
2830        * remove all "vepitem" elements from the pool in a safe way
2831        * */
2832
2833       /* *INDENT-OFF* */
2834       pool_flush (vepitem, vsm->vepitems,
2835         ({
2836           if (vepitem->type == FD_TYPE_EPOLL || FD_TYPE_VCOM_SOCKET)
2837           {
2838               vcom_socket_epoll_ctl1 (vepitem->epfd, EPOLL_CTL_DEL,
2839                                      vepitem->fd, NULL);
2840              vepitem_init (vepitem);
2841           }
2842         }));
2843       /* *INDENT-ON* */
2844
2845       pool_free (vsm->vepitems);
2846       hash_free (vsm->epitemidx_by_epfdfd);
2847
2848       /* free vepitemidxs for each epfd */
2849       /* *INDENT-OFF* */
2850       hash_foreach (epfd, vepitemidxs,
2851                     vsm->epitemidxs_by_epfd,
2852       ({
2853         vec_free (vepitemidxs);
2854       }));
2855       /* *INDENT-ON* */
2856       hash_free (vsm->epitemidxs_by_epfd);
2857
2858       /* free vepitemidxs for each fd */
2859       /* *INDENT-OFF* */
2860       hash_foreach (fd, vepitemidxs,
2861                     vsm->epitemidxs_by_fd,
2862       ({
2863         vec_free (vepitemidxs);
2864       }));
2865       /* *INDENT-ON* */
2866       hash_free (vsm->epitemidxs_by_fd);
2867
2868
2869       /*
2870        * from active list of vsockets,
2871        * close socket and vppcom session
2872        * */
2873
2874       /* *INDENT-OFF* */
2875       pool_foreach (vsock, vsm->vsockets,
2876         ({
2877           if (vsock->type == SOCKET_TYPE_VPPCOM_BOUND)
2878             {
2879               vppcom_session_close (vsock->sid);
2880               vcom_socket_close_socket (vsock->fd);
2881               vsocket_init (vsock);
2882             }
2883         }));
2884       /* *INDENT-ON* */
2885
2886       /*
2887        * return vsocket element to the pool
2888        * */
2889
2890       /* *INDENT-OFF* */
2891       pool_flush (vsock, vsm->vsockets,
2892         ({
2893           // vsocket_init(vsock);
2894           ;
2895         }));
2896       /* *INDENT-ON* */
2897
2898       pool_free (vsm->vsockets);
2899       hash_free (vsm->sockidx_by_fd);
2900
2901       /*
2902        * from active list of vepolls,
2903        * close epoll and vppcom_epoll
2904        * */
2905
2906       /* *INDENT-OFF* */
2907       pool_foreach (vepoll, vsm->vepolls,
2908         ({
2909           if (vepoll->type == EPOLL_TYPE_VPPCOM_BOUND)
2910             {
2911               vppcom_session_close (vepoll->vep_idx);
2912               vcom_socket_close_epoll (vepoll->epfd); /* TBD: */
2913               vepoll_init (vepoll);
2914             }
2915         }));
2916       /* *INDENT-ON* */
2917
2918       /*
2919        * return vepoll element to the pool
2920        * */
2921
2922       /* *INDENT-OFF* */
2923       pool_flush (vepoll, vsm->vepolls,
2924         ({
2925           // vepoll_init(vepoll);
2926           ;
2927         }));
2928       /* *INDENT-ON* */
2929
2930       pool_free (vsm->vepolls);
2931       hash_free (vsm->epollidx_by_epfd);
2932
2933       vsm->init = 0;
2934     }
2935 }
2936
2937
2938 /*
2939  * fd.io coding-style-patch-verification: ON
2940  *
2941  * Local Variables:
2942  * eval: (c-set-style "gnu")
2943  * End:
2944  */