]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
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> | |
aba5acdf | 16 | #include <fcntl.h> |
e34d3dcc | 17 | #include <inttypes.h> |
aba5acdf SH |
18 | #include <sys/ioctl.h> |
19 | #include <sys/socket.h> | |
03156410 SH |
20 | #include <netinet/in.h> |
21 | #include <arpa/inet.h> | |
22 | #include <string.h> | |
23 | ||
aba5acdf SH |
24 | #include <linux/netdevice.h> |
25 | #include <linux/if.h> | |
26 | #include <linux/if_arp.h> | |
27 | #include <linux/sockios.h> | |
aba5acdf | 28 | |
e34d3dcc | 29 | #include <rt_names.h> |
aba5acdf | 30 | #include "utils.h" |
e34d3dcc | 31 | #include "ip_common.h" |
aba5acdf SH |
32 | |
33 | static void usage(void) __attribute__((noreturn)); | |
34 | ||
35 | static void usage(void) | |
36 | { | |
e34d3dcc | 37 | fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n"); |
2a898320 ND |
38 | fprintf(stderr, " [ table TABLE_ID ]\n"); |
39 | fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); | |
aba5acdf SH |
40 | #if 0 |
41 | fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n"); | |
42 | #endif | |
43 | exit(-1); | |
44 | } | |
45 | ||
56f5daac | 46 | struct rtfilter { |
e34d3dcc ND |
47 | int tb; |
48 | int af; | |
49 | int iif; | |
aba5acdf SH |
50 | inet_prefix mdst; |
51 | inet_prefix msrc; | |
52 | } filter; | |
53 | ||
e34d3dcc | 54 | int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
aba5acdf | 55 | { |
56f5daac | 56 | FILE *fp = (FILE *)arg; |
e34d3dcc ND |
57 | struct rtmsg *r = NLMSG_DATA(n); |
58 | int len = n->nlmsg_len; | |
56f5daac | 59 | struct rtattr *tb[RTA_MAX+1]; |
e34d3dcc | 60 | char obuf[256]; |
56f5daac | 61 | |
e34d3dcc ND |
62 | SPRINT_BUF(b1); |
63 | __u32 table; | |
64 | int iif = 0; | |
65 | int family; | |
66 | ||
67 | if ((n->nlmsg_type != RTM_NEWROUTE && | |
505f9186 | 68 | n->nlmsg_type != RTM_DELROUTE)) { |
e34d3dcc ND |
69 | fprintf(stderr, "Not a multicast route: %08x %08x %08x\n", |
70 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
71 | return 0; | |
67ef60a2 | 72 | } |
e34d3dcc ND |
73 | len -= NLMSG_LENGTH(sizeof(*r)); |
74 | if (len < 0) { | |
75 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
76 | return -1; | |
aba5acdf | 77 | } |
e34d3dcc ND |
78 | if (r->rtm_type != RTN_MULTICAST) { |
79 | fprintf(stderr, "Not a multicast route (type: %s)\n", | |
80 | rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); | |
81 | return 0; | |
e588a7db | 82 | } |
aba5acdf | 83 | |
e34d3dcc ND |
84 | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); |
85 | table = rtm_get_table(r, tb); | |
86 | ||
87 | if (filter.tb > 0 && filter.tb != table) | |
88 | return 0; | |
89 | ||
90 | if (tb[RTA_IIF]) | |
9f1370c0 | 91 | iif = rta_getattr_u32(tb[RTA_IIF]); |
e34d3dcc ND |
92 | if (filter.iif && filter.iif != iif) |
93 | return 0; | |
94 | ||
95 | if (filter.af && filter.af != r->rtm_family) | |
96 | return 0; | |
97 | ||
45b01c46 | 98 | if (tb[RTA_DST] && filter.mdst.bitlen > 0) { |
d17b136f | 99 | inet_prefix dst = { .family = r->rtm_family }; |
45b01c46 | 100 | |
45b01c46 MR |
101 | memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), RTA_PAYLOAD(tb[RTA_DST])); |
102 | if (inet_addr_match(&dst, &filter.mdst, filter.mdst.bitlen)) | |
103 | return 0; | |
104 | } | |
e34d3dcc | 105 | |
45b01c46 | 106 | if (tb[RTA_SRC] && filter.msrc.bitlen > 0) { |
d17b136f | 107 | inet_prefix src = { .family = r->rtm_family }; |
45b01c46 | 108 | |
45b01c46 MR |
109 | memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), RTA_PAYLOAD(tb[RTA_SRC])); |
110 | if (inet_addr_match(&src, &filter.msrc, filter.msrc.bitlen)) | |
111 | return 0; | |
112 | } | |
e34d3dcc | 113 | |
56e3eb4c | 114 | family = get_real_family(r->rtm_type, r->rtm_family); |
e34d3dcc ND |
115 | |
116 | if (n->nlmsg_type == RTM_DELROUTE) | |
117 | fprintf(fp, "Deleted "); | |
118 | ||
119 | if (tb[RTA_SRC]) | |
120 | len = snprintf(obuf, sizeof(obuf), | |
7faf1588 | 121 | "(%s, ", rt_addr_n2a_rta(family, tb[RTA_SRC])); |
e34d3dcc ND |
122 | else |
123 | len = sprintf(obuf, "(unknown, "); | |
124 | if (tb[RTA_DST]) | |
125 | snprintf(obuf + len, sizeof(obuf) - len, | |
7faf1588 | 126 | "%s)", rt_addr_n2a_rta(family, tb[RTA_DST])); |
e34d3dcc ND |
127 | else |
128 | snprintf(obuf + len, sizeof(obuf) - len, "unknown) "); | |
129 | ||
130 | fprintf(fp, "%-32s Iif: ", obuf); | |
131 | if (iif) | |
132 | fprintf(fp, "%-10s ", ll_index_to_name(iif)); | |
133 | else | |
134 | fprintf(fp, "unresolved "); | |
135 | ||
136 | if (tb[RTA_MULTIPATH]) { | |
137 | struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); | |
138 | int first = 1; | |
139 | ||
140 | len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); | |
141 | ||
142 | for (;;) { | |
143 | if (len < sizeof(*nh)) | |
144 | break; | |
145 | if (nh->rtnh_len > len) | |
146 | break; | |
147 | ||
148 | if (first) { | |
149 | fprintf(fp, "Oifs: "); | |
150 | first = 0; | |
aba5acdf | 151 | } |
e34d3dcc ND |
152 | fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex)); |
153 | if (nh->rtnh_hops > 1) | |
154 | fprintf(fp, "(ttl %d) ", nh->rtnh_hops); | |
155 | else | |
156 | fprintf(fp, " "); | |
157 | len -= NLMSG_ALIGN(nh->rtnh_len); | |
158 | nh = RTNH_NEXT(nh); | |
aba5acdf | 159 | } |
aba5acdf | 160 | } |
e763e331 NA |
161 | fprintf(fp, " State: %s", |
162 | r->rtm_flags & RTNH_F_UNRESOLVED ? "unresolved" : "resolved"); | |
2055bf15 YG |
163 | if (r->rtm_flags & RTNH_F_OFFLOAD) |
164 | fprintf(fp, " offload"); | |
e34d3dcc ND |
165 | if (show_stats && tb[RTA_MFC_STATS]) { |
166 | struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]); | |
167 | ||
168 | fprintf(fp, "%s %"PRIu64" packets, %"PRIu64" bytes", _SL_, | |
169 | (uint64_t)mfcs->mfcs_packets, | |
170 | (uint64_t)mfcs->mfcs_bytes); | |
171 | if (mfcs->mfcs_wrong_if) | |
172 | fprintf(fp, ", %"PRIu64" arrived on wrong iif.", | |
173 | (uint64_t)mfcs->mfcs_wrong_if); | |
174 | } | |
590bf22a NA |
175 | if (show_stats && tb[RTA_EXPIRES]) { |
176 | struct timeval tv; | |
177 | ||
178 | __jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES])); | |
179 | fprintf(fp, ", Age %4i.%.2i", (int)tv.tv_sec, | |
180 | (int)tv.tv_usec/10000); | |
181 | } | |
3dc98cf2 DS |
182 | |
183 | if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) | |
184 | fprintf(fp, " Table: %s", | |
185 | rtnl_rttable_n2a(table, b1, sizeof(b1))); | |
186 | ||
e34d3dcc ND |
187 | fprintf(fp, "\n"); |
188 | fflush(fp); | |
189 | return 0; | |
aba5acdf SH |
190 | } |
191 | ||
093b7646 | 192 | void ipmroute_reset_filter(int ifindex) |
e34d3dcc ND |
193 | { |
194 | memset(&filter, 0, sizeof(filter)); | |
195 | filter.mdst.bitlen = -1; | |
196 | filter.msrc.bitlen = -1; | |
093b7646 | 197 | filter.iif = ifindex; |
e34d3dcc | 198 | } |
aba5acdf SH |
199 | |
200 | static int mroute_list(int argc, char **argv) | |
201 | { | |
e34d3dcc ND |
202 | char *id = NULL; |
203 | int family; | |
204 | ||
093b7646 | 205 | ipmroute_reset_filter(0); |
e34d3dcc ND |
206 | if (preferred_family == AF_UNSPEC) |
207 | family = AF_INET; | |
208 | else | |
209 | family = AF_INET6; | |
210 | if (family == AF_INET) { | |
211 | filter.af = RTNL_FAMILY_IPMR; | |
212 | filter.tb = RT_TABLE_DEFAULT; /* for backward compatibility */ | |
213 | } else | |
214 | filter.af = RTNL_FAMILY_IP6MR; | |
215 | ||
aba5acdf | 216 | while (argc > 0) { |
e34d3dcc ND |
217 | if (matches(*argv, "table") == 0) { |
218 | __u32 tid; | |
56f5daac | 219 | |
aba5acdf | 220 | NEXT_ARG(); |
e34d3dcc ND |
221 | if (rtnl_rttable_a2n(&tid, *argv)) { |
222 | if (strcmp(*argv, "all") == 0) { | |
223 | filter.tb = 0; | |
224 | } else if (strcmp(*argv, "help") == 0) { | |
225 | usage(); | |
226 | } else { | |
227 | invarg("table id value is invalid\n", *argv); | |
228 | } | |
229 | } else | |
230 | filter.tb = tid; | |
231 | } else if (strcmp(*argv, "iif") == 0) { | |
232 | NEXT_ARG(); | |
233 | id = *argv; | |
aba5acdf SH |
234 | } else if (matches(*argv, "from") == 0) { |
235 | NEXT_ARG(); | |
e34d3dcc | 236 | get_prefix(&filter.msrc, *argv, family); |
aba5acdf SH |
237 | } else { |
238 | if (strcmp(*argv, "to") == 0) { | |
239 | NEXT_ARG(); | |
240 | } | |
241 | if (matches(*argv, "help") == 0) | |
242 | usage(); | |
e34d3dcc | 243 | get_prefix(&filter.mdst, *argv, family); |
aba5acdf | 244 | } |
e34d3dcc | 245 | argc--; argv++; |
aba5acdf SH |
246 | } |
247 | ||
e34d3dcc ND |
248 | ll_init_map(&rth); |
249 | ||
250 | if (id) { | |
251 | int idx; | |
252 | ||
253 | if ((idx = ll_name_to_index(id)) == 0) { | |
254 | fprintf(stderr, "Cannot find device \"%s\"\n", id); | |
255 | return -1; | |
256 | } | |
257 | filter.iif = idx; | |
258 | } | |
259 | ||
260 | if (rtnl_wilddump_request(&rth, filter.af, RTM_GETROUTE) < 0) { | |
261 | perror("Cannot send dump request"); | |
262 | return 1; | |
263 | } | |
264 | ||
265 | if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) { | |
266 | fprintf(stderr, "Dump terminated\n"); | |
267 | exit(1); | |
268 | } | |
269 | ||
270 | exit(0); | |
aba5acdf SH |
271 | } |
272 | ||
273 | int do_multiroute(int argc, char **argv) | |
274 | { | |
275 | if (argc < 1) | |
276 | return mroute_list(0, NULL); | |
277 | #if 0 | |
278 | if (matches(*argv, "add") == 0) | |
279 | return mroute_modify(RTM_NEWADDR, argc-1, argv+1); | |
280 | if (matches(*argv, "delete") == 0) | |
281 | return mroute_modify(RTM_DELADDR, argc-1, argv+1); | |
282 | if (matches(*argv, "get") == 0) | |
283 | return mroute_get(argc-1, argv+1); | |
284 | #endif | |
285 | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 | |
286 | || matches(*argv, "lst") == 0) | |
287 | return mroute_list(argc-1, argv+1); | |
288 | if (matches(*argv, "help") == 0) | |
289 | usage(); | |
290 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); | |
291 | exit(-1); | |
292 | } |