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