]>
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" |
0f1475c2 | 32 | #include "json_print.h" |
aba5acdf SH |
33 | |
34 | static void usage(void) __attribute__((noreturn)); | |
35 | ||
36 | static void usage(void) | |
37 | { | |
8589eb4e MC |
38 | fprintf(stderr, |
39 | "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n" | |
40 | " [ table TABLE_ID ]\n" | |
41 | "TABLE_ID := [ local | main | default | all | NUMBER ]\n" | |
aba5acdf | 42 | #if 0 |
8589eb4e | 43 | "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n" |
aba5acdf | 44 | #endif |
8589eb4e | 45 | ); |
aba5acdf SH |
46 | exit(-1); |
47 | } | |
48 | ||
578cadcc | 49 | static struct rtfilter { |
e34d3dcc ND |
50 | int tb; |
51 | int af; | |
52 | int iif; | |
aba5acdf SH |
53 | inet_prefix mdst; |
54 | inet_prefix msrc; | |
55 | } filter; | |
56 | ||
cd554f2c | 57 | int print_mroute(struct nlmsghdr *n, void *arg) |
aba5acdf | 58 | { |
e34d3dcc ND |
59 | struct rtmsg *r = NLMSG_DATA(n); |
60 | int len = n->nlmsg_len; | |
56f5daac | 61 | struct rtattr *tb[RTA_MAX+1]; |
09e0528c | 62 | FILE *fp = arg; |
0f1475c2 | 63 | const char *src, *dst; |
e34d3dcc | 64 | SPRINT_BUF(b1); |
0f1475c2 | 65 | SPRINT_BUF(b2); |
e34d3dcc ND |
66 | __u32 table; |
67 | int iif = 0; | |
68 | int family; | |
69 | ||
70 | if ((n->nlmsg_type != RTM_NEWROUTE && | |
505f9186 | 71 | n->nlmsg_type != RTM_DELROUTE)) { |
e34d3dcc ND |
72 | fprintf(stderr, "Not a multicast route: %08x %08x %08x\n", |
73 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
74 | return 0; | |
67ef60a2 | 75 | } |
e34d3dcc ND |
76 | len -= NLMSG_LENGTH(sizeof(*r)); |
77 | if (len < 0) { | |
78 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
79 | return -1; | |
aba5acdf | 80 | } |
e06e9a6b | 81 | |
e34d3dcc | 82 | if (r->rtm_type != RTN_MULTICAST) { |
e06e9a6b SH |
83 | fprintf(stderr, |
84 | "Non multicast route received, kernel does support IP multicast?\n"); | |
85 | return -1; | |
e588a7db | 86 | } |
aba5acdf | 87 | |
e34d3dcc ND |
88 | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); |
89 | table = rtm_get_table(r, tb); | |
90 | ||
91 | if (filter.tb > 0 && filter.tb != table) | |
92 | return 0; | |
93 | ||
94 | if (tb[RTA_IIF]) | |
9f1370c0 | 95 | iif = rta_getattr_u32(tb[RTA_IIF]); |
e34d3dcc ND |
96 | if (filter.iif && filter.iif != iif) |
97 | return 0; | |
98 | ||
99 | if (filter.af && filter.af != r->rtm_family) | |
100 | return 0; | |
101 | ||
ba6052df SP |
102 | if (inet_addr_match_rta(&filter.mdst, tb[RTA_DST])) |
103 | return 0; | |
45b01c46 | 104 | |
ba6052df SP |
105 | if (inet_addr_match_rta(&filter.msrc, tb[RTA_SRC])) |
106 | return 0; | |
e34d3dcc | 107 | |
56e3eb4c | 108 | family = get_real_family(r->rtm_type, r->rtm_family); |
e34d3dcc | 109 | |
0f1475c2 | 110 | open_json_object(NULL); |
e34d3dcc | 111 | if (n->nlmsg_type == RTM_DELROUTE) |
0f1475c2 | 112 | print_bool(PRINT_ANY, "deleted", "Deleted ", true); |
e34d3dcc ND |
113 | |
114 | if (tb[RTA_SRC]) | |
0f1475c2 SH |
115 | src = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_SRC]), |
116 | RTA_DATA(tb[RTA_SRC]), b1, sizeof(b1)); | |
e34d3dcc | 117 | else |
0f1475c2 SH |
118 | src = "unknown"; |
119 | ||
e34d3dcc | 120 | if (tb[RTA_DST]) |
0f1475c2 SH |
121 | dst = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_DST]), |
122 | RTA_DATA(tb[RTA_DST]), b2, sizeof(b2)); | |
e34d3dcc | 123 | else |
0f1475c2 SH |
124 | dst = "unknown"; |
125 | ||
126 | if (is_json_context()) { | |
127 | print_string(PRINT_JSON, "src", NULL, src); | |
128 | print_string(PRINT_JSON, "dst", NULL, dst); | |
129 | } else { | |
130 | char obuf[256]; | |
131 | ||
132 | snprintf(obuf, sizeof(obuf), "(%s,%s)", src, dst); | |
133 | print_string(PRINT_FP, NULL, | |
134 | "%-32s Iif: ", obuf); | |
135 | } | |
e34d3dcc | 136 | |
e34d3dcc | 137 | if (iif) |
0f1475c2 SH |
138 | print_color_string(PRINT_ANY, COLOR_IFNAME, |
139 | "iif", "%-10s ", ll_index_to_name(iif)); | |
e34d3dcc | 140 | else |
0f1475c2 | 141 | print_string(PRINT_ANY,"iif", "%s ", "unresolved"); |
e34d3dcc ND |
142 | |
143 | if (tb[RTA_MULTIPATH]) { | |
144 | struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); | |
145 | int first = 1; | |
146 | ||
0f1475c2 | 147 | open_json_array(PRINT_JSON, "multipath"); |
e34d3dcc ND |
148 | len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); |
149 | ||
150 | for (;;) { | |
151 | if (len < sizeof(*nh)) | |
152 | break; | |
153 | if (nh->rtnh_len > len) | |
154 | break; | |
155 | ||
0f1475c2 | 156 | open_json_object(NULL); |
e34d3dcc | 157 | if (first) { |
0f1475c2 | 158 | print_string(PRINT_FP, NULL, "Oifs: ", NULL); |
e34d3dcc | 159 | first = 0; |
aba5acdf | 160 | } |
0f1475c2 SH |
161 | |
162 | print_color_string(PRINT_ANY, COLOR_IFNAME, | |
163 | "oif", "%s", ll_index_to_name(nh->rtnh_ifindex)); | |
164 | ||
e34d3dcc | 165 | if (nh->rtnh_hops > 1) |
0f1475c2 SH |
166 | print_uint(PRINT_ANY, |
167 | "ttl", "(ttl %u) ", nh->rtnh_hops); | |
e34d3dcc | 168 | else |
0f1475c2 SH |
169 | print_string(PRINT_FP, NULL, " ", NULL); |
170 | ||
171 | close_json_object(); | |
e34d3dcc ND |
172 | len -= NLMSG_ALIGN(nh->rtnh_len); |
173 | nh = RTNH_NEXT(nh); | |
aba5acdf | 174 | } |
0f1475c2 | 175 | close_json_array(PRINT_JSON, NULL); |
aba5acdf | 176 | } |
0f1475c2 SH |
177 | |
178 | print_string(PRINT_ANY, "state", " State: %s", | |
179 | (r->rtm_flags & RTNH_F_UNRESOLVED) ? "unresolved" : "resolved"); | |
180 | ||
2055bf15 | 181 | if (r->rtm_flags & RTNH_F_OFFLOAD) |
0f1475c2 SH |
182 | print_null(PRINT_ANY, "offload", " offload", NULL); |
183 | ||
e34d3dcc ND |
184 | if (show_stats && tb[RTA_MFC_STATS]) { |
185 | struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]); | |
186 | ||
b85076cd | 187 | print_nl(); |
4db2ff0d | 188 | print_u64(PRINT_ANY, "packets", " %"PRIu64" packets,", |
0f1475c2 | 189 | mfcs->mfcs_packets); |
4db2ff0d | 190 | print_u64(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes); |
0f1475c2 | 191 | |
e34d3dcc | 192 | if (mfcs->mfcs_wrong_if) |
4db2ff0d | 193 | print_u64(PRINT_ANY, "wrong_if", |
0f1475c2 SH |
194 | ", %"PRIu64" arrived on wrong iif.", |
195 | mfcs->mfcs_wrong_if); | |
e34d3dcc | 196 | } |
0f1475c2 | 197 | |
590bf22a NA |
198 | if (show_stats && tb[RTA_EXPIRES]) { |
199 | struct timeval tv; | |
0f1475c2 | 200 | double age; |
590bf22a NA |
201 | |
202 | __jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES])); | |
0f1475c2 SH |
203 | age = tv.tv_sec; |
204 | age += tv.tv_usec / 1000000.; | |
205 | print_float(PRINT_ANY, "expires", | |
206 | ", Age %.2f", age); | |
590bf22a | 207 | } |
3dc98cf2 DS |
208 | |
209 | if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) | |
0f1475c2 SH |
210 | print_string(PRINT_ANY, "table", " Table: %s", |
211 | rtnl_rttable_n2a(table, b1, sizeof(b1))); | |
3dc98cf2 | 212 | |
0f1475c2 SH |
213 | print_string(PRINT_FP, NULL, "\n", NULL); |
214 | close_json_object(); | |
09e0528c | 215 | fflush(fp); |
e34d3dcc | 216 | return 0; |
aba5acdf SH |
217 | } |
218 | ||
093b7646 | 219 | void ipmroute_reset_filter(int ifindex) |
e34d3dcc ND |
220 | { |
221 | memset(&filter, 0, sizeof(filter)); | |
222 | filter.mdst.bitlen = -1; | |
223 | filter.msrc.bitlen = -1; | |
093b7646 | 224 | filter.iif = ifindex; |
e34d3dcc | 225 | } |
aba5acdf | 226 | |
e41ede89 DA |
227 | static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen) |
228 | { | |
229 | int err; | |
230 | ||
231 | if (filter.tb) { | |
232 | err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb); | |
233 | if (err) | |
234 | return err; | |
235 | } | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
aba5acdf SH |
240 | static int mroute_list(int argc, char **argv) |
241 | { | |
e34d3dcc | 242 | char *id = NULL; |
98ce9927 | 243 | int family = preferred_family; |
e34d3dcc | 244 | |
093b7646 | 245 | ipmroute_reset_filter(0); |
98ce9927 DA |
246 | if (family == AF_INET || family == AF_UNSPEC) { |
247 | family = RTNL_FAMILY_IPMR; | |
e34d3dcc ND |
248 | filter.af = RTNL_FAMILY_IPMR; |
249 | filter.tb = RT_TABLE_DEFAULT; /* for backward compatibility */ | |
98ce9927 DA |
250 | } else if (family == AF_INET6) { |
251 | family = RTNL_FAMILY_IP6MR; | |
e34d3dcc | 252 | filter.af = RTNL_FAMILY_IP6MR; |
98ce9927 DA |
253 | } else { |
254 | /* family does not have multicast routing */ | |
255 | return 0; | |
256 | } | |
e34d3dcc | 257 | |
ba6052df SP |
258 | filter.msrc.family = filter.mdst.family = family; |
259 | ||
aba5acdf | 260 | while (argc > 0) { |
e34d3dcc ND |
261 | if (matches(*argv, "table") == 0) { |
262 | __u32 tid; | |
56f5daac | 263 | |
aba5acdf | 264 | NEXT_ARG(); |
e34d3dcc ND |
265 | if (rtnl_rttable_a2n(&tid, *argv)) { |
266 | if (strcmp(*argv, "all") == 0) { | |
267 | filter.tb = 0; | |
268 | } else if (strcmp(*argv, "help") == 0) { | |
269 | usage(); | |
270 | } else { | |
271 | invarg("table id value is invalid\n", *argv); | |
272 | } | |
273 | } else | |
274 | filter.tb = tid; | |
275 | } else if (strcmp(*argv, "iif") == 0) { | |
276 | NEXT_ARG(); | |
277 | id = *argv; | |
aba5acdf SH |
278 | } else if (matches(*argv, "from") == 0) { |
279 | NEXT_ARG(); | |
ba6052df SP |
280 | if (get_prefix(&filter.msrc, *argv, family)) |
281 | invarg("from value is invalid\n", *argv); | |
aba5acdf SH |
282 | } else { |
283 | if (strcmp(*argv, "to") == 0) { | |
284 | NEXT_ARG(); | |
285 | } | |
286 | if (matches(*argv, "help") == 0) | |
287 | usage(); | |
ba6052df SP |
288 | if (get_prefix(&filter.mdst, *argv, family)) |
289 | invarg("to value is invalid\n", *argv); | |
aba5acdf | 290 | } |
e34d3dcc | 291 | argc--; argv++; |
aba5acdf SH |
292 | } |
293 | ||
e34d3dcc ND |
294 | ll_init_map(&rth); |
295 | ||
296 | if (id) { | |
297 | int idx; | |
298 | ||
fe99adbc SP |
299 | idx = ll_name_to_index(id); |
300 | if (!idx) | |
301 | return nodev(id); | |
e34d3dcc ND |
302 | filter.iif = idx; |
303 | } | |
304 | ||
e41ede89 | 305 | if (rtnl_routedump_req(&rth, filter.af, iproute_dump_filter) < 0) { |
e34d3dcc ND |
306 | perror("Cannot send dump request"); |
307 | return 1; | |
308 | } | |
309 | ||
0f1475c2 | 310 | new_json_obj(json); |
e34d3dcc | 311 | if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) { |
0f1475c2 | 312 | delete_json_obj(); |
e34d3dcc ND |
313 | fprintf(stderr, "Dump terminated\n"); |
314 | exit(1); | |
315 | } | |
0f1475c2 | 316 | delete_json_obj(); |
e34d3dcc | 317 | |
0f1475c2 | 318 | return 0; |
aba5acdf SH |
319 | } |
320 | ||
321 | int do_multiroute(int argc, char **argv) | |
322 | { | |
323 | if (argc < 1) | |
324 | return mroute_list(0, NULL); | |
325 | #if 0 | |
326 | if (matches(*argv, "add") == 0) | |
327 | return mroute_modify(RTM_NEWADDR, argc-1, argv+1); | |
328 | if (matches(*argv, "delete") == 0) | |
329 | return mroute_modify(RTM_DELADDR, argc-1, argv+1); | |
330 | if (matches(*argv, "get") == 0) | |
331 | return mroute_get(argc-1, argv+1); | |
332 | #endif | |
333 | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 | |
334 | || matches(*argv, "lst") == 0) | |
335 | return mroute_list(argc-1, argv+1); | |
336 | if (matches(*argv, "help") == 0) | |
337 | usage(); | |
338 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); | |
339 | exit(-1); | |
340 | } |