blob: 1058b5a401d57c05abeafafaf86073c148072def
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
4 | * |
5 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
6 | * |
7 | * Changes: |
8 | * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated |
9 | */ |
10 | |
11 | #include <fnmatch.h> |
12 | #include <net/if.h> |
13 | #include <net/if_arp.h> |
14 | |
15 | #include "ip_common.h" /* #include "libbb.h" is inside */ |
16 | #include "rt_names.h" |
17 | #include "utils.h" |
18 | |
19 | #ifndef IFF_LOWER_UP |
20 | /* from linux/if.h */ |
21 | #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ |
22 | #endif |
23 | |
24 | struct filter_t { |
25 | char *label; |
26 | char *flushb; |
27 | struct rtnl_handle *rth; |
28 | int scope, scopemask; |
29 | int flags, flagmask; |
30 | int flushp; |
31 | int flushe; |
32 | int ifindex; |
33 | family_t family; |
34 | smallint showqueue; |
35 | smallint oneline; |
36 | smallint up; |
37 | smallint flushed; |
38 | inet_prefix pfx; |
39 | } FIX_ALIASING; |
40 | typedef struct filter_t filter_t; |
41 | |
42 | #define G_filter (*(filter_t*)&bb_common_bufsiz1) |
43 | |
44 | |
45 | static void print_link_flags(unsigned flags, unsigned mdown) |
46 | { |
47 | static const int flag_masks[] = { |
48 | IFF_LOOPBACK, IFF_BROADCAST, IFF_POINTOPOINT, |
49 | IFF_MULTICAST, IFF_NOARP, IFF_UP, IFF_LOWER_UP }; |
50 | static const char flag_labels[] ALIGN1 = |
51 | "LOOPBACK\0""BROADCAST\0""POINTOPOINT\0" |
52 | "MULTICAST\0""NOARP\0""UP\0""LOWER_UP\0"; |
53 | |
54 | bb_putchar('<'); |
55 | if (flags & IFF_UP && !(flags & IFF_RUNNING)) |
56 | printf("NO-CARRIER,"); |
57 | flags &= ~IFF_RUNNING; |
58 | #if 0 |
59 | _PF(ALLMULTI); |
60 | _PF(PROMISC); |
61 | _PF(MASTER); |
62 | _PF(SLAVE); |
63 | _PF(DEBUG); |
64 | _PF(DYNAMIC); |
65 | _PF(AUTOMEDIA); |
66 | _PF(PORTSEL); |
67 | _PF(NOTRAILERS); |
68 | #endif |
69 | flags = print_flags_separated(flag_masks, flag_labels, flags, ","); |
70 | if (flags) |
71 | printf("%x", flags); |
72 | if (mdown) |
73 | printf(",M-DOWN"); |
74 | printf("> "); |
75 | } |
76 | |
77 | static void print_queuelen(char *name) |
78 | { |
79 | struct ifreq ifr; |
80 | int s; |
81 | |
82 | s = socket(AF_INET, SOCK_STREAM, 0); |
83 | if (s < 0) |
84 | return; |
85 | |
86 | memset(&ifr, 0, sizeof(ifr)); |
87 | strncpy_IFNAMSIZ(ifr.ifr_name, name); |
88 | if (ioctl_or_warn(s, SIOCGIFTXQLEN, &ifr) < 0) { |
89 | close(s); |
90 | return; |
91 | } |
92 | close(s); |
93 | |
94 | if (ifr.ifr_qlen) |
95 | printf("qlen %d", ifr.ifr_qlen); |
96 | } |
97 | |
98 | static NOINLINE int print_linkinfo(const struct nlmsghdr *n) |
99 | { |
100 | struct ifinfomsg *ifi = NLMSG_DATA(n); |
101 | struct rtattr *tb[IFLA_MAX+1]; |
102 | int len = n->nlmsg_len; |
103 | |
104 | if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) |
105 | return 0; |
106 | |
107 | len -= NLMSG_LENGTH(sizeof(*ifi)); |
108 | if (len < 0) |
109 | return -1; |
110 | |
111 | if (G_filter.ifindex && ifi->ifi_index != G_filter.ifindex) |
112 | return 0; |
113 | if (G_filter.up && !(ifi->ifi_flags & IFF_UP)) |
114 | return 0; |
115 | |
116 | memset(tb, 0, sizeof(tb)); |
117 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); |
118 | if (tb[IFLA_IFNAME] == NULL) { |
119 | bb_error_msg("nil ifname"); |
120 | return -1; |
121 | } |
122 | if (G_filter.label |
123 | && (!G_filter.family || G_filter.family == AF_PACKET) |
124 | && fnmatch(G_filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0) |
125 | ) { |
126 | return 0; |
127 | } |
128 | |
129 | if (n->nlmsg_type == RTM_DELLINK) |
130 | printf("Deleted "); |
131 | |
132 | printf("%d: %s", ifi->ifi_index, |
133 | /*tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>" - we checked tb[IFLA_IFNAME] above*/ |
134 | (char*)RTA_DATA(tb[IFLA_IFNAME]) |
135 | ); |
136 | |
137 | { |
138 | unsigned m_flag = 0; |
139 | if (tb[IFLA_LINK]) { |
140 | SPRINT_BUF(b1); |
141 | int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); |
142 | if (iflink == 0) |
143 | printf("@NONE: "); |
144 | else { |
145 | printf("@%s: ", ll_idx_n2a(iflink, b1)); |
146 | m_flag = ll_index_to_flags(iflink); |
147 | m_flag = !(m_flag & IFF_UP); |
148 | } |
149 | } else { |
150 | printf(": "); |
151 | } |
152 | print_link_flags(ifi->ifi_flags, m_flag); |
153 | } |
154 | |
155 | if (tb[IFLA_MTU]) |
156 | printf("mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); |
157 | if (tb[IFLA_QDISC]) |
158 | printf("qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC])); |
159 | #ifdef IFLA_MASTER |
160 | if (tb[IFLA_MASTER]) { |
161 | SPRINT_BUF(b1); |
162 | printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); |
163 | } |
164 | #endif |
165 | /* IFLA_OPERSTATE was added to kernel with the same commit as IFF_DORMANT */ |
166 | #ifdef IFF_DORMANT |
167 | if (tb[IFLA_OPERSTATE]) { |
168 | static const char operstate_labels[] ALIGN1 = |
169 | "UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0" |
170 | "TESTING\0""DORMANT\0""UP\0"; |
171 | printf("state %s ", nth_string(operstate_labels, |
172 | *(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE]))); |
173 | } |
174 | #endif |
175 | if (G_filter.showqueue) |
176 | print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); |
177 | |
178 | if (!G_filter.family || G_filter.family == AF_PACKET) { |
179 | SPRINT_BUF(b1); |
180 | printf("%c link/%s ", _SL_, ll_type_n2a(ifi->ifi_type, b1)); |
181 | |
182 | if (tb[IFLA_ADDRESS]) { |
183 | fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), |
184 | RTA_PAYLOAD(tb[IFLA_ADDRESS]), |
185 | ifi->ifi_type, |
186 | b1, sizeof(b1)), stdout); |
187 | } |
188 | if (tb[IFLA_BROADCAST]) { |
189 | if (ifi->ifi_flags & IFF_POINTOPOINT) |
190 | printf(" peer "); |
191 | else |
192 | printf(" brd "); |
193 | fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), |
194 | RTA_PAYLOAD(tb[IFLA_BROADCAST]), |
195 | ifi->ifi_type, |
196 | b1, sizeof(b1)), stdout); |
197 | } |
198 | } |
199 | bb_putchar('\n'); |
200 | /*fflush_all();*/ |
201 | return 0; |
202 | } |
203 | |
204 | static int flush_update(void) |
205 | { |
206 | if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) { |
207 | bb_perror_msg("can't send flush request"); |
208 | return -1; |
209 | } |
210 | G_filter.flushp = 0; |
211 | return 0; |
212 | } |
213 | |
214 | static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM, |
215 | struct nlmsghdr *n, void *arg UNUSED_PARAM) |
216 | { |
217 | struct ifaddrmsg *ifa = NLMSG_DATA(n); |
218 | int len = n->nlmsg_len; |
219 | struct rtattr * rta_tb[IFA_MAX+1]; |
220 | char abuf[256]; |
221 | SPRINT_BUF(b1); |
222 | |
223 | if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) |
224 | return 0; |
225 | len -= NLMSG_LENGTH(sizeof(*ifa)); |
226 | if (len < 0) { |
227 | bb_error_msg("wrong nlmsg len %d", len); |
228 | return -1; |
229 | } |
230 | |
231 | if (G_filter.flushb && n->nlmsg_type != RTM_NEWADDR) |
232 | return 0; |
233 | |
234 | memset(rta_tb, 0, sizeof(rta_tb)); |
235 | parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); |
236 | |
237 | if (!rta_tb[IFA_LOCAL]) |
238 | rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; |
239 | if (!rta_tb[IFA_ADDRESS]) |
240 | rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; |
241 | |
242 | if (G_filter.ifindex && G_filter.ifindex != (int) ifa->ifa_index) |
243 | return 0; |
244 | if ((G_filter.scope ^ ifa->ifa_scope) & G_filter.scopemask) |
245 | return 0; |
246 | if ((G_filter.flags ^ ifa->ifa_flags) & G_filter.flagmask) |
247 | return 0; |
248 | if (G_filter.label) { |
249 | const char *label; |
250 | if (rta_tb[IFA_LABEL]) |
251 | label = RTA_DATA(rta_tb[IFA_LABEL]); |
252 | else |
253 | label = ll_idx_n2a(ifa->ifa_index, b1); |
254 | if (fnmatch(G_filter.label, label, 0) != 0) |
255 | return 0; |
256 | } |
257 | if (G_filter.pfx.family) { |
258 | if (rta_tb[IFA_LOCAL]) { |
259 | inet_prefix dst; |
260 | memset(&dst, 0, sizeof(dst)); |
261 | dst.family = ifa->ifa_family; |
262 | memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); |
263 | if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen)) |
264 | return 0; |
265 | } |
266 | } |
267 | |
268 | if (G_filter.flushb) { |
269 | struct nlmsghdr *fn; |
270 | if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > (unsigned) G_filter.flushe) { |
271 | if (flush_update()) |
272 | return -1; |
273 | } |
274 | fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp)); |
275 | memcpy(fn, n, n->nlmsg_len); |
276 | fn->nlmsg_type = RTM_DELADDR; |
277 | fn->nlmsg_flags = NLM_F_REQUEST; |
278 | fn->nlmsg_seq = ++G_filter.rth->seq; |
279 | G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb; |
280 | G_filter.flushed = 1; |
281 | return 0; |
282 | } |
283 | |
284 | if (n->nlmsg_type == RTM_DELADDR) |
285 | printf("Deleted "); |
286 | |
287 | if (G_filter.oneline) |
288 | printf("%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); |
289 | if (ifa->ifa_family == AF_INET) |
290 | printf(" inet "); |
291 | else if (ifa->ifa_family == AF_INET6) |
292 | printf(" inet6 "); |
293 | else |
294 | printf(" family %d ", ifa->ifa_family); |
295 | |
296 | if (rta_tb[IFA_LOCAL]) { |
297 | fputs(rt_addr_n2a(ifa->ifa_family, |
298 | RTA_DATA(rta_tb[IFA_LOCAL]), |
299 | abuf, sizeof(abuf)), stdout); |
300 | |
301 | if (rta_tb[IFA_ADDRESS] == NULL |
302 | || memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0 |
303 | ) { |
304 | printf("/%d ", ifa->ifa_prefixlen); |
305 | } else { |
306 | printf(" peer %s/%d ", |
307 | rt_addr_n2a(ifa->ifa_family, |
308 | RTA_DATA(rta_tb[IFA_ADDRESS]), |
309 | abuf, sizeof(abuf)), |
310 | ifa->ifa_prefixlen); |
311 | } |
312 | } |
313 | |
314 | if (rta_tb[IFA_BROADCAST]) { |
315 | printf("brd %s ", |
316 | rt_addr_n2a(ifa->ifa_family, |
317 | RTA_DATA(rta_tb[IFA_BROADCAST]), |
318 | abuf, sizeof(abuf)) |
319 | ); |
320 | } |
321 | if (rta_tb[IFA_ANYCAST]) { |
322 | printf("any %s ", |
323 | rt_addr_n2a(ifa->ifa_family, |
324 | RTA_DATA(rta_tb[IFA_ANYCAST]), |
325 | abuf, sizeof(abuf)) |
326 | ); |
327 | } |
328 | printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1)); |
329 | if (ifa->ifa_flags & IFA_F_SECONDARY) { |
330 | ifa->ifa_flags &= ~IFA_F_SECONDARY; |
331 | printf("secondary "); |
332 | } |
333 | if (ifa->ifa_flags & IFA_F_TENTATIVE) { |
334 | ifa->ifa_flags &= ~IFA_F_TENTATIVE; |
335 | printf("tentative "); |
336 | } |
337 | if (ifa->ifa_flags & IFA_F_DEPRECATED) { |
338 | ifa->ifa_flags &= ~IFA_F_DEPRECATED; |
339 | printf("deprecated "); |
340 | } |
341 | if (!(ifa->ifa_flags & IFA_F_PERMANENT)) { |
342 | printf("dynamic "); |
343 | } else |
344 | ifa->ifa_flags &= ~IFA_F_PERMANENT; |
345 | if (ifa->ifa_flags) |
346 | printf("flags %02x ", ifa->ifa_flags); |
347 | if (rta_tb[IFA_LABEL]) |
348 | fputs((char*)RTA_DATA(rta_tb[IFA_LABEL]), stdout); |
349 | if (rta_tb[IFA_CACHEINFO]) { |
350 | struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); |
351 | char buf[128]; |
352 | bb_putchar(_SL_); |
353 | if (ci->ifa_valid == 0xFFFFFFFFU) |
354 | sprintf(buf, "valid_lft forever"); |
355 | else |
356 | sprintf(buf, "valid_lft %dsec", ci->ifa_valid); |
357 | if (ci->ifa_prefered == 0xFFFFFFFFU) |
358 | sprintf(buf+strlen(buf), " preferred_lft forever"); |
359 | else |
360 | sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); |
361 | printf(" %s", buf); |
362 | } |
363 | bb_putchar('\n'); |
364 | /*fflush_all();*/ |
365 | return 0; |
366 | } |
367 | |
368 | |
369 | struct nlmsg_list { |
370 | struct nlmsg_list *next; |
371 | struct nlmsghdr h; |
372 | }; |
373 | |
374 | static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo) |
375 | { |
376 | for (; ainfo; ainfo = ainfo->next) { |
377 | struct nlmsghdr *n = &ainfo->h; |
378 | struct ifaddrmsg *ifa = NLMSG_DATA(n); |
379 | |
380 | if (n->nlmsg_type != RTM_NEWADDR) |
381 | continue; |
382 | if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) |
383 | return -1; |
384 | if ((int) ifa->ifa_index != ifindex |
385 | || (G_filter.family && G_filter.family != (family_t) ifa->ifa_family) |
386 | ) { |
387 | continue; |
388 | } |
389 | print_addrinfo(NULL, n, NULL); |
390 | } |
391 | return 0; |
392 | } |
393 | |
394 | |
395 | static int FAST_FUNC store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
396 | { |
397 | struct nlmsg_list **linfo = (struct nlmsg_list**)arg; |
398 | struct nlmsg_list *h; |
399 | struct nlmsg_list **lp; |
400 | |
401 | h = xzalloc(n->nlmsg_len + sizeof(void*)); |
402 | |
403 | memcpy(&h->h, n, n->nlmsg_len); |
404 | /*h->next = NULL; - xzalloc did it */ |
405 | |
406 | for (lp = linfo; *lp; lp = &(*lp)->next) |
407 | continue; |
408 | *lp = h; |
409 | |
410 | ll_remember_index(who, n, NULL); |
411 | return 0; |
412 | } |
413 | |
414 | static void ipaddr_reset_filter(int _oneline) |
415 | { |
416 | memset(&G_filter, 0, sizeof(G_filter)); |
417 | G_filter.oneline = _oneline; |
418 | } |
419 | |
420 | /* Return value becomes exitcode. It's okay to not return at all */ |
421 | int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush) |
422 | { |
423 | static const char option[] ALIGN1 = "to\0""scope\0""up\0""label\0""dev\0"; |
424 | |
425 | struct nlmsg_list *linfo = NULL; |
426 | struct nlmsg_list *ainfo = NULL; |
427 | struct nlmsg_list *l; |
428 | struct rtnl_handle rth; |
429 | char *filter_dev = NULL; |
430 | int no_link = 0; |
431 | |
432 | ipaddr_reset_filter(oneline); |
433 | G_filter.showqueue = 1; |
434 | |
435 | if (G_filter.family == AF_UNSPEC) |
436 | G_filter.family = preferred_family; |
437 | |
438 | if (flush) { |
439 | if (!*argv) { |
440 | bb_error_msg_and_die(bb_msg_requires_arg, "flush"); |
441 | } |
442 | if (G_filter.family == AF_PACKET) { |
443 | bb_error_msg_and_die("can't flush link addresses"); |
444 | } |
445 | } |
446 | |
447 | while (*argv) { |
448 | const smalluint key = index_in_strings(option, *argv); |
449 | if (key == 0) { /* to */ |
450 | NEXT_ARG(); |
451 | get_prefix(&G_filter.pfx, *argv, G_filter.family); |
452 | if (G_filter.family == AF_UNSPEC) { |
453 | G_filter.family = G_filter.pfx.family; |
454 | } |
455 | } else if (key == 1) { /* scope */ |
456 | uint32_t scope = 0; |
457 | NEXT_ARG(); |
458 | G_filter.scopemask = -1; |
459 | if (rtnl_rtscope_a2n(&scope, *argv)) { |
460 | if (strcmp(*argv, "all") != 0) { |
461 | invarg(*argv, "scope"); |
462 | } |
463 | scope = RT_SCOPE_NOWHERE; |
464 | G_filter.scopemask = 0; |
465 | } |
466 | G_filter.scope = scope; |
467 | } else if (key == 2) { /* up */ |
468 | G_filter.up = 1; |
469 | } else if (key == 3) { /* label */ |
470 | NEXT_ARG(); |
471 | G_filter.label = *argv; |
472 | } else { |
473 | if (key == 4) /* dev */ |
474 | NEXT_ARG(); |
475 | if (filter_dev) |
476 | duparg2("dev", *argv); |
477 | filter_dev = *argv; |
478 | } |
479 | argv++; |
480 | } |
481 | |
482 | xrtnl_open(&rth); |
483 | |
484 | xrtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK); |
485 | xrtnl_dump_filter(&rth, store_nlmsg, &linfo); |
486 | |
487 | if (filter_dev) { |
488 | G_filter.ifindex = xll_name_to_index(filter_dev); |
489 | } |
490 | |
491 | if (flush) { |
492 | char flushb[4096-512]; |
493 | |
494 | G_filter.flushb = flushb; |
495 | G_filter.flushp = 0; |
496 | G_filter.flushe = sizeof(flushb); |
497 | G_filter.rth = &rth; |
498 | |
499 | for (;;) { |
500 | xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETADDR); |
501 | G_filter.flushed = 0; |
502 | xrtnl_dump_filter(&rth, print_addrinfo, NULL); |
503 | if (G_filter.flushed == 0) { |
504 | return 0; |
505 | } |
506 | if (flush_update() < 0) { |
507 | return 1; |
508 | } |
509 | } |
510 | } |
511 | |
512 | if (G_filter.family != AF_PACKET) { |
513 | xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETADDR); |
514 | xrtnl_dump_filter(&rth, store_nlmsg, &ainfo); |
515 | } |
516 | |
517 | |
518 | if (G_filter.family && G_filter.family != AF_PACKET) { |
519 | struct nlmsg_list **lp; |
520 | lp = &linfo; |
521 | |
522 | if (G_filter.oneline) |
523 | no_link = 1; |
524 | |
525 | while ((l = *lp) != NULL) { |
526 | int ok = 0; |
527 | struct ifinfomsg *ifi = NLMSG_DATA(&l->h); |
528 | struct nlmsg_list *a; |
529 | |
530 | for (a = ainfo; a; a = a->next) { |
531 | struct nlmsghdr *n = &a->h; |
532 | struct ifaddrmsg *ifa = NLMSG_DATA(n); |
533 | |
534 | if ((int) ifa->ifa_index != (int) ifi->ifi_index |
535 | || (G_filter.family && G_filter.family != (family_t) ifa->ifa_family) |
536 | ) { |
537 | continue; |
538 | } |
539 | if ((G_filter.scope ^ ifa->ifa_scope) & G_filter.scopemask) |
540 | continue; |
541 | if ((G_filter.flags ^ ifa->ifa_flags) & G_filter.flagmask) |
542 | continue; |
543 | if (G_filter.pfx.family || G_filter.label) { |
544 | struct rtattr *tb[IFA_MAX+1]; |
545 | memset(tb, 0, sizeof(tb)); |
546 | parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); |
547 | if (!tb[IFA_LOCAL]) |
548 | tb[IFA_LOCAL] = tb[IFA_ADDRESS]; |
549 | |
550 | if (G_filter.pfx.family && tb[IFA_LOCAL]) { |
551 | inet_prefix dst; |
552 | memset(&dst, 0, sizeof(dst)); |
553 | dst.family = ifa->ifa_family; |
554 | memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); |
555 | if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen)) |
556 | continue; |
557 | } |
558 | if (G_filter.label) { |
559 | SPRINT_BUF(b1); |
560 | const char *label; |
561 | if (tb[IFA_LABEL]) |
562 | label = RTA_DATA(tb[IFA_LABEL]); |
563 | else |
564 | label = ll_idx_n2a(ifa->ifa_index, b1); |
565 | if (fnmatch(G_filter.label, label, 0) != 0) |
566 | continue; |
567 | } |
568 | } |
569 | |
570 | ok = 1; |
571 | break; |
572 | } |
573 | if (!ok) |
574 | *lp = l->next; |
575 | else |
576 | lp = &l->next; |
577 | } |
578 | } |
579 | |
580 | for (l = linfo; l; l = l->next) { |
581 | if (no_link || print_linkinfo(&l->h) == 0) { |
582 | struct ifinfomsg *ifi = NLMSG_DATA(&l->h); |
583 | if (G_filter.family != AF_PACKET) |
584 | print_selected_addrinfo(ifi->ifi_index, ainfo); |
585 | } |
586 | } |
587 | |
588 | return 0; |
589 | } |
590 | |
591 | static int default_scope(inet_prefix *lcl) |
592 | { |
593 | if (lcl->family == AF_INET) { |
594 | if (lcl->bytelen >= 1 && *(uint8_t*)&lcl->data == 127) |
595 | return RT_SCOPE_HOST; |
596 | } |
597 | return 0; |
598 | } |
599 | |
600 | /* Return value becomes exitcode. It's okay to not return at all */ |
601 | static int ipaddr_modify(int cmd, char **argv) |
602 | { |
603 | static const char option[] ALIGN1 = |
604 | "peer\0""remote\0""broadcast\0""brd\0" |
605 | "anycast\0""scope\0""dev\0""label\0""local\0"; |
606 | struct rtnl_handle rth; |
607 | struct { |
608 | struct nlmsghdr n; |
609 | struct ifaddrmsg ifa; |
610 | char buf[256]; |
611 | } req; |
612 | char *d = NULL; |
613 | char *l = NULL; |
614 | inet_prefix lcl; |
615 | inet_prefix peer; |
616 | int local_len = 0; |
617 | int peer_len = 0; |
618 | int brd_len = 0; |
619 | int any_len = 0; |
620 | bool scoped = 0; |
621 | |
622 | memset(&req, 0, sizeof(req)); |
623 | |
624 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); |
625 | req.n.nlmsg_flags = NLM_F_REQUEST; |
626 | req.n.nlmsg_type = cmd; |
627 | req.ifa.ifa_family = preferred_family; |
628 | |
629 | while (*argv) { |
630 | unsigned arg = index_in_strings(option, *argv); |
631 | /* if search fails, "local" is assumed */ |
632 | if ((int)arg >= 0) |
633 | NEXT_ARG(); |
634 | |
635 | if (arg <= 1) { /* peer, remote */ |
636 | if (peer_len) { |
637 | duparg("peer", *argv); |
638 | } |
639 | get_prefix(&peer, *argv, req.ifa.ifa_family); |
640 | peer_len = peer.bytelen; |
641 | if (req.ifa.ifa_family == AF_UNSPEC) { |
642 | req.ifa.ifa_family = peer.family; |
643 | } |
644 | addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); |
645 | req.ifa.ifa_prefixlen = peer.bitlen; |
646 | } else if (arg <= 3) { /* broadcast, brd */ |
647 | inet_prefix addr; |
648 | if (brd_len) { |
649 | duparg("broadcast", *argv); |
650 | } |
651 | if (LONE_CHAR(*argv, '+')) { |
652 | brd_len = -1; |
653 | } else if (LONE_DASH(*argv)) { |
654 | brd_len = -2; |
655 | } else { |
656 | get_addr(&addr, *argv, req.ifa.ifa_family); |
657 | if (req.ifa.ifa_family == AF_UNSPEC) |
658 | req.ifa.ifa_family = addr.family; |
659 | addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); |
660 | brd_len = addr.bytelen; |
661 | } |
662 | } else if (arg == 4) { /* anycast */ |
663 | inet_prefix addr; |
664 | if (any_len) { |
665 | duparg("anycast", *argv); |
666 | } |
667 | get_addr(&addr, *argv, req.ifa.ifa_family); |
668 | if (req.ifa.ifa_family == AF_UNSPEC) { |
669 | req.ifa.ifa_family = addr.family; |
670 | } |
671 | addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); |
672 | any_len = addr.bytelen; |
673 | } else if (arg == 5) { /* scope */ |
674 | uint32_t scope = 0; |
675 | if (rtnl_rtscope_a2n(&scope, *argv)) { |
676 | invarg(*argv, "scope"); |
677 | } |
678 | req.ifa.ifa_scope = scope; |
679 | scoped = 1; |
680 | } else if (arg == 6) { /* dev */ |
681 | d = *argv; |
682 | } else if (arg == 7) { /* label */ |
683 | l = *argv; |
684 | addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l) + 1); |
685 | } else { |
686 | /* local (specified or assumed) */ |
687 | if (local_len) { |
688 | duparg2("local", *argv); |
689 | } |
690 | get_prefix(&lcl, *argv, req.ifa.ifa_family); |
691 | if (req.ifa.ifa_family == AF_UNSPEC) { |
692 | req.ifa.ifa_family = lcl.family; |
693 | } |
694 | addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); |
695 | local_len = lcl.bytelen; |
696 | } |
697 | argv++; |
698 | } |
699 | |
700 | if (!d) { |
701 | /* There was no "dev IFACE", but we need that */ |
702 | bb_error_msg_and_die("need \"dev IFACE\""); |
703 | } |
704 | if (l && strncmp(d, l, strlen(d)) != 0) { |
705 | bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l); |
706 | } |
707 | |
708 | if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { |
709 | peer = lcl; |
710 | addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); |
711 | } |
712 | if (req.ifa.ifa_prefixlen == 0) |
713 | req.ifa.ifa_prefixlen = lcl.bitlen; |
714 | |
715 | if (brd_len < 0 && cmd != RTM_DELADDR) { |
716 | inet_prefix brd; |
717 | int i; |
718 | if (req.ifa.ifa_family != AF_INET) { |
719 | bb_error_msg_and_die("broadcast can be set only for IPv4 addresses"); |
720 | } |
721 | brd = peer; |
722 | if (brd.bitlen <= 30) { |
723 | for (i = 31; i >= brd.bitlen; i--) { |
724 | if (brd_len == -1) |
725 | brd.data[0] |= htonl(1<<(31-i)); |
726 | else |
727 | brd.data[0] &= ~htonl(1<<(31-i)); |
728 | } |
729 | addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); |
730 | brd_len = brd.bytelen; |
731 | } |
732 | } |
733 | if (!scoped && cmd != RTM_DELADDR) |
734 | req.ifa.ifa_scope = default_scope(&lcl); |
735 | |
736 | xrtnl_open(&rth); |
737 | |
738 | ll_init_map(&rth); |
739 | |
740 | req.ifa.ifa_index = xll_name_to_index(d); |
741 | |
742 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) |
743 | return 2; |
744 | |
745 | return 0; |
746 | } |
747 | |
748 | /* Return value becomes exitcode. It's okay to not return at all */ |
749 | int FAST_FUNC do_ipaddr(char **argv) |
750 | { |
751 | static const char commands[] ALIGN1 = |
752 | "add\0""delete\0""list\0""show\0""lst\0""flush\0"; |
753 | int cmd = 2; |
754 | if (*argv) { |
755 | cmd = index_in_substrings(commands, *argv); |
756 | if (cmd < 0) |
757 | invarg(*argv, applet_name); |
758 | argv++; |
759 | if (cmd <= 1) |
760 | return ipaddr_modify((cmd == 0) ? RTM_NEWADDR : RTM_DELADDR, argv); |
761 | } |
762 | /* 2 == list, 3 == show, 4 == lst */ |
763 | return ipaddr_list_or_flush(argv, cmd == 5); |
764 | } |
765 |