diff options
Diffstat (limited to 'ip/rtmon.c')
-rw-r--r-- | ip/rtmon.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/ip/rtmon.c b/ip/rtmon.c new file mode 100644 index 0000000..aad9968 --- /dev/null +++ b/ip/rtmon.c @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * rtmon.c RTnetlink listener. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <string.h> + +#include "version.h" + +#include "utils.h" +#include "libnetlink.h" + +static int init_phase = 1; + +static void write_stamp(FILE *fp) +{ + char buf[128]; + struct nlmsghdr *n1 = (void *)buf; + struct timeval tv; + + n1->nlmsg_type = NLMSG_TSTAMP; + n1->nlmsg_flags = 0; + n1->nlmsg_seq = 0; + n1->nlmsg_pid = 0; + n1->nlmsg_len = NLMSG_LENGTH(4*2); + gettimeofday(&tv, NULL); + ((__u32 *)NLMSG_DATA(n1))[0] = tv.tv_sec; + ((__u32 *)NLMSG_DATA(n1))[1] = tv.tv_usec; + fwrite((void *)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp); +} + +static int dump_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + + if (!init_phase) + write_stamp(fp); + fwrite((void *)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp); + fflush(fp); + return 0; +} + +static int dump_msg2(struct nlmsghdr *n, void *arg) +{ + return dump_msg(NULL, n, arg); +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: rtmon [ OPTIONS ] file FILE [ all | LISTofOBJECTS ]\n" + "OPTIONS := { -f[amily] { inet | inet6 | link | help } |\n" + " -4 | -6 | -0 | -V[ersion] }\n" + "LISTofOBJECTS := [ link ] [ address ] [ route ]\n"); + exit(-1); +} + +int +main(int argc, char **argv) +{ + FILE *fp; + struct rtnl_handle rth; + int family = AF_UNSPEC; + unsigned int groups = ~0U; + int llink = 0; + int laddr = 0; + int lroute = 0; + char *file = NULL; + + while (argc > 1) { + if (matches(argv[1], "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "link") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "help") == 0) + usage(); + else { + fprintf(stderr, "Protocol ID \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + } else if (strcmp(argv[1], "-4") == 0) { + family = AF_INET; + } else if (strcmp(argv[1], "-6") == 0) { + family = AF_INET6; + } else if (strcmp(argv[1], "-0") == 0) { + family = AF_PACKET; + } else if (matches(argv[1], "-Version") == 0) { + printf("rtmon utility, iproute2-%s\n", version); + exit(0); + } else if (matches(argv[1], "file") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + file = argv[1]; + } else if (matches(argv[1], "link") == 0) { + llink = 1; + groups = 0; + } else if (matches(argv[1], "address") == 0) { + laddr = 1; + groups = 0; + } else if (matches(argv[1], "route") == 0) { + lroute = 1; + groups = 0; + } else if (strcmp(argv[1], "all") == 0) { + groups = ~0U; + } else if (matches(argv[1], "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (file == NULL) { + fprintf(stderr, "Not enough information: argument \"file\" is required\n"); + exit(-1); + } + if (llink) + groups |= nl_mgrp(RTNLGRP_LINK); + if (laddr) { + if (!family || family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); + if (!family || family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR); + } + if (lroute) { + if (!family || family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE); + if (!family || family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE); + } + + fp = fopen(file, "w"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + if (rtnl_linkdump_req(&rth, AF_UNSPEC) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + write_stamp(fp); + + if (rtnl_dump_filter(&rth, dump_msg2, fp) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + init_phase = 0; + + if (rtnl_listen(&rth, dump_msg, (void *)fp) < 0) + exit(2); + + exit(0); +} |