ip6: set local flag on outbound echo reply
[vpp.git] / src / vpp / app / vpp_prometheus_export.c
1 /*
2  *------------------------------------------------------------------
3  * vpp_get_stats.c
4  *
5  * Copyright (c) 2018 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 <arpa/inet.h>
21 #include <sys/epoll.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <netdb.h>
30 #include <sys/socket.h>
31 #include <vpp-api/client/stat_client.h>
32 #include <vlib/vlib.h>
33 #include <ctype.h>
34
35 /* https://github.com/prometheus/prometheus/wiki/Default-port-allocations */
36 #define SERVER_PORT 9482
37
38 static char *
39 prom_string (char *s)
40 {
41   char *p = s;
42   while (*p)
43     {
44       if (!isalnum (*p))
45         *p = '_';
46       p++;
47     }
48   return s;
49 }
50
51 static void
52 dump_metrics (FILE * stream, u8 ** patterns)
53 {
54   stat_segment_data_t *res;
55   int i, j, k;
56   static u32 *stats = 0;
57
58 retry:
59   res = stat_segment_dump (stats);
60   if (res == 0)
61     {                           /* Memory layout has changed */
62       if (stats)
63         vec_free (stats);
64       stats = stat_segment_ls (patterns);
65       goto retry;
66     }
67
68   for (i = 0; i < vec_len (res); i++)
69     {
70       switch (res[i].type)
71         {
72         case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
73           fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
74           for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
75             for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
76               fformat (stream, "%s{thread=\"%d\",interface=\"%d\"} %lld\n",
77                        prom_string (res[i].name), k, j,
78                        res[i].simple_counter_vec[k][j]);
79           break;
80
81         case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
82           fformat (stream, "# TYPE %s_packets counter\n",
83                    prom_string (res[i].name));
84           fformat (stream, "# TYPE %s_bytes counter\n",
85                    prom_string (res[i].name));
86           for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
87             for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
88               {
89                 fformat (stream,
90                          "%s_packets{thread=\"%d\",interface=\"%d\"} %lld\n",
91                          prom_string (res[i].name), k, j,
92                          res[i].combined_counter_vec[k][j].packets);
93                 fformat (stream,
94                          "%s_bytes{thread=\"%d\",interface=\"%d\"} %lld\n",
95                          prom_string (res[i].name), k, j,
96                          res[i].combined_counter_vec[k][j].bytes);
97               }
98           break;
99         case STAT_DIR_TYPE_ERROR_INDEX:
100           for (j = 0; j < vec_len (res[i].error_vector); j++)
101             {
102               fformat (stream, "# TYPE %s counter\n",
103                        prom_string (res[i].name));
104               fformat (stream, "%s{thread=\"%d\"} %lld\n",
105                        prom_string (res[i].name), j, res[i].error_vector[j]);
106             }
107           break;
108
109         case STAT_DIR_TYPE_SCALAR_INDEX:
110           fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
111           fformat (stream, "%s %.2f\n", prom_string (res[i].name),
112                    res[i].scalar_value);
113           break;
114
115         case STAT_DIR_TYPE_NAME_VECTOR:
116           fformat (stream, "# TYPE %s_info gauge\n",
117                    prom_string (res[i].name));
118           for (k = 0; k < vec_len (res[i].name_vector); k++)
119             if (res[i].name_vector[k])
120               fformat (stream, "%s_info{index=\"%d\",name=\"%s\"} 1\n",
121                        prom_string (res[i].name), k, res[i].name_vector[k]);
122           break;
123
124         case STAT_DIR_TYPE_EMPTY:
125           break;
126
127         default:
128           fformat (stderr, "Unknown value %d\n", res[i].type);
129           ;
130         }
131     }
132   stat_segment_data_free (res);
133
134 }
135
136
137 #define ROOTPAGE  "<html><head><title>Metrics exporter</title></head><body><ul><li><a href=\"/metrics\">metrics</a></li></ul></body></html>"
138 #define NOT_FOUND_ERROR "<html><head><title>Document not found</title></head><body><h1>404 - Document not found</h1></body></html>"
139
140 static void
141 http_handler (FILE * stream, u8 ** patterns)
142 {
143   char status[80] = { 0 };
144   if (fgets (status, sizeof (status) - 1, stream) == 0)
145     {
146       fprintf (stderr, "fgets error: %s %s\n", status, strerror (errno));
147       return;
148     }
149   char *saveptr;
150   char *method = strtok_r (status, " \t\r\n", &saveptr);
151   if (method == 0 || strncmp (method, "GET", 4) != 0)
152     {
153       fputs ("HTTP/1.0 405 Method Not Allowed\r\n", stream);
154       return;
155     }
156   char *request_uri = strtok_r (NULL, " \t", &saveptr);
157   char *protocol = strtok_r (NULL, " \t\r\n", &saveptr);
158   if (protocol == 0 || strncmp (protocol, "HTTP/1.", 7) != 0)
159     {
160       fputs ("HTTP/1.0 400 Bad Request\r\n", stream);
161       return;
162     }
163   /* Read the other headers */
164   for (;;)
165     {
166       char header[1024];
167       if (fgets (header, sizeof (header) - 1, stream) == 0)
168         {
169           fprintf (stderr, "fgets error: %s\n", strerror (errno));
170           return;
171         }
172       if (header[0] == '\n' || header[1] == '\n')
173         {
174           break;
175         }
176     }
177   if (strcmp (request_uri, "/") == 0)
178     {
179       fprintf (stream, "HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n",
180                (unsigned long) strlen (ROOTPAGE));
181       fputs (ROOTPAGE, stream);
182       return;
183     }
184   if (strcmp (request_uri, "/metrics") != 0)
185     {
186       fprintf (stream,
187                "HTTP/1.0 404 Not Found\r\nContent-Length: %lu\r\n\r\n",
188                (unsigned long) strlen (NOT_FOUND_ERROR));
189       fputs (NOT_FOUND_ERROR, stream);
190       return;
191     }
192   fputs ("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", stream);
193   dump_metrics (stream, patterns);
194 }
195
196 static int
197 start_listen (u16 port)
198 {
199   struct sockaddr_in6 serveraddr;
200   int addrlen = sizeof (serveraddr);
201   int enable = 1;
202
203   int listenfd = socket (AF_INET6, SOCK_STREAM, 0);
204   if (listenfd == -1)
205     {
206       perror ("Failed opening socket");
207       return -1;
208     }
209
210   int rv =
211     setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (int));
212   if (rv < 0)
213     {
214       perror ("Failed setsockopt");
215       close (listenfd);
216       return -1;
217     }
218
219   clib_memset (&serveraddr, 0, sizeof (serveraddr));
220   serveraddr.sin6_family = AF_INET6;
221   serveraddr.sin6_port = htons (port);
222   serveraddr.sin6_addr = in6addr_any;
223
224   if (bind (listenfd, (struct sockaddr *) &serveraddr, addrlen) < 0)
225     {
226       fprintf (stderr, "bind() error %s\n", strerror (errno));
227       close (listenfd);
228       return -1;
229     }
230   if (listen (listenfd, 1000000) != 0)
231     {
232       fprintf (stderr, "listen() error for %s\n", strerror (errno));
233       close (listenfd);
234       return -1;
235     }
236   return listenfd;
237 }
238
239 /* Socket epoll, linux-specific */
240 union my_sockaddr
241 {
242   struct sockaddr_storage storage;
243   struct sockaddr addr;
244   struct sockaddr_in sin_addr;
245   struct sockaddr_in6 sin6_addr;
246 };
247
248
249
250 int
251 main (int argc, char **argv)
252 {
253   unformat_input_t _argv, *a = &_argv;
254   u8 *stat_segment_name, *pattern = 0, **patterns = 0;
255   int rv;
256
257   /* Allocating 32MB heap */
258   clib_mem_init (0, 32 << 20);
259
260   unformat_init_command_line (a, argv);
261
262   stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
263
264   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
265     {
266       if (unformat (a, "socket-name %s", &stat_segment_name))
267         ;
268       else if (unformat (a, "%s", &pattern))
269         {
270           vec_add1 (patterns, pattern);
271         }
272       else
273         {
274           fformat (stderr,
275                    "%s: usage [socket-name <name>] <patterns> ...\n",
276                    argv[0]);
277           exit (1);
278         }
279     }
280
281   if (vec_len (patterns) == 0)
282     {
283       fformat (stderr,
284                "%s: usage [socket-name <name>] <patterns> ...\n", argv[0]);
285       exit (1);
286     }
287
288   rv = stat_segment_connect ((char *) stat_segment_name);
289   if (rv)
290     {
291       fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
292                stat_segment_name);
293       exit (1);
294     }
295
296   int fd = start_listen (SERVER_PORT);
297   if (fd < 0)
298     {
299       exit (1);
300     }
301   for (;;)
302     {
303       int conn_sock = accept (fd, NULL, NULL);
304       if (conn_sock < 0)
305         {
306           fprintf (stderr, "Accept failed: %s", strerror (errno));
307           continue;
308         }
309       else
310         {
311           struct sockaddr_in6 clientaddr = { 0 };
312           char address[INET6_ADDRSTRLEN];
313           socklen_t addrlen;
314           getpeername (conn_sock, (struct sockaddr *) &clientaddr, &addrlen);
315           if (inet_ntop
316               (AF_INET6, &clientaddr.sin6_addr, address, sizeof (address)))
317             {
318               fprintf (stderr, "Client address is [%s]:%d\n", address,
319                        ntohs (clientaddr.sin6_port));
320             }
321         }
322
323       FILE *stream = fdopen (conn_sock, "r+");
324       if (stream == NULL)
325         {
326           fprintf (stderr, "fdopen error: %s\n", strerror (errno));
327           close (conn_sock);
328           continue;
329         }
330       /* Single reader at the moment */
331       http_handler (stream, patterns);
332       fclose (stream);
333     }
334
335   stat_segment_disconnect ();
336   close (fd);
337
338   exit (0);
339 }
340
341 /*
342  * fd.io coding-style-patch-verification: ON
343  *
344  * Local Variables:
345  * eval: (c-set-style "gnu")
346  * End:
347  */