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 
print_link_flags(unsigned flags,unsigned mdown)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 
print_queuelen(char * name)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 
print_linkinfo(const struct nlmsghdr * n)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 
flush_update(void)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 
print_addrinfo(const struct sockaddr_nl * who UNUSED_PARAM,struct nlmsghdr * n,void * arg UNUSED_PARAM)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 != 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 > 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 
print_selected_addrinfo(int ifindex,struct nlmsg_list * ainfo)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 (ifa->ifa_index != ifindex
379 		 || (G_filter.family && G_filter.family != ifa->ifa_family)
380 		) {
381 			continue;
382 		}
383 		print_addrinfo(NULL, n, NULL);
384 	}
385 	return 0;
386 }
387 
388 
store_nlmsg(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)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 
ipaddr_reset_filter(int _oneline)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 */
ipaddr_list_or_flush(char ** argv,int flush)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 (ifa->ifa_index != ifi->ifi_index
529 				 || (G_filter.family && G_filter.family != 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 
default_scope(inet_prefix * lcl)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 */
ipaddr_modify(int cmd,int flags,char ** argv)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 */
do_ipaddr(char ** argv)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