FD.io VPP  v18.10-34-gcce845e
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  clib_unix_warning ("socket_read");
83  return -1;
84  }
85  _vec_len (scm->socket_rx_buffer) += n;
86  }
87 
88 #if CLIB_DEBUG > 1
89  if (n > 0)
90  clib_warning ("read %d bytes", n);
91 #endif
92 
93  mbp = (msgbuf_t *) (scm->socket_rx_buffer);
94  data_len = ntohl (mbp->data_len);
95  current_rx_index = vec_len (scm->socket_rx_buffer);
96  vec_validate (scm->socket_rx_buffer, current_rx_index + data_len);
97  _vec_len (scm->socket_rx_buffer) = current_rx_index;
98  mbp = (msgbuf_t *) (scm->socket_rx_buffer);
99  msg_size = data_len + sizeof (*mbp);
100 
101  while (vec_len (scm->socket_rx_buffer) < msg_size)
102  {
103  n = read (scm->socket_fd,
105  msg_size - vec_len (scm->socket_rx_buffer));
106  if (n < 0 && errno != EAGAIN)
107  {
108  clib_unix_warning ("socket_read");
109  return -1;
110  }
111  _vec_len (scm->socket_rx_buffer) += n;
112  }
113 
114  if (vec_len (scm->socket_rx_buffer) >= data_len + sizeof (*mbp))
115  {
116  vl_msg_api_socket_handler ((void *) (mbp->data));
117 
118  if (vec_len (scm->socket_rx_buffer) == data_len + sizeof (*mbp))
119  _vec_len (scm->socket_rx_buffer) = 0;
120  else
121  vec_delete (scm->socket_rx_buffer, data_len + sizeof (*mbp), 0);
122  mbp = 0;
123 
124  /* Quit if we're out of data, and not expecting a ping reply */
125  if (vec_len (scm->socket_rx_buffer) == 0
126  && scm->control_pings_outstanding == 0)
127  break;
128  }
129  if (wait && clib_time_now (&scm->clib_time) >= timeout)
130  return -1;
131  }
132  return 0;
133 }
134 
135 int
137 {
139  int n;
140 
141  msgbuf_t msgbuf = {
142  .q = 0,
143  .gc_mark_timestamp = 0,
144  .data_len = htonl (scm->socket_tx_nbytes),
145  };
146 
147  n = write (scm->socket_fd, &msgbuf, sizeof (msgbuf));
148  if (n < sizeof (msgbuf))
149  {
150  clib_unix_warning ("socket write (msgbuf)");
151  return -1;
152  }
153 
154  n = write (scm->socket_fd, scm->socket_tx_buffer, scm->socket_tx_nbytes);
155  if (n < scm->socket_tx_nbytes)
156  {
157  clib_unix_warning ("socket write (msg)");
158  return -1;
159  }
160 
161  return n;
162 }
163 
164 void *
166 {
167  socket_client_main.socket_tx_nbytes = nbytes;
168  return ((void *) socket_client_main.socket_tx_buffer);
169 }
170 
171 void
173 {
175 
177  {
180  }
181  if (scm->socket_fd && (close (scm->socket_fd) < 0))
182  clib_unix_warning ("close");
183  scm->socket_fd = 0;
184 }
185 
186 void
188 {
190  scm->socket_enable = enable;
191 }
192 
193 clib_error_t *
194 vl_sock_api_recv_fd_msg (int socket_fd, int fds[], int n_fds, u32 wait)
195 {
197  char msgbuf[16];
198  char ctl[CMSG_SPACE (sizeof (int) * n_fds)
199  + CMSG_SPACE (sizeof (struct ucred))];
200  struct msghdr mh = { 0 };
201  struct iovec iov[1];
202  ssize_t size = 0;
203  struct ucred *cr = 0;
204  struct cmsghdr *cmsg;
205  pid_t pid __attribute__ ((unused));
206  uid_t uid __attribute__ ((unused));
207  gid_t gid __attribute__ ((unused));
208  f64 timeout;
209 
210  iov[0].iov_base = msgbuf;
211  iov[0].iov_len = 5;
212  mh.msg_iov = iov;
213  mh.msg_iovlen = 1;
214  mh.msg_control = ctl;
215  mh.msg_controllen = sizeof (ctl);
216 
217  memset (ctl, 0, sizeof (ctl));
218 
219  if (wait != ~0)
220  {
221  timeout = clib_time_now (&scm->clib_time) + wait;
222  while (size != 5 && clib_time_now (&scm->clib_time) < timeout)
223  size = recvmsg (socket_fd, &mh, MSG_DONTWAIT);
224  }
225  else
226  size = recvmsg (socket_fd, &mh, 0);
227 
228  if (size != 5)
229  {
230  return (size == 0) ? clib_error_return (0, "disconnected") :
231  clib_error_return_unix (0, "recvmsg: malformed message (fd %d)",
232  socket_fd);
233  }
234 
235  cmsg = CMSG_FIRSTHDR (&mh);
236  while (cmsg)
237  {
238  if (cmsg->cmsg_level == SOL_SOCKET)
239  {
240  if (cmsg->cmsg_type == SCM_CREDENTIALS)
241  {
242  cr = (struct ucred *) CMSG_DATA (cmsg);
243  uid = cr->uid;
244  gid = cr->gid;
245  pid = cr->pid;
246  }
247  else if (cmsg->cmsg_type == SCM_RIGHTS)
248  {
249  clib_memcpy (fds, CMSG_DATA (cmsg), sizeof (int) * n_fds);
250  }
251  }
252  cmsg = CMSG_NXTHDR (&mh, cmsg);
253  }
254  return 0;
255 }
256 
259 {
261  ssvm_private_t *memfd = &scm->memfd_segment;
262  i32 retval = ntohl (mp->retval);
263  api_main_t *am = &api_main;
264  clib_error_t *error;
265  int my_fd = -1;
266  u8 *new_name;
267 
268  if (retval)
269  {
270  clib_warning ("failed to init shmem");
271  return;
272  }
273 
274  /*
275  * Check the socket for the magic fd
276  */
277  error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 1, 5);
278  if (error)
279  {
280  clib_error_report (error);
281  retval = -99;
282  return;
283  }
284 
285  memset (memfd, 0, sizeof (*memfd));
286  memfd->fd = my_fd;
287 
288  /* Note: this closes memfd.fd */
289  retval = ssvm_slave_init_memfd (memfd);
290  if (retval)
291  clib_warning ("WARNING: segment map returned %d", retval);
292 
293  /*
294  * Pivot to the memory client segment that vpp just created
295  */
296  am->vlib_rp = (void *) (memfd->requested_va + MMAP_PAGESIZE);
297  am->shmem_hdr = (void *) am->vlib_rp->user_ctx;
298 
299  new_name = format (0, "%v[shm]%c", scm->name, 0);
301  vl_client_connect_to_vlib_no_map ("pvt", (char *) new_name,
302  32 /* input_queue_length */ );
304  vec_free (new_name);
305 }
306 
307 static void
309 {
311  if (!mp->response)
312  {
313  scm->socket_enable = 1;
314  scm->client_index = clib_net_to_host_u32 (mp->index);
315  }
316 }
317 
318 #define foreach_sock_client_api_msg \
319 _(SOCKCLNT_CREATE_REPLY, sockclnt_create_reply) \
320 _(SOCK_INIT_SHM_REPLY, sock_init_shm_reply) \
321 
322 static void
323 noop_handler (void *notused)
324 {
325 }
326 
327 void
329 {
330 
331 #define _(N,n) \
332  vl_msg_api_set_handlers(VL_API_##N, #n, \
333  vl_api_##n##_t_handler, \
334  noop_handler, \
335  vl_api_##n##_t_endian, \
336  vl_api_##n##_t_print, \
337  sizeof(vl_api_##n##_t), 1);
339 #undef _
340 }
341 
342 int
343 vl_socket_client_connect (char *socket_path, char *client_name,
344  u32 socket_buffer_size)
345 {
348  clib_socket_t *sock;
349  clib_error_t *error;
350 
351  /* Already connected? */
352  if (scm->socket_fd)
353  return (-2);
354 
355  /* bogus call? */
356  if (socket_path == 0 || client_name == 0)
357  return (-3);
358 
359  sock = &scm->client_socket;
360  sock->config = socket_path;
362 
363  if ((error = clib_socket_init (sock)))
364  {
365  clib_error_report (error);
366  return (-1);
367  }
368 
370 
371  scm->socket_fd = sock->fd;
372  scm->socket_buffer_size = socket_buffer_size ? socket_buffer_size :
376  _vec_len (scm->socket_rx_buffer) = 0;
377  _vec_len (scm->socket_tx_buffer) = 0;
378  scm->name = format (0, "%s", client_name);
379 
380  mp = vl_socket_client_msg_alloc (sizeof (*mp));
381  mp->_vl_msg_id = htons (VL_API_SOCKCLNT_CREATE);
382  strncpy ((char *) mp->name, client_name, sizeof (mp->name) - 1);
383  mp->name[sizeof (mp->name) - 1] = 0;
384  mp->context = 0xfeedface;
385 
386  clib_time_init (&scm->clib_time);
387 
388  if (vl_socket_client_write () <= 0)
389  return (-1);
390 
391  if (vl_socket_client_read (5))
392  return (-1);
393 
394  return (0);
395 }
396 
397 int
399 {
402  int rv, i;
403  u64 *cfg;
404 
405  mp = vl_socket_client_msg_alloc (sizeof (*mp) +
406  vec_len (config) * sizeof (u64));
407  memset (mp, 0, sizeof (*mp));
408  mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SOCK_INIT_SHM);
409  mp->client_index = clib_host_to_net_u32 (scm->client_index);
410  mp->requested_size = 64 << 20;
411 
412  if (config)
413  {
414  for (i = 0; i < vec_len (config); i++)
415  {
416  cfg = (u64 *) & config[i];
417  mp->configs[i] = *cfg;
418  }
419  mp->nitems = vec_len (config);
420  }
421  rv = vl_socket_client_write ();
422  if (rv <= 0)
423  return rv;
424 
425  if (vl_socket_client_read (1))
426  return -1;
427 
428  return 0;
429 }
430 
431 clib_error_t *
432 vl_socket_client_recv_fd_msg (int fds[], int n_fds, u32 wait)
433 {
435  if (!scm->socket_fd)
436  return clib_error_return (0, "no socket");
437  return vl_sock_api_recv_fd_msg (scm->client_socket.fd, fds, n_fds, wait);
438 }
439 
440 /*
441  * fd.io coding-style-patch-verification: ON
442  *
443  * Local Variables:
444  * eval: (c-set-style "gnu")
445  * End:
446  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:437
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)
static f64 clib_time_now(clib_time_t *c)
Definition: time.h:213
int i
clib_error_t * clib_socket_init(clib_socket_t *s)
Definition: socket.c:383
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:137
u8 data[0]
actual message begins here
Definition: api_common.h:140
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)
memset(h->entries, 0, sizeof(h->entries[0])*entries)
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:254
#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:264
unsigned int u32
Definition: types.h:88
u64 configs[nitems]
Definition: memclnt.api:193
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:201
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:339
#define clib_warning(format, args...)
Definition: error.h:59
#define clib_memcpy(a, b, c)
Definition: string.h:75
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:786
u32 data_len
message length not including header
Definition: api_common.h:138
Message header structure.
Definition: api_common.h:135
#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