Print UDP RTT on its own line.
[tinc] / src / info.c
1 /*
2     info.c -- Show information about a node, subnet or address
3     Copyright (C) 2012-2017 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include "control_common.h"
23 #include "list.h"
24 #include "subnet.h"
25 #include "tincctl.h"
26 #include "info.h"
27 #include "utils.h"
28 #include "xalloc.h"
29
30 void logger(int level, int priority, const char *format, ...) {
31         va_list ap;
32         va_start(ap, format);
33         vfprintf(stderr, format, ap);
34         va_end(ap);
35         fputc('\n', stderr);
36 }
37
38 char *strip_weight(char *netstr) {
39         int len = strlen(netstr);
40
41         if(len >= 3 && !strcmp(netstr + len - 3, "#10")) {
42                 netstr[len - 3] = 0;
43         }
44
45         return netstr;
46 }
47
48 static int info_node(int fd, const char *item) {
49         // Check the list of nodes
50         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_NODES, item);
51
52         bool found = false;
53         char line[4096];
54
55         char node[4096];
56         char id[4096];
57         char from[4096];
58         char to[4096];
59         char subnet[4096];
60         char host[4096];
61         char port[4096];
62         char via[4096];
63         char nexthop[4096];
64         int code, req, cipher, digest, maclength, compression, distance;
65         short int pmtu, minmtu, maxmtu;
66         unsigned int options;
67         union {
68                 node_status_t bits;
69                 uint32_t raw;
70         } status_union;
71         node_status_t status;
72         long int last_state_change;
73         int udp_ping_rtt;
74         uint64_t in_packets, in_bytes, out_packets, out_bytes;
75
76         while(recvline(fd, line, sizeof(line))) {
77                 int n = sscanf(line, "%d %d %4095s %4095s %4095s port %4095s %d %d %d %d %x %"PRIx32" %4095s %4095s %d %hd %hd %hd %ld %d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, &code, &req, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_union.raw, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change, &udp_ping_rtt, &in_packets, &in_bytes, &out_packets, &out_bytes);
78
79                 if(n == 2) {
80                         break;
81                 }
82
83                 if(n != 24) {
84                         fprintf(stderr, "Unable to parse node dump from tincd.\n");
85                         return 1;
86                 }
87
88                 if(!strcmp(node, item)) {
89                         found = true;
90                         break;
91                 }
92         }
93
94         if(!found) {
95                 fprintf(stderr, "Unknown node %s.\n", item);
96                 return 1;
97         }
98
99         while(recvline(fd, line, sizeof(line))) {
100                 if(sscanf(line, "%d %d %4095s", &code, &req, node) == 2) {
101                         break;
102                 }
103         }
104
105         printf("Node:         %s\n", item);
106         printf("Node ID:      %s\n", id);
107         printf("Address:      %s port %s\n", host, port);
108
109         char timestr[32] = "never";
110         time_t lsc_time = last_state_change;
111
112         if(last_state_change) {
113                 strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", localtime(&lsc_time));
114         }
115
116         status = status_union.bits;
117
118         if(status.reachable) {
119                 printf("Online since: %s\n", timestr);
120         } else {
121                 printf("Last seen:    %s\n", timestr);
122         }
123
124         printf("Status:      ");
125
126         if(status.validkey) {
127                 printf(" validkey");
128         }
129
130         if(status.visited) {
131                 printf(" visited");
132         }
133
134         if(status.reachable) {
135                 printf(" reachable");
136         }
137
138         if(status.indirect) {
139                 printf(" indirect");
140         }
141
142         if(status.sptps) {
143                 printf(" sptps");
144         }
145
146         if(status.udp_confirmed) {
147                 printf(" udp_confirmed");
148         }
149
150         printf("\n");
151
152         printf("Options:     ");
153
154         if(options & OPTION_INDIRECT) {
155                 printf(" indirect");
156         }
157
158         if(options & OPTION_TCPONLY) {
159                 printf(" tcponly");
160         }
161
162         if(options & OPTION_PMTU_DISCOVERY) {
163                 printf(" pmtu_discovery");
164         }
165
166         if(options & OPTION_CLAMP_MSS) {
167                 printf(" clamp_mss");
168         }
169
170         printf("\n");
171         printf("Protocol:     %d.%d\n", PROT_MAJOR, OPTION_VERSION(options));
172         printf("Reachability: ");
173
174         if(!strcmp(host, "MYSELF")) {
175                 printf("can reach itself\n");
176         } else if(!status.reachable) {
177                 printf("unreachable\n");
178         } else if(strcmp(via, item)) {
179                 printf("indirectly via %s\n", via);
180         } else if(!status.validkey) {
181                 printf("unknown\n");
182         } else if(minmtu > 0) {
183                 printf("directly with UDP\nPMTU:         %d\n", pmtu);
184
185                 if(udp_ping_rtt != -1) {
186                         printf("RTT:          %d.%03d\n", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
187                 }
188         } else if(!strcmp(nexthop, item)) {
189                 printf("directly with TCP\n");
190         } else {
191                 printf("none, forwarded via %s\n", nexthop);
192         }
193
194         printf("RX:           %"PRIu64" packets  %"PRIu64" bytes\n", in_packets, in_bytes);
195         printf("TX:           %"PRIu64" packets  %"PRIu64" bytes\n", out_packets, out_bytes);
196
197         // List edges
198         printf("Edges:       ");
199         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_EDGES, item);
200
201         while(recvline(fd, line, sizeof(line))) {
202                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, from, to);
203
204                 if(n == 2) {
205                         break;
206                 }
207
208                 if(n != 4) {
209                         fprintf(stderr, "Unable to parse edge dump from tincd.\n%s\n", line);
210                         return 1;
211                 }
212
213                 if(!strcmp(from, item)) {
214                         printf(" %s", to);
215                 }
216         }
217
218         printf("\n");
219
220         // List subnets
221         printf("Subnets:     ");
222         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
223
224         while(recvline(fd, line, sizeof(line))) {
225                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, subnet, from);
226
227                 if(n == 2) {
228                         break;
229                 }
230
231                 if(n != 4) {
232                         fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
233                         return 1;
234                 }
235
236                 if(!strcmp(from, item)) {
237                         printf(" %s", strip_weight(subnet));
238                 }
239         }
240
241         printf("\n");
242
243         return 0;
244 }
245
246 static int info_subnet(int fd, const char *item) {
247         subnet_t subnet, find;
248
249         if(!str2net(&find, item)) {
250                 fprintf(stderr, "Could not parse subnet or address '%s'.\n", item);
251                 return 1;
252         }
253
254         bool address = !strchr(item, '/');
255         bool weight = strchr(item, '#');
256         bool found = false;
257
258         char line[4096];
259         char netstr[4096];
260         char owner[4096];
261
262         int code, req;
263
264         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
265
266         while(recvline(fd, line, sizeof(line))) {
267                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, netstr, owner);
268
269                 if(n == 2) {
270                         break;
271                 }
272
273                 if(n != 4 || !str2net(&subnet, netstr)) {
274                         fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
275                         return 1;
276                 }
277
278                 if(find.type != subnet.type) {
279                         continue;
280                 }
281
282                 if(weight) {
283                         if(find.weight != subnet.weight) {
284                                 continue;
285                         }
286                 }
287
288                 if(find.type == SUBNET_IPV4) {
289                         if(address) {
290                                 if(maskcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, subnet.net.ipv4.prefixlength)) {
291                                         continue;
292                                 }
293                         } else {
294                                 if(find.net.ipv4.prefixlength != subnet.net.ipv4.prefixlength) {
295                                         continue;
296                                 }
297
298                                 if(memcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, sizeof(subnet.net.ipv4))) {
299                                         continue;
300                                 }
301                         }
302                 } else if(find.type == SUBNET_IPV6) {
303                         if(address) {
304                                 if(maskcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, subnet.net.ipv6.prefixlength)) {
305                                         continue;
306                                 }
307                         } else {
308                                 if(find.net.ipv6.prefixlength != subnet.net.ipv6.prefixlength) {
309                                         continue;
310                                 }
311
312                                 if(memcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, sizeof(subnet.net.ipv6))) {
313                                         continue;
314                                 }
315                         }
316                 }
317
318                 if(find.type == SUBNET_MAC) {
319                         if(memcmp(&find.net.mac.address, &subnet.net.mac.address, sizeof(subnet.net.mac))) {
320                                 continue;
321                         }
322                 }
323
324                 found = true;
325                 printf("Subnet: %s\n", strip_weight(netstr));
326                 printf("Owner:  %s\n", owner);
327         }
328
329         if(!found) {
330                 if(address) {
331                         fprintf(stderr, "Unknown address %s.\n", item);
332                 } else {
333                         fprintf(stderr, "Unknown subnet %s.\n", item);
334                 }
335
336                 return 1;
337         }
338
339         return 0;
340 }
341
342 int info(int fd, const char *item) {
343         if(check_id(item)) {
344                 return info_node(fd, item);
345         }
346
347         if(strchr(item, '.') || strchr(item, ':')) {
348                 return info_subnet(fd, item);
349         }
350
351         fprintf(stderr, "Argument is not a node name, subnet or address.\n");
352         return 1;
353 }