/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * rtmon.c RTnetlink listener. * * Authors: Alexey Kuznetsov, */ #include #include #include #include #include #include #include #include #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); }