udp: fix csum computation when offload disabled
[vpp.git] / src / vpp / app / vppctl.c
1 /*
2  * Copyright (c) 2017 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 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <sys/epoll.h>
19 #include <sys/ioctl.h>
20 #include <signal.h>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #define DEBUG 0
29
30 #if DEBUG
31 #define TELCMDS
32 #define TELOPTS
33 #endif
34
35 #include <vppinfra/clib.h>
36 #include <arpa/telnet.h>
37 #ifndef STATIC_VPPCTL
38 #include <vpp/vnet/config.h>
39 #endif
40
41 #define SOCKET_FILE "/run/vpp/cli.sock"
42
43 volatile int window_resized = 0;
44 struct termios orig_tio;
45
46 static void
47 send_ttype (int sock_fd, int is_interactive)
48 {
49   char *term;
50   static char buf[2048];
51
52   /* wipe the buffer so there is no potential
53    * for inter-invocation leakage */
54   memset (buf, 0, sizeof (buf));
55
56   term = is_interactive ? getenv ("TERM") : "vppctl";
57   if (term == NULL)
58     term = "dumb";
59
60   int len = snprintf (buf, sizeof (buf),
61                       "%c%c%c"
62                       "%c%s"
63                       "%c%c",
64                       IAC, SB, TELOPT_TTYPE, 0, term, IAC, SE);
65   if (send (sock_fd, buf, len, 0) < 0)
66     {
67       perror ("send_ttype");
68     }
69 }
70
71 static void
72 send_naws (int sock_fd)
73 {
74   struct winsize ws;
75   static char buf[2048];
76
77   memset (buf, 0, sizeof (buf));
78   if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
79     {
80       fprintf (stderr, "ioctl(TIOCGWINSZ)");
81       return;
82     }
83
84   int len = snprintf (buf, sizeof (buf),
85                       "%c%c%c"
86                       "%c%c%c%c"
87                       "%c%c",
88                       IAC, SB, TELOPT_NAWS, ws.ws_col >> 8, ws.ws_col & 0xff,
89                       ws.ws_row >> 8, ws.ws_row & 0xff, IAC, SE);
90   int n_written = write (sock_fd, buf, len);
91   if (n_written < len)
92     {
93       perror ("send_naws");
94     }
95 }
96
97 static void
98 signal_handler_winch (int signum)
99 {
100   window_resized = 1;
101 }
102
103 static void
104 signal_handler_term (int signum)
105 {
106   tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
107 }
108
109 static int
110 process_input (int sock_fd, unsigned char *rx_buf, int rx_buf_len,
111                int is_interactive, int *sent_ttype)
112 {
113   int i = 0;
114   int j = 0;
115
116   while (i < rx_buf_len)
117     {
118       if (rx_buf[i] == IAC)
119         {
120           if (rx_buf[i + 1] == SB)
121             {
122               char opt = rx_buf[i + 2];
123               i += 3;
124 #if DEBUG
125               if (rx_buf[i] != IAC)
126                 {
127                   fprintf (stderr, "SB ");
128                 }
129               while (rx_buf[i] != IAC && i < rx_buf_len)
130                 fprintf (stderr, "%02x ", rx_buf[i++]);
131               fprintf (stderr, "\n");
132 #else
133               while (rx_buf[i] != IAC && i < rx_buf_len)
134                 {
135                   i++;
136                 }
137 #endif
138               i += 2;
139               if (opt == TELOPT_TTYPE)
140                 {
141                   send_ttype (sock_fd, is_interactive);
142                   *sent_ttype = 1;
143                 }
144               else if (is_interactive && opt == TELOPT_NAWS)
145                 send_naws (sock_fd);
146             }
147           else
148             {
149 #if DEBUG
150               fprintf (stderr, "IAC at %d, IAC %s %s", i,
151                        TELCMD (rx_buf[i + 1]), TELOPT (rx_buf[i + 2]));
152 #endif
153               i += 3;
154             }
155         }
156       else
157         {
158           /* i is always the same or ahead of j, so at worst this is a no-op */
159           rx_buf[j] = rx_buf[i];
160           i++;
161           j++;
162         }
163     }
164   return j;
165 }
166
167 #if !defined(STATIC_VPPCTL) && defined(CLIB_SANITIZE_ADDR)
168 /* default options for Address Sanitizer */
169 const char *
170 __asan_default_options (void)
171 {
172   return VPP_SANITIZE_ADDR_OPTIONS;
173 }
174 #endif /* CLIB_SANITIZE_ADDR */
175
176 int
177 main (int argc, char *argv[])
178 {
179   struct epoll_event event;
180   struct sigaction sa;
181   struct termios tio;
182   int efd = -1;
183   char *cmd = 0;
184   unsigned long cmd_len = 0;
185   int do_quit = 0;
186   int is_interactive = 0;
187   int acked = 1;                /* counts messages from VPP; starts at 1 */
188   int sent_ttype = 0;
189   char *sock_fname = SOCKET_FILE;
190   int sock_fd = -1;
191   int error = 0;
192   int arg = 0;
193
194   /* process command line */
195   argc--;
196   argv++;
197
198   if (argc > 1 && strncmp (argv[0], "-s", 2) == 0)
199     {
200       sock_fname = argv[1];
201       argc -= 2;
202       argv += 2;
203     }
204
205   struct sockaddr_un saddr = { 0 };
206   saddr.sun_family = AF_UNIX;
207
208   if (strlen (sock_fname) > sizeof (saddr.sun_path) - 1)
209     {
210       perror ("socket path too long");
211       exit (1);
212     }
213
214   strncpy (saddr.sun_path, sock_fname, sizeof (saddr.sun_path) - 1);
215
216   sock_fd = socket (AF_UNIX, SOCK_STREAM, 0);
217   if (sock_fd < 0)
218     {
219       perror ("socket");
220       exit (1);
221     }
222
223   if (connect (sock_fd, (struct sockaddr *) &saddr, sizeof (saddr)) < 0)
224     {
225       perror ("connect");
226       exit (1);
227     }
228
229   for (arg = 0; arg < argc; arg++)
230     {
231       cmd_len += strlen (argv[arg]) + 1;
232     }
233   if (cmd_len > 0)
234     {
235       cmd_len++; // account for 0 at end
236       cmd = malloc (cmd_len);
237       if (!cmd)
238         {
239           error = errno;
240           perror ("malloc failed");
241           goto done;
242         }
243       memset (cmd, 0, cmd_len);
244       unsigned long space_left = cmd_len - 1; // reserve space for 0 at end
245       while (argc--)
246         {
247           strncat (cmd, *argv, space_left);
248           space_left -= strlen (*argv);
249           ++argv;
250           strncat (cmd, " ", space_left);
251           --space_left;
252         }
253       cmd[cmd_len - 2] = '\n';
254       cmd[cmd_len - 1] = 0;
255     }
256
257   is_interactive = isatty (STDIN_FILENO) && cmd == 0;
258
259   if (is_interactive)
260     {
261       /* Capture terminal resize events */
262       memset (&sa, 0, sizeof (struct sigaction));
263       sa.sa_handler = signal_handler_winch;
264       if (sigaction (SIGWINCH, &sa, 0) < 0)
265         {
266           error = errno;
267           perror ("sigaction for SIGWINCH");
268           goto done;
269         }
270
271       /* Capture SIGTERM to reset tty settings */
272       sa.sa_handler = signal_handler_term;
273       if (sigaction (SIGTERM, &sa, 0) < 0)
274         {
275           error = errno;
276           perror ("sigaction for SIGTERM");
277           goto done;
278         }
279
280       /* Save the original tty state so we can restore it later */
281       if (tcgetattr (STDIN_FILENO, &orig_tio) < 0)
282         {
283           error = errno;
284           perror ("tcgetattr");
285           goto done;
286         }
287
288       /* Tweak the tty settings */
289       tio = orig_tio;
290       /* echo off, canonical mode off, ext'd input processing off */
291       tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
292       tio.c_cc[VMIN] = 1;       /* 1 byte at a time */
293       tio.c_cc[VTIME] = 0;      /* no timer */
294
295       if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio) < 0)
296         {
297           error = errno;
298           perror ("tcsetattr");
299           goto done;
300         }
301     }
302
303   efd = epoll_create1 (0);
304
305   /* register STDIN */
306   if (cmd == 0)
307     {
308       event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
309       event.data.fd = STDIN_FILENO;
310       if (epoll_ctl (efd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0)
311         {
312           /* ignore EPERM; it means stdin is something like /dev/null */
313           if (errno != EPERM)
314             {
315               error = errno;
316               fprintf (stderr, "epoll_ctl[%d]", STDIN_FILENO);
317               perror (0);
318               goto done;
319             }
320         }
321     }
322
323   /* register socket */
324   event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
325   event.data.fd = sock_fd;
326   if (epoll_ctl (efd, EPOLL_CTL_ADD, sock_fd, &event) != 0)
327     {
328       error = errno;
329       fprintf (stderr, "epoll_ctl[%d]", sock_fd);
330       perror (0);
331       goto done;
332     }
333
334   while (1)
335     {
336       int n;
337       static int sent_cmd = 0;
338
339       if (window_resized)
340         {
341           window_resized = 0;
342           send_naws (sock_fd);
343         }
344
345       if ((n = epoll_wait (efd, &event, 1, -1)) < 0)
346         {
347           /* maybe we received signal */
348           if (errno == EINTR)
349             continue;
350
351           error = errno;
352           perror ("epoll_wait");
353           goto done;
354         }
355
356       if (n == 0)
357         continue;
358
359       if (event.data.fd == STDIN_FILENO && cmd == 0)
360         {
361           int n;
362           char c[100];
363
364           if (!sent_ttype)
365             continue;           /* not ready for this yet */
366
367           n = read (STDIN_FILENO, c, sizeof (c));
368           if (n > 0)
369             {
370               int n_written = write (sock_fd, c, n);
371               if (n_written < n)
372                 error = errno;
373               if (error)
374                 goto done;
375             }
376           else if (n < 0)
377             fprintf (stderr, "read rv=%d", n);
378           else /* EOF */
379             do_quit = 1;
380         }
381       else if (event.data.fd == sock_fd)
382         {
383           unsigned char rx_buf[100];
384           memset (rx_buf, 0, sizeof (rx_buf));
385           int nread = recv (sock_fd, rx_buf, sizeof (rx_buf), 0);
386
387           if (nread < 0)
388             error = errno;
389           if (error)
390             break;
391
392           if (nread == 0)
393             break;
394
395           int len = process_input (sock_fd, rx_buf, nread, is_interactive,
396                                    &sent_ttype);
397
398           if (len > 0)
399             {
400               unsigned char *p = rx_buf, *q = rx_buf;
401
402               while (len)
403                 {
404                   /* Search for and skip NUL bytes */
405                   while (q < (p + len) && *q)
406                     q++;
407
408                   n = write (STDOUT_FILENO, p, q - p);
409                   if (n < 0)
410                     {
411                       error = errno;
412                       perror ("write");
413                       goto done;
414                     }
415
416                   while (q < (p + len) && !*q)
417                     {
418                       q++;
419                       acked++;  /* every NUL is an acknowledgement */
420                     }
421                   len -= q - p;
422                   p = q;
423                 }
424             }
425
426           if (do_quit && do_quit < acked)
427             {
428               /* Ask the other end to close the connection */
429               char quit_str[] = "quit\n";
430               int n = write (sock_fd, quit_str, strlen (quit_str));
431               if (n < strlen (quit_str))
432                 {
433                   error = errno;
434                   perror ("write quit");
435                 }
436               do_quit = 0;
437             }
438           if (cmd && sent_ttype && !sent_cmd)
439             {
440               /* We wait until after the TELNET TTYPE option has been sent.
441                * That is to make sure the session at the VPP end has switched
442                * to line-by-line mode, and thus avoid prompts and echoing.
443                * Note that it does also disable further TELNET option processing.
444                */
445               int n_written = write (sock_fd, cmd, strlen (cmd) + 1);
446               sent_cmd = 1;
447               if (n_written < strlen (cmd))
448                 {
449                   error = errno;
450                   perror ("write command");
451                   goto done;
452                 }
453               do_quit = acked;  /* quit after the next response */
454             }
455         }
456       else
457         {
458           error = errno;
459           perror ("unknown fd");
460           goto done;
461         }
462     }
463
464   close (sock_fd);
465
466 done:
467   free (cmd);
468   if (efd > -1)
469     close (efd);
470
471   if (is_interactive)
472     tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
473
474   if (error)
475     {
476       return 1;
477     }
478
479   return 0;
480 }
481
482
483 /*
484  * fd.io coding-style-patch-verification: ON
485  *
486  * Local Variables:
487  * eval: (c-set-style "gnu")
488  * End:
489  */