FD.io VPP  v19.01.3-6-g70449b9b9
Vector Packet Processing
socket_client.c
Go to the documentation of this file.
1 /*
2  *------------------------------------------------------------------
3  * socket_client.c - API message handling over sockets, client code.
4  *
5  * Copyright (c) 2017 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19 
20 #include <stdio.h>
21 #define __USE_GNU
22 #include <sys/socket.h>
23 
24 #include <svm/ssvm.h>
27 
29 
30 #define vl_typedefs /* define message structures */
32 #undef vl_typedefs
33 
34 #define vl_endianfun /* define message structures */
36 #undef vl_endianfun
37 
38 /* instantiate all the print functions we know about */
39 #define vl_print(handle, ...) clib_warning (__VA_ARGS__)
40 #define vl_printfun
42 #undef vl_printfun
43 
45 
46 /* Debug aid */
47 u32 vl (void *p) __attribute__ ((weak));
48 
49 u32
50 vl (void *p)
51 {
52  return vec_len (p);
53 }
54 
55 int
57 {
59  u32 data_len = 0, msg_size;
60  int n, current_rx_index;
61  msgbuf_t *mbp = 0;
62  f64 timeout;
63 
64  if (scm->socket_fd == 0)
65  return -1;
66 
67  if (wait)
68  timeout = clib_time_now (&scm->clib_time) + wait;
69 
70  while (1)
71  {
72  while (vec_len (scm->socket_rx_buffer) < sizeof (*mbp))
73  {
74  current_rx_index = vec_len (scm->socket_rx_buffer);
75  vec_validate (scm->socket_rx_buffer, current_rx_index
76  + scm->socket_buffer_size - 1);
77  _vec_len (scm->socket_rx_buffer) = current_rx_index;
78  n = read (scm->socket_fd, scm->socket_rx_buffer + current_rx_index,
79  scm->socket_buffer_size);
80  if (n < 0)
81  {
82  if (errno == EAGAIN)
83  continue;
84 
85  clib_unix_warning ("socket_read");
86  return -1;
87  }
88  _vec_len (scm->socket_rx_buffer) += n;
89  }
90 
91 #if CLIB_DEBUG > 1
92  if (n > 0)
93  clib_warning ("read %d bytes", n);
94 #endif
95 
96  mbp = (msgbuf_t *) (scm->socket_rx_buffer);
97  data_len = ntohl (mbp->data_len);
98  current_rx_index = vec_len (scm->socket_rx_buffer);
99  vec_validate (scm->socket_rx_buffer, current_rx_index + data_len);
100  _vec_len (scm->socket_rx_buffer) = current_rx_index;
101  mbp = (msgbuf_t *) (scm->socket_rx_buffer);
102  msg_size = data_len + sizeof (*mbp);
103 
104  while (vec_len (scm->socket_rx_buffer) < msg_size)
105  {
106  n = read (scm->socket_fd,
108  msg_size - vec_len (scm->socket_rx_buffer));
109  if (n < 0)
110  {
111  if (errno == EAGAIN)
112  continue;
113 
114  clib_unix_warning ("socket_read");
115  return -1;
116  }
117  _vec_len (scm->socket_rx_buffer) += n;
118  }
119 
120  if (vec_len (scm->socket_rx_buffer) >= data_len + sizeof (*mbp))
121  {
122  vl_msg_api_socket_handler ((void *) (mbp->data));
123 
124  if (vec_len (scm->socket_rx_buffer) == data_len + sizeof (*mbp))
125  _vec_len (scm->socket_rx_buffer) = 0;
126  else
127  vec_delete (scm->socket_rx_buffer, data_len + sizeof (*mbp), 0);
128  mbp = 0;
129 
130  /* Quit if we're out of data, and not expecting a ping reply */
131  if (vec_len (scm->socket_rx_buffer) == 0
132  && scm->control_pings_outstanding == 0)
133  break;
134  }
135  if (wait && clib_time_now (&scm->clib_time) >= timeout)
136  return -1;
137  }
138  return 0;
139 }
140 
141 int
143 {
145  int n;
146 
147  msgbuf_t msgbuf = {
148  .q = 0,
149  .gc_mark_timestamp = 0,
150  .data_len = htonl (scm->socket_tx_nbytes),
151  };
152 
153  n = write (scm->socket_fd, &msgbuf, sizeof (msgbuf));
154  if (n < sizeof (msgbuf))
155  {
156  clib_unix_warning ("socket write (msgbuf)");
157  return -1;
158  }
159 
160  n = write (scm->socket_fd, scm->socket_tx_buffer, scm->socket_tx_nbytes);
161  if (n < scm->socket_tx_nbytes)
162  {
163  clib_unix_warning ("socket write (msg)");
164  return -1;
165  }
166 
167  return n;
168 }
169 
170 void *
172 {
173  socket_client_main.socket_tx_nbytes = nbytes;
174  return ((void *) socket_client_main.socket_tx_buffer);
175 }
176 
177 void
179 {
181 
183  {
186  }
187  if (scm->socket_fd && (close (scm->socket_fd) < 0))
188  clib_unix_warning ("close");
189  scm->socket_fd = 0;
190 }
191 
192 void
194 {
196  scm->socket_enable = enable;
197 }
198 
199 clib_error_t *
200 vl_sock_api_recv_fd_msg (int socket_fd, int fds[], int n_fds, u32 wait)
201 {
203  char msgbuf[16];
204  char ctl[CMSG_SPACE (sizeof (int) * n_fds)
205  + CMSG_SPACE (sizeof (struct ucred))];
206  struct msghdr mh = { 0 };
207  struct iovec iov[1];
208  ssize_t size = 0;
209  struct ucred *cr = 0;
210  struct cmsghdr *cmsg;
211  pid_t pid __attribute__ ((unused));
212  uid_t uid __attribute__ ((unused));
213  gid_t gid __attribute__ ((unused));
214  f64 timeout;
215 
216  iov[0].iov_base = msgbuf;
217  iov[0].iov_len = 5;
218  mh.msg_iov = iov;
219  mh.msg_iovlen = 1;
220  mh.msg_control = ctl;
221  mh.msg_controllen = sizeof (ctl);
222 
223  clib_memset (ctl, 0, sizeof (ctl));
224 
225  if (wait != ~0)
226  {
227  timeout = clib_time_now (&scm->clib_time) + wait;
228  while (size != 5 && clib_time_now (&scm->clib_time) < timeout)
229  size = recvmsg (socket_fd, &mh, MSG_DONTWAIT);
230  }
231  else
232  size = recvmsg (socket_fd, &mh, 0);
233 
234  if (size != 5)
235  {
236  return (size == 0) ? clib_error_return (0, "disconnected") :
237  clib_error_return_unix (0, "recvmsg: malformed message (fd %d)",
238  socket_fd);
239  }
240 
241  cmsg = CMSG_FIRSTHDR (&mh);
242  while (cmsg)
243  {
244  if (cmsg->cmsg_level == SOL_SOCKET)
245  {
246  if (cmsg->cmsg_type == SCM_CREDENTIALS)
247  {
248  cr = (struct ucred *) CMSG_DATA (cmsg);
249  uid = cr->uid;
250  gid = cr->gid;
251  pid = cr->pid;
252  }
253  else if (cmsg->cmsg_type == SCM_RIGHTS)
254  {
255  clib_memcpy_fast (fds, CMSG_DATA (cmsg), sizeof (int) * n_fds);
256  }
257  }
258  cmsg = CMSG_NXTHDR (&mh, cmsg);
259  }
260  return 0;
261 }
262 
265 {
267  ssvm_private_t *memfd = &scm->memfd_segment;
268  i32 retval = ntohl (mp->retval);
269  api_main_t *am = &api_main;
270  clib_error_t *error;
271  int my_fd = -1;
272  u8 *new_name;
273 
274  if (retval)
275  {
276  clib_warning ("failed to init shmem");
277  return;
278  }
279 
280  /*
281  * Check the socket for the magic fd
282  */
283  error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 1, 5);
284  if (error)
285  {
286  clib_error_report (error);
287  retval = -99;
288  return;
289  }
290 
291  clib_memset (memfd, 0, sizeof (*memfd));
292  memfd->fd = my_fd;
293 
294  /* Note: this closes memfd.fd */
295  retval = ssvm_slave_init_memfd (memfd);
296  if (retval)
297  clib_warning ("WARNING: segment map returned %d", retval);
298 
299  /*
300  * Pivot to the memory client segment that vpp just created
301  */
302  am->vlib_rp = (void *) (memfd->requested_va + MMAP_PAGESIZE);
303  am->shmem_hdr = (void *) am->vlib_rp->user_ctx;
304 
305  new_name = format (0, "%v[shm]%c", scm->name, 0);
307  vl_client_connect_to_vlib_no_map ("pvt", (char *) new_name,
308  32 /* input_queue_length */ );
310  vec_free (new_name);
311 }
312 
313 static void
315 {
317  if (!mp->response)
318  {
319  scm->socket_enable = 1;
320  scm->client_index = clib_net_to_host_u32 (mp->index);
321  }
322 }
323 
324 #define foreach_sock_client_api_msg \
325 _(SOCKCLNT_CREATE_REPLY, sockclnt_create_reply) \
326 _(SOCK_INIT_SHM_REPLY, sock_init_shm_reply) \
327 
328 static void
329 noop_handler (void *notused)
330 {
331 }
332 
333 void
335 {
336 
337 #define _(N,n) \
338  vl_msg_api_set_handlers(VL_API_##N, #n, \
339  vl_api_##n##_t_handler, \
340  noop_handler, \
341  vl_api_##n##_t_endian, \
342  vl_api_##n##_t_print, \
343  sizeof(vl_api_##n##_t), 1);
345 #undef _
346 }
347 
348 int
349 vl_socket_client_connect (char *socket_path, char *client_name,
350  u32 socket_buffer_size)
351 {
354  clib_socket_t *sock;
355  clib_error_t *error;
356 
357  /* Already connected? */
358  if (scm->socket_fd)
359  return (-2);
360 
361  /* bogus call? */
362  if (socket_path == 0 || client_name == 0)
363  return (-3);
364 
365  sock = &scm->client_socket;
366  sock->config = socket_path;
367  sock->flags = CLIB_SOCKET_F_IS_CLIENT
369 
370  if ((error = clib_socket_init (sock)))
371  {
372  clib_error_report (error);
373  return (-1);
374  }
375 
377 
378  scm->socket_fd = sock->fd;
379  scm->socket_buffer_size = socket_buffer_size ? socket_buffer_size :
383  _vec_len (scm->socket_rx_buffer) = 0;
384  _vec_len (scm->socket_tx_buffer) = 0;
385  scm->name = format (0, "%s", client_name);
386 
387  mp = vl_socket_client_msg_alloc (sizeof (*mp));
388  mp->_vl_msg_id = htons (VL_API_SOCKCLNT_CREATE);
389  strncpy ((char *) mp->name, client_name, sizeof (mp->name) - 1);
390  mp->name[sizeof (mp->name) - 1] = 0;
391  mp->context = 0xfeedface;
392 
393  clib_time_init (&scm->clib_time);
394 
395  if (vl_socket_client_write () <= 0)
396  return (-1);
397 
398  if (vl_socket_client_read (5))
399  return (-1);
400 
401  return (0);
402 }
403 
404 int
406 {
409  int rv, i;
410  u64 *cfg;
411 
412  mp = vl_socket_client_msg_alloc (sizeof (*mp) +
413  vec_len (config) * sizeof (u64));
414  clib_memset (mp, 0, sizeof (*mp));
415  mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SOCK_INIT_SHM);
416  mp->client_index = clib_host_to_net_u32 (scm->client_index);
417  mp->requested_size = 64 << 20;
418 
419  if (config)
420  {
421  for (i = 0; i < vec_len (config); i++)
422  {
423  cfg = (u64 *) & config[i];
424  mp->configs[i] = *cfg;
425  }
426  mp->nitems = vec_len (config);
427  }
428  rv = vl_socket_client_write ();
429  if (rv <= 0)
430  return rv;
431 
432  if (vl_socket_client_read (1))
433  return -1;
434 
435  return 0;
436 }
437 
438 clib_error_t *
439 vl_socket_client_recv_fd_msg (int fds[], int n_fds, u32 wait)
440 {
442  if (!scm->socket_fd)
443  return clib_error_return (0, "no socket");
444  return vl_sock_api_recv_fd_msg (scm->client_socket.fd, fds, n_fds, wait);
445 }
446 
447 /*
448  * fd.io coding-style-patch-verification: ON
449  *
450  * Local Variables:
451  * eval: (c-set-style "gnu")
452  * End:
453  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
#define CLIB_SOCKET_F_NON_BLOCKING_CONNECT
Definition: socket.h:61
void vl_msg_api_socket_handler(void *the_msg)
Definition: api_shared.c:636
uword requested_va
Definition: ssvm.h:87
u8 vl_mem_client_is_connected(void)
ssvm_private_t memfd_segment
Definition: socket_client.h:42
int vl_client_connect_to_vlib_no_map(const char *svm_name, const char *client_name, int rx_queue_size)
int vl_socket_client_read(int wait)
Definition: socket_client.c:56
unsigned long u64
Definition: types.h:89
static void noop_handler(void *notused)
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
static f64 clib_time_now(clib_time_t *c)
Definition: time.h:215
int i
clib_error_t * clib_socket_init(clib_socket_t *s)
Definition: socket.c:384
int vl_socket_client_connect(char *socket_path, char *client_name, u32 socket_buffer_size)
svm_queue_t * q
message allocated in this shmem ring
Definition: api_common.h:138
u8 data[0]
actual message begins here
Definition: api_common.h:141
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
u32 vl(void *p)
Definition: socket_client.c:50
void vl_socket_client_enable_disable(int enable)
unsigned char u8
Definition: types.h:56
double f64
Definition: types.h:142
static void vl_api_sock_init_shm_reply_t_handler(vl_api_sock_init_shm_reply_t *mp)
#define SOCKET_CLIENT_DEFAULT_BUFFER_SIZE
Definition: socket_client.h:47
void * vl_socket_client_msg_alloc(int nbytes)
clib_time_t clib_time
Definition: socket_client.h:41
volatile void * user_ctx
Definition: svm_common.h:47
svm_region_t * vlib_rp
Current binary api segment descriptor.
Definition: api_common.h:255
#define clib_error_return(e, args...)
Definition: error.h:99
struct vl_shmem_hdr_ * shmem_hdr
Binary API shared-memory segment header pointer.
Definition: api_common.h:265
unsigned int u32
Definition: types.h:88
u64 configs[nitems]
Definition: memclnt.api:194
static void vl_api_sockclnt_create_reply_t_handler(vl_api_sockclnt_create_reply_t *mp)
uword size
int vl_socket_client_init_shm(vl_api_shm_elem_config_t *config)
void vl_sock_client_install_message_handlers(void)
#define clib_error_return_unix(e, args...)
Definition: error.h:102
clib_error_t * vl_sock_api_recv_fd_msg(int socket_fd, int fds[], int n_fds, u32 wait)
void clib_time_init(clib_time_t *c)
Definition: time.c:178
API main structure, used by both vpp and binary API clients.
Definition: api_common.h:202
socket_client_main_t socket_client_main
Definition: socket_client.c:44
clib_error_t * vl_socket_client_recv_fd_msg(int fds[], int n_fds, u32 wait)
void vl_socket_client_disconnect(void)
clib_socket_t client_socket
Definition: socket_client.h:32
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
#define clib_warning(format, args...)
Definition: error.h:59
int fd
memfd segments
Definition: ssvm.h:92
signed int i32
Definition: types.h:77
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:788
u32 data_len
message length not including header
Definition: api_common.h:139
Message header structure.
Definition: api_common.h:136
#define CLIB_SOCKET_F_IS_CLIENT
Definition: socket.h:59
#define clib_error_report(e)
Definition: error.h:113
struct _socket_t clib_socket_t
#define CLIB_SOCKET_F_SEQPACKET
Definition: socket.h:63
#define MMAP_PAGESIZE
Definition: ssvm.h:42
void vl_client_install_client_message_handlers(void)
void vl_client_disconnect_from_vlib_no_unmap(void)
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
u32 client_index
Client index allocated by VPP.
Definition: socket_client.h:30
#define clib_unix_warning(format, args...)
Definition: error.h:68
void ssvm_delete_memfd(ssvm_private_t *memfd)
Definition: ssvm.c:337
int vl_socket_client_write(void)
int socket_enable
Can temporarily disable the connection but still can keep it around...
Definition: socket_client.h:28
api_main_t api_main
Definition: api_shared.c:35
int ssvm_slave_init_memfd(ssvm_private_t *memfd)
Initialize memfd segment slave.
Definition: ssvm.c:285
#define foreach_sock_client_api_msg