09e65805d901c19d6e07337c4dfae9162c191ac3
[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
74         while(recvline(fd, line, sizeof(line))) {
75                 int n = sscanf(line, "%d %d %4095s %4095s %4095s port %4095s %d %d %d %d %x %"PRIx32" %4095s %4095s %d %hd %hd %hd %ld", &code, &req, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_union.raw, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
76
77                 if(n == 2) {
78                         break;
79                 }
80
81                 if(n != 19) {
82                         fprintf(stderr, "Unable to parse node dump from tincd.\n");
83                         return 1;
84                 }
85
86                 if(!strcmp(node, item)) {
87                         found = true;
88                         break;
89                 }
90         }
91
92         if(!found) {
93                 fprintf(stderr, "Unknown node %s.\n", item);
94                 return 1;
95         }
96
97         while(recvline(fd, line, sizeof(line))) {
98                 if(sscanf(line, "%d %d %4095s", &code, &req, node) == 2) {
99                         break;
100                 }
101         }
102
103         printf("Node:         %s\n", item);
104         printf("Node ID:      %s\n", id);
105         printf("Address:      %s port %s\n", host, port);
106
107         char timestr[32] = "never";
108         time_t lsc_time = last_state_change;
109
110         if(last_state_change) {
111                 strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", localtime(&lsc_time));
112         }
113
114         status = status_union.bits;
115
116         if(status.reachable) {
117                 printf("Online since: %s\n", timestr);
118         } else {
119                 printf("Last seen:    %s\n", timestr);
120         }
121
122         printf("Status:      ");
123
124         if(status.validkey) {
125                 printf(" validkey");
126         }
127
128         if(status.visited) {
129                 printf(" visited");
130         }
131
132         if(status.reachable) {
133                 printf(" reachable");
134         }
135
136         if(status.indirect) {
137                 printf(" indirect");
138         }
139
140         if(status.sptps) {
141                 printf(" sptps");
142         }
143
144         if(status.udp_confirmed) {
145                 printf(" udp_confirmed");
146         }
147
148         printf("\n");
149
150         printf("Options:     ");
151
152         if(options & OPTION_INDIRECT) {
153                 printf(" indirect");
154         }
155
156         if(options & OPTION_TCPONLY) {
157                 printf(" tcponly");
158         }
159
160         if(options & OPTION_PMTU_DISCOVERY) {
161                 printf(" pmtu_discovery");
162         }
163
164         if(options & OPTION_CLAMP_MSS) {
165                 printf(" clamp_mss");
166         }
167
168         printf("\n");
169         printf("Protocol:     %d.%d\n", PROT_MAJOR, OPTION_VERSION(options));
170         printf("Reachability: ");
171
172         if(!strcmp(host, "MYSELF")) {
173                 printf("can reach itself\n");
174         } else if(!status.reachable) {
175                 printf("unreachable\n");
176         } else if(strcmp(via, item)) {
177                 printf("indirectly via %s\n", via);
178         } else if(!status.validkey) {
179                 printf("unknown\n");
180         } else if(minmtu > 0) {
181                 printf("directly with UDP\nPMTU:         %d\n", pmtu);
182         } else if(!strcmp(nexthop, item)) {
183                 printf("directly with TCP\n");
184         } else {
185                 printf("none, forwarded via %s\n", nexthop);
186         }
187
188         // List edges
189         printf("Edges:       ");
190         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_EDGES, item);
191
192         while(recvline(fd, line, sizeof(line))) {
193                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, from, to);
194
195                 if(n == 2) {
196                         break;
197                 }
198
199                 if(n != 4) {
200                         fprintf(stderr, "Unable to parse edge dump from tincd.\n%s\n", line);
201                         return 1;
202                 }
203
204                 if(!strcmp(from, item)) {
205                         printf(" %s", to);
206                 }
207         }
208
209         printf("\n");
210
211         // List subnets
212         printf("Subnets:     ");
213         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
214
215         while(recvline(fd, line, sizeof(line))) {
216                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, subnet, from);
217
218                 if(n == 2) {
219                         break;
220                 }
221
222                 if(n != 4) {
223                         fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
224                         return 1;
225                 }
226
227                 if(!strcmp(from, item)) {
228                         printf(" %s", strip_weight(subnet));
229                 }
230         }
231
232         printf("\n");
233
234         return 0;
235 }
236
237 static int info_subnet(int fd, const char *item) {
238         subnet_t subnet, find;
239
240         if(!str2net(&find, item)) {
241                 fprintf(stderr, "Could not parse subnet or address '%s'.\n", item);
242                 return 1;
243         }
244
245         bool address = !strchr(item, '/');
246         bool weight = strchr(item, '#');
247         bool found = false;
248
249         char line[4096];
250         char netstr[4096];
251         char owner[4096];
252
253         int code, req;
254
255         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
256
257         while(recvline(fd, line, sizeof(line))) {
258                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, netstr, owner);
259
260                 if(n == 2) {
261                         break;
262                 }
263
264                 if(n != 4 || !str2net(&subnet, netstr)) {
265                         fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
266                         return 1;
267                 }
268
269                 if(find.type != subnet.type) {
270                         continue;
271                 }
272
273                 if(weight) {
274                         if(find.weight != subnet.weight) {
275                                 continue;
276                         }
277                 }
278
279                 if(find.type == SUBNET_IPV4) {
280                         if(address) {
281                                 if(maskcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, subnet.net.ipv4.prefixlength)) {
282                                         continue;
283                                 }
284                         } else {
285                                 if(find.net.ipv4.prefixlength != subnet.net.ipv4.prefixlength) {
286                                         continue;
287                                 }
288
289                                 if(memcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, sizeof(subnet.net.ipv4))) {
290                                         continue;
291                                 }
292                         }
293                 } else if(find.type == SUBNET_IPV6) {
294                         if(address) {
295                                 if(maskcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, subnet.net.ipv6.prefixlength)) {
296                                         continue;
297                                 }
298                         } else {
299                                 if(find.net.ipv6.prefixlength != subnet.net.ipv6.prefixlength) {
300                                         continue;
301                                 }
302
303                                 if(memcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, sizeof(subnet.net.ipv6))) {
304                                         continue;
305                                 }
306                         }
307                 }
308
309                 if(find.type == SUBNET_MAC) {
310                         if(memcmp(&find.net.mac.address, &subnet.net.mac.address, sizeof(subnet.net.mac))) {
311                                 continue;
312                         }
313                 }
314
315                 found = true;
316                 printf("Subnet: %s\n", strip_weight(netstr));
317                 printf("Owner:  %s\n", owner);
318         }
319
320         if(!found) {
321                 if(address) {
322                         fprintf(stderr, "Unknown address %s.\n", item);
323                 } else {
324                         fprintf(stderr, "Unknown subnet %s.\n", item);
325                 }
326
327                 return 1;
328         }
329
330         return 0;
331 }
332
333 int info(int fd, const char *item) {
334         if(check_id(item)) {
335                 return info_node(fd, item);
336         }
337
338         if(strchr(item, '.') || strchr(item, ':')) {
339                 return info_subnet(fd, item);
340         }
341
342         fprintf(stderr, "Argument is not a node name, subnet or address.\n");
343         return 1;
344 }