dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / src / vppinfra / socket.c
1 /*
2  * Copyright (c) 2015 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 /*
16   Copyright (c) 2001, 2002, 2003, 2005 Eliot Dresselhaus
17
18   Permission is hereby granted, free of charge, to any person obtaining
19   a copy of this software and associated documentation files (the
20   "Software"), to deal in the Software without restriction, including
21   without limitation the rights to use, copy, modify, merge, publish,
22   distribute, sublicense, and/or sell copies of the Software, and to
23   permit persons to whom the Software is furnished to do so, subject to
24   the following conditions:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37
38 #include <sys/un.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <netdb.h>
44 #include <unistd.h>
45 #include <stdio.h>
46 #include <fcntl.h>
47 #include <string.h>             /* strchr */
48
49 #include <vppinfra/mem.h>
50 #include <vppinfra/vec.h>
51 #include <vppinfra/socket.h>
52 #include <vppinfra/format.h>
53 #include <vppinfra/error.h>
54
55 void
56 clib_socket_tx_add_formatted (clib_socket_t * s, char *fmt, ...)
57 {
58   va_list va;
59   va_start (va, fmt);
60   clib_socket_tx_add_va_formatted (s, fmt, &va);
61   va_end (va);
62 }
63
64 /* Return and bind to an unused port. */
65 static word
66 find_free_port (word sock)
67 {
68   word port;
69
70   for (port = IPPORT_USERRESERVED; port < 1 << 16; port++)
71     {
72       struct sockaddr_in a;
73
74       memset (&a, 0, sizeof (a));       /* Warnings be gone */
75
76       a.sin_family = PF_INET;
77       a.sin_addr.s_addr = INADDR_ANY;
78       a.sin_port = htons (port);
79
80       if (bind (sock, (struct sockaddr *) &a, sizeof (a)) >= 0)
81         break;
82     }
83
84   return port < 1 << 16 ? port : -1;
85 }
86
87 /* Convert a config string to a struct sockaddr and length for use
88    with bind or connect. */
89 static clib_error_t *
90 socket_config (char *config,
91                void *addr, socklen_t * addr_len, u32 ip4_default_address)
92 {
93   clib_error_t *error = 0;
94
95   if (!config)
96     config = "";
97
98   /* Anything that begins with a / is a local PF_LOCAL socket. */
99   if (config[0] == '/')
100     {
101       struct sockaddr_un *su = addr;
102       su->sun_family = PF_LOCAL;
103       clib_memcpy (&su->sun_path, config,
104                    clib_min (sizeof (su->sun_path), 1 + strlen (config)));
105       *addr_len = sizeof (su[0]);
106     }
107
108   /* Hostname or hostname:port or port. */
109   else
110     {
111       char *host_name;
112       int port = -1;
113       struct sockaddr_in *sa = addr;
114
115       host_name = 0;
116       port = -1;
117       if (config[0] != 0)
118         {
119           unformat_input_t i;
120
121           unformat_init_string (&i, config, strlen (config));
122           if (unformat (&i, "%s:%d", &host_name, &port)
123               || unformat (&i, "%s:0x%x", &host_name, &port))
124             ;
125           else if (unformat (&i, "%s", &host_name))
126             ;
127           else
128             error = clib_error_return (0, "unknown input `%U'",
129                                        format_unformat_error, &i);
130           unformat_free (&i);
131
132           if (error)
133             goto done;
134         }
135
136       sa->sin_family = PF_INET;
137       *addr_len = sizeof (sa[0]);
138       if (port != -1)
139         sa->sin_port = htons (port);
140       else
141         sa->sin_port = 0;
142
143       if (host_name)
144         {
145           struct in_addr host_addr;
146
147           /* Recognize localhost to avoid host lookup in most common cast. */
148           if (!strcmp (host_name, "localhost"))
149             sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
150
151           else if (inet_aton (host_name, &host_addr))
152             sa->sin_addr = host_addr;
153
154           else if (host_name && strlen (host_name) > 0)
155             {
156               struct hostent *host = gethostbyname (host_name);
157               if (!host)
158                 error = clib_error_return (0, "unknown host `%s'", config);
159               else
160                 clib_memcpy (&sa->sin_addr.s_addr, host->h_addr_list[0],
161                              host->h_length);
162             }
163
164           else
165             sa->sin_addr.s_addr = htonl (ip4_default_address);
166
167           vec_free (host_name);
168           if (error)
169             goto done;
170         }
171     }
172
173 done:
174   return error;
175 }
176
177 static clib_error_t *
178 default_socket_write (clib_socket_t * s)
179 {
180   clib_error_t *err = 0;
181   word written = 0;
182   word fd = 0;
183   word tx_len;
184
185   fd = s->fd;
186
187   /* Map standard input to standard output.
188      Typically, fd is a socket for which read/write both work. */
189   if (fd == 0)
190     fd = 1;
191
192   tx_len = vec_len (s->tx_buffer);
193   written = write (fd, s->tx_buffer, tx_len);
194
195   /* Ignore certain errors. */
196   if (written < 0 && !unix_error_is_fatal (errno))
197     written = 0;
198
199   /* A "real" error occurred. */
200   if (written < 0)
201     {
202       err = clib_error_return_unix (0, "write %wd bytes", tx_len);
203       vec_free (s->tx_buffer);
204       goto done;
205     }
206
207   /* Reclaim the transmitted part of the tx buffer on successful writes. */
208   else if (written > 0)
209     {
210       if (written == tx_len)
211         _vec_len (s->tx_buffer) = 0;
212       else
213         vec_delete (s->tx_buffer, written, 0);
214     }
215
216   /* If a non-fatal error occurred AND
217      the buffer is full, then we must free it. */
218   else if (written == 0 && tx_len > 64 * 1024)
219     {
220       vec_free (s->tx_buffer);
221     }
222
223 done:
224   return err;
225 }
226
227 static clib_error_t *
228 default_socket_read (clib_socket_t * sock, int n_bytes)
229 {
230   word fd, n_read;
231   u8 *buf;
232
233   /* RX side of socket is down once end of file is reached. */
234   if (sock->flags & SOCKET_RX_END_OF_FILE)
235     return 0;
236
237   fd = sock->fd;
238
239   n_bytes = clib_max (n_bytes, 4096);
240   vec_add2 (sock->rx_buffer, buf, n_bytes);
241
242   if ((n_read = read (fd, buf, n_bytes)) < 0)
243     {
244       n_read = 0;
245
246       /* Ignore certain errors. */
247       if (!unix_error_is_fatal (errno))
248         goto non_fatal;
249
250       return clib_error_return_unix (0, "read %d bytes", n_bytes);
251     }
252
253   /* Other side closed the socket. */
254   if (n_read == 0)
255     sock->flags |= SOCKET_RX_END_OF_FILE;
256
257 non_fatal:
258   _vec_len (sock->rx_buffer) += n_read - n_bytes;
259
260   return 0;
261 }
262
263 static clib_error_t *
264 default_socket_close (clib_socket_t * s)
265 {
266   if (close (s->fd) < 0)
267     return clib_error_return_unix (0, "close");
268   return 0;
269 }
270
271 static void
272 socket_init_funcs (clib_socket_t * s)
273 {
274   if (!s->write_func)
275     s->write_func = default_socket_write;
276   if (!s->read_func)
277     s->read_func = default_socket_read;
278   if (!s->close_func)
279     s->close_func = default_socket_close;
280 }
281
282 clib_error_t *
283 clib_socket_init (clib_socket_t * s)
284 {
285   union
286   {
287     struct sockaddr sa;
288     struct sockaddr_un su;
289   } addr;
290   socklen_t addr_len = 0;
291   clib_error_t *error = 0;
292   word port;
293
294   error = socket_config (s->config, &addr.sa, &addr_len,
295                          (s->flags & SOCKET_IS_SERVER
296                           ? INADDR_LOOPBACK : INADDR_ANY));
297   if (error)
298     goto done;
299
300   socket_init_funcs (s);
301
302   s->fd = socket (addr.sa.sa_family, SOCK_STREAM, 0);
303   if (s->fd < 0)
304     {
305       error = clib_error_return_unix (0, "socket");
306       goto done;
307     }
308
309   port = 0;
310   if (addr.sa.sa_family == PF_INET)
311     port = ((struct sockaddr_in *) &addr)->sin_port;
312
313   if (s->flags & SOCKET_IS_SERVER)
314     {
315       uword need_bind = 1;
316
317       if (addr.sa.sa_family == PF_INET)
318         {
319           if (port == 0)
320             {
321               port = find_free_port (s->fd);
322               if (port < 0)
323                 {
324                   error = clib_error_return (0, "no free port");
325                   goto done;
326                 }
327               need_bind = 0;
328             }
329         }
330       if (addr.sa.sa_family == PF_LOCAL)
331         unlink (((struct sockaddr_un *) &addr)->sun_path);
332
333       /* Make address available for multiple users. */
334       {
335         int v = 1;
336         if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof (v)) < 0)
337           clib_unix_warning ("setsockopt SO_REUSEADDR fails");
338       }
339
340       if (need_bind && bind (s->fd, &addr.sa, addr_len) < 0)
341         {
342           error = clib_error_return_unix (0, "bind");
343           goto done;
344         }
345
346       if (listen (s->fd, 5) < 0)
347         {
348           error = clib_error_return_unix (0, "listen");
349           goto done;
350         }
351     }
352   else
353     {
354       if ((s->flags & SOCKET_NON_BLOCKING_CONNECT)
355           && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
356         {
357           error = clib_error_return_unix (0, "fcntl NONBLOCK");
358           goto done;
359         }
360
361       if (connect (s->fd, &addr.sa, addr_len) < 0
362           && !((s->flags & SOCKET_NON_BLOCKING_CONNECT) &&
363                errno == EINPROGRESS))
364         {
365           error = clib_error_return_unix (0, "connect");
366           goto done;
367         }
368     }
369
370   return error;
371
372 done:
373   if (s->fd > 0)
374     close (s->fd);
375   return error;
376 }
377
378 clib_error_t *
379 clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
380 {
381   clib_error_t *err = 0;
382   socklen_t len = 0;
383
384   memset (client, 0, sizeof (client[0]));
385
386   /* Accept the new socket connection. */
387   client->fd = accept (server->fd, 0, 0);
388   if (client->fd < 0)
389     return clib_error_return_unix (0, "accept");
390
391   /* Set the new socket to be non-blocking. */
392   if (fcntl (client->fd, F_SETFL, O_NONBLOCK) < 0)
393     {
394       err = clib_error_return_unix (0, "fcntl O_NONBLOCK");
395       goto close_client;
396     }
397
398   /* Get peer info. */
399   len = sizeof (client->peer);
400   if (getpeername (client->fd, (struct sockaddr *) &client->peer, &len) < 0)
401     {
402       err = clib_error_return_unix (0, "getpeername");
403       goto close_client;
404     }
405
406   client->flags = SOCKET_IS_CLIENT;
407
408   socket_init_funcs (client);
409   return 0;
410
411 close_client:
412   close (client->fd);
413   return err;
414 }
415
416 /*
417  * fd.io coding-style-patch-verification: ON
418  *
419  * Local Variables:
420  * eval: (c-set-style "gnu")
421  * End:
422  */