mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-24 13:09:48 +00:00
mroute was using /proc/net/ip_mr_[vif|cache] to display mroute entries. Hence, only RT_TABLE_DEFAULT was displayed and only IPv4. With rtnetlink, it is possible to display all tables for IPv4 and IPv6. The output format is kept. Also, like before the patch, statistics are displayed when user specify the '-s' argument. The patch also adds the support of 'ip monitor mroute', which is now possible. Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
277 lines
6.4 KiB
C
277 lines
6.4 KiB
C
/*
|
|
* ipmroute.c "ip mroute".
|
|
*
|
|
* 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.
|
|
*
|
|
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <string.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/if.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/sockios.h>
|
|
|
|
#include <rt_names.h>
|
|
#include "utils.h"
|
|
#include "ip_common.h"
|
|
|
|
static void usage(void) __attribute__((noreturn));
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
|
|
fprintf(stderr, " [ table TABLE_ID ]\n");
|
|
fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
|
|
#if 0
|
|
fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n");
|
|
#endif
|
|
exit(-1);
|
|
}
|
|
|
|
struct rtfilter
|
|
{
|
|
int tb;
|
|
int af;
|
|
int iif;
|
|
inet_prefix mdst;
|
|
inet_prefix msrc;
|
|
} filter;
|
|
|
|
int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
|
|
{
|
|
FILE *fp = (FILE*)arg;
|
|
struct rtmsg *r = NLMSG_DATA(n);
|
|
int len = n->nlmsg_len;
|
|
struct rtattr * tb[RTA_MAX+1];
|
|
char abuf[256];
|
|
char obuf[256];
|
|
SPRINT_BUF(b1);
|
|
__u32 table;
|
|
int iif = 0;
|
|
int family;
|
|
|
|
if ((n->nlmsg_type != RTM_NEWROUTE &&
|
|
n->nlmsg_type != RTM_DELROUTE) ||
|
|
!(n->nlmsg_flags & NLM_F_MULTI)) {
|
|
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, "Not a multicast route (type: %s)\n",
|
|
rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
|
|
return 0;
|
|
}
|
|
|
|
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 = *(int*)RTA_DATA(tb[RTA_IIF]);
|
|
if (filter.iif && filter.iif != iif)
|
|
return 0;
|
|
|
|
if (filter.af && filter.af != r->rtm_family)
|
|
return 0;
|
|
|
|
if (tb[RTA_DST] &&
|
|
filter.mdst.bitlen > 0 &&
|
|
inet_addr_match(RTA_DATA(tb[RTA_DST]), &filter.mdst, filter.mdst.bitlen))
|
|
return 0;
|
|
|
|
if (tb[RTA_SRC] &&
|
|
filter.msrc.bitlen > 0 &&
|
|
inet_addr_match(RTA_DATA(tb[RTA_SRC]), &filter.msrc, filter.msrc.bitlen))
|
|
return 0;
|
|
|
|
family = r->rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6;
|
|
|
|
if (n->nlmsg_type == RTM_DELROUTE)
|
|
fprintf(fp, "Deleted ");
|
|
|
|
if (tb[RTA_SRC])
|
|
len = snprintf(obuf, sizeof(obuf),
|
|
"(%s, ", rt_addr_n2a(family,
|
|
RTA_PAYLOAD(tb[RTA_SRC]),
|
|
RTA_DATA(tb[RTA_SRC]),
|
|
abuf, sizeof(abuf)));
|
|
else
|
|
len = sprintf(obuf, "(unknown, ");
|
|
if (tb[RTA_DST])
|
|
snprintf(obuf + len, sizeof(obuf) - len,
|
|
"%s)", rt_addr_n2a(family, RTA_PAYLOAD(tb[RTA_DST]),
|
|
RTA_DATA(tb[RTA_DST]),
|
|
abuf, sizeof(abuf)));
|
|
else
|
|
snprintf(obuf + len, sizeof(obuf) - len, "unknown) ");
|
|
|
|
fprintf(fp, "%-32s Iif: ", obuf);
|
|
if (iif)
|
|
fprintf(fp, "%-10s ", ll_index_to_name(iif));
|
|
else
|
|
fprintf(fp, "unresolved ");
|
|
|
|
if (tb[RTA_MULTIPATH]) {
|
|
struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
|
|
int first = 1;
|
|
|
|
len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
|
|
|
|
for (;;) {
|
|
if (len < sizeof(*nh))
|
|
break;
|
|
if (nh->rtnh_len > len)
|
|
break;
|
|
|
|
if (first) {
|
|
fprintf(fp, "Oifs: ");
|
|
first = 0;
|
|
}
|
|
fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex));
|
|
if (nh->rtnh_hops > 1)
|
|
fprintf(fp, "(ttl %d) ", nh->rtnh_hops);
|
|
else
|
|
fprintf(fp, " ");
|
|
len -= NLMSG_ALIGN(nh->rtnh_len);
|
|
nh = RTNH_NEXT(nh);
|
|
}
|
|
}
|
|
if (show_stats && tb[RTA_MFC_STATS]) {
|
|
struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]);
|
|
|
|
fprintf(fp, "%s %"PRIu64" packets, %"PRIu64" bytes", _SL_,
|
|
(uint64_t)mfcs->mfcs_packets,
|
|
(uint64_t)mfcs->mfcs_bytes);
|
|
if (mfcs->mfcs_wrong_if)
|
|
fprintf(fp, ", %"PRIu64" arrived on wrong iif.",
|
|
(uint64_t)mfcs->mfcs_wrong_if);
|
|
}
|
|
fprintf(fp, "\n");
|
|
fflush(fp);
|
|
return 0;
|
|
}
|
|
|
|
void ipmroute_reset_filter(void)
|
|
{
|
|
memset(&filter, 0, sizeof(filter));
|
|
filter.mdst.bitlen = -1;
|
|
filter.msrc.bitlen = -1;
|
|
}
|
|
|
|
static int mroute_list(int argc, char **argv)
|
|
{
|
|
char *id = NULL;
|
|
int family;
|
|
|
|
ipmroute_reset_filter();
|
|
if (preferred_family == AF_UNSPEC)
|
|
family = AF_INET;
|
|
else
|
|
family = AF_INET6;
|
|
if (family == AF_INET) {
|
|
filter.af = RTNL_FAMILY_IPMR;
|
|
filter.tb = RT_TABLE_DEFAULT; /* for backward compatibility */
|
|
} else
|
|
filter.af = RTNL_FAMILY_IP6MR;
|
|
|
|
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();
|
|
get_prefix(&filter.msrc, *argv, family);
|
|
} else {
|
|
if (strcmp(*argv, "to") == 0) {
|
|
NEXT_ARG();
|
|
}
|
|
if (matches(*argv, "help") == 0)
|
|
usage();
|
|
get_prefix(&filter.mdst, *argv, family);
|
|
}
|
|
argc--; argv++;
|
|
}
|
|
|
|
ll_init_map(&rth);
|
|
|
|
if (id) {
|
|
int idx;
|
|
|
|
if ((idx = ll_name_to_index(id)) == 0) {
|
|
fprintf(stderr, "Cannot find device \"%s\"\n", id);
|
|
return -1;
|
|
}
|
|
filter.iif = idx;
|
|
}
|
|
|
|
if (rtnl_wilddump_request(&rth, filter.af, RTM_GETROUTE) < 0) {
|
|
perror("Cannot send dump request");
|
|
return 1;
|
|
}
|
|
|
|
if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) {
|
|
fprintf(stderr, "Dump terminated\n");
|
|
exit(1);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
int do_multiroute(int argc, char **argv)
|
|
{
|
|
if (argc < 1)
|
|
return mroute_list(0, NULL);
|
|
#if 0
|
|
if (matches(*argv, "add") == 0)
|
|
return mroute_modify(RTM_NEWADDR, argc-1, argv+1);
|
|
if (matches(*argv, "delete") == 0)
|
|
return mroute_modify(RTM_DELADDR, argc-1, argv+1);
|
|
if (matches(*argv, "get") == 0)
|
|
return mroute_get(argc-1, argv+1);
|
|
#endif
|
|
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);
|
|
}
|