/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * ipmonitor.c "ip monitor". * * Authors: Alexey Kuznetsov, */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "ip_common.h" #include "nh_common.h" static void usage(void) __attribute__((noreturn)); static int prefix_banner; int listen_all_nsid; static void usage(void) { fprintf(stderr, "Usage: ip monitor [ all | OBJECTS ] [ FILE ] [ label ] [ all-nsid ]\n" " [ dev DEVICE ]\n" "OBJECTS := address | link | mroute | neigh | netconf |\n" " nexthop | nsid | prefix | route | rule | stats\n" "FILE := file FILENAME\n"); exit(-1); } static void print_headers(FILE *fp, char *label, struct rtnl_ctrl_data *ctrl) { if (timestamp) print_timestamp(fp); if (listen_all_nsid) { if (ctrl == NULL || ctrl->nsid < 0) fprintf(fp, "[nsid current]"); else fprintf(fp, "[nsid %d]", ctrl->nsid); } if (prefix_banner) fprintf(fp, "%s", label); } static int accept_msg(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { FILE *fp = (FILE *)arg; switch (n->nlmsg_type) { case RTM_NEWROUTE: case RTM_DELROUTE: { struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (r->rtm_flags & RTM_F_CLONED) return 0; if (r->rtm_family == RTNL_FAMILY_IPMR || r->rtm_family == RTNL_FAMILY_IP6MR) { print_headers(fp, "[MROUTE]", ctrl); print_mroute(n, arg); return 0; } else { print_headers(fp, "[ROUTE]", ctrl); print_route(n, arg); return 0; } } case RTM_NEWNEXTHOP: case RTM_DELNEXTHOP: print_headers(fp, "[NEXTHOP]", ctrl); print_cache_nexthop(n, arg, true); return 0; case RTM_NEWNEXTHOPBUCKET: case RTM_DELNEXTHOPBUCKET: print_headers(fp, "[NEXTHOPBUCKET]", ctrl); print_nexthop_bucket(n, arg); return 0; case RTM_NEWLINK: case RTM_DELLINK: ll_remember_index(n, NULL); print_headers(fp, "[LINK]", ctrl); print_linkinfo(n, arg); return 0; case RTM_NEWADDR: case RTM_DELADDR: print_headers(fp, "[ADDR]", ctrl); print_addrinfo(n, arg); return 0; case RTM_NEWADDRLABEL: case RTM_DELADDRLABEL: print_headers(fp, "[ADDRLABEL]", ctrl); print_addrlabel(n, arg); return 0; case RTM_NEWNEIGH: case RTM_DELNEIGH: case RTM_GETNEIGH: if (preferred_family) { struct ndmsg *r = NLMSG_DATA(n); if (r->ndm_family != preferred_family) return 0; } print_headers(fp, "[NEIGH]", ctrl); print_neigh(n, arg); return 0; case RTM_NEWPREFIX: print_headers(fp, "[PREFIX]", ctrl); print_prefix(n, arg); return 0; case RTM_NEWRULE: case RTM_DELRULE: print_headers(fp, "[RULE]", ctrl); print_rule(n, arg); return 0; case NLMSG_TSTAMP: print_nlmsg_timestamp(fp, n); return 0; case RTM_NEWNETCONF: case RTM_DELNETCONF: print_headers(fp, "[NETCONF]", ctrl); print_netconf(ctrl, n, arg); return 0; case RTM_DELNSID: case RTM_NEWNSID: print_headers(fp, "[NSID]", ctrl); print_nsid(n, arg); return 0; case RTM_NEWSTATS: print_headers(fp, "[STATS]", ctrl); ipstats_print(n, arg); return 0; case NLMSG_ERROR: case NLMSG_NOOP: case NLMSG_DONE: break; /* ignore */ default: fprintf(stderr, "Unknown message: type=0x%08x(%d) flags=0x%08x(%d) len=0x%08x(%d)\n", n->nlmsg_type, n->nlmsg_type, n->nlmsg_flags, n->nlmsg_flags, n->nlmsg_len, n->nlmsg_len); } return 0; } #define IPMON_LLINK BIT(0) #define IPMON_LADDR BIT(1) #define IPMON_LROUTE BIT(2) #define IPMON_LMROUTE BIT(3) #define IPMON_LPREFIX BIT(4) #define IPMON_LNEIGH BIT(5) #define IPMON_LNETCONF BIT(6) #define IPMON_LSTATS BIT(7) #define IPMON_LRULE BIT(8) #define IPMON_LNSID BIT(9) #define IPMON_LNEXTHOP BIT(10) #define IPMON_L_ALL (~0) int do_ipmonitor(int argc, char **argv) { unsigned int groups = 0, lmask = 0; /* "needed" mask, failure to enable is an error */ unsigned int nmask; char *file = NULL; int ifindex = 0; rtnl_close(&rth); while (argc > 0) { if (matches(*argv, "file") == 0) { NEXT_ARG(); file = *argv; } else if (matches(*argv, "label") == 0) { prefix_banner = 1; } else if (matches(*argv, "link") == 0) { lmask |= IPMON_LLINK; } else if (matches(*argv, "address") == 0) { lmask |= IPMON_LADDR; } else if (matches(*argv, "route") == 0) { lmask |= IPMON_LROUTE; } else if (matches(*argv, "mroute") == 0) { lmask |= IPMON_LMROUTE; } else if (matches(*argv, "prefix") == 0) { lmask |= IPMON_LPREFIX; } else if (matches(*argv, "neigh") == 0) { lmask |= IPMON_LNEIGH; } else if (matches(*argv, "netconf") == 0) { lmask |= IPMON_LNETCONF; } else if (matches(*argv, "rule") == 0) { lmask |= IPMON_LRULE; } else if (matches(*argv, "nsid") == 0) { lmask |= IPMON_LNSID; } else if (matches(*argv, "nexthop") == 0) { lmask |= IPMON_LNEXTHOP; } else if (strcmp(*argv, "stats") == 0) { lmask |= IPMON_LSTATS; } else if (strcmp(*argv, "all") == 0) { prefix_banner = 1; } else if (matches(*argv, "all-nsid") == 0) { listen_all_nsid = 1; } else if (matches(*argv, "help") == 0) { usage(); } else if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); ifindex = ll_name_to_index(*argv); if (!ifindex) invarg("Device does not exist\n", *argv); } else { fprintf(stderr, "Argument \"%s\" is unknown, try \"ip monitor help\".\n", *argv); exit(-1); } argc--; argv++; } ipaddr_reset_filter(1, ifindex); iproute_reset_filter(ifindex); ipmroute_reset_filter(ifindex); ipneigh_reset_filter(ifindex); ipnetconf_reset_filter(ifindex); nmask = lmask; if (!lmask) lmask = IPMON_L_ALL; if (lmask & IPMON_LLINK) groups |= nl_mgrp(RTNLGRP_LINK); if (lmask & IPMON_LADDR) { if (!preferred_family || preferred_family == AF_INET) groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); if (!preferred_family || preferred_family == AF_INET6) groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR); } if (lmask & IPMON_LROUTE) { if (!preferred_family || preferred_family == AF_INET) groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE); if (!preferred_family || preferred_family == AF_INET6) groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE); if (!preferred_family || preferred_family == AF_MPLS) groups |= nl_mgrp(RTNLGRP_MPLS_ROUTE); } if (lmask & IPMON_LMROUTE) { if (!preferred_family || preferred_family == AF_INET) groups |= nl_mgrp(RTNLGRP_IPV4_MROUTE); if (!preferred_family || preferred_family == AF_INET6) groups |= nl_mgrp(RTNLGRP_IPV6_MROUTE); } if (lmask & IPMON_LPREFIX) { if (!preferred_family || preferred_family == AF_INET6) groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX); } if (lmask & IPMON_LNEIGH) { groups |= nl_mgrp(RTNLGRP_NEIGH); } if (lmask & IPMON_LNETCONF) { if (!preferred_family || preferred_family == AF_INET) groups |= nl_mgrp(RTNLGRP_IPV4_NETCONF); if (!preferred_family || preferred_family == AF_INET6) groups |= nl_mgrp(RTNLGRP_IPV6_NETCONF); if (!preferred_family || preferred_family == AF_MPLS) groups |= nl_mgrp(RTNLGRP_MPLS_NETCONF); } if (lmask & IPMON_LRULE) { if (!preferred_family || preferred_family == AF_INET) groups |= nl_mgrp(RTNLGRP_IPV4_RULE); if (!preferred_family || preferred_family == AF_INET6) groups |= nl_mgrp(RTNLGRP_IPV6_RULE); } if (lmask & IPMON_LNSID) { groups |= nl_mgrp(RTNLGRP_NSID); } if (file) { FILE *fp; int err; fp = fopen(file, "r"); if (fp == NULL) { perror("Cannot fopen"); exit(-1); } err = rtnl_from_file(fp, accept_msg, stdout); fclose(fp); return err; } if (rtnl_open(&rth, groups) < 0) exit(1); if (lmask & IPMON_LNEXTHOP && rtnl_add_nl_group(&rth, RTNLGRP_NEXTHOP) < 0) { fprintf(stderr, "Failed to add nexthop group to list\n"); exit(1); } if (lmask & IPMON_LSTATS && rtnl_add_nl_group(&rth, RTNLGRP_STATS) < 0 && nmask & IPMON_LSTATS) { fprintf(stderr, "Failed to add stats group to list\n"); exit(1); } if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0) exit(1); ll_init_map(&rth); netns_nsid_socket_init(); netns_map_init(); if (rtnl_listen(&rth, accept_msg, stdout) < 0) exit(2); return 0; }