Releasing 1.1pre15.
[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         if(len >= 3 && !strcmp(netstr + len - 3, "#10"))
41                 netstr[len - 3] = 0;
42         return netstr;
43 }
44
45 static int info_node(int fd, const char *item) {
46         // Check the list of nodes
47         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_NODES, item);
48
49         bool found = false;
50         char line[4096];
51
52         char node[4096];
53         char id[4096];
54         char from[4096];
55         char to[4096];
56         char subnet[4096];
57         char host[4096];
58         char port[4096];
59         char via[4096];
60         char nexthop[4096];
61         int code, req, cipher, digest, maclength, compression, distance;
62         short int pmtu, minmtu, maxmtu;
63         unsigned int options;
64         union {
65                 node_status_t bits;
66                 uint32_t raw;
67         } status_union;
68         node_status_t status;
69         long int last_state_change;
70
71         while(recvline(fd, line, sizeof line)) {
72                 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);
73
74                 if(n == 2)
75                         break;
76
77                 if(n != 19) {
78                         fprintf(stderr, "Unable to parse node dump from tincd.\n");
79                         return 1;
80                 }
81
82                 if(!strcmp(node, item)) {
83                         found = true;
84                         break;
85                 }
86         }
87
88         if(!found) {
89                 fprintf(stderr, "Unknown node %s.\n", item);
90                 return 1;
91         }
92
93         while(recvline(fd, line, sizeof line)) {
94                 if(sscanf(line, "%d %d %4095s", &code, &req, node) == 2)
95                         break;
96         }
97
98         printf("Node:         %s\n", item);
99         printf("Node ID:      %s\n", id);
100         printf("Address:      %s port %s\n", host, port);
101
102         char timestr[32] = "never";
103         time_t lsc_time = last_state_change;
104
105         if(last_state_change)
106                 strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&lsc_time));
107
108         status = status_union.bits;
109
110         if(status.reachable)
111                 printf("Online since: %s\n", timestr);
112         else
113                 printf("Last seen:    %s\n", timestr);
114
115         printf("Status:      ");
116         if(status.validkey)
117                 printf(" validkey");
118         if(status.visited)
119                 printf(" visited");
120         if(status.reachable)
121                 printf(" reachable");
122         if(status.indirect)
123                 printf(" indirect");
124         if(status.sptps)
125                 printf(" sptps");
126         if(status.udp_confirmed)
127                 printf(" udp_confirmed");
128         printf("\n");
129
130         printf("Options:     ");
131         if(options & OPTION_INDIRECT)
132                 printf(" indirect");
133         if(options & OPTION_TCPONLY)
134                 printf(" tcponly");
135         if(options & OPTION_PMTU_DISCOVERY)
136                 printf(" pmtu_discovery");
137         if(options & OPTION_CLAMP_MSS)
138                 printf(" clamp_mss");
139         printf("\n");
140         printf("Protocol:     %d.%d\n", PROT_MAJOR, OPTION_VERSION(options));
141         printf("Reachability: ");
142         if(!strcmp(host, "MYSELF"))
143                 printf("can reach itself\n");
144         else if(!status.reachable)
145                 printf("unreachable\n");
146         else if(strcmp(via, item))
147                 printf("indirectly via %s\n", via);
148         else if(!status.validkey)
149                 printf("unknown\n");
150         else if(minmtu > 0)
151                 printf("directly with UDP\nPMTU:         %d\n", pmtu);
152         else if(!strcmp(nexthop, item))
153                 printf("directly with TCP\n");
154         else
155                 printf("none, forwarded via %s\n", nexthop);
156
157         // List edges
158         printf("Edges:       ");
159         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_EDGES, item);
160         while(recvline(fd, line, sizeof line)) {
161                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, from, to);
162                 if(n == 2)
163                         break;
164                 if(n != 4) {
165                         fprintf(stderr, "Unable to parse edge dump from tincd.\n%s\n", line);
166                         return 1;
167                 }
168                 if(!strcmp(from, item))
169                         printf(" %s", to);
170         }
171         printf("\n");
172
173         // List subnets
174         printf("Subnets:     ");
175         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
176         while(recvline(fd, line, sizeof line)) {
177                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, subnet, from);
178                 if(n == 2)
179                         break;
180                 if(n != 4) {
181                         fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
182                         return 1;
183                 }
184                 if(!strcmp(from, item))
185                         printf(" %s", strip_weight(subnet));
186         }
187         printf("\n");
188
189         return 0;
190 }
191
192 static int info_subnet(int fd, const char *item) {
193         subnet_t subnet, find;
194
195         if(!str2net(&find, item)) {
196                 fprintf(stderr, "Could not parse subnet or address '%s'.\n", item);
197                 return 1;
198         }
199
200         bool address = !strchr(item, '/');
201         bool weight = strchr(item, '#');
202         bool found = false;
203
204         char line[4096];
205         char netstr[4096];
206         char owner[4096];
207
208         int code, req;
209
210         sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item);
211         while(recvline(fd, line, sizeof line)) {
212                 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, netstr, owner);
213                 if(n == 2)
214                         break;
215
216                 if(n != 4 || !str2net(&subnet, netstr)) {
217                         fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
218                         return 1;
219                 }
220
221                 if(find.type != subnet.type)
222                         continue;
223
224                 if(weight) {
225                         if(find.weight != subnet.weight)
226                                 continue;
227                 }
228
229                 if(find.type == SUBNET_IPV4) {
230                         if(address) {
231                                 if(maskcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, subnet.net.ipv4.prefixlength))
232                                         continue;
233                         } else {
234                                 if(find.net.ipv4.prefixlength != subnet.net.ipv4.prefixlength)
235                                         continue;
236                                 if(memcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, sizeof subnet.net.ipv4))
237                                         continue;
238                         }
239                 } else if(find.type == SUBNET_IPV6) {
240                         if(address) {
241                                 if(maskcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, subnet.net.ipv6.prefixlength))
242                                         continue;
243                         } else {
244                                 if(find.net.ipv6.prefixlength != subnet.net.ipv6.prefixlength)
245                                         continue;
246                                 if(memcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, sizeof subnet.net.ipv6))
247                                         continue;
248                         }
249                 } if(find.type == SUBNET_MAC) {
250                         if(memcmp(&find.net.mac.address, &subnet.net.mac.address, sizeof subnet.net.mac))
251                                 continue;
252                 }
253
254                 found = true;
255                 printf("Subnet: %s\n", strip_weight(netstr));
256                 printf("Owner:  %s\n", owner);
257         }
258
259         if(!found) {
260                 if(address)
261                         fprintf(stderr, "Unknown address %s.\n", item);
262                 else
263                         fprintf(stderr, "Unknown subnet %s.\n", item);
264                 return 1;
265         }
266
267         return 0;
268 }
269
270 int info(int fd, const char *item) {
271         if(check_id(item))
272                 return info_node(fd, item);
273         if(strchr(item, '.') || strchr(item, ':'))
274                 return info_subnet(fd, item);
275
276         fprintf(stderr, "Argument is not a node name, subnet or address.\n");
277         return 1;
278 }