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