osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 1 | /* |
| 2 | * ipmroute.c "ip mroute". |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License |
| 6 | * as published by the Free Software Foundation; either version |
| 7 | * 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <unistd.h> |
| 16 | #include <syslog.h> |
| 17 | #include <fcntl.h> |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 18 | #include <inttypes.h> |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 19 | #include <sys/ioctl.h> |
| 20 | #include <sys/socket.h> |
osdl.org!shemminger | 0315641 | 2004-06-09 22:58:42 +0000 | [diff] [blame] | 21 | #include <netinet/in.h> |
| 22 | #include <arpa/inet.h> |
| 23 | #include <string.h> |
| 24 | |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 25 | #include <linux/netdevice.h> |
| 26 | #include <linux/if.h> |
| 27 | #include <linux/if_arp.h> |
| 28 | #include <linux/sockios.h> |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 29 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 30 | #include <rt_names.h> |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 31 | #include "utils.h" |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 32 | #include "ip_common.h" |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 33 | |
| 34 | static void usage(void) __attribute__((noreturn)); |
| 35 | |
| 36 | static void usage(void) |
| 37 | { |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 38 | fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n"); |
Nicolas Dichtel | 2a89832 | 2012-12-14 09:56:47 -0800 | [diff] [blame] | 39 | fprintf(stderr, " [ table TABLE_ID ]\n"); |
| 40 | fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 41 | #if 0 |
| 42 | fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n"); |
| 43 | #endif |
| 44 | exit(-1); |
| 45 | } |
| 46 | |
Stephen Hemminger | 56f5daa | 2016-03-21 11:52:19 -0700 | [diff] [blame] | 47 | struct rtfilter { |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 48 | int tb; |
| 49 | int af; |
| 50 | int iif; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 51 | inet_prefix mdst; |
| 52 | inet_prefix msrc; |
| 53 | } filter; |
| 54 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 55 | int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 56 | { |
Stephen Hemminger | 56f5daa | 2016-03-21 11:52:19 -0700 | [diff] [blame] | 57 | FILE *fp = (FILE *)arg; |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 58 | struct rtmsg *r = NLMSG_DATA(n); |
| 59 | int len = n->nlmsg_len; |
Stephen Hemminger | 56f5daa | 2016-03-21 11:52:19 -0700 | [diff] [blame] | 60 | struct rtattr *tb[RTA_MAX+1]; |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 61 | char obuf[256]; |
Stephen Hemminger | 56f5daa | 2016-03-21 11:52:19 -0700 | [diff] [blame] | 62 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 63 | SPRINT_BUF(b1); |
| 64 | __u32 table; |
| 65 | int iif = 0; |
| 66 | int family; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 67 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 68 | if ((n->nlmsg_type != RTM_NEWROUTE && |
Nicolas Dichtel | 505f918 | 2015-04-22 10:27:07 +0200 | [diff] [blame] | 69 | n->nlmsg_type != RTM_DELROUTE)) { |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 70 | fprintf(stderr, "Not a multicast route: %08x %08x %08x\n", |
| 71 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); |
| 72 | return 0; |
Thomas Jarosch | 67ef60a | 2011-10-03 05:24:08 +0000 | [diff] [blame] | 73 | } |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 74 | len -= NLMSG_LENGTH(sizeof(*r)); |
| 75 | if (len < 0) { |
| 76 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); |
| 77 | return -1; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 78 | } |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 79 | if (r->rtm_type != RTN_MULTICAST) { |
| 80 | fprintf(stderr, "Not a multicast route (type: %s)\n", |
| 81 | rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); |
| 82 | return 0; |
Thomas Jarosch | e588a7d | 2011-10-03 05:24:18 +0000 | [diff] [blame] | 83 | } |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 84 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 85 | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); |
| 86 | table = rtm_get_table(r, tb); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 87 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 88 | if (filter.tb > 0 && filter.tb != table) |
| 89 | return 0; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 90 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 91 | if (tb[RTA_IIF]) |
Stephen Hemminger | 56f5daa | 2016-03-21 11:52:19 -0700 | [diff] [blame] | 92 | iif = *(int *)RTA_DATA(tb[RTA_IIF]); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 93 | if (filter.iif && filter.iif != iif) |
| 94 | return 0; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 95 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 96 | if (filter.af && filter.af != r->rtm_family) |
| 97 | return 0; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 98 | |
Mazhar Rana | 45b01c4 | 2015-06-11 21:37:36 +0530 | [diff] [blame] | 99 | if (tb[RTA_DST] && filter.mdst.bitlen > 0) { |
Phil Sutter | d17b136 | 2016-07-18 16:48:42 +0200 | [diff] [blame] | 100 | inet_prefix dst = { .family = r->rtm_family }; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 101 | |
Mazhar Rana | 45b01c4 | 2015-06-11 21:37:36 +0530 | [diff] [blame] | 102 | memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), RTA_PAYLOAD(tb[RTA_DST])); |
| 103 | if (inet_addr_match(&dst, &filter.mdst, filter.mdst.bitlen)) |
| 104 | return 0; |
| 105 | } |
| 106 | |
| 107 | if (tb[RTA_SRC] && filter.msrc.bitlen > 0) { |
Phil Sutter | d17b136 | 2016-07-18 16:48:42 +0200 | [diff] [blame] | 108 | inet_prefix src = { .family = r->rtm_family }; |
Mazhar Rana | 45b01c4 | 2015-06-11 21:37:36 +0530 | [diff] [blame] | 109 | |
Mazhar Rana | 45b01c4 | 2015-06-11 21:37:36 +0530 | [diff] [blame] | 110 | memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), RTA_PAYLOAD(tb[RTA_SRC])); |
| 111 | if (inet_addr_match(&src, &filter.msrc, filter.msrc.bitlen)) |
| 112 | return 0; |
| 113 | } |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 114 | |
Nikolay Aleksandrov | 56e3eb4 | 2016-08-20 14:53:10 +0200 | [diff] [blame] | 115 | family = get_real_family(r->rtm_type, r->rtm_family); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 116 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 117 | if (n->nlmsg_type == RTM_DELROUTE) |
| 118 | fprintf(fp, "Deleted "); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 119 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 120 | if (tb[RTA_SRC]) |
| 121 | len = snprintf(obuf, sizeof(obuf), |
Phil Sutter | 7faf158 | 2016-03-22 19:35:18 +0100 | [diff] [blame] | 122 | "(%s, ", rt_addr_n2a_rta(family, tb[RTA_SRC])); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 123 | else |
| 124 | len = sprintf(obuf, "(unknown, "); |
| 125 | if (tb[RTA_DST]) |
| 126 | snprintf(obuf + len, sizeof(obuf) - len, |
Phil Sutter | 7faf158 | 2016-03-22 19:35:18 +0100 | [diff] [blame] | 127 | "%s)", rt_addr_n2a_rta(family, tb[RTA_DST])); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 128 | else |
| 129 | snprintf(obuf + len, sizeof(obuf) - len, "unknown) "); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 130 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 131 | fprintf(fp, "%-32s Iif: ", obuf); |
| 132 | if (iif) |
| 133 | fprintf(fp, "%-10s ", ll_index_to_name(iif)); |
| 134 | else |
| 135 | fprintf(fp, "unresolved "); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 136 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 137 | if (tb[RTA_MULTIPATH]) { |
| 138 | struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); |
| 139 | int first = 1; |
| 140 | |
| 141 | len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); |
| 142 | |
| 143 | for (;;) { |
| 144 | if (len < sizeof(*nh)) |
| 145 | break; |
| 146 | if (nh->rtnh_len > len) |
| 147 | break; |
| 148 | |
| 149 | if (first) { |
| 150 | fprintf(fp, "Oifs: "); |
| 151 | first = 0; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 152 | } |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 153 | fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex)); |
| 154 | if (nh->rtnh_hops > 1) |
| 155 | fprintf(fp, "(ttl %d) ", nh->rtnh_hops); |
| 156 | else |
| 157 | fprintf(fp, " "); |
| 158 | len -= NLMSG_ALIGN(nh->rtnh_len); |
| 159 | nh = RTNH_NEXT(nh); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 160 | } |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 161 | } |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 162 | if (show_stats && tb[RTA_MFC_STATS]) { |
| 163 | struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]); |
| 164 | |
| 165 | fprintf(fp, "%s %"PRIu64" packets, %"PRIu64" bytes", _SL_, |
| 166 | (uint64_t)mfcs->mfcs_packets, |
| 167 | (uint64_t)mfcs->mfcs_bytes); |
| 168 | if (mfcs->mfcs_wrong_if) |
| 169 | fprintf(fp, ", %"PRIu64" arrived on wrong iif.", |
| 170 | (uint64_t)mfcs->mfcs_wrong_if); |
| 171 | } |
Nikolay Aleksandrov | 590bf22 | 2016-09-21 11:45:58 +0200 | [diff] [blame] | 172 | if (show_stats && tb[RTA_EXPIRES]) { |
| 173 | struct timeval tv; |
| 174 | |
| 175 | __jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES])); |
| 176 | fprintf(fp, ", Age %4i.%.2i", (int)tv.tv_sec, |
| 177 | (int)tv.tv_usec/10000); |
| 178 | } |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 179 | fprintf(fp, "\n"); |
| 180 | fflush(fp); |
| 181 | return 0; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 182 | } |
| 183 | |
vadimk | 093b764 | 2014-10-20 12:25:17 +0300 | [diff] [blame] | 184 | void ipmroute_reset_filter(int ifindex) |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 185 | { |
| 186 | memset(&filter, 0, sizeof(filter)); |
| 187 | filter.mdst.bitlen = -1; |
| 188 | filter.msrc.bitlen = -1; |
vadimk | 093b764 | 2014-10-20 12:25:17 +0300 | [diff] [blame] | 189 | filter.iif = ifindex; |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 190 | } |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 191 | |
| 192 | static int mroute_list(int argc, char **argv) |
| 193 | { |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 194 | char *id = NULL; |
| 195 | int family; |
| 196 | |
vadimk | 093b764 | 2014-10-20 12:25:17 +0300 | [diff] [blame] | 197 | ipmroute_reset_filter(0); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 198 | if (preferred_family == AF_UNSPEC) |
| 199 | family = AF_INET; |
| 200 | else |
| 201 | family = AF_INET6; |
| 202 | if (family == AF_INET) { |
| 203 | filter.af = RTNL_FAMILY_IPMR; |
| 204 | filter.tb = RT_TABLE_DEFAULT; /* for backward compatibility */ |
| 205 | } else |
| 206 | filter.af = RTNL_FAMILY_IP6MR; |
| 207 | |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 208 | while (argc > 0) { |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 209 | if (matches(*argv, "table") == 0) { |
| 210 | __u32 tid; |
Stephen Hemminger | 56f5daa | 2016-03-21 11:52:19 -0700 | [diff] [blame] | 211 | |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 212 | NEXT_ARG(); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 213 | if (rtnl_rttable_a2n(&tid, *argv)) { |
| 214 | if (strcmp(*argv, "all") == 0) { |
| 215 | filter.tb = 0; |
| 216 | } else if (strcmp(*argv, "help") == 0) { |
| 217 | usage(); |
| 218 | } else { |
| 219 | invarg("table id value is invalid\n", *argv); |
| 220 | } |
| 221 | } else |
| 222 | filter.tb = tid; |
| 223 | } else if (strcmp(*argv, "iif") == 0) { |
| 224 | NEXT_ARG(); |
| 225 | id = *argv; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 226 | } else if (matches(*argv, "from") == 0) { |
| 227 | NEXT_ARG(); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 228 | get_prefix(&filter.msrc, *argv, family); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 229 | } else { |
| 230 | if (strcmp(*argv, "to") == 0) { |
| 231 | NEXT_ARG(); |
| 232 | } |
| 233 | if (matches(*argv, "help") == 0) |
| 234 | usage(); |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 235 | get_prefix(&filter.mdst, *argv, family); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 236 | } |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 237 | argc--; argv++; |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 238 | } |
| 239 | |
Nicolas Dichtel | e34d3dc | 2012-12-14 10:08:17 -0800 | [diff] [blame] | 240 | ll_init_map(&rth); |
| 241 | |
| 242 | if (id) { |
| 243 | int idx; |
| 244 | |
| 245 | if ((idx = ll_name_to_index(id)) == 0) { |
| 246 | fprintf(stderr, "Cannot find device \"%s\"\n", id); |
| 247 | return -1; |
| 248 | } |
| 249 | filter.iif = idx; |
| 250 | } |
| 251 | |
| 252 | if (rtnl_wilddump_request(&rth, filter.af, RTM_GETROUTE) < 0) { |
| 253 | perror("Cannot send dump request"); |
| 254 | return 1; |
| 255 | } |
| 256 | |
| 257 | if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) { |
| 258 | fprintf(stderr, "Dump terminated\n"); |
| 259 | exit(1); |
| 260 | } |
| 261 | |
| 262 | exit(0); |
osdl.org!shemminger | aba5acd | 2004-04-15 20:56:59 +0000 | [diff] [blame] | 263 | } |
| 264 | |
| 265 | int do_multiroute(int argc, char **argv) |
| 266 | { |
| 267 | if (argc < 1) |
| 268 | return mroute_list(0, NULL); |
| 269 | #if 0 |
| 270 | if (matches(*argv, "add") == 0) |
| 271 | return mroute_modify(RTM_NEWADDR, argc-1, argv+1); |
| 272 | if (matches(*argv, "delete") == 0) |
| 273 | return mroute_modify(RTM_DELADDR, argc-1, argv+1); |
| 274 | if (matches(*argv, "get") == 0) |
| 275 | return mroute_get(argc-1, argv+1); |
| 276 | #endif |
| 277 | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 |
| 278 | || matches(*argv, "lst") == 0) |
| 279 | return mroute_list(argc-1, argv+1); |
| 280 | if (matches(*argv, "help") == 0) |
| 281 | usage(); |
| 282 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); |
| 283 | exit(-1); |
| 284 | } |