/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * ipmroute.c "ip mroute". * * Authors: Alexey Kuznetsov, */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "ip_common.h" #include "json_print.h" static void usage(void) __attribute__((noreturn)); static void usage(void) { fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n" " [ table TABLE_ID ]\n" "TABLE_ID := [ local | main | default | all | NUMBER ]\n" ); exit(-1); } static struct rtfilter { int tb; int af; int iif; inet_prefix mdst; inet_prefix msrc; } filter; int print_mroute(struct nlmsghdr *n, void *arg) { struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[RTA_MAX+1]; FILE *fp = arg; const char *src, *dst; SPRINT_BUF(b1); SPRINT_BUF(b2); __u32 table; int iif = 0; int family; if ((n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE)) { fprintf(stderr, "Not a multicast route: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (r->rtm_type != RTN_MULTICAST) { fprintf(stderr, "Non multicast route received, kernel does support IP multicast?\n"); return -1; } parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); table = rtm_get_table(r, tb); if (filter.tb > 0 && filter.tb != table) return 0; if (tb[RTA_IIF]) iif = rta_getattr_u32(tb[RTA_IIF]); if (filter.iif && filter.iif != iif) return 0; if (filter.af && filter.af != r->rtm_family) return 0; if (inet_addr_match_rta(&filter.mdst, tb[RTA_DST])) return 0; if (inet_addr_match_rta(&filter.msrc, tb[RTA_SRC])) return 0; family = get_real_family(r->rtm_type, r->rtm_family); open_json_object(NULL); if (n->nlmsg_type == RTM_DELROUTE) print_bool(PRINT_ANY, "deleted", "Deleted ", true); if (tb[RTA_SRC]) src = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_SRC]), RTA_DATA(tb[RTA_SRC]), b1, sizeof(b1)); else src = "unknown"; if (tb[RTA_DST]) dst = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_DST]), RTA_DATA(tb[RTA_DST]), b2, sizeof(b2)); else dst = "unknown"; if (is_json_context()) { print_string(PRINT_JSON, "src", NULL, src); print_string(PRINT_JSON, "dst", NULL, dst); } else { char obuf[256]; snprintf(obuf, sizeof(obuf), "(%s,%s)", src, dst); print_string(PRINT_FP, NULL, "%-32s Iif: ", obuf); } if (iif) print_color_string(PRINT_ANY, COLOR_IFNAME, "iif", "%-10s ", ll_index_to_name(iif)); else print_string(PRINT_ANY,"iif", "%s ", "unresolved"); if (tb[RTA_MULTIPATH]) { struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); int first = 1; open_json_array(PRINT_JSON, "multipath"); len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); for (;;) { if (len < sizeof(*nh)) break; if (nh->rtnh_len > len) break; open_json_object(NULL); if (first) { print_string(PRINT_FP, NULL, "Oifs: ", NULL); first = 0; } print_color_string(PRINT_ANY, COLOR_IFNAME, "oif", "%s", ll_index_to_name(nh->rtnh_ifindex)); if (nh->rtnh_hops > 1) print_uint(PRINT_ANY, "ttl", "(ttl %u) ", nh->rtnh_hops); else print_string(PRINT_FP, NULL, " ", NULL); close_json_object(); len -= NLMSG_ALIGN(nh->rtnh_len); nh = RTNH_NEXT(nh); } close_json_array(PRINT_JSON, NULL); } print_string(PRINT_ANY, "state", " State: %s", (r->rtm_flags & RTNH_F_UNRESOLVED) ? "unresolved" : "resolved"); if (r->rtm_flags & RTNH_F_OFFLOAD) print_null(PRINT_ANY, "offload", " offload", NULL); if (show_stats && tb[RTA_MFC_STATS]) { struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]); print_nl(); print_u64(PRINT_ANY, "packets", " %"PRIu64" packets,", mfcs->mfcs_packets); print_u64(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes); if (mfcs->mfcs_wrong_if) print_u64(PRINT_ANY, "wrong_if", ", %"PRIu64" arrived on wrong iif.", mfcs->mfcs_wrong_if); } if (show_stats && tb[RTA_EXPIRES]) { struct timeval tv; double age; __jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES])); age = tv.tv_sec; age += tv.tv_usec / 1000000.; print_float(PRINT_ANY, "expires", ", Age %.2f", age); } if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) print_string(PRINT_ANY, "table", " Table: %s", rtnl_rttable_n2a(table, b1, sizeof(b1))); print_string(PRINT_FP, NULL, "\n", NULL); close_json_object(); fflush(fp); return 0; } void ipmroute_reset_filter(int ifindex) { memset(&filter, 0, sizeof(filter)); filter.mdst.bitlen = -1; filter.msrc.bitlen = -1; filter.iif = ifindex; } static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen) { int err; if (filter.tb) { err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb); if (err) return err; } return 0; } static int mroute_list(int argc, char **argv) { char *id = NULL; int family = preferred_family; ipmroute_reset_filter(0); if (family == AF_INET || family == AF_UNSPEC) { family = RTNL_FAMILY_IPMR; filter.af = RTNL_FAMILY_IPMR; filter.tb = RT_TABLE_DEFAULT; /* for backward compatibility */ } else if (family == AF_INET6) { family = RTNL_FAMILY_IP6MR; filter.af = RTNL_FAMILY_IP6MR; } else { /* family does not have multicast routing */ return 0; } filter.msrc.family = filter.mdst.family = family; while (argc > 0) { if (matches(*argv, "table") == 0) { __u32 tid; NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) { if (strcmp(*argv, "all") == 0) { filter.tb = 0; } else if (strcmp(*argv, "help") == 0) { usage(); } else { invarg("table id value is invalid\n", *argv); } } else filter.tb = tid; } else if (strcmp(*argv, "iif") == 0) { NEXT_ARG(); id = *argv; } else if (matches(*argv, "from") == 0) { NEXT_ARG(); if (get_prefix(&filter.msrc, *argv, family)) invarg("from value is invalid\n", *argv); } else { if (strcmp(*argv, "to") == 0) { NEXT_ARG(); } if (matches(*argv, "help") == 0) usage(); if (get_prefix(&filter.mdst, *argv, family)) invarg("to value is invalid\n", *argv); } argc--; argv++; } ll_init_map(&rth); if (id) { int idx; idx = ll_name_to_index(id); if (!idx) return nodev(id); filter.iif = idx; } if (rtnl_routedump_req(&rth, filter.af, iproute_dump_filter) < 0) { perror("Cannot send dump request"); return 1; } new_json_obj(json); if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) { delete_json_obj(); fprintf(stderr, "Dump terminated\n"); exit(1); } delete_json_obj(); return 0; } int do_multiroute(int argc, char **argv) { if (argc < 1) return mroute_list(0, NULL); if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 || matches(*argv, "lst") == 0) return mroute_list(argc-1, argv+1); if (matches(*argv, "help") == 0) usage(); fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); exit(-1); }