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