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