Commit e34d3dcc authored by Nicolas Dichtel's avatar Nicolas Dichtel Committed by Stephen Hemminger

ip: use rtnelink to manage mroute

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: default avatarNicolas Dichtel <nicolas.dichtel@6wind.com>
parent e509fb1b
...@@ -16,11 +16,14 @@ extern int ipaddr_list_link(int argc, char **argv); ...@@ -16,11 +16,14 @@ extern int ipaddr_list_link(int argc, char **argv);
extern int iproute_monitor(int argc, char **argv); extern int iproute_monitor(int argc, char **argv);
extern void iplink_usage(void) __attribute__((noreturn)); extern void iplink_usage(void) __attribute__((noreturn));
extern void iproute_reset_filter(void); extern void iproute_reset_filter(void);
extern void ipmroute_reset_filter(void);
extern void ipaddr_reset_filter(int); extern void ipaddr_reset_filter(int);
extern void ipneigh_reset_filter(void); extern void ipneigh_reset_filter(void);
extern void ipntable_reset_filter(void); extern void ipntable_reset_filter(void);
extern int print_route(const struct sockaddr_nl *who, extern int print_route(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg); struct nlmsghdr *n, void *arg);
extern int print_mroute(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg);
extern int print_prefix(const struct sockaddr_nl *who, extern int print_prefix(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg); struct nlmsghdr *n, void *arg);
extern int print_rule(const struct sockaddr_nl *who, extern int print_rule(const struct sockaddr_nl *who,
......
...@@ -43,10 +43,26 @@ int accept_msg(const struct sockaddr_nl *who, ...@@ -43,10 +43,26 @@ int accept_msg(const struct sockaddr_nl *who,
print_timestamp(fp); print_timestamp(fp);
if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) { if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) {
if (prefix_banner) struct rtmsg *r = NLMSG_DATA(n);
fprintf(fp, "[ROUTE]"); int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
print_route(who, n, arg);
return 0; if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
if (r->rtm_family == RTNL_FAMILY_IPMR ||
r->rtm_family == RTNL_FAMILY_IP6MR) {
if (prefix_banner)
fprintf(fp, "[MROUTE]");
print_mroute(who, n, arg);
return 0;
} else {
if (prefix_banner)
fprintf(fp, "[ROUTE]");
print_route(who, n, arg);
return 0;
}
} }
if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) { if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
ll_remember_index(who, n, NULL); ll_remember_index(who, n, NULL);
...@@ -123,6 +139,7 @@ int do_ipmonitor(int argc, char **argv) ...@@ -123,6 +139,7 @@ int do_ipmonitor(int argc, char **argv)
int llink=0; int llink=0;
int laddr=0; int laddr=0;
int lroute=0; int lroute=0;
int lmroute=0;
int lprefix=0; int lprefix=0;
int lneigh=0; int lneigh=0;
int lnetconf=0; int lnetconf=0;
...@@ -130,6 +147,7 @@ int do_ipmonitor(int argc, char **argv) ...@@ -130,6 +147,7 @@ int do_ipmonitor(int argc, char **argv)
rtnl_close(&rth); rtnl_close(&rth);
ipaddr_reset_filter(1); ipaddr_reset_filter(1);
iproute_reset_filter(); iproute_reset_filter();
ipmroute_reset_filter();
ipneigh_reset_filter(); ipneigh_reset_filter();
while (argc > 0) { while (argc > 0) {
...@@ -145,6 +163,9 @@ int do_ipmonitor(int argc, char **argv) ...@@ -145,6 +163,9 @@ int do_ipmonitor(int argc, char **argv)
} else if (matches(*argv, "route") == 0) { } else if (matches(*argv, "route") == 0) {
lroute=1; lroute=1;
groups = 0; groups = 0;
} else if (matches(*argv, "mroute") == 0) {
lmroute=1;
groups = 0;
} else if (matches(*argv, "prefix") == 0) { } else if (matches(*argv, "prefix") == 0) {
lprefix=1; lprefix=1;
groups = 0; groups = 0;
...@@ -180,6 +201,12 @@ int do_ipmonitor(int argc, char **argv) ...@@ -180,6 +201,12 @@ int do_ipmonitor(int argc, char **argv)
if (!preferred_family || preferred_family == AF_INET6) if (!preferred_family || preferred_family == AF_INET6)
groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE); groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE);
} }
if (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 (lprefix) { if (lprefix) {
if (!preferred_family || preferred_family == AF_INET6) if (!preferred_family || preferred_family == AF_INET6)
groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX); groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <unistd.h> #include <unistd.h>
#include <syslog.h> #include <syslog.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
...@@ -26,16 +27,15 @@ ...@@ -26,16 +27,15 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <rt_names.h>
#include "utils.h" #include "utils.h"
#include "ip_common.h"
char filter_dev[16];
int filter_family;
static void usage(void) __attribute__((noreturn)); static void usage(void) __attribute__((noreturn));
static void usage(void) static void usage(void)
{ {
fprintf(stderr, "Usage: ip mroute show [ PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n"); fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
fprintf(stderr, " [ table TABLE_ID ]\n"); fprintf(stderr, " [ table TABLE_ID ]\n");
fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
#if 0 #if 0
...@@ -44,151 +44,214 @@ static void usage(void) ...@@ -44,151 +44,214 @@ static void usage(void)
exit(-1); exit(-1);
} }
static char *viftable[32];
struct rtfilter struct rtfilter
{ {
int tb;
int af;
int iif;
inet_prefix mdst; inet_prefix mdst;
inet_prefix msrc; inet_prefix msrc;
} filter; } filter;
static void read_viftable(void) int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
{ {
char buf[256]; FILE *fp = (FILE*)arg;
FILE *fp = fopen("/proc/net/ip_mr_vif", "r"); struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
if (!fp) struct rtattr * tb[RTA_MAX+1];
return; char abuf[256];
char obuf[256];
if (!fgets(buf, sizeof(buf), fp)) { SPRINT_BUF(b1);
fclose(fp); __u32 table;
return; 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;
} }
while (fgets(buf, sizeof(buf), fp)) { len -= NLMSG_LENGTH(sizeof(*r));
int vifi; if (len < 0) {
char dev[256]; fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
if (sscanf(buf, "%d%s", &vifi, dev) < 2)
continue;
if (vifi<0 || vifi>31)
continue;
viftable[vifi] = strdup(dev);
} }
fclose(fp); if (r->rtm_type != RTN_MULTICAST) {
} fprintf(stderr, "Not a multicast route (type: %s)\n",
rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
static void read_mroute_list(FILE *ofp) return 0;
{
char buf[256];
FILE *fp = fopen("/proc/net/ip_mr_cache", "r");
if (!fp)
return;
if (!fgets(buf, sizeof(buf), fp)) {
fclose(fp);
return;
} }
while (fgets(buf, sizeof(buf), fp)) { parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
inet_prefix maddr, msrc; table = rtm_get_table(r, tb);
unsigned pkts, b, w;
int vifi; if (filter.tb > 0 && filter.tb != table)
char oiflist[256]; return 0;
char sbuf[256];
char mbuf[256]; if (tb[RTA_IIF])
char obuf[256]; iif = *(int*)RTA_DATA(tb[RTA_IIF]);
if (filter.iif && filter.iif != iif)
oiflist[0] = 0; return 0;
if (sscanf(buf, "%x%x%d%u%u%u %[^\n]",
maddr.data, msrc.data, &vifi, if (filter.af && filter.af != r->rtm_family)
&pkts, &b, &w, oiflist) < 6) return 0;
continue;
if (tb[RTA_DST] &&
if (vifi!=-1 && (vifi < 0 || vifi>31)) filter.mdst.bitlen > 0 &&
continue; inet_addr_match(RTA_DATA(tb[RTA_DST]), &filter.mdst, filter.mdst.bitlen))
return 0;
if (filter_dev[0] && (vifi<0 || strcmp(filter_dev, viftable[vifi])))
continue; if (tb[RTA_SRC] &&
if (filter.mdst.family && inet_addr_match(&maddr, &filter.mdst, filter.mdst.bitlen)) filter.msrc.bitlen > 0 &&
continue; inet_addr_match(RTA_DATA(tb[RTA_SRC]), &filter.msrc, filter.msrc.bitlen))
if (filter.msrc.family && inet_addr_match(&msrc, &filter.msrc, filter.msrc.bitlen)) return 0;
continue;
family = r->rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6;
snprintf(obuf, sizeof(obuf), "(%s, %s)",
format_host(AF_INET, 4, &msrc.data[0], sbuf, sizeof(sbuf)), if (n->nlmsg_type == RTM_DELROUTE)
format_host(AF_INET, 4, &maddr.data[0], mbuf, sizeof(mbuf))); fprintf(fp, "Deleted ");
fprintf(ofp, "%-32s Iif: ", obuf); if (tb[RTA_SRC])
len = snprintf(obuf, sizeof(obuf),
if (vifi == -1) "(%s, ", rt_addr_n2a(family,
fprintf(ofp, "unresolved "); RTA_PAYLOAD(tb[RTA_SRC]),
else RTA_DATA(tb[RTA_SRC]),
fprintf(ofp, "%-10s ", viftable[vifi]); abuf, sizeof(abuf)));
else
if (oiflist[0]) { len = sprintf(obuf, "(unknown, ");
char *next = NULL; if (tb[RTA_DST])
char *p = oiflist; snprintf(obuf + len, sizeof(obuf) - len,
int ovifi, ottl; "%s)", rt_addr_n2a(family, RTA_PAYLOAD(tb[RTA_DST]),
RTA_DATA(tb[RTA_DST]),
fprintf(ofp, "Oifs: "); abuf, sizeof(abuf)));
else
while (p) { snprintf(obuf + len, sizeof(obuf) - len, "unknown) ");
next = strchr(p, ' ');
if (next) { fprintf(fp, "%-32s Iif: ", obuf);
*next = 0; if (iif)
next++; fprintf(fp, "%-10s ", ll_index_to_name(iif));
} else
if (sscanf(p, "%d:%d", &ovifi, &ottl)<2) { fprintf(fp, "unresolved ");
p = next;
continue; if (tb[RTA_MULTIPATH]) {
} struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
p = next; int first = 1;
fprintf(ofp, "%s", viftable[ovifi]); len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
if (ottl>1)
fprintf(ofp, "(ttl %d) ", ovifi); for (;;) {
else if (len < sizeof(*nh))
fprintf(ofp, " "); 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 && b) {
fprintf(ofp, "%s %u packets, %u bytes", _SL_, pkts, b);
if (w)
fprintf(ofp, ", %u arrived on wrong iif.", w);
}
fprintf(ofp, "\n");
} }
fclose(fp); 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) 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) { while (argc > 0) {
if (strcmp(*argv, "iif") == 0) { if (matches(*argv, "table") == 0) {
__u32 tid;
NEXT_ARG(); NEXT_ARG();
strncpy(filter_dev, *argv, sizeof(filter_dev)-1); 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) { } else if (matches(*argv, "from") == 0) {
NEXT_ARG(); NEXT_ARG();
get_prefix(&filter.msrc, *argv, AF_INET); get_prefix(&filter.msrc, *argv, family);
} else { } else {
if (strcmp(*argv, "to") == 0) { if (strcmp(*argv, "to") == 0) {
NEXT_ARG(); NEXT_ARG();
} }
if (matches(*argv, "help") == 0) if (matches(*argv, "help") == 0)
usage(); usage();
get_prefix(&filter.mdst, *argv, AF_INET); get_prefix(&filter.mdst, *argv, family);
} }
argv++; argc--; argc--; argv++;
} }
read_viftable(); ll_init_map(&rth);
read_mroute_list(stdout);
return 0; 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) int do_multiroute(int argc, char **argv)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment