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