From 53735a9d964579829d089f4b7572aef50c4e1468 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 16 Jul 2012 01:05:25 +0200 Subject: [PATCH] "tincctl info" gives more human readable information about nodes or subnets. --- doc/tinc.texi | 4 + doc/tincctl.8.in | 4 + src/Makefile.am | 2 +- src/info.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++ src/info.h | 26 +++++ src/tincctl.c | 15 +++ 6 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 src/info.c create mode 100644 src/info.h diff --git a/doc/tinc.texi b/doc/tinc.texi index 32944395..876fe438 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -2098,6 +2098,10 @@ Dump a list of all meta connections with ourself. @item dump graph Dump a graph of the VPN in dotty format. +@item info @var{node} | @var{subnet} | @var{address} +Show information about a particular @var{node}, @var{subnet} or @var{address}. +If an @var{address} is given, any matching subnet will be shown. + @item purge Purges all information remembered about unreachable nodes. diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in index 788e7f1b..8693dc09 100644 --- a/doc/tincctl.8.in +++ b/doc/tincctl.8.in @@ -101,6 +101,7 @@ If is omitted, the default length will be 2048 bits. When saving keys to existing files, tinc will not delete the old keys; you have to remove them manually. + .It dump nodes Dump a list of all known nodes in the VPN. .It dump edges @@ -113,6 +114,9 @@ Dump a list of all meta connections with ourself. Dump a graph of the VPN in .Xr dotty 1 format. +.It info Ar node | subnet | address +Show information about a particular node, subnet or address. +If an address is given, any matching subnet will be shown. .It purge Purges all information remembered about unreachable nodes. .It debug Ar N diff --git a/src/Makefile.am b/src/Makefile.am index 563149c6..b0f77910 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,7 +24,7 @@ nodist_tincd_SOURCES = \ tincctl_SOURCES = \ utils.c getopt.c getopt1.c dropin.c \ - list.c tincctl.c top.c + info.c list.c subnet_parse.c tincctl.c top.c nodist_tincctl_SOURCES = \ ecdsagen.c rsagen.c diff --git a/src/info.c b/src/info.c new file mode 100644 index 00000000..d0764da2 --- /dev/null +++ b/src/info.c @@ -0,0 +1,248 @@ +/* + info.c -- Show information about a node, subnet or address + Copyright (C) 2012 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "system.h" + +#include "control_common.h" +#include "list.h" +#include "subnet.h" +#include "tincctl.h" +#include "info.h" +#include "xalloc.h" + +void logger(int level, int priority, const char *format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +static int info_node(int fd, const char *item) { + // Check the list of nodes + sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_NODES, item); + + bool found = false; + char line[4096]; + + char node[4096]; + char from[4096]; + char to[4096]; + char subnet[4096]; + char host[4096]; + char port[4096]; + char via[4096]; + char nexthop[4096]; + int code, req, cipher, digest, maclength, compression, distance; + short int pmtu, minmtu, maxmtu; + unsigned int options; + node_status_t status; + + while(recvline(fd, line, sizeof line)) { + 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); + + if(n == 2) + break; + + if(n != 17) { + *port = 0; + 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); + + if(n != 16) { + fprintf(stderr, "Unable to parse node dump from tincd.\n"); + return 1; + } + } + + if(!strcmp(node, item)) { + found = true; + break; + } + } + + if(!found) { + fprintf(stderr, "Unknown node %s.\n", item); + return 1; + } + + while(recvline(fd, line, sizeof line)) { + if(sscanf(line, "%d %d %s", &code, &req, node) == 2) + break; + } + + printf("Node: %s\n", item); + if(*port) + printf("Address: %s port %s\n", host, port); + printf("Status: "); + if(status.validkey) + printf(" validkey"); + if(status.visited) + printf(" visited"); + if(status.reachable) + printf(" reachable"); + if(status.indirect) + printf(" indirect"); + if(status.ecdh) + printf(" ecdh"); + printf("\n"); + printf("Options: "); + if(options & OPTION_INDIRECT) + printf(" indirect"); + if(options & OPTION_TCPONLY) + printf(" tcponly"); + if(options & OPTION_PMTU_DISCOVERY) + printf(" pmtu_discovery"); + if(options & OPTION_CLAMP_MSS) + printf(" clamp_mss"); + printf("\n"); + printf("Reachability: "); + if(!*port) + printf("can reach itself\n"); + else if(!status.reachable) + printf("unreachable\n"); + else if(strcmp(via, item)) + printf("indirectly via %s\n", via); + else if(!status.validkey) + printf("unknown\n"); + else if(minmtu > 0) + printf("directly with UDP\nPMTU: %d\n", pmtu); + else if(!strcmp(nexthop, item)) + printf("directly with TCP\n"); + else + printf("none, forwarded via %s\n", nexthop); + + // List edges + printf("Edges: "); + sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_EDGES, item); + while(recvline(fd, line, sizeof line)) { + int n = sscanf(line, "%d %d %s to %s", &code, &req, from, to); + if(n == 2) + break; + if(n != 4) { + fprintf(stderr, "Unable to parse edge dump from tincd.\n%s\n", line); + return 1; + } + if(!strcmp(from, item)) + printf(" %s", to); + } + printf("\n"); + + // List subnets + printf("Subnets: "); + sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item); + while(recvline(fd, line, sizeof line)) { + int n = sscanf(line, "%d %d %s owner %s", &code, &req, subnet, from); + if(n == 2) + break; + if(n != 4) { + fprintf(stderr, "Unable to parse subnet dump from tincd.\n"); + return 1; + } + if(!strcmp(from, item)) + printf(" %s", subnet); + } + printf("\n"); + + return 0; +} + +static int info_subnet(int fd, const char *item) { + subnet_t subnet, find; + + if(!str2net(&find, item)) + return 1; + + bool address = !strchr(item, '/'); + bool weight = strchr(item, '#'); + bool found = false; + + char line[4096]; + char netstr[4096]; + char owner[4096]; + + int code, req; + + sendline(fd, "%d %d %s", CONTROL, REQ_DUMP_SUBNETS, item); + while(recvline(fd, line, sizeof line)) { + int n = sscanf(line, "%d %d %s owner %s", &code, &req, netstr, owner); + if(n == 2) + break; + + if(n != 4 || !str2net(&subnet, netstr)) { + fprintf(stderr, "Unable to parse subnet dump from tincd.\n"); + return 1; + } + + if(find.type != subnet.type) + continue; + + if(weight) { + if(find.weight != subnet.weight) + continue; + } + + if(find.type == SUBNET_IPV4) { + if(address) { + if(maskcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, subnet.net.ipv4.prefixlength)) + continue; + } else { + if(find.net.ipv4.prefixlength != subnet.net.ipv4.prefixlength) + continue; + if(memcmp(&find.net.ipv4.address, &subnet.net.ipv4.address, sizeof subnet.net.ipv4)) + continue; + } + } else if(find.type == SUBNET_IPV6) { + if(address) { + if(maskcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, subnet.net.ipv6.prefixlength)) + continue; + } else { + if(find.net.ipv6.prefixlength != subnet.net.ipv6.prefixlength) + continue; + if(memcmp(&find.net.ipv6.address, &subnet.net.ipv6.address, sizeof subnet.net.ipv6)) + continue; + } + } if(find.type == SUBNET_MAC) { + if(memcmp(&find.net.mac.address, &subnet.net.mac.address, sizeof subnet.net.mac)) + continue; + } + + found = true; + printf("Subnet: %s\n", netstr); + printf("Owner: %s\n", owner); + } + + if(!found) { + if(address) + fprintf(stderr, "Unknown address %s.\n", item); + else + fprintf(stderr, "Unknown subnet %s.\n", item); + return 1; + } + + return 0; +} + +int info(int fd, const char *item) { + if(check_id(item)) + return info_node(fd, item); + if(strchr(item, '.') || strchr(item, ':')) + return info_subnet(fd, item); + + fprintf(stderr, "Argument is not a node name, subnet or address.\n"); + return 1; +} diff --git a/src/info.h b/src/info.h new file mode 100644 index 00000000..239ceeac --- /dev/null +++ b/src/info.h @@ -0,0 +1,26 @@ +/* + info.h -- header for info.c. + Copyright (C) 2012 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __TINC_INFO_H__ +#define __TINC_INFO_H__ + +extern int info(int fd, const char *item); + +#endif + diff --git a/src/tincctl.c b/src/tincctl.c index 8a330955..f3a73b77 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -25,6 +25,7 @@ #include "protocol.h" #include "control_common.h" #include "ecdsagen.h" +#include "info.h" #include "rsagen.h" #include "utils.h" #include "tincctl.h" @@ -120,6 +121,7 @@ static void usage(bool status) { " subnets - all known subnets in the VPN\n" " connections - all meta connections with ourself\n" " graph - graph of the VPN in dotty format\n" + " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n" " purge Purge unreachable nodes\n" " debug N Set debug level\n" " retry Retry all outgoing connections\n" @@ -1286,6 +1288,18 @@ static int cmd_version(int argc, char *argv[]) { return 0; } +static int cmd_info(int argc, char *argv[]) { + if(argc != 2) { + fprintf(stderr, "Invalid number of arguments.\n"); + return 1; + } + + if(!connect_tincd()) + return 1; + + return info(fd, argv[1]); +} + static const char *conffiles[] = { "tinc.conf", "tinc-up", @@ -1374,6 +1388,7 @@ static const struct { {"generate-ecdsa-keys", cmd_generate_ecdsa_keys}, {"help", cmd_help}, {"version", cmd_version}, + {"info", cmd_info}, {"edit", cmd_edit}, {NULL, NULL}, }; -- 2.20.1