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